diff --git a/pantalaimon/client.py b/pantalaimon/client.py
index dabc36a..807c7dc 100644
--- a/pantalaimon/client.py
+++ b/pantalaimon/client.py
@@ -9,12 +9,15 @@ from nio import (AsyncClient, ClientConfig, EncryptionError,
KeyVerificationEvent, LocalProtocolError,
KeyVerificationStart, KeyVerificationKey, KeyVerificationMac)
from nio.store import SqliteStore
+from nio.crypto import Sas
from pantalaimon.log import logger
from pantalaimon.thread_messages import (
DevicesMessage,
- DeviceAuthStringMessage,
- InfoMessage
+ InviteSasSignal,
+ ShowSasSignal,
+ SasDoneSignal,
+ DaemonResponse
)
@@ -72,9 +75,8 @@ class PanClient(AsyncClient):
}
}
- async def send_info(self, string):
- """Send a info message to the UI thread."""
- message = InfoMessage(string)
+ async def send_message(self, message):
+ """Send a thread message to the UI thread."""
await self.queue.put(message)
async def sync_tasks(self, response):
@@ -125,10 +127,11 @@ class PanClient(AsyncClient):
logger.info(f"{event.sender} via {event.from_device} has started "
f"a key verification process.")
- message = DeviceStartSasMessage(
+ message = InviteSasSignal(
self.user_id,
event.sender,
- event.from_device
+ event.from_device,
+ event.transaction_id
)
task = loop.create_task(
@@ -144,10 +147,11 @@ class PanClient(AsyncClient):
device = sas.other_olm_device
emoji = sas.get_emoji()
- message = DeviceAuthStringMessage(
+ message = ShowSasSignal(
self.user_id,
device.user_id,
device.id,
+ sas.transaction_id,
emoji
)
@@ -163,11 +167,14 @@ class PanClient(AsyncClient):
device = sas.other_olm_device
if sas.verified:
- task = loop.create_task(
- self.send_info(f"Device {device.id} of user "
- f"{device.user_id} succesfully "
- f"verified.")
- )
+ task = loop.create_task(self.send_message(
+ SasDoneSignal(
+ self.user_id,
+ device.user_id,
+ device.id,
+ sas.transaction_id
+ )
+ ))
self.key_verificatins_tasks.append(task)
def start_loop(self):
@@ -199,13 +206,42 @@ class PanClient(AsyncClient):
sas = self.get_active_sas(user_id, device_id)
if not sas:
- await self.send_info("No such verification process found.")
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ Sas._txid_error[0],
+ Sas._txid_error[1]
+ )
+
+ )
return
try:
await self.accept_key_verification(sas.transaction_id)
- except (LocalProtocolError, ClientConnectionError) as e:
- await self.send_info(f"Error accepting key verification: {e}")
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ "m.ok",
+ "Successfully accepted the key verification request"
+ ))
+ except LocalProtocolError as e:
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ Sas._unexpected_message_error[0],
+ e
+ ))
+ except ClientConnectionError as e:
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ "m.connection_error",
+ e
+ ))
async def confirm_sas(self, message):
user_id = message.user_id
@@ -214,20 +250,49 @@ class PanClient(AsyncClient):
sas = self.get_active_sas(user_id, device_id)
if not sas:
- await self.send_info("No such verification process found.")
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ Sas._txid_error[0],
+ Sas._txid_error[1]
+ )
+
+ )
return
try:
await self.confirm_short_auth_string(sas.transaction_id)
except ClientConnectionError as e:
- await self.send_info(f"Error confirming short auth string: {e}")
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ "m.connection_error",
+ e
+ ))
+
+ return
device = sas.other_olm_device
+
if sas.verified:
- await self.send_info(f"Device {device.id} of user {device.user_id}"
- f" succesfully verified.")
+ await self.send_message(
+ SasDoneSignal(
+ self.user_id,
+ device.user_id,
+ device.id,
+ sas.transaction_id
+ )
+ )
else:
- await self.send_info(f"Waiting for {device.user_id} to confirm...")
+ await self.send_message(
+ DaemonResponse(
+ message.message_id,
+ self.user_id,
+ "m.ok",
+ f"Waiting for {device.user_id} to confirm."
+ ))
async def loop_stop(self):
"""Stop the client loop."""
diff --git a/pantalaimon/daemon.py b/pantalaimon/daemon.py
index b1a320a..92236d8 100755
--- a/pantalaimon/daemon.py
+++ b/pantalaimon/daemon.py
@@ -24,8 +24,9 @@ from pantalaimon.thread_messages import (
ExportKeysMessage,
ImportKeysMessage,
DeviceConfirmSasMessage,
- DeviceAcceptSasMessage,
- InfoMessage
+ SasMessage,
+ AcceptSasMessage,
+ DaemonResponse
)
@@ -89,7 +90,7 @@ class ProxyDaemon:
pan_client.start_loop()
- async def _verify_device(self, client, device):
+ async def _verify_device(self, message_id, client, device):
ret = client.verify_device(device)
if ret:
@@ -100,9 +101,9 @@ class ProxyDaemon:
f"{device.user_id} already verified")
logger.info(msg)
- await self.send_info(msg)
+ await self.send_response(message_id, client.user_id, "m.ok", msg)
- async def _unverify_device(self, client, device):
+ async def _unverify_device(self, message_id, client, device):
ret = client.unverify_device(device)
if ret:
@@ -113,11 +114,11 @@ class ProxyDaemon:
f"{device.user_id} already unverified")
logger.info(msg)
- await self.send_info(msg)
+ await self.send_response(message_id, client.user_id, "m.ok", msg)
- async def send_info(self, string):
- """Send a info message to the UI thread."""
- message = InfoMessage(string)
+ async def send_response(self, message_id, pan_user, code, message):
+ """Send a thread response message to the UI thread."""
+ message = DaemonResponse(message_id, pan_user, code, message)
await self.send_queue.put(message)
async def receive_message(self, message):
@@ -125,8 +126,7 @@ class ProxyDaemon:
if isinstance(
message,
- (DeviceVerifyMessage, DeviceUnverifyMessage,
- DeviceConfirmSasMessage, DeviceAcceptSasMessage)
+ (DeviceVerifyMessage, DeviceUnverifyMessage)
):
device = client.device_store[message.user_id].get(
@@ -137,15 +137,22 @@ class ProxyDaemon:
if not device:
msg = (f"No device found for {message.user_id} and "
f"{message.device_id}")
- await self.send_info(msg)
+ await self.send_response(
+ message.message_id,
+ message.pan_user,
+ "m.unknown_device",
+ msg
+ )
logger.info(msg)
return
if isinstance(message, DeviceVerifyMessage):
- await self._verify_device(client, device)
+ await self._verify_device(message.message_id, client, device)
elif isinstance(message, DeviceUnverifyMessage):
- await self._unverify_device(client, device)
- elif isinstance(message, DeviceAcceptSasMessage):
+ await self._unverify_device(message.message_id, client, device)
+
+ elif isinstance(message, SasMessage):
+ if isinstance(message, AcceptSasMessage):
await client.accept_sas(message)
elif isinstance(message, DeviceConfirmSasMessage):
await client.confirm_sas(message)
@@ -160,12 +167,23 @@ class ProxyDaemon:
info_msg = (f"Error exporting keys for {client.user_id} to"
f" {path} {e}")
logger.info(info_msg)
- await self.send_info(info_msg)
+ await self.send_response(
+ message.message_id,
+ client.user_id,
+ "m.os_error",
+ str(e)
+ )
+
else:
info_msg = (f"Succesfully exported keys for {client.user_id} "
f"to {path}")
logger.info(info_msg)
- await self.send_info(info_msg)
+ await self.send_response(
+ message.message_id,
+ client.user_id,
+ "m.ok",
+ info_msg
+ )
elif isinstance(message, ImportKeysMessage):
path = os.path.abspath(os.path.expanduser(message.file_path))
@@ -177,12 +195,22 @@ class ProxyDaemon:
info_msg = (f"Error importing keys for {client.user_id} "
f"from {path} {e}")
logger.info(info_msg)
- await self.send_info(info_msg)
+ await self.send_response(
+ message.message_id,
+ client.user_id,
+ "m.os_error",
+ str(e)
+ )
else:
info_msg = (f"Succesfully imported keys for {client.user_id} "
f"from {path}")
logger.info(info_msg)
- await self.send_info(info_msg)
+ await self.send_response(
+ message.message_id,
+ client.user_id,
+ "m.ok",
+ info_msg
+ )
def get_access_token(self, request):
# type: (aiohttp.web.BaseRequest) -> str
diff --git a/pantalaimon/main.py b/pantalaimon/main.py
index cd55378..3a0b7f3 100644
--- a/pantalaimon/main.py
+++ b/pantalaimon/main.py
@@ -14,7 +14,7 @@ from logbook import StderrHandler
from aiohttp import web
from pantalaimon.ui import GlibT
-from pantalaimon.thread_messages import InfoMessage
+from pantalaimon.thread_messages import DaemonResponse
from pantalaimon.daemon import ProxyDaemon
from pantalaimon.config import PanConfig, PanConfigError, parse_log_level
from pantalaimon.log import logger
@@ -81,8 +81,8 @@ async def message_router(receive_queue, send_queue, proxies):
return None
- async def send_info(string):
- message = InfoMessage(string)
+ async def send_info(message_id, pan_user, code, string):
+ message = DaemonResponse(message_id, pan_user, code, string)
await send_queue.put(message)
while True:
@@ -94,7 +94,12 @@ async def message_router(receive_queue, send_queue, proxies):
if not proxy:
msg = f"No pan client found for {message.pan_user}."
logger.warn(msg)
- send_info(msg)
+ await send_info(
+ message.message_id,
+ message.pan_user,
+ "m.unknown_client",
+ msg
+ )
await proxy.receive_message(message)
diff --git a/pantalaimon/panctl.py b/pantalaimon/panctl.py
index 6a0bdc5..b837ba9 100644
--- a/pantalaimon/panctl.py
+++ b/pantalaimon/panctl.py
@@ -276,15 +276,34 @@ class PanCtl:
self.ctl = self.pan_bus["org.pantalaimon1.control"]
self.devices = self.pan_bus["org.pantalaimon1.devices"]
- self.ctl.Info.connect(self.show_info)
- self.devices.SasReceived.connect(self.show_sas)
+ self.own_message_ids = []
- def show_info(self, message):
- print(message)
+ self.ctl.Response.connect(self.show_response)
+ self.devices.VerificationInvite.connect(self.show_sas_invite)
+ self.devices.VerificationString.connect(self.show_sas)
+ self.devices.VerificationDone.connect(self.sas_done)
+
+ def show_response(self, response_id, pan_user, message):
+ if response_id not in self.own_message_ids:
+ return
+
+ self.own_message_ids.remove(response_id)
+
+ print(message["message"])
+
+ def sas_done(self, pan_user, user_id, device_id, _):
+ print(f"Device {device_id} of user {user_id}"
+ f" succesfully verified for pan user {pan_user}.")
+
+ def show_sas_invite(self, pan_user, user_id, device_id, _):
+ print(f"{user_id} has started an interactive device "
+ f"verification for his device {device_id} with pan user "
+ f"{pan_user}\n"
+ f"Accept the invitation with the accept-verification command.")
# The emoji printing logic was taken from weechat-matrix and was written by
# dkasak.
- def show_sas(self, pan_user, user_id, device_id, emoji):
+ def show_sas(self, pan_user, user_id, device_id, _, emoji):
emojis = [x[0] for x in emoji]
descriptions = [x[1] for x in emoji]
@@ -378,27 +397,39 @@ class PanCtl:
self.list_users()
elif command == "import-keys":
- self.ctl.ImportKeys(args.pan_user, args.path, args.passphrase)
+ self.own_message_ids.append(
+ self.ctl.ImportKeys(
+ args.pan_user,
+ args.path,
+ args.passphrase
+ ))
elif command == "export-keys":
- self.ctl.ExportKeys(args.pan_user, args.path, args.passphrase)
+ self.own_message_ids.append(
+ self.ctl.ExportKeys(
+ args.pan_user,
+ args.path,
+ args.passphrase
+ ))
elif command == "list-devices":
self.list_devices(args)
elif command == "accept-verification":
- self.devices.AcceptKeyVerification(
- args.pan_user,
- args.user_id,
- args.device_id
- )
+ self.own_message_ids.append(
+ self.devices.AcceptKeyVerification(
+ args.pan_user,
+ args.user_id,
+ args.device_id
+ ))
elif command == "confirm-verification":
- self.devices.ConfirmKeyVerification(
- args.pan_user,
- args.user_id,
- args.device_id
- )
+ self.own_message_ids.append(
+ self.devices.ConfirmKeyVerification(
+ args.pan_user,
+ args.user_id,
+ args.device_id
+ ))
def main():
diff --git a/pantalaimon/thread_messages.py b/pantalaimon/thread_messages.py
index f4c4d51..48f21bc 100644
--- a/pantalaimon/thread_messages.py
+++ b/pantalaimon/thread_messages.py
@@ -7,8 +7,11 @@ class Message:
@attr.s
-class InfoMessage(Message):
- string = attr.ib()
+class DaemonResponse(Message):
+ message_id = attr.ib()
+ pan_user = attr.ib()
+ code = attr.ib()
+ message = attr.ib()
@attr.s
@@ -19,6 +22,7 @@ class DevicesMessage(Message):
@attr.s
class _KeysOperation(Message):
+ message_id = attr.ib()
pan_user = attr.ib()
file_path = attr.ib()
passphrase = attr.ib()
@@ -36,6 +40,7 @@ class ExportKeysMessage(_KeysOperation):
@attr.s
class _VerificationMessage(Message):
+ message_id = attr.ib()
pan_user = attr.ib()
user_id = attr.ib()
device_id = attr.ib()
@@ -52,20 +57,43 @@ class DeviceUnverifyMessage(_VerificationMessage):
@attr.s
-class DeviceStartSasMessage(_VerificationMessage):
+class SasMessage(_VerificationMessage):
pass
@attr.s
-class DeviceAcceptSasMessage(_VerificationMessage):
+class DeviceConfirmSasMessage(SasMessage):
pass
@attr.s
-class DeviceConfirmSasMessage(_VerificationMessage):
+class AcceptSasMessage(SasMessage):
pass
@attr.s
-class DeviceAuthStringMessage(_VerificationMessage):
- short_string = attr.ib()
+class _SasSignal:
+ pan_user = attr.ib()
+ user_id = attr.ib()
+ device_id = attr.ib()
+ transaction_id = attr.ib()
+
+
+@attr.s
+class StartSasSignal(_SasSignal):
+ pass
+
+
+@attr.s
+class InviteSasSignal(_SasSignal):
+ pass
+
+
+@attr.s
+class ShowSasSignal(_SasSignal):
+ emoji = attr.ib()
+
+
+@attr.s
+class SasDoneSignal(_SasSignal):
+ pass
diff --git a/pantalaimon/ui.py b/pantalaimon/ui.py
index db5375b..77a78b4 100644
--- a/pantalaimon/ui.py
+++ b/pantalaimon/ui.py
@@ -12,16 +12,31 @@ from pantalaimon.thread_messages import (
DeviceVerifyMessage,
DeviceUnverifyMessage,
DevicesMessage,
- InfoMessage,
- DeviceAcceptSasMessage,
+ AcceptSasMessage,
DeviceConfirmSasMessage,
- DeviceAuthStringMessage,
ImportKeysMessage,
ExportKeysMessage,
+ StartSasSignal,
+ ShowSasSignal,
+ InviteSasSignal,
+ SasDoneSignal,
+ DaemonResponse
)
from pantalaimon.log import logger
+class IdCounter:
+ def __init__(self):
+ self._message_id = 0
+
+ @property
+ def message_id(self):
+ ret = self._message_id
+ self._message_id += 1
+
+ return ret
+
+
class Control:
"""
@@ -34,40 +49,59 @@ class Control:
+
+
-
-
+
+
+
+
"""
- def __init__(self, queue, user_list=None):
+ Response = signal()
+
+ def __init__(self, queue, user_list, id_counter):
self.users = user_list
self.queue = queue
+ self.id_counter = id_counter
+
+ @property
+ def message_id(self):
+ return self.id_counter.message_id
def ListUsers(self):
"""Return the list of pan users."""
return self.users
def ExportKeys(self, pan_user, filepath, passphrase):
- message = ExportKeysMessage(pan_user, filepath, passphrase)
+ message = ExportKeysMessage(
+ self.message_id,
+ pan_user,
+ filepath,
+ passphrase
+ )
self.queue.put(message)
- return
+ return message.message_id
def ImportKeys(self, pan_user, filepath, passphrase):
- message = ImportKeysMessage(pan_user, filepath, passphrase)
+ message = ImportKeysMessage(
+ self.message_id,
+ pan_user,
+ filepath,
+ passphrase
+ )
self.queue.put(message)
- return
-
- Info = signal()
+ return message.message_id
class Devices:
@@ -89,30 +123,64 @@ class Devices:
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"""
- SasReceived = signal()
+ VerificationInvite = signal()
+ VerificationCancel = signal()
+ VerificationString = signal()
+ VerificationDone = signal()
- def __init__(self, queue, device_list):
+ def __init__(self, queue, device_list, id_counter):
self.device_list = device_list
self.queue = queue
+ self.id_counter = id_counter
+
+ @property
+ def message_id(self):
+ return self.id_counter.message_id
def List(self, pan_user):
device_store = self.device_list.get(pan_user, None)
@@ -170,14 +238,25 @@ class Devices:
return
def ConfirmKeyVerification(self, pan_user, user_id, device_id):
- message = DeviceConfirmSasMessage(pan_user, user_id, device_id)
+ message = DeviceConfirmSasMessage(
+ self.message_id,
+ pan_user,
+ user_id,
+ device_id
+ )
+ print("HEEEELOOO {}".format(message.message_id))
self.queue.put(message)
- return
+ return message.message_id
def AcceptKeyVerification(self, pan_user, user_id, device_id):
- message = DeviceAcceptSasMessage(pan_user, user_id, device_id)
+ message = AcceptSasMessage(
+ self.message_id,
+ pan_user,
+ user_id,
+ device_id
+ )
self.queue.put(message)
- return
+ return message.message_id
def update_devices(self, message):
device_store = self.device_list[message.user_id]
@@ -217,8 +296,10 @@ class GlibT:
self.users = self.store.load_all_users()
self.devices = self.store.load_all_devices()
- self.control_if = Control(self.send_queue, self.users)
- self.device_if = Devices(self.send_queue, self.devices)
+ id_counter = IdCounter()
+
+ self.control_if = Control(self.send_queue, self.users, id_counter)
+ self.device_if = Devices(self.send_queue, self.devices, id_counter)
self.bus = SessionBus()
self.bus.publish("org.pantalaimon1", self.control_if, self.device_if)
@@ -234,16 +315,40 @@ class GlibT:
if isinstance(message, DevicesMessage):
self.device_if.update_devices(message)
- elif isinstance(message, DeviceAuthStringMessage):
- self.device_if.SasReceived(
+ elif isinstance(message, InviteSasSignal):
+ self.device_if.VerificationInvite(
message.pan_user,
message.user_id,
message.device_id,
- message.short_string
+ message.transaction_id
)
- elif isinstance(message, InfoMessage):
- self.control_if.Info(message.string)
+ elif isinstance(message, ShowSasSignal):
+ self.device_if.VerificationString(
+ message.pan_user,
+ message.user_id,
+ message.device_id,
+ message.transaction_id,
+ message.emoji,
+ )
+
+ elif isinstance(message, SasDoneSignal):
+ self.device_if.VerificationDone(
+ message.pan_user,
+ message.user_id,
+ message.device_id,
+ message.transaction_id,
+ )
+
+ elif isinstance(message, DaemonResponse):
+ self.control_if.Response(
+ message.message_id,
+ message.pan_user,
+ {
+ "code": message.code,
+ "message": message.message
+ }
+ )
self.receive_queue.task_done()
return True