From 127373fdcc91c5a4403e25444526822e59083a0d Mon Sep 17 00:00:00 2001 From: Igor Artemenko Date: Tue, 6 Dec 2022 12:38:38 +0000 Subject: [PATCH 01/12] Fix typo --- pantalaimon/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantalaimon/client.py b/pantalaimon/client.py index 5b4ce05..be32859 100644 --- a/pantalaimon/client.py +++ b/pantalaimon/client.py @@ -135,7 +135,7 @@ class InvalidLimit(Exception): class SqliteQStore(SqliteStore): def _create_database(self): return SqliteQueueDatabase( - self.database_path, pragmas=(("foregign_keys", 1), ("secure_delete", 1)) + self.database_path, pragmas=(("foreign_keys", 1), ("secure_delete", 1)) ) def close(self): From 807deb94ee9e39bb570a904960d750e8d0dd5086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerber=20L=C3=B3r=C3=A1nt=20Viktor?= Date: Fri, 3 Feb 2023 05:45:50 +0100 Subject: [PATCH 02/12] fix media --- pantalaimon/daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantalaimon/daemon.py b/pantalaimon/daemon.py index 3264cab..c5e4ac4 100755 --- a/pantalaimon/daemon.py +++ b/pantalaimon/daemon.py @@ -1273,7 +1273,7 @@ class ProxyDaemon: client = next(iter(self.pan_clients.values())) try: - response = await client.download(server_name, media_id, file_name) + response = await client.download(server_name=server_name, media_id=media_id, filename=file_name) except ClientConnectionError as e: raise e From 6638393042654b3f497ac1ea4b5f550b7024b490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 25 May 2023 19:57:25 +0200 Subject: [PATCH 03/12] Fix tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c3a2630..d90feda 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ envlist = coverage deps = -rtest-requirements.txt install_command = pip install {opts} {packages} -passenv = TOXENV CI +passenv = TOXENV,CI commands = pytest [testenv:coverage] From 3968c69aa846889970df1372ba9aa54c1c5e4290 Mon Sep 17 00:00:00 2001 From: Igor Artemenko Date: Wed, 5 Apr 2023 16:17:04 +0000 Subject: [PATCH 04/12] Set Pantalaimon presence to offline Before this change, Pantalaimon users would always appear online because that is the default state when the /sync endpoint's set_presence parameter is not set. By explicitly setting the parameter to "offline", only the user-facing client (which executes its own /sync request) affects the presence state. --- pantalaimon/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pantalaimon/client.py b/pantalaimon/client.py index be32859..ececee3 100644 --- a/pantalaimon/client.py +++ b/pantalaimon/client.py @@ -554,6 +554,7 @@ class PanClient(AsyncClient): full_state=True, since=next_batch, loop_sleep_time=loop_sleep_time, + set_presence="offline", ) ) self.task = task From 8b2a1173fd228e536024ecda49d36671ff16de70 Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Tue, 1 Oct 2024 18:50:43 -0700 Subject: [PATCH 05/12] update README with new setup instructions --- README.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47916d8..0573108 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ specifies one or more homeservers for pantalaimon to connect to. A minimal pantalaimon configuration looks like this: ```dosini [local-matrix] -Homeserver = https://localhost:8448 +Homeserver = https://localhost:443 ListenAddress = localhost ListenPort = 8009 ``` @@ -140,3 +140,40 @@ To control the daemon an interactive utility is provided in the form of `panctl` can be used to verify, blacklist or ignore devices, import or export session keys, or to introspect devices of users that we share encrypted rooms with. + +### Setup +This is all coming from an excellent comment that you can find [here](https://github.com/matrix-org/pantalaimon/issues/154#issuecomment-1951591191). + + + +1) Ensure you have an OS keyring installed. In my case I installed gnome-keyring. You may also want a GUI like seahorse to inspect the keyring. (pantalaimon will work without a keyring but your client will have to log in with the password every time pantalaimon is restarted, instead of being able to reuse the access token from the previous successful login.) + +2) In case you have prior attempts, clean the slate by deleting the ~/.local/share/pantalaimon directory. + +3) Start pantalaimon. + +4) Connect a client to the ListenAddress:ListenPort you specified in pantalaimon.conf, eg to 127.0.0.1:8009, using the same username and password you would've used to login to your homeserver directly. + +5) The login should succeed, but at this point all encrypted messages will fail to decrypt. This is fine. + +6) Start another client that you were already using for your encrypted chats previously. In my case this was app.element.io, so the rest of the steps here assume that. + +7) Run panctl. At the prompt, run start-verification . here is the full user ID like @arnavion:arnavion.dev. If you only have the one Element session, panctl will show you the device ID as an autocomplete hint so you don't have to look it up. If you do need to look it up, go to Element -> profile icon -> All Settings -> Sessions, expand the "Current session" item, and the "Session ID" is the device ID. + +8) In Element you will see a popup "Incoming Verification Request". Click "Continue". It will change to a popup containing some emojis, and panctl will print the same emojis. Click the "They match" button. It will now change to a popup like "Waiting for other client to confirm..." + +9) In panctl, run confirm-verification , ie the same command as before but with confirm-verification instead of start-verification. + +10) At this point, if you look at all your sessions in Element (profile icon -> All Settings -> Sessions), you should see "pantalaimon" in the "Other sessions" list as a "Verified" session. + +11) Export the E2E room keys that Element was using via profile icon -> Security & Privacy -> Export E2E room keys. Pick any password and then save the file to some path. + +12) Back in panctl, run import-keys . After a few seconds, in the output of pantalaimon, you should see a log like INFO: pantalaimon: Successfully imported keys for from + +13) Close and restart the client you had used in step 5, ie the one you want to connect to pantalaimon. Now, finally, you should be able to see the encrypted chats be decrypted. + +14) Delete the E2E room keys backup file from step 12. You don't need it any more. + +15) If in step 11 you had other unverified sessions from pantalaimon from your prior attempts, you can sign out of them too. + +You will probably have to repeat steps 12-15 any time you start a new encrypted chat in Element. From 634ac7ed68580ef8afafc02a82bf33e7c1c31259 Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Tue, 1 Oct 2024 19:14:39 -0700 Subject: [PATCH 06/12] chore: remove depreciated dependency pydbus for dasbus --- pantalaimon/panctl.py | 6 +++--- pantalaimon/ui.py | 26 +++++++++++++------------- setup.py | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pantalaimon/panctl.py b/pantalaimon/panctl.py index 1f97fe7..6519d8b 100644 --- a/pantalaimon/panctl.py +++ b/pantalaimon/panctl.py @@ -34,7 +34,7 @@ from prompt_toolkit import HTML, PromptSession, print_formatted_text from prompt_toolkit.completion import Completer, Completion, PathCompleter from prompt_toolkit.document import Document from prompt_toolkit.patch_stdout import patch_stdout -from pydbus import SessionBus +from dasbus.connection import SessionMessageBus PTK2 = ptk_version.startswith("2.") @@ -404,8 +404,8 @@ class PanCtl: commands = list(command_help.keys()) def __attrs_post_init__(self): - self.bus = SessionBus() - self.pan_bus = self.bus.get("org.pantalaimon1") + self.bus = SessionMessageBus() + self.pan_bus = self.bus.get_connection("org.pantalaimon1") self.ctl = self.pan_bus["org.pantalaimon1.control"] self.devices = self.pan_bus["org.pantalaimon1.devices"] diff --git a/pantalaimon/ui.py b/pantalaimon/ui.py index 813b67e..08e7e50 100644 --- a/pantalaimon/ui.py +++ b/pantalaimon/ui.py @@ -17,7 +17,7 @@ from importlib import util UI_ENABLED = ( util.find_spec("gi") is not None and util.find_spec("gi.repository") is not None - and util.find_spec("pydbus") is not None + and util.find_spec("dasbus") is not None ) if UI_ENABLED: @@ -28,8 +28,8 @@ if UI_ENABLED: import dbus import notify2 from gi.repository import GLib - from pydbus import SessionBus - from pydbus.generic import signal + from dasbus import SessionMessageBus + from dasbus.signal import Signal from dbus.mainloop.glib import DBusGMainLoop from nio import RoomKeyRequest, RoomKeyRequestCancellation @@ -123,8 +123,8 @@ if UI_ENABLED: """ - Response = signal() - UnverifiedDevices = signal() + Response = Signal() + UnverifiedDevices = Signal() def __init__(self, queue, server_list, id_counter): self.queue = queue @@ -297,13 +297,13 @@ if UI_ENABLED: """ - VerificationInvite = signal() - VerificationCancel = signal() - VerificationString = signal() - VerificationDone = signal() + VerificationInvite = Signal() + VerificationCancel = Signal() + VerificationString = Signal() + VerificationDone = Signal() - KeyRequest = signal() - KeyRequestCancel = signal() + KeyRequest = Signal() + KeyRequestCancel = Signal() def __init__(self, queue, id_counter): self.device_list = dict() @@ -466,8 +466,8 @@ if UI_ENABLED: self.control_if = Control(self.send_queue, self.server_list, id_counter) self.device_if = Devices(self.send_queue, id_counter) - self.bus = SessionBus() - self.bus.publish("org.pantalaimon1", self.control_if, self.device_if) + self.bus = SessionMessageBus() + self.bus.publish_object("org.pantalaimon1", self.control_if, self.device_if) def unverified_notification(self, message): notification = notify2.Notification( diff --git a/setup.py b/setup.py index 46798ba..75e0184 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup( "ui": [ "dbus-python >= 1.2, < 1.3", "PyGObject >= 3.36, < 3.39", - "pydbus >= 0.6, < 0.7", + "dasbus == 1.71", "notify2 >= 0.3, < 0.4", ] }, From 76dc74d250ee4d626da01c31725b46ae3ace5222 Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Tue, 1 Oct 2024 19:20:29 -0700 Subject: [PATCH 07/12] chore: Format code with ruff --- pantalaimon/client.py | 12 ++++---- pantalaimon/config.py | 1 - pantalaimon/daemon.py | 14 +++++---- pantalaimon/index.py | 1 - pantalaimon/store.py | 1 - setup.py | 13 +++++---- tests/conftest.py | 47 +++++++++++++----------------- tests/pan_client_test.py | 8 +++-- tests/proxy_test.py | 34 +++++++++------------- tests/store_test.py | 63 ++++++++++++++++++++++------------------ 10 files changed, 96 insertions(+), 98 deletions(-) diff --git a/pantalaimon/client.py b/pantalaimon/client.py index ececee3..882f6be 100644 --- a/pantalaimon/client.py +++ b/pantalaimon/client.py @@ -709,7 +709,6 @@ class PanClient(AsyncClient): for share in self.get_active_key_requests( message.user_id, message.device_id ): - continued = True if not self.continue_key_share(share): @@ -811,8 +810,9 @@ class PanClient(AsyncClient): if not isinstance(event, MegolmEvent): logger.warn( - "Encrypted event is not a megolm event:" - "\n{}".format(pformat(event_dict)) + "Encrypted event is not a megolm event:" "\n{}".format( + pformat(event_dict) + ) ) return False @@ -836,9 +836,9 @@ class PanClient(AsyncClient): decrypted_event.source["content"]["url"] = decrypted_event.url if decrypted_event.thumbnail_url: - decrypted_event.source["content"]["info"][ - "thumbnail_url" - ] = decrypted_event.thumbnail_url + decrypted_event.source["content"]["info"]["thumbnail_url"] = ( + decrypted_event.thumbnail_url + ) event_dict.update(decrypted_event.source) event_dict["decrypted"] = True diff --git a/pantalaimon/config.py b/pantalaimon/config.py index 8ee51d2..a5e59d1 100644 --- a/pantalaimon/config.py +++ b/pantalaimon/config.py @@ -186,7 +186,6 @@ class PanConfig: try: for section_name, section in config.items(): - if section_name == "Default": continue diff --git a/pantalaimon/daemon.py b/pantalaimon/daemon.py index c5e4ac4..2e48ac6 100755 --- a/pantalaimon/daemon.py +++ b/pantalaimon/daemon.py @@ -227,7 +227,8 @@ class ProxyDaemon: if ret: msg = ( - f"Device {device.id} of user " f"{device.user_id} successfully verified." + f"Device {device.id} of user " + f"{device.user_id} successfully verified." ) await client.send_update_device(device) else: @@ -309,7 +310,6 @@ class ProxyDaemon: DeviceUnblacklistMessage, ), ): - device = client.device_store[message.user_id].get(message.device_id, None) if not device: @@ -616,7 +616,9 @@ class ProxyDaemon: await pan_client.close() return - logger.info(f"Successfully started new background sync client for " f"{user_id}") + logger.info( + f"Successfully started new background sync client for " f"{user_id}" + ) await self.send_ui_message( UpdateUsersMessage(self.name, user_id, pan_client.device_id) @@ -733,7 +735,7 @@ class ProxyDaemon: return decryption_method(body, ignore_failures=False) except EncryptionError: logger.info("Error decrypting sync, waiting for next pan " "sync") - await client.synced.wait(), + (await client.synced.wait(),) logger.info("Pan synced, retrying decryption.") try: @@ -1273,7 +1275,9 @@ class ProxyDaemon: client = next(iter(self.pan_clients.values())) try: - response = await client.download(server_name=server_name, media_id=media_id, filename=file_name) + response = await client.download( + server_name=server_name, media_id=media_id, filename=file_name + ) except ClientConnectionError as e: raise e diff --git a/pantalaimon/index.py b/pantalaimon/index.py index 7350e58..e4f4aa0 100644 --- a/pantalaimon/index.py +++ b/pantalaimon/index.py @@ -230,7 +230,6 @@ if False: ) for message in query: - event = message.event event_dict = { diff --git a/pantalaimon/store.py b/pantalaimon/store.py index 60a36a4..ee6f377 100644 --- a/pantalaimon/store.py +++ b/pantalaimon/store.py @@ -431,7 +431,6 @@ class PanStore: device_store = defaultdict(dict) for d in account.device_keys: - if d.deleted: continue diff --git a/setup.py b/setup.py index 46798ba..5b5fe45 100644 --- a/setup.py +++ b/setup.py @@ -11,8 +11,7 @@ setup( url="https://github.com/matrix-org/pantalaimon", author="The Matrix.org Team", author_email="poljar@termina.org.uk", - description=("A Matrix proxy daemon that adds E2E encryption " - "capabilities."), + description=("A Matrix proxy daemon that adds E2E encryption " "capabilities."), long_description=long_description, long_description_content_type="text/markdown", license="Apache License, Version 2.0", @@ -29,7 +28,7 @@ setup( "cachetools >= 3.0.0", "prompt_toolkit > 2, < 4", "typing;python_version<'3.5'", - "matrix-nio[e2e] >= 0.20, < 0.21" + "matrix-nio[e2e] >= 0.20, < 0.21", ], extras_require={ "ui": [ @@ -40,8 +39,10 @@ setup( ] }, entry_points={ - "console_scripts": ["pantalaimon=pantalaimon.main:main", - "panctl=pantalaimon.panctl:main"], + "console_scripts": [ + "pantalaimon=pantalaimon.main:main", + "panctl=pantalaimon.panctl:main", + ], }, - zip_safe=False + zip_safe=False, ) diff --git a/tests/conftest.py b/tests/conftest.py index 6103f77..ceb8902 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -34,11 +34,9 @@ class Provider(BaseProvider): def client(self): return ClientInfo(faker.mx_id(), faker.access_token()) - def avatar_url(self): return "mxc://{}/{}#auto".format( - faker.hostname(), - "".join(choices(ascii_letters) for i in range(24)) + faker.hostname(), "".join(choices(ascii_letters) for i in range(24)) ) def olm_key_pair(self): @@ -56,7 +54,6 @@ class Provider(BaseProvider): ) - faker.add_provider(Provider) @@ -80,13 +77,7 @@ def tempdir(): @pytest.fixture def panstore(tempdir): for _ in range(10): - store = SqliteStore( - faker.mx_id(), - faker.device_id(), - tempdir, - "", - "pan.db" - ) + store = SqliteStore(faker.mx_id(), faker.device_id(), tempdir, "", "pan.db") account = OlmAccount() store.save_account(account) @@ -130,21 +121,23 @@ async def pan_proxy_server(tempdir, aiohttp_server): recv_queue=ui_queue.async_q, proxy=None, ssl=False, - client_store_class=SqliteStore + client_store_class=SqliteStore, ) - app.add_routes([ - web.post("/_matrix/client/r0/login", proxy.login), - web.get("/_matrix/client/r0/sync", proxy.sync), - web.get("/_matrix/client/r0/rooms/{room_id}/messages", proxy.messages), - web.put( - r"/_matrix/client/r0/rooms/{room_id}/send/{event_type}/{txnid}", - proxy.send_message - ), - web.post("/_matrix/client/r0/user/{user_id}/filter", proxy.filter), - web.post("/_matrix/client/r0/search", proxy.search), - web.options("/_matrix/client/r0/search", proxy.search_opts), - ]) + app.add_routes( + [ + web.post("/_matrix/client/r0/login", proxy.login), + web.get("/_matrix/client/r0/sync", proxy.sync), + web.get("/_matrix/client/r0/rooms/{room_id}/messages", proxy.messages), + web.put( + r"/_matrix/client/r0/rooms/{room_id}/send/{event_type}/{txnid}", + proxy.send_message, + ), + web.post("/_matrix/client/r0/user/{user_id}/filter", proxy.filter), + web.post("/_matrix/client/r0/search", proxy.search), + web.options("/_matrix/client/r0/search", proxy.search_opts), + ] + ) server = await aiohttp_server(app) @@ -161,7 +154,7 @@ async def running_proxy(pan_proxy_server, aioresponse, aiohttp_client): "access_token": "abc123", "device_id": "GHTYAJCE", "home_server": "example.org", - "user_id": "@example:example.org" + "user_id": "@example:example.org", } aioclient = await aiohttp_client(server) @@ -170,7 +163,7 @@ async def running_proxy(pan_proxy_server, aioresponse, aiohttp_client): "https://example.org/_matrix/client/r0/login", status=200, payload=login_response, - repeat=True + repeat=True, ) await aioclient.post( @@ -179,7 +172,7 @@ async def running_proxy(pan_proxy_server, aioresponse, aiohttp_client): "type": "m.login.password", "user": "example", "password": "wordpass", - } + }, ) yield server, aioclient, proxy, queues diff --git a/tests/pan_client_test.py b/tests/pan_client_test.py index 5932432..318d1b3 100644 --- a/tests/pan_client_test.py +++ b/tests/pan_client_test.py @@ -380,7 +380,9 @@ class TestClass(object): ) aioresponse.get( - sync_url, status=200, payload=self.initial_sync_response, + sync_url, + status=200, + payload=self.initial_sync_response, ) aioresponse.get(sync_url, status=200, payload=self.empty_sync, repeat=True) @@ -454,7 +456,9 @@ class TestClass(object): ) aioresponse.get( - sync_url, status=200, payload=self.initial_sync_response, + sync_url, + status=200, + payload=self.initial_sync_response, ) aioresponse.get(sync_url, status=200, payload=self.empty_sync, repeat=True) diff --git a/tests/proxy_test.py b/tests/proxy_test.py index b50379e..f0856d8 100644 --- a/tests/proxy_test.py +++ b/tests/proxy_test.py @@ -27,7 +27,7 @@ class TestClass(object): "access_token": "abc123", "device_id": "GHTYAJCE", "home_server": "example.org", - "user_id": "@example:example.org" + "user_id": "@example:example.org", } @property @@ -36,12 +36,7 @@ class TestClass(object): @property def keys_upload_response(self): - return { - "one_time_key_counts": { - "curve25519": 10, - "signed_curve25519": 20 - } - } + return {"one_time_key_counts": {"curve25519": 10, "signed_curve25519": 20}} @property def example_devices(self): @@ -52,10 +47,7 @@ class TestClass(object): devices[device.user_id][device.id] = device bob_device = OlmDevice( - BOB_ID, - BOB_DEVICE, - {"ed25519": BOB_ONETIME, - "curve25519": BOB_CURVE} + BOB_ID, BOB_DEVICE, {"ed25519": BOB_ONETIME, "curve25519": BOB_CURVE} ) devices[BOB_ID][BOB_DEVICE] = bob_device @@ -71,7 +63,7 @@ class TestClass(object): "https://example.org/_matrix/client/r0/login", status=200, payload=self.login_response, - repeat=True + repeat=True, ) assert not daemon.pan_clients @@ -82,7 +74,7 @@ class TestClass(object): "type": "m.login.password", "user": "example", "password": "wordpass", - } + }, ) assert resp.status == 200 @@ -105,11 +97,11 @@ class TestClass(object): "https://example.org/_matrix/client/r0/login", status=200, payload=self.login_response, - repeat=True + repeat=True, ) sync_url = re.compile( - r'^https://example\.org/_matrix/client/r0/sync\?access_token=.*' + r"^https://example\.org/_matrix/client/r0/sync\?access_token=.*" ) aioresponse.get( @@ -124,14 +116,16 @@ class TestClass(object): "type": "m.login.password", "user": "example", "password": "wordpass", - } + }, ) # Check that the pan client started to sync after logging in. pan_client = list(daemon.pan_clients.values())[0] assert len(pan_client.rooms) == 1 - async def test_pan_client_keys_upload(self, pan_proxy_server, aiohttp_client, aioresponse): + async def test_pan_client_keys_upload( + self, pan_proxy_server, aiohttp_client, aioresponse + ): server, daemon, _ = pan_proxy_server client = await aiohttp_client(server) @@ -140,11 +134,11 @@ class TestClass(object): "https://example.org/_matrix/client/r0/login", status=200, payload=self.login_response, - repeat=True + repeat=True, ) sync_url = re.compile( - r'^https://example\.org/_matrix/client/r0/sync\?access_token=.*' + r"^https://example\.org/_matrix/client/r0/sync\?access_token=.*" ) aioresponse.get( @@ -169,7 +163,7 @@ class TestClass(object): "type": "m.login.password", "user": "example", "password": "wordpass", - } + }, ) pan_client = list(daemon.pan_clients.values())[0] diff --git a/tests/store_test.py b/tests/store_test.py index 1d16e10..13acfb0 100644 --- a/tests/store_test.py +++ b/tests/store_test.py @@ -27,7 +27,7 @@ class TestClass(object): "type": "m.room.message", "unsigned": {"age": 43289803095}, "user_id": "@example2:localhost", - "age": 43289803095 + "age": 43289803095, } ) @@ -43,39 +43,41 @@ class TestClass(object): "type": "m.room.message", "unsigned": {"age": 43289803095}, "user_id": "@example2:localhost", - "age": 43289803095 + "age": 43289803095, } ) @property def encrypted_media_event(self): - return RoomEncryptedMedia.from_dict({ - "room_id": "!testroom:localhost", - "event_id": "$15163622445EBvZK:localhost", - "origin_server_ts": 1516362244030, - "sender": "@example2:localhost", - "type": "m.room.message", - "content": { - "body": "orange_cat.jpg", - "msgtype": "m.image", - "file": { - "v": "v2", - "key": { - "alg": "A256CTR", - "ext": True, - "k": "yx0QvkgYlasdWEsdalkejaHBzCkKEBAp3tB7dGtWgrs", - "key_ops": ["encrypt", "decrypt"], - "kty": "oct" + return RoomEncryptedMedia.from_dict( + { + "room_id": "!testroom:localhost", + "event_id": "$15163622445EBvZK:localhost", + "origin_server_ts": 1516362244030, + "sender": "@example2:localhost", + "type": "m.room.message", + "content": { + "body": "orange_cat.jpg", + "msgtype": "m.image", + "file": { + "v": "v2", + "key": { + "alg": "A256CTR", + "ext": True, + "k": "yx0QvkgYlasdWEsdalkejaHBzCkKEBAp3tB7dGtWgrs", + "key_ops": ["encrypt", "decrypt"], + "kty": "oct", + }, + "iv": "0pglXX7fspIBBBBAEERLFd", + "hashes": { + "sha256": "eXRDFvh+aXsQRj8a+5ZVVWUQ9Y6u9DYiz4tq1NvbLu8" + }, + "url": "mxc://localhost/maDtasSiPFjROFMnlwxIhhyW", + "mimetype": "image/jpeg", }, - "iv": "0pglXX7fspIBBBBAEERLFd", - "hashes": { - "sha256": "eXRDFvh+aXsQRj8a+5ZVVWUQ9Y6u9DYiz4tq1NvbLu8" - }, - "url": "mxc://localhost/maDtasSiPFjROFMnlwxIhhyW", - "mimetype": "image/jpeg" - } + }, } - }) + ) def test_account_loading(self, panstore): accounts = panstore.load_all_users() @@ -131,6 +133,7 @@ class TestClass(object): pytest.skip("Indexing needs to be enabled to test this") from pantalaimon.index import Index, IndexStore + loop = asyncio.get_event_loop() store = IndexStore("example", tempdir) @@ -148,8 +151,10 @@ class TestClass(object): assert len(result["results"]) == 1 assert result["count"] == 1 assert result["results"][0]["result"] == self.test_event.source - assert (result["results"][0]["context"]["events_after"][0] - == self.another_event.source) + assert ( + result["results"][0]["context"]["events_after"][0] + == self.another_event.source + ) def test_media_storage(self, panstore): server_name = "test" From 369f73f3fb53975fd1f448ede53e021fd2afe85a Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Tue, 1 Oct 2024 19:27:30 -0700 Subject: [PATCH 08/12] chore: removing unused imports --- pantalaimon/client.py | 1 - pantalaimon/index.py | 1 - pantalaimon/main.py | 1 - pantalaimon/store.py | 2 +- tests/proxy_test.py | 2 -- tests/store_test.py | 5 +---- 6 files changed, 2 insertions(+), 10 deletions(-) diff --git a/pantalaimon/client.py b/pantalaimon/client.py index 882f6be..f2f6895 100644 --- a/pantalaimon/client.py +++ b/pantalaimon/client.py @@ -16,7 +16,6 @@ import asyncio import os from collections import defaultdict from pprint import pformat -from typing import Any, Dict, Optional from urllib.parse import urlparse from aiohttp.client_exceptions import ClientConnectionError diff --git a/pantalaimon/index.py b/pantalaimon/index.py index e4f4aa0..3d49614 100644 --- a/pantalaimon/index.py +++ b/pantalaimon/index.py @@ -23,7 +23,6 @@ if False: import json import os from functools import partial - from typing import Any, Dict, List, Optional, Tuple import attr import tantivy diff --git a/pantalaimon/main.py b/pantalaimon/main.py index 0ab42d6..abc411c 100644 --- a/pantalaimon/main.py +++ b/pantalaimon/main.py @@ -15,7 +15,6 @@ import asyncio import os import signal -from typing import Optional import click import janus diff --git a/pantalaimon/store.py b/pantalaimon/store.py index ee6f377..0dfe045 100644 --- a/pantalaimon/store.py +++ b/pantalaimon/store.py @@ -15,7 +15,7 @@ import json import os from collections import defaultdict -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict import attr from nio.crypto import TrustState, GroupSessionStore diff --git a/tests/proxy_test.py b/tests/proxy_test.py index f0856d8..ecf515e 100644 --- a/tests/proxy_test.py +++ b/tests/proxy_test.py @@ -1,9 +1,7 @@ -import asyncio import json import re from collections import defaultdict -from aiohttp import web from nio.crypto import OlmDevice from conftest import faker diff --git a/tests/store_test.py b/tests/store_test.py index 13acfb0..2ecef85 100644 --- a/tests/store_test.py +++ b/tests/store_test.py @@ -1,12 +1,10 @@ import asyncio -import pdb import pprint import pytest from nio import RoomMessage, RoomEncryptedMedia from urllib.parse import urlparse -from conftest import faker from pantalaimon.index import INDEXING_ENABLED from pantalaimon.store import FetchTask, MediaInfo, UploadInfo @@ -81,7 +79,6 @@ class TestClass(object): def test_account_loading(self, panstore): accounts = panstore.load_all_users() - # pdb.set_trace() assert len(accounts) == 10 def test_token_saving(self, panstore, access_token): @@ -132,7 +129,7 @@ class TestClass(object): if not INDEXING_ENABLED: pytest.skip("Indexing needs to be enabled to test this") - from pantalaimon.index import Index, IndexStore + from pantalaimon.index import IndexStore loop = asyncio.get_event_loop() From 5caaaf565176144adb35d12ca5e0c5a918e19444 Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Wed, 2 Oct 2024 18:12:51 -0700 Subject: [PATCH 09/12] chore: Add code fences to the README --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0573108..412db2a 100644 --- a/README.md +++ b/README.md @@ -146,34 +146,35 @@ This is all coming from an excellent comment that you can find [here](https://gi -1) Ensure you have an OS keyring installed. In my case I installed gnome-keyring. You may also want a GUI like seahorse to inspect the keyring. (pantalaimon will work without a keyring but your client will have to log in with the password every time pantalaimon is restarted, instead of being able to reuse the access token from the previous successful login.) +1) Ensure you have an OS keyring installed. In my case I installed `gnome-keyring`. You may also want a GUI like `seahorse` to inspect the keyring. (pantalaimon will work without a keyring but your client will have to log in with the password every time `pantalaimon` is restarted, instead of being able to reuse the access token from the previous successful login.) -2) In case you have prior attempts, clean the slate by deleting the ~/.local/share/pantalaimon directory. +2) In case you have prior attempts, clean the slate by deleting the `~/.local/share/pantalaimon` directory. -3) Start pantalaimon. +3) Start `pantalaimon`. -4) Connect a client to the ListenAddress:ListenPort you specified in pantalaimon.conf, eg to 127.0.0.1:8009, using the same username and password you would've used to login to your homeserver directly. +4) Connect a client to the `ListenAddress:ListenPort` you specified in `pantalaimon.conf`, eg to `127.0.0.1:8009`, using the same username and password you would've used to login to your homeserver directly. 5) The login should succeed, but at this point all encrypted messages will fail to decrypt. This is fine. -6) Start another client that you were already using for your encrypted chats previously. In my case this was app.element.io, so the rest of the steps here assume that. +6) Start another client that you were already using for your encrypted chats previously. In my case this was `app.element.io`, so the rest of the steps here assume that. -7) Run panctl. At the prompt, run start-verification . here is the full user ID like @arnavion:arnavion.dev. If you only have the one Element session, panctl will show you the device ID as an autocomplete hint so you don't have to look it up. If you do need to look it up, go to Element -> profile icon -> All Settings -> Sessions, expand the "Current session" item, and the "Session ID" is the device ID. +7) Run `panctl`. At the prompt, run `start-verification `. `` here is the full user ID like `@arnavion:arnavion.dev`. If you only have the one Element session, `panctl` will show you the device ID as an autocomplete hint so you don't have to look it up. If you do need to look it up, go to Element -> profile icon -> All Settings -> Sessions, expand the "Current session" item, and the "Session ID" is the device ID. -8) In Element you will see a popup "Incoming Verification Request". Click "Continue". It will change to a popup containing some emojis, and panctl will print the same emojis. Click the "They match" button. It will now change to a popup like "Waiting for other client to confirm..." +8) In Element you will see a popup "Incoming Verification Request". Click "Continue". It will change to a popup containing some emojis, and `panctl` will print the same emojis. Click the "They match" button. It will now change to a popup like "Waiting for other client to confirm..." -9) In panctl, run confirm-verification , ie the same command as before but with confirm-verification instead of start-verification. +9) In `panctl`, run `confirm-verification `, ie the same command as before but with `confirm-verification` instead of `start-verification`. 10) At this point, if you look at all your sessions in Element (profile icon -> All Settings -> Sessions), you should see "pantalaimon" in the "Other sessions" list as a "Verified" session. 11) Export the E2E room keys that Element was using via profile icon -> Security & Privacy -> Export E2E room keys. Pick any password and then save the file to some path. -12) Back in panctl, run import-keys . After a few seconds, in the output of pantalaimon, you should see a log like INFO: pantalaimon: Successfully imported keys for from +12) Back in `panctl`, run `import-keys `. After a few seconds, in the output of `pantalaimon`, you should see a log like `INFO: pantalaimon: Successfully imported keys for from `. -13) Close and restart the client you had used in step 5, ie the one you want to connect to pantalaimon. Now, finally, you should be able to see the encrypted chats be decrypted. +13) Close and restart the client you had used in step 5, ie the one you want to connect to `pantalaimon`. Now, finally, you should be able to see the encrypted chats be decrypted. 14) Delete the E2E room keys backup file from step 12. You don't need it any more. + 15) If in step 11 you had other unverified sessions from pantalaimon from your prior attempts, you can sign out of them too. -You will probably have to repeat steps 12-15 any time you start a new encrypted chat in Element. +You will probably have to repeat steps 11-14 any time you start a new encrypted chat in Element. From 5426d5bf9d5109f113bdf10a6133eefdcacad2ff Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Mon, 28 Oct 2024 20:07:12 -0700 Subject: [PATCH 10/12] chore: upgrade matrix-nio and fix dasbus version --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 379f76f..5b9abec 100644 --- a/setup.py +++ b/setup.py @@ -28,13 +28,13 @@ setup( "cachetools >= 3.0.0", "prompt_toolkit > 2, < 4", "typing;python_version<'3.5'", - "matrix-nio[e2e] >= 0.20, < 0.21", + "matrix-nio[e2e] >= 0.24, < 0.25.2", ], extras_require={ "ui": [ "dbus-python >= 1.2, < 1.3", "PyGObject >= 3.36, < 3.39", - "dasbus == 1.71", + "dasbus == 1.7", "notify2 >= 0.3, < 0.4", ] }, From e2abab1ecc8a5cfec171f164456d10a514d4216e Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Mon, 28 Oct 2024 20:11:44 -0700 Subject: [PATCH 11/12] chore: update pip install instructions in README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 412db2a..0e45f28 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,11 @@ Installing pantalaimon works like usually with python packages: python setup.py install +or you can use `pip` and install it with: +``` +pip install .[ui] +``` + Pantalaimon can also be found on pypi: pip install pantalaimon From bfb3b0615342c7625ceae7f95bc91fe9d2e3d54c Mon Sep 17 00:00:00 2001 From: Hank Greenburg Date: Mon, 28 Oct 2024 20:13:16 -0700 Subject: [PATCH 12/12] docs: Update how to install packages with package manager --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0e45f28..7f2bd2d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ or you can use `pip` and install it with: pip install .[ui] ``` +It is recommended that you create a virtual environment first or install dependencies +via your package manager. They are usually found with `python-`. + Pantalaimon can also be found on pypi: pip install pantalaimon