Exemplo n.º 1
0
    def _handle_SEND(self, chunk):
        if chunk.size == 0:  # keep-alive
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        content_type = chunk.content_type.lower()
        if not contains_mime_type(self.accept_types, content_type):
            self.msrp_session.send_report(chunk, 413, 'Unwanted Message')
            return
        if chunk.contflag == '#':
            self.incoming_queue.pop(chunk.message_id, None)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        elif chunk.contflag == '+':
            self.incoming_queue[chunk.message_id].append(chunk.data)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        else:
            data = ''.join(self.incoming_queue.pop(chunk.message_id, [])) + chunk.data

        if content_type == 'message/cpim':
            try:
                payload = CPIMPayload.decode(data)
            except CPIMParserError:
                self.msrp_session.send_report(chunk, 400, 'CPIM Parser Error')
                return
            else:
                message = Message(**{name: getattr(payload, name) for name in Message.__slots__})
                if not contains_mime_type(self.accept_wrapped_types, message.content_type):
                    self.msrp_session.send_report(chunk, 413, 'Unwanted Message')
                    return
                if message.timestamp is None:
                    message.timestamp = ISOTimestamp.now()
                if message.sender is None:
                    message.sender = self.remote_identity
                private = self.session.remote_focus and len(message.recipients) == 1 and message.recipients[0] != self.remote_identity
        else:
            payload = SimplePayload.decode(data, content_type)
            message = Message(payload.content, payload.content_type, sender=self.remote_identity, recipients=[self.local_identity], timestamp=ISOTimestamp.now())
            private = False

        try:
            message.content = self.encryption.otr_session.handle_input(message.content, message.content_type)
        except IgnoreMessage:
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        except UnencryptedMessage:
            encrypted = False
            encryption_active = True
        except EncryptedMessageError, e:
            self.msrp_session.send_report(chunk, 400, str(e))
            notification_center = NotificationCenter()
            notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error=str(e)))
            return
Exemplo n.º 2
0
    def _handle_SEND(self, chunk):
        if chunk.size == 0:  # keep-alive
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        content_type = chunk.content_type.lower()
        if not contains_mime_type(self.accept_types, content_type):
            self.msrp_session.send_report(chunk, 413, 'Unwanted Message')
            return
        if chunk.contflag == '#':
            self.incoming_queue.pop(chunk.message_id, None)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        elif chunk.contflag == '+':
            self.incoming_queue[chunk.message_id].append(chunk.data)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        else:
            data = ''.join(self.incoming_queue.pop(chunk.message_id, [])) + chunk.data

        if content_type == 'message/cpim':
            try:
                payload = CPIMPayload.decode(data)
            except CPIMParserError:
                self.msrp_session.send_report(chunk, 400, 'CPIM Parser Error')
                return
            else:
                message = Message(**{name: getattr(payload, name) for name in Message.__slots__})
                if not contains_mime_type(self.accept_wrapped_types, message.content_type):
                    self.msrp_session.send_report(chunk, 413, 'Unwanted Message')
                    return
                if message.timestamp is None:
                    message.timestamp = ISOTimestamp.now()
                if message.sender is None:
                    message.sender = self.remote_identity
                private = self.session.remote_focus and len(message.recipients) == 1 and message.recipients[0] != self.remote_identity
        else:
            payload = SimplePayload.decode(data, content_type)
            message = Message(payload.content, payload.content_type, sender=self.remote_identity, recipients=[self.local_identity], timestamp=ISOTimestamp.now())
            private = False

        try:
            message.content = self.encryption.otr_session.handle_input(message.content, message.content_type)
        except IgnoreMessage:
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        except UnencryptedMessage:
            encrypted = False
            encryption_active = True
        except EncryptedMessageError, e:
            self.msrp_session.send_report(chunk, 400, str(e))
            notification_center = NotificationCenter()
            notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error=str(e)))
            return
Exemplo n.º 3
0
    def _message_queue_handler(self):
        notification_center = NotificationCenter()
        try:
            while True:
                message = self.message_queue.wait()
                if self.msrp_session is None:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended')
                        notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data)
                    break

                try:
                    if isinstance(message.content, unicode):
                        message.content = message.content.encode('utf8')
                        charset = 'utf8'
                    else:
                        charset = None

                    if not isinstance(message, QueuedOTRInternalMessage):
                        try:
                            message.content = self.encryption.otr_session.handle_output(message.content, message.content_type)
                        except OTRError, e:
                            raise ChatStreamError(str(e))

                    message.sender = message.sender or self.local_identity
                    message.recipients = message.recipients or [self.remote_identity]

                    # check if we MUST use CPIM
                    need_cpim = (message.sender != self.local_identity or message.recipients != [self.remote_identity] or
                                 message.courtesy_recipients or message.subject or message.timestamp or message.required or message.additional_headers)

                    if need_cpim or not contains_mime_type(self.remote_accept_types, message.content_type):
                        if not contains_mime_type(self.remote_accept_wrapped_types, message.content_type):
                            raise ChatStreamError('Unsupported content_type for outgoing message: %r' % message.content_type)
                        if not self.cpim_enabled:
                            raise ChatStreamError('Additional message meta-data cannot be sent, because the CPIM wrapper is not used')
                        if not self.private_messages_allowed and message.recipients != [self.remote_identity]:
                            raise ChatStreamError('The remote end does not support private messages')
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__})
                    elif self.prefer_cpim and self.cpim_enabled and contains_mime_type(self.remote_accept_wrapped_types, message.content_type):
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__})
                    else:
                        payload = SimplePayload(message.content, message.content_type, charset)
                except ChatStreamError, e:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id, message=None, code=0, reason=e.args[0])
                        notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data)
                    continue
Exemplo n.º 4
0
    def _message_queue_handler(self):
        notification_center = NotificationCenter()
        try:
            while True:
                message = self.message_queue.wait()
                if self.msrp_session is None:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended')
                        notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data)
                    break

                try:
                    if isinstance(message.content, unicode):
                        message.content = message.content.encode('utf8')
                        charset = 'utf8'
                    else:
                        charset = None

                    if not isinstance(message, QueuedOTRInternalMessage):
                        try:
                            message.content = self.encryption.otr_session.handle_output(message.content, message.content_type)
                        except OTRError, e:
                            raise ChatStreamError(str(e))

                    message.sender = message.sender or self.local_identity
                    message.recipients = message.recipients or [self.remote_identity]

                    # check if we MUST use CPIM
                    need_cpim = (message.sender != self.local_identity or message.recipients != [self.remote_identity] or
                                 message.courtesy_recipients or message.subject or message.timestamp or message.required or message.additional_headers)

                    if need_cpim or not contains_mime_type(self.remote_accept_types, message.content_type):
                        if not contains_mime_type(self.remote_accept_wrapped_types, message.content_type):
                            raise ChatStreamError('Unsupported content_type for outgoing message: %r' % message.content_type)
                        if not self.cpim_enabled:
                            raise ChatStreamError('Additional message meta-data cannot be sent, because the CPIM wrapper is not used')
                        if not self.private_messages_allowed and message.recipients != [self.remote_identity]:
                            raise ChatStreamError('The remote end does not support private messages')
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__})
                    elif self.prefer_cpim and self.cpim_enabled and contains_mime_type(self.remote_accept_wrapped_types, message.content_type):
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__})
                    else:
                        payload = SimplePayload(message.content, message.content_type, charset)
                except ChatStreamError, e:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id, message=None, code=0, reason=e.args[0])
                        notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data)
                    continue
Exemplo n.º 5
0
    def new_from_sdp(cls, session, remote_sdp, stream_index):
        remote_stream = remote_sdp.media[stream_index]
        if remote_stream.media != b'message':
            raise UnknownStreamError
        expected_transport = 'TCP/TLS/MSRP' if session.account.msrp.transport == 'tls' else 'TCP/MSRP'
        if remote_stream.transport != expected_transport.encode():
            raise InvalidStreamError(
                "expected %s transport in chat stream, got %s" %
                (expected_transport, remote_stream.transport))
        if remote_stream.formats != [b'*']:
            raise InvalidStreamError("wrong format list specified")
        stream = cls()
        stream.remote_role = remote_stream.attributes.getfirst(
            'setup', 'active')

        if remote_stream.direction != b'sendrecv':
            raise InvalidStreamError(
                "Unsupported direction for chat stream: %s" %
                remote_stream.direction)
        remote_accept_types = remote_stream.attributes.getfirst(
            b'accept-types')
        if remote_accept_types is None:
            raise InvalidStreamError(
                "remote SDP media does not have 'accept-types' attribute")
        if not any(
                contains_mime_type(cls.accept_types, mime_type)
                for mime_type in remote_accept_types.decode().split()):
            raise InvalidStreamError("no compatible media types found")
        return stream
Exemplo n.º 6
0
 def new_from_sdp(cls, session, remote_sdp, stream_index):
     remote_stream = remote_sdp.media[stream_index]
     if remote_stream.media != 'message':
         raise UnknownStreamError
     expected_transport = 'TCP/TLS/MSRP' if session.account.msrp.transport=='tls' else 'TCP/MSRP'
     if remote_stream.transport != expected_transport:
         raise InvalidStreamError("expected %s transport in chat stream, got %s" % (expected_transport, remote_stream.transport))
     if remote_stream.formats != ['*']:
         raise InvalidStreamError("wrong format list specified")
     stream = cls()
     stream.remote_role = remote_stream.attributes.getfirst('setup', 'active')
     if remote_stream.direction != 'sendrecv':
         raise InvalidStreamError("Unsupported direction for chat stream: %s" % remote_stream.direction)
     remote_accept_types = remote_stream.attributes.getfirst('accept-types')
     if remote_accept_types is None:
         raise InvalidStreamError("remote SDP media does not have 'accept-types' attribute")
     if not any(contains_mime_type(cls.accept_types, mime_type) for mime_type in remote_accept_types.split()):
         raise InvalidStreamError("no compatible media types found")
     return stream
Exemplo n.º 7
0
    def _message_queue_handler(self):
        notification_center = NotificationCenter()
        try:
            while True:
                message = self.message_queue.wait()
                if self.msrp_session is None:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id,
                                                message=None,
                                                code=0,
                                                reason='Stream ended')
                        notification_center.post_notification(
                            'ChatStreamDidNotDeliverMessage',
                            sender=self,
                            data=data)
                    break

                try:
                    if isinstance(message.content, str):
                        message.content = message.content.encode('utf8')
                        charset = 'utf8'
                    else:
                        charset = None

                    if not isinstance(message, QueuedOTRInternalMessage):
                        try:
                            message.content = self.encryption.otr_session.handle_output(
                                message.content, message.content_type)
                        except OTRError as e:
                            raise ChatStreamError(str(e))

                    message.sender = message.sender or self.local_identity
                    message.recipients = message.recipients or [
                        self.remote_identity
                    ]

                    # check if we MUST use CPIM
                    need_cpim = (message.sender != self.local_identity or
                                 message.recipients != [self.remote_identity]
                                 or message.courtesy_recipients
                                 or message.subject or message.timestamp
                                 or message.required
                                 or message.additional_headers)

                    if need_cpim or not contains_mime_type(
                            self.remote_accept_types, message.content_type):
                        if not contains_mime_type(
                                self.remote_accept_wrapped_types,
                                message.content_type):
                            raise ChatStreamError(
                                'Unsupported content_type for outgoing message: %r'
                                % message.content_type)
                        if not self.cpim_enabled:
                            raise ChatStreamError(
                                'Additional message meta-data cannot be sent, because the CPIM wrapper is not used'
                            )
                        if not self.private_messages_allowed and message.recipients != [
                                self.remote_identity
                        ]:
                            raise ChatStreamError(
                                'The remote end does not support private messages'
                            )
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset,
                                              **{
                                                  name: getattr(message, name)
                                                  for name in Message.__slots__
                                              })
                    elif self.prefer_cpim and self.cpim_enabled and contains_mime_type(
                            self.remote_accept_wrapped_types,
                            message.content_type):
                        if message.timestamp is None:
                            message.timestamp = ISOTimestamp.now()
                        payload = CPIMPayload(charset=charset,
                                              **{
                                                  name: getattr(message, name)
                                                  for name in Message.__slots__
                                              })
                    else:
                        payload = SimplePayload(message.content,
                                                message.content_type, charset)
                except ChatStreamError as e:
                    if message.notify_progress:
                        data = NotificationData(message_id=message.id,
                                                message=None,
                                                code=0,
                                                reason=e.args[0])
                        notification_center.post_notification(
                            'ChatStreamDidNotDeliverMessage',
                            sender=self,
                            data=data)
                    continue
                else:
                    content, content_type = payload.encode()

                message_id = message.id
                notify_progress = message.notify_progress
                report = 'yes' if notify_progress else 'no'

                chunk = self.msrp_session.make_message(
                    content, content_type=content_type, message_id=message_id)
                chunk.add_header(FailureReportHeader(report))
                chunk.add_header(SuccessReportHeader(report))

                try:
                    self.msrp_session.send_chunk(
                        chunk,
                        response_cb=partial(self._on_transaction_response,
                                            message_id))
                except Exception as e:
                    if notify_progress:
                        data = NotificationData(message_id=message_id,
                                                message=None,
                                                code=0,
                                                reason=str(e))
                        notification_center.post_notification(
                            'ChatStreamDidNotDeliverMessage',
                            sender=self,
                            data=data)
                except ProcExit:
                    if notify_progress:
                        data = NotificationData(message_id=message_id,
                                                message=None,
                                                code=0,
                                                reason='Stream ended')
                        notification_center.post_notification(
                            'ChatStreamDidNotDeliverMessage',
                            sender=self,
                            data=data)
                    raise
                else:
                    if notify_progress:
                        self.sent_messages.add(message_id)
                        notification_center.post_notification(
                            'ChatStreamDidSendMessage',
                            sender=self,
                            data=NotificationData(message=chunk))
        finally:
            self.message_queue_thread = None
            while self.sent_messages:
                message_id = self.sent_messages.pop()
                data = NotificationData(message_id=message_id,
                                        message=None,
                                        code=0,
                                        reason='Stream ended')
                notification_center.post_notification(
                    'ChatStreamDidNotDeliverMessage', sender=self, data=data)
            message_queue, self.message_queue = self.message_queue, queue()
            while message_queue:
                message = message_queue.wait()
                if message.notify_progress:
                    data = NotificationData(message_id=message.id,
                                            message=None,
                                            code=0,
                                            reason='Stream ended')
                    notification_center.post_notification(
                        'ChatStreamDidNotDeliverMessage',
                        sender=self,
                        data=data)
Exemplo n.º 8
0
    def _handle_SEND(self, chunk):
        if chunk.size == 0:  # keep-alive
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        content_type = chunk.content_type.lower()
        if not contains_mime_type(self.accept_types, content_type):
            self.msrp_session.send_report(chunk, 413, 'Unwanted Message')
            return
        if chunk.contflag == '#':
            self.incoming_queue.pop(chunk.message_id, None)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        elif chunk.contflag == '+':
            self.incoming_queue[chunk.message_id].append(chunk.data)
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        else:
            data = ''.join(self.incoming_queue.pop(chunk.message_id,
                                                   [])) + chunk.data.decode()

        if content_type == 'message/cpim':
            try:
                payload = CPIMPayload.decode(data)
            except CPIMParserError:
                self.msrp_session.send_report(chunk, 400, 'CPIM Parser Error')
                return
            else:
                message = Message(**{
                    name: getattr(payload, name)
                    for name in Message.__slots__
                })
                if not contains_mime_type(self.accept_wrapped_types,
                                          message.content_type):
                    self.msrp_session.send_report(chunk, 413,
                                                  'Unwanted Message')
                    return
                if message.timestamp is None:
                    message.timestamp = ISOTimestamp.now()
                if message.sender is None:
                    message.sender = self.remote_identity
                private = self.session.remote_focus and len(
                    message.recipients
                ) == 1 and message.recipients[0] != self.remote_identity
        else:
            payload = SimplePayload.decode(data, content_type)
            message = Message(payload.content,
                              payload.content_type,
                              sender=self.remote_identity,
                              recipients=[self.local_identity],
                              timestamp=ISOTimestamp.now())
            private = False

        try:
            message.content = self.encryption.otr_session.handle_input(
                message.content.encode(), message.content_type)
        except IgnoreMessage:
            self.msrp_session.send_report(chunk, 200, 'OK')
            return
        except UnencryptedMessage:
            encrypted = False
            encryption_active = True
        except EncryptedMessageError as e:
            self.msrp_session.send_report(chunk, 400, str(e))
            notification_center = NotificationCenter()
            notification_center.post_notification(
                'ChatStreamOTRError',
                sender=self,
                data=NotificationData(error=str(e)))
            return
        except OTRError as e:
            self.msrp_session.send_report(chunk, 200, 'OK')
            notification_center = NotificationCenter()
            notification_center.post_notification(
                'ChatStreamOTRError',
                sender=self,
                data=NotificationData(error=str(e)))
            return
        else:
            encrypted = encryption_active = self.encryption.active

        if payload.charset is not None:
            message.content = message.content.decode(payload.charset)
        elif payload.content_type.startswith('text/'):
            message.content = message.content.decode('utf8')

        notification_center = NotificationCenter()
        if message.content_type.lower() == IsComposingDocument.content_type:
            try:
                document = IsComposingDocument.parse(message.content)
            except (ParserError, OSError) as e:
                self.msrp_session.send_report(chunk, 400, str(e))
                return
            self.msrp_session.send_report(chunk, 200, 'OK')
            data = NotificationData(
                state=document.state.value,
                refresh=document.refresh.value
                if document.refresh is not None else 120,
                content_type=document.content_type.value
                if document.content_type is not None else None,
                last_active=document.last_active.value
                if document.last_active is not None else None,
                sender=message.sender,
                recipients=message.recipients,
                private=private,
                encrypted=encrypted,
                encryption_active=encryption_active)
            notification_center.post_notification(
                'ChatStreamGotComposingIndication', sender=self, data=data)
        else:
            self.msrp_session.send_report(chunk, 200, 'OK')
            data = NotificationData(message=message,
                                    private=private,
                                    encrypted=encrypted,
                                    encryption_active=encryption_active)
            notification_center.post_notification('ChatStreamGotMessage',
                                                  sender=self,
                                                  data=data)
Exemplo n.º 9
0
 def start(self, local_sdp, remote_sdp, stream_index):
     self.greenlet = api.getcurrent()
     notification_center = NotificationCenter()
     context = 'sdp_negotiation'
     try:
         remote_media = remote_sdp.media[stream_index]
         self.remote_media = remote_media
         self.remote_accept_types = remote_media.attributes.getfirst(
             b'accept-types', b'').decode().split()
         self.remote_accept_wrapped_types = remote_media.attributes.getfirst(
             b'accept-wrapped-types', b'').decode().split()
         self.cpim_enabled = contains_mime_type(
             self.accept_types, 'message/cpim') and contains_mime_type(
                 self.remote_accept_types, 'message/cpim')
         remote_uri_path = remote_media.attributes.getfirst(b'path')
         if remote_uri_path is None:
             raise AttributeError(
                 "remote SDP media does not have 'path' attribute")
         full_remote_path = [
             URI.parse(uri) for uri in remote_uri_path.decode().split()
         ]
         remote_transport = 'tls' if full_remote_path[0].use_tls else 'tcp'
         if self.transport != remote_transport:
             raise MSRPStreamError(
                 "remote transport ('%s') different from local transport ('%s')"
                 % (remote_transport, self.transport))
         if isinstance(self.session.account,
                       Account) and self.local_role == 'actpass':
             remote_setup = remote_media.attributes.getfirst(
                 'setup', 'passive')
             if remote_setup == 'passive':
                 # If actpass is offered connectors are always started as passive
                 # We need to switch to active if the remote answers with passive
                 if self.session.account.msrp.connection_model == 'relay':
                     self.msrp_connector.mode = 'active'
                 else:
                     local_uri = self.msrp_connector.local_uri
                     logger = self.msrp_connector.logger
                     self.msrp_connector = DirectConnector(
                         logger=logger, use_sessmatch=True)
                     self.msrp_connector.prepare(local_uri)
         context = 'start'
         self.msrp = self.msrp_connector.complete(full_remote_path)
         if self.msrp_session_class is not None:
             self.msrp_session = self.msrp_session_class(
                 self.msrp,
                 accept_types=self.accept_types,
                 on_incoming_cb=self._handle_incoming,
                 automatic_reports=False)
         self.msrp_connector = None
     except Exception as e:
         self._failure_reason = str(e)
         traceback.print_exc()
         notification_center.post_notification(
             'MediaStreamDidFail',
             sender=self,
             data=NotificationData(context=context,
                                   reason=self._failure_reason))
     else:
         notification_center.post_notification('MediaStreamDidStart',
                                               sender=self)
     finally:
         self.greenlet = None