Commit 00c2cd54 authored by Tijmen de Mes's avatar Tijmen de Mes

Added OTR supoort to message stream

parent 4c4a95e8
...@@ -317,6 +317,12 @@ class BlinkMessage(MSRPChatMessage): ...@@ -317,6 +317,12 @@ class BlinkMessage(MSRPChatMessage):
self.disposition = disposition self.disposition = disposition
self.is_secure = is_secure self.is_secure = is_secure
self.direction = direction self.direction = direction
print(f"MSG: {self.timestamp}")
class OTRInternalMessage(BlinkMessage):
def __init__(self, content):
super(OTRInternalMessage, self).__init__(content, 'text/plain')
@implementer(IObserver) @implementer(IObserver)
...@@ -486,6 +492,76 @@ class OutgoingMessage(object): ...@@ -486,6 +492,76 @@ class OutgoingMessage(object):
notification_center.post_notification('BlinkMessageDidFail', sender=self.session, data=NotificationData(data=notification.data, id=self.id)) notification_center.post_notification('BlinkMessageDidFail', sender=self.session, data=NotificationData(data=notification.data, id=self.id))
@implementer(IObserver)
class InternalOTROutgoingMessage(OutgoingMessage):
@property
def message(self):
return OTRInternalMessage(self.content, self.content_type)
def _send(self, routes=None):
if routes is not None or self.session.routes:
notification_center = NotificationCenter()
routes = routes if routes is not None else self.session.routes
from_uri = self.account.uri
content = self.content
content = content if isinstance(content, bytes) else content.encode()
additional_sip_headers = []
if self.account.sms.use_cpim:
ns = CPIMNamespace('urn:ietf:params:imdn', 'imdn')
additional_headers = [CPIMHeader('Message-ID', ns, self.id)]
payload = CPIMPayload(content,
self.content_type,
charset='utf-8',
sender=ChatIdentity(from_uri, self.account.display_name),
recipients=[ChatIdentity(self.sip_uri, None)],
timestamp=str(self.timestamp),
additional_headers=additional_headers)
payload, content_type = payload.encode()
else:
payload = content
content_type = self.content_type
route = routes[0]
message_request = Message(FromHeader(from_uri, self.account.display_name),
ToHeader(self.sip_uri),
RouteHeader(route.uri),
content_type,
payload,
credentials=self.account.credentials,
extra_headers=additional_sip_headers)
notification_center.add_observer(self, sender=message_request)
message_request.send()
else:
pass
# TODO
def send(self):
if self.session is None:
return
if self.session.routes:
self._send()
else:
self._lookup()
def _NH_DNSLookupDidSucceed(self, notification):
notification.center.remove_observer(self, sender=notification.sender)
if notification.sender is self.lookup:
routes = notification.data.result
self.session.routes = routes
self._send()
def _NH_DNSLookupDidFail(self, notification):
notification.center.remove_observer(self, sender=notification.sender)
return
def _NH_SIPMessageDidSucceed(self, notification):
return
def _NH_SIPMessageDidFail(self, notification):
return
class RequestList(list): class RequestList(list):
def __getitem__(self, key): def __getitem__(self, key):
if isinstance(key, int): if isinstance(key, int):
...@@ -1143,6 +1219,10 @@ class MessageManager(object, metaclass=Singleton): ...@@ -1143,6 +1219,10 @@ class MessageManager(object, metaclass=Singleton):
bisect.insort_right(self.pgp_requests, export_request) bisect.insort_right(self.pgp_requests, export_request)
export_request.dialog.show() export_request.dialog.show()
def send_otr_message(self, session, data):
outgoing_message = InternalOTROutgoingMessage(session.account, session.contact, data, 'text/plain', session=session)
self._send_message(outgoing_message)
def send_composing_indication(self, session, state, refresh=None, last_active=None): def send_composing_indication(self, session, state, refresh=None, last_active=None):
if not session.account.sms.enable_iscomposing: if not session.account.sms.enable_iscomposing:
return return
......
...@@ -4,9 +4,12 @@ from application.notification import IObserver, NotificationCenter, Notification ...@@ -4,9 +4,12 @@ from application.notification import IObserver, NotificationCenter, Notification
from application.python import Null from application.python import Null
from application.system import makedirs from application.system import makedirs
from otr import OTRTransport
from otr.exceptions import IgnoreMessage, UnencryptedMessage, EncryptedMessageError, OTRError, OTRFinishedError
from sipsimple.account import AccountManager from sipsimple.account import AccountManager
from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.streams import IMediaStream, MediaStreamType, UnknownStreamError from sipsimple.streams import IMediaStream, MediaStreamType, UnknownStreamError
from sipsimple.streams.msrp.chat import OTREncryption
from sipsimple.threading import run_in_thread from sipsimple.threading import run_in_thread
from sipsimple.threading.green import run_in_green_thread from sipsimple.threading.green import run_in_green_thread
...@@ -40,6 +43,7 @@ class MessageStream(object, metaclass=MediaStreamType): ...@@ -40,6 +43,7 @@ class MessageStream(object, metaclass=MediaStreamType):
self.public_key = None self.public_key = None
self.remote_public_key = None self.remote_public_key = None
self.other_private_keys = [] self.other_private_keys = []
self.encryption = OTREncryption(self)
notification_center = NotificationCenter() notification_center = NotificationCenter()
notification_center.add_observer(self, name='PGPKeysShouldReload') notification_center.add_observer(self, name='PGPKeysShouldReload')
...@@ -75,6 +79,14 @@ class MessageStream(object, metaclass=MediaStreamType): ...@@ -75,6 +79,14 @@ class MessageStream(object, metaclass=MediaStreamType):
def end(self): def end(self):
pass pass
@property
def local_uri(self):
return None
@property
def msrp(self):
return None
@property @property
def can_encrypt(self): def can_encrypt(self):
return self.private_key is not None and self.remote_public_key is not None return self.private_key is not None and self.remote_public_key is not None
...@@ -148,11 +160,72 @@ class MessageStream(object, metaclass=MediaStreamType): ...@@ -148,11 +160,72 @@ class MessageStream(object, metaclass=MediaStreamType):
notification_center = NotificationCenter() notification_center = NotificationCenter()
notification_center.post_notification('PGPKeysDidGenerate', sender=session, data=NotificationData(private_key=private_key, public_key=private_key.pubkey)) notification_center.post_notification('PGPKeysDidGenerate', sender=session, data=NotificationData(private_key=private_key, public_key=private_key.pubkey))
def inject_otr_message(self, data):
from blink.messages import MessageManager
MessageManager().send_otr_message(self.blink_session, data)
def enable_pgp(self): def enable_pgp(self):
self._load_pgp_keys() self._load_pgp_keys()
def enable_otr(self):
self.encryption.start()
def disable_otr(self):
self.encryption.stop()
def check_otr(self, message):
content = None
notification_center = NotificationCenter()
try:
content = self.encryption.otr_session.handle_input(message.content.encode(), message.content_type)
except IgnoreMessage:
return None
except UnencryptedMessage:
return message
except EncryptedMessageError as e:
log.warning(f'OTR encrypted message error: {e}')
return None
except OTRFinishedError:
log.info('OTR has finished')
return None
except OTRError as e:
log.warning(f'OTR message error: {e}')
return None
else:
content = content.decode() if isinstance(content, bytes) else content
if content.startswith('?OTR:'):
notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error='OTR message could not be decoded'))
log.warning('OTR message could not be decoded')
if self.encryption.active:
self.encryption.stop()
return None
log.info("Message uses OTR encryption, message is decoded")
message.is_secure = self.encryption.active
message.content = content.decode() if isinstance(content, bytes) else content
return message
def encrypt(self, content, content_type=None): def encrypt(self, content, content_type=None):
# print('-- Encrypting message') # print('-- Encrypting message')
if self.encryption.active:
try:
encrypted_content = self.encryption.otr_session.handle_output(content.encode(), content_type)
except OTRError as e:
log.info("Encryption failed OTR encryption has been disabled by remote party")
self.encryption.stop()
raise Exception(f"OTR encryption has been disabled by remote party {e}")
except OTRFinishedError:
log.info("OTR encryption has been disabled by remote party")
self.encryption.stop()
raise Exception("OTR encryption has been disabled by remote party")
else:
if not encrypted_content.startswith(b'?OTR:'):
self.encryption.stop()
log.info("OTR encryption has been stopped")
raise Exception("OTR encryption has been stopped")
return str(encrypted_content.decode())
pgp_message = PGPMessage.new(content, compression=CompressionAlgorithm.Uncompressed) pgp_message = PGPMessage.new(content, compression=CompressionAlgorithm.Uncompressed)
cipher = SymmetricKeyAlgorithm.AES256 cipher = SymmetricKeyAlgorithm.AES256
...@@ -266,3 +339,6 @@ class MessageStream(object, metaclass=MediaStreamType): ...@@ -266,3 +339,6 @@ class MessageStream(object, metaclass=MediaStreamType):
if loaded_key is None: if loaded_key is None:
continue continue
self.other_private_keys.append((account, loaded_key)) self.other_private_keys.append((account, loaded_key))
OTRTransport.register(MessageStream)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment