mirror of
https://github.com/Sneed-Group/pypush-plus-plus
synced 2025-01-09 17:33:47 +00:00
start redoing demo
This commit is contained in:
parent
524e4b1a35
commit
12cd7ac2cd
4 changed files with 43 additions and 31 deletions
11
apns.py
11
apns.py
|
@ -109,6 +109,8 @@ class APNSConnection:
|
||||||
await self._queue_park.wait() # Wait for a new payload to be added to the queue
|
await self._queue_park.wait() # Wait for a new payload to be added to the queue
|
||||||
logger.debug(f"Woken by event, checking for {id}")
|
logger.debug(f"Woken by event, checking for {id}")
|
||||||
# Check if the new payload matches the id
|
# Check if the new payload matches the id
|
||||||
|
if len(self._incoming_queue) == 0:
|
||||||
|
continue # all payloads have been removed by someone else
|
||||||
if self._incoming_queue[-1].id != id:
|
if self._incoming_queue[-1].id != id:
|
||||||
continue
|
continue
|
||||||
if filter is not None:
|
if filter is not None:
|
||||||
|
@ -294,11 +296,16 @@ class APNSConnection:
|
||||||
|
|
||||||
# TODO: Check ACK code
|
# TODO: Check ACK code
|
||||||
|
|
||||||
async def expect_notification(self, topic: str, filter: Callable | None = None):
|
async def expect_notification(self, topics: str | list[str], filter: Callable | None = None):
|
||||||
"""Waits for a notification to be received, and acks it"""
|
"""Waits for a notification to be received, and acks it"""
|
||||||
|
|
||||||
|
if isinstance(topics, list):
|
||||||
|
topic_hashes = [sha1(topic.encode()).digest() for topic in topics]
|
||||||
|
else:
|
||||||
|
topic_hashes = [sha1(topics.encode()).digest()]
|
||||||
|
|
||||||
def f(payload: APNSPayload):
|
def f(payload: APNSPayload):
|
||||||
if payload.fields_with_id(2)[0].value != sha1(topic.encode()).digest():
|
if payload.fields_with_id(2)[0].value not in topic_hashes:
|
||||||
return False
|
return False
|
||||||
if filter is not None:
|
if filter is not None:
|
||||||
return filter(payload)
|
return filter(payload)
|
||||||
|
|
27
demo.py
27
demo.py
|
@ -13,6 +13,8 @@ import apns
|
||||||
import ids
|
import ids
|
||||||
import imessage
|
import imessage
|
||||||
|
|
||||||
|
import trio
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.NOTSET, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()]
|
level=logging.NOTSET, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()]
|
||||||
)
|
)
|
||||||
|
@ -58,13 +60,6 @@ def safe_b64decode(s):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
# Try and load config.json
|
|
||||||
try:
|
|
||||||
with open("config.json", "r") as f:
|
|
||||||
CONFIG = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
CONFIG = {}
|
|
||||||
|
|
||||||
token = CONFIG.get("push", {}).get("token")
|
token = CONFIG.get("push", {}).get("token")
|
||||||
if token is not None:
|
if token is not None:
|
||||||
token = b64decode(token)
|
token = b64decode(token)
|
||||||
|
@ -138,9 +133,21 @@ async def main():
|
||||||
im = imessage.iMessageUser(conn, user)
|
im = imessage.iMessageUser(conn, user)
|
||||||
|
|
||||||
# Send a message to myself
|
# Send a message to myself
|
||||||
await im.send(imessage.iMessage.create(im, "Hello, world!", [user.current_handle]))
|
async with trio.open_nursery() as nursery:
|
||||||
print(await im.receive())
|
nursery.start_soon(input_task, im)
|
||||||
|
nursery.start_soon(output_task, im)
|
||||||
|
|
||||||
|
async def input_task(im: imessage.iMessageUser):
|
||||||
|
while True:
|
||||||
|
cmd = await trio.to_thread.run_sync(input, "> ", cancellable=True)
|
||||||
|
if cmd != "":
|
||||||
|
await im.send(imessage.iMessage.create(im, cmd, [im.user.current_handle]))
|
||||||
|
|
||||||
|
async def output_task(im: imessage.iMessageUser):
|
||||||
|
while True:
|
||||||
|
msg = await im.receive()
|
||||||
|
print(str(msg))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import trio
|
|
||||||
trio.run(main)
|
trio.run(main)
|
|
@ -21,7 +21,7 @@ logging.basicConfig(
|
||||||
)
|
)
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
apns.COURIER_HOST = "windows.courier.push.apple.com"
|
apns.COURIER_HOST = "windows.courier.push.apple.com" # Use windows courier so that /etc/hosts override doesn't affect it
|
||||||
|
|
||||||
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
||||||
context.set_alpn_protocols(["apns-security-v3"])
|
context.set_alpn_protocols(["apns-security-v3"])
|
||||||
|
@ -72,8 +72,8 @@ class APNSProxy:
|
||||||
logging.info(f"<- {payload}")
|
logging.info(f"<- {payload}")
|
||||||
|
|
||||||
def tamper(self, payload: apns.APNSPayload, to_server) -> apns.APNSPayload:
|
def tamper(self, payload: apns.APNSPayload, to_server) -> apns.APNSPayload:
|
||||||
if not to_server:
|
#if not to_server:
|
||||||
payload = self.tamper_lookup_keys(payload)
|
# payload = self.tamper_lookup_keys(payload)
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
30
imessage.py
30
imessage.py
|
@ -261,7 +261,8 @@ class iMessage(Message):
|
||||||
"""Creates a basic outgoing `iMessage` from the given text and participants"""
|
"""Creates a basic outgoing `iMessage` from the given text and participants"""
|
||||||
|
|
||||||
sender = user.user.current_handle
|
sender = user.user.current_handle
|
||||||
participants += [sender]
|
if sender not in participants:
|
||||||
|
participants += [sender]
|
||||||
|
|
||||||
return iMessage(
|
return iMessage(
|
||||||
text=text,
|
text=text,
|
||||||
|
@ -492,18 +493,12 @@ class iMessageUser:
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def receive(self) -> Message | None:
|
async def receive(self) -> Message:
|
||||||
"""
|
"""
|
||||||
Will return the next iMessage in the queue, or None if there are no messages
|
Will return the next iMessage in the queue, or None if there are no messages
|
||||||
"""
|
"""
|
||||||
for type, (topic, cls) in MESSAGE_TYPES.items():
|
body = await self._receive_raw([t for t, _ in MESSAGE_TYPES.items()], [t[0] for _, t in MESSAGE_TYPES.items()])
|
||||||
body = await self._receive_raw(type, topic)
|
t = MESSAGE_TYPES[body["c"]][1]
|
||||||
if body is not None:
|
|
||||||
t = cls
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
if not await self._verify_payload(body["P"], body["sP"], body["t"]):
|
if not await self._verify_payload(body["P"], body["sP"], body["t"]):
|
||||||
raise Exception("Failed to verify payload")
|
raise Exception("Failed to verify payload")
|
||||||
|
@ -516,7 +511,7 @@ class iMessageUser:
|
||||||
return t.from_raw(decrypted, body["sP"])
|
return t.from_raw(decrypted, body["sP"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to parse message : {e}")
|
logger.error(f"Failed to parse message : {e}")
|
||||||
return None
|
return Message(text="Failed to parse message", sender="System", participants=[], id=uuid.uuid4(), _raw=body)
|
||||||
|
|
||||||
KEY_CACHE_HANDLE: str = ""
|
KEY_CACHE_HANDLE: str = ""
|
||||||
KEY_CACHE: dict[bytes, dict[str, tuple[bytes, bytes]]] = {}
|
KEY_CACHE: dict[bytes, dict[str, tuple[bytes, bytes]]] = {}
|
||||||
|
@ -545,8 +540,7 @@ class iMessageUser:
|
||||||
logger.warning(f"Participant {key} has no identities, this is probably not a real account")
|
logger.warning(f"Participant {key} has no identities, this is probably not a real account")
|
||||||
|
|
||||||
for key, participant in lookup.items():
|
for key, participant in lookup.items():
|
||||||
if not key in self.USER_CACHE:
|
self.USER_CACHE[key] = [] # Clear so that we don't keep appending multiple times
|
||||||
self.USER_CACHE[key] = []
|
|
||||||
|
|
||||||
for identity in participant["identities"]:
|
for identity in participant["identities"]:
|
||||||
if not "client-data" in identity:
|
if not "client-data" in identity:
|
||||||
|
@ -626,18 +620,22 @@ class iMessageUser:
|
||||||
|
|
||||||
await self.connection.send_notification(topic, body, message_id)
|
await self.connection.send_notification(topic, body, message_id)
|
||||||
|
|
||||||
async def _receive_raw(self, c: int, topic: str) -> dict:
|
async def _receive_raw(self, c: int | list[int], topics: str | list[str]) -> dict:
|
||||||
def check(payload: apns.APNSPayload):
|
def check(payload: apns.APNSPayload):
|
||||||
# Check if the "c" key matches
|
# Check if the "c" key matches
|
||||||
body = payload.fields_with_id(3)[0].value
|
body = payload.fields_with_id(3)[0].value
|
||||||
if body is None:
|
if body is None:
|
||||||
return False
|
return False
|
||||||
body = plistlib.loads(body)
|
body = plistlib.loads(body)
|
||||||
if not "c" in body or body["c"] != c:
|
if not "c" in body:
|
||||||
|
return False
|
||||||
|
if isinstance(c, int) and body["c"] != c:
|
||||||
|
return False
|
||||||
|
elif isinstance(c, list) and body["c"] not in c:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
payload = await self.connection.expect_notification(topic, check)
|
payload = await self.connection.expect_notification(topics, check)
|
||||||
|
|
||||||
body = payload.fields_with_id(3)[0].value
|
body = payload.fields_with_id(3)[0].value
|
||||||
body = plistlib.loads(body)
|
body = plistlib.loads(body)
|
||||||
|
|
Loading…
Reference in a new issue