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):
self.disposition = disposition
self.is_secure = is_secure
self.direction = direction
print(f"MSG: {self.timestamp}")
class OTRInternalMessage(BlinkMessage):
def __init__(self, content):
super(OTRInternalMessage, self).__init__(content, 'text/plain')
@implementer(IObserver)
......@@ -486,6 +492,76 @@ class OutgoingMessage(object):
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):
def __getitem__(self, key):
if isinstance(key, int):
......@@ -1143,6 +1219,10 @@ class MessageManager(object, metaclass=Singleton):
bisect.insort_right(self.pgp_requests, export_request)
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):
if not session.account.sms.enable_iscomposing:
return
......
......@@ -4,9 +4,12 @@ from application.notification import IObserver, NotificationCenter, Notification
from application.python import Null
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.configuration.settings import SIPSimpleSettings
from sipsimple.streams import IMediaStream, MediaStreamType, UnknownStreamError
from sipsimple.streams.msrp.chat import OTREncryption
from sipsimple.threading import run_in_thread
from sipsimple.threading.green import run_in_green_thread
......@@ -40,6 +43,7 @@ class MessageStream(object, metaclass=MediaStreamType):
self.public_key = None
self.remote_public_key = None
self.other_private_keys = []
self.encryption = OTREncryption(self)
notification_center = NotificationCenter()
notification_center.add_observer(self, name='PGPKeysShouldReload')
......@@ -75,6 +79,14 @@ class MessageStream(object, metaclass=MediaStreamType):
def end(self):
pass
@property
def local_uri(self):
return None
@property
def msrp(self):
return None
@property
def can_encrypt(self):
return self.private_key is not None and self.remote_public_key is not None
......@@ -148,11 +160,72 @@ class MessageStream(object, metaclass=MediaStreamType):
notification_center = NotificationCenter()
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):
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):
# 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)
cipher = SymmetricKeyAlgorithm.AES256
......@@ -266,3 +339,6 @@ class MessageStream(object, metaclass=MediaStreamType):
if loaded_key is None:
continue
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