From 8928cb8816cad59411defe104e3fb3408ce95e5e Mon Sep 17 00:00:00 2001 From: holger krekel Date: Thu, 7 Mar 2024 12:11:23 +0100 Subject: [PATCH] test the protocol --- chatmaild/src/chatmaild/metadata.py | 47 ++++++++-------- .../src/chatmaild/tests/test_metadata.py | 55 +++++++++++++++++-- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/chatmaild/src/chatmaild/metadata.py b/chatmaild/src/chatmaild/metadata.py index 6420943d..83e2f4ef 100644 --- a/chatmaild/src/chatmaild/metadata.py +++ b/chatmaild/src/chatmaild/metadata.py @@ -4,7 +4,7 @@ from socketserver import ( StreamRequestHandler, ThreadingMixIn, ) -from .config import read_config, Config +from .config import read_config import sys import logging import os @@ -18,39 +18,29 @@ DICTPROXY_COMMIT_TRANSACTION_CHAR = "C" DICTPROXY_TRANSACTION_CHARS = "SBC" -def handle_dovecot_protocol(rfile, wfile, tokens, requests_session, config: Config): +def handle_dovecot_protocol(rfile, wfile, tokens, notify_guid): # HELLO message, ignored. msg = rfile.readline().strip().decode() - def post_with_token(token): - response = requests_session.post( - "https://notifications.delta.chat/notify", - data=token, - timeout=60, - ) - if response.status_code == 410: - # 410 Gone status code - # means the token is no longer valid. - del tokens[guid] - transactions = {} while True: msg = rfile.readline().strip().decode() if not msg: break - res = handle_dovecot_request(msg, transactions, tokens, post_with_token) + res = handle_dovecot_request(msg, transactions, tokens, notify_guid) if res: wfile.write(res.encode("ascii")) wfile.flush() -def handle_dovecot_request(msg, transactions, tokens, post_with_token): +def handle_dovecot_request(msg, transactions, tokens, notify_guid): # see https://doc.dovecot.org/3.0/developer_manual/design/dict_protocol/ + print("got", msg) short_command = msg[0] parts = msg[1:].split("\t") if short_command == DICTPROXY_LOOKUP_CHAR: - return b"N\n" + return "N\n" if short_command not in (DICTPROXY_TRANSACTION_CHARS): return @@ -58,10 +48,10 @@ def handle_dovecot_request(msg, transactions, tokens, post_with_token): transaction_id = parts[0] if short_command == DICTPROXY_BEGIN_TRANSACTION_CHAR: - transactions[transaction_id] = b"O\n" + transactions[transaction_id] = "O\n" elif short_command == DICTPROXY_COMMIT_TRANSACTION_CHAR: # returns whether it failed or succeeded. - return transactions.pop(transaction_id, b"N\n") + return transactions.pop(transaction_id, "N\n") elif short_command == DICTPROXY_SET_CHAR: # See header of # @@ -81,12 +71,10 @@ def handle_dovecot_request(msg, transactions, tokens, post_with_token): tokens[keyname[1]] = value elif keyname[0] == "priv" and keyname[2] == "messagenew": guid = keyname[1] - token = tokens.get(guid) - if token: - post_with_token(token) + notify_guid(guid) else: # Transaction failed. - transactions[transaction_id] = b"F\n" + transactions[transaction_id] = "F\n" class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer): @@ -102,11 +90,24 @@ def main(): tokens = {} requests_session = requests.Session() + def notify_guid(guid): + token = tokens.get(guid) + if token: + response = requests_session.post( + "https://notifications.delta.chat/notify", + data=tokens[guid], + timeout=60, + ) + if response.status_code == 410: + # 410 Gone status code + # means the token is no longer valid. + del tokens[guid] + class Handler(StreamRequestHandler): def handle(self): try: handle_dovecot_protocol( - self.rfile, self.wfile, tokens, requests_session, config + self.rfile, self.wfile, tokens, requests_session ) except Exception: logging.exception("Exception in the handler") diff --git a/chatmaild/src/chatmaild/tests/test_metadata.py b/chatmaild/src/chatmaild/tests/test_metadata.py index 3173563c..5a7dce93 100644 --- a/chatmaild/src/chatmaild/tests/test_metadata.py +++ b/chatmaild/src/chatmaild/tests/test_metadata.py @@ -1,15 +1,23 @@ +import io + from chatmaild.metadata import ( handle_dovecot_request, + handle_dovecot_protocol, ) +def test_handle_dovecot_request_lookup_fails(): + res = handle_dovecot_request("Lpriv/123/chatmail", {}, {}, None) + assert res == "N\n" + + def test_handle_dovecot_request_happy_path(): tokens = {} transactions = {} # lookups return the same NOTFOUND result res = handle_dovecot_request("Lpriv/123/chatmail", transactions, tokens, None) - assert res == b"N\n" + assert res == "N\n" assert not tokens and not transactions # set device token in a transaction @@ -17,7 +25,7 @@ def test_handle_dovecot_request_happy_path(): msg = f"B{tx}\tuser" res = handle_dovecot_request(msg, transactions, tokens, None) assert not res and not tokens - assert transactions == {tx: b"O\n"} + assert transactions == {tx: "O\n"} msg = f"S{tx}\tpriv/guid00/devicetoken\t01234" res = handle_dovecot_request(msg, transactions, tokens, None) @@ -27,7 +35,7 @@ def test_handle_dovecot_request_happy_path(): msg = f"C{tx}" res = handle_dovecot_request(msg, transactions, tokens, None) - assert res == b"O\n" + assert res == "O\n" assert len(transactions) == 0 assert tokens["guid00"] == "01234" @@ -36,6 +44,43 @@ def test_handle_dovecot_request_happy_path(): msg = f"S{tx}\tpriv/guid00/messagenew" requests = [] assert handle_dovecot_request(msg, transactions, tokens, requests.append) is None - assert requests == ["01234"] - assert handle_dovecot_request(f"C{tx}\tuser", transactions, tokens, None) == b"O\n" + assert requests == ["guid00"] + assert handle_dovecot_request(f"C{tx}\tuser", transactions, tokens, None) == "O\n" assert not transactions + + +def test_handle_dovecot_protocol_set_devicetoken(): + rfile = io.BytesIO( + b"\n".join( + [ + b"HELLO", + b"Btx00\tuser", + b"Stx00\tpriv/guid00/devicetoken\t01234", + b"Ctx00", + ] + ) + ) + tokens = {} + wfile = io.BytesIO() + handle_dovecot_protocol(rfile, wfile, tokens, None) + assert tokens["guid00"] == "01234" + assert wfile.getvalue() == b"O\n" + + +def test_handle_dovecot_protocol_messagenew(): + rfile = io.BytesIO( + b"\n".join( + [ + b"HELLO", + b"Btx01\tuser", + b"Stx01\tpriv/guid00/messagenew", + b"Ctx01", + ] + ) + ) + tokens = {"guid00": "01234"} + wfile = io.BytesIO() + requests = [] + handle_dovecot_protocol(rfile, wfile, tokens, requests.append) + assert wfile.getvalue() == b"O\n" + assert requests == ["guid00"]