Exemple #1
0
 def _start_outgoing_sip_session(self, target_uri):
     notification_center = NotificationCenter()
     # self.xmpp_identity is our local identity
     from_uri = self.xmpp_identity.uri.as_sip_uri()
     del from_uri.parameters['gr']    # no GRUU in From header
     contact_uri = self.xmpp_identity.uri.as_sip_uri()
     contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
     to_uri = target_uri.as_sip_uri()
     lookup = DNSLookup()
     settings = SIPSimpleSettings()
     account = DefaultAccount()
     if account.sip.outbound_proxy is not None:
         uri = SIPURI(host=account.sip.outbound_proxy.host,
                      port=account.sip.outbound_proxy.port,
                      parameters={'transport': account.sip.outbound_proxy.transport})
     else:
         uri = to_uri
     try:
         routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait()
     except DNSLookupError:
         log.warning('DNS lookup error while looking for %s proxy' % uri)
         notification_center.post_notification('ChatSessionDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
         return
     self.msrp_stream = MediaStreamRegistry().get('chat')()
     route = routes.pop(0)
     from_header = FromHeader(from_uri)
     to_header = ToHeader(to_uri)
     contact_header = ContactHeader(contact_uri)
     self.sip_session = Session(account)
     notification_center.add_observer(self, sender=self.sip_session)
     notification_center.add_observer(self, sender=self.msrp_stream)
     self.sip_session.connect(from_header, to_header, contact_header=contact_header, route=route, streams=[self.msrp_stream])
Exemple #2
0
 def _start_outgoing_sip_session(self, target_uri):
     notification_center = NotificationCenter()
     # self.xmpp_identity is our local identity
     from_uri = self.xmpp_identity.uri.as_sip_uri()
     del from_uri.parameters['gr']    # no GRUU in From header
     contact_uri = self.xmpp_identity.uri.as_sip_uri()
     contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
     to_uri = target_uri.as_sip_uri()
     lookup = DNSLookup()
     settings = SIPSimpleSettings()
     account = DefaultAccount()
     if account.sip.outbound_proxy is not None:
         uri = SIPURI(host=account.sip.outbound_proxy.host,
                      port=account.sip.outbound_proxy.port,
                      parameters={'transport': account.sip.outbound_proxy.transport})
     else:
         uri = to_uri
     try:
         routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait()
     except DNSLookupError:
         log.warning('DNS lookup error while looking for %s proxy' % uri)
         notification_center.post_notification('ChatSessionDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
         return
     self.msrp_stream = MediaStreamRegistry.get('chat')()
     route = routes.pop(0)
     from_header = FromHeader(from_uri)
     to_header = ToHeader(to_uri)
     contact_header = ContactHeader(contact_uri)
     self.sip_session = Session(account)
     notification_center.add_observer(self, sender=self.sip_session)
     notification_center.add_observer(self, sender=self.msrp_stream)
     self.sip_session.connect(from_header, to_header, contact_header=contact_header, route=route, streams=[self.msrp_stream])
Exemple #3
0
 def _NH_DNSLookupDidSucceed(self, notification):
     notification_center = NotificationCenter()
     notification_center.remove_observer(self, sender=notification.sender)
     account = DefaultAccount()
     conference_application = ConferenceApplication()
     try:
         room = conference_application.get_room(self.room_uri)
     except RoomNotFoundError:
         log.msg("Room %s - failed to add %s" % (self.room_uri_str, self.refer_to_uri))
         self._refer_request.end(500)
         return
     active_media = set(room.active_media).intersection(("audio", "chat"))
     if not active_media:
         log.msg("Room %s - failed to add %s" % (self.room_uri_str, self.refer_to_uri))
         self._refer_request.end(500)
         return
     registry = MediaStreamRegistry()
     for stream_type in active_media:
         self.streams.append(registry.get(stream_type)())
     self.session = Session(account)
     notification_center.add_observer(self, sender=self.session)
     original_from_header = self._refer_headers.get("From")
     if original_from_header.display_name:
         original_identity = "%s <%s@%s>" % (
             original_from_header.display_name,
             original_from_header.uri.user,
             original_from_header.uri.host,
         )
     else:
         original_identity = "%s@%s" % (original_from_header.uri.user, original_from_header.uri.host)
     from_header = FromHeader(SIPURI.new(self.room_uri), u"Conference Call")
     to_header = ToHeader(self.refer_to_uri)
     extra_headers = []
     if self._refer_headers.get("Referred-By", None) is not None:
         extra_headers.append(Header.new(self._refer_headers.get("Referred-By")))
     else:
         extra_headers.append(Header("Referred-By", str(original_from_header.uri)))
     if ThorNodeConfig.enabled:
         extra_headers.append(Header("Thor-Scope", "conference-invitation"))
     extra_headers.append(Header("X-Originator-From", str(original_from_header.uri)))
     extra_headers.append(SubjectHeader(u"Join conference request from %s" % original_identity))
     route = notification.data.result[0]
     self.session.connect(
         from_header, to_header, route=route, streams=self.streams, is_focus=True, extra_headers=extra_headers
     )
Exemple #4
0
 def _NH_DNSLookupDidSucceed(self, notification):
     notification_center = NotificationCenter()
     notification_center.remove_observer(self, sender=notification.sender)
     account = DefaultAccount()
     conference_application = ConferenceApplication()
     try:
         room = conference_application.get_room(self.room_uri)
     except RoomNotFoundError:
         log.info('Room %s - failed to add %s' %
                  (self.room_uri_str, self.refer_to_uri))
         self._refer_request.end(500)
         return
     active_media = set(room.active_media).intersection(('audio', 'chat'))
     if not active_media:
         log.info('Room %s - failed to add %s' %
                  (self.room_uri_str, self.refer_to_uri))
         self._refer_request.end(500)
         return
     for stream_type in active_media:
         self.streams.append(MediaStreamRegistry.get(stream_type)())
     self.session = Session(account)
     notification_center.add_observer(self, sender=self.session)
     original_from_header = self._refer_headers.get('From')
     if original_from_header.display_name:
         original_identity = "%s <%s@%s>" % (
             original_from_header.display_name,
             original_from_header.uri.user, original_from_header.uri.host)
     else:
         original_identity = "%s@%s" % (original_from_header.uri.user,
                                        original_from_header.uri.host)
     from_header = FromHeader(SIPURI.new(self.room_uri), u'Conference Call')
     to_header = ToHeader(self.refer_to_uri)
     extra_headers = []
     if self._refer_headers.get('Referred-By', None) is not None:
         extra_headers.append(
             Header.new(self._refer_headers.get('Referred-By')))
     else:
         extra_headers.append(
             Header('Referred-By', str(original_from_header.uri)))
     if ThorNodeConfig.enabled:
         extra_headers.append(Header('Thor-Scope', 'conference-invitation'))
     extra_headers.append(
         Header('X-Originator-From', str(original_from_header.uri)))
     extra_headers.append(
         SubjectHeader(u'Join conference request from %s' %
                       original_identity))
     route = notification.data.result[0]
     self.session.connect(from_header,
                          to_header,
                          route=route,
                          streams=self.streams,
                          is_focus=True,
                          extra_headers=extra_headers)
 def resetStream(self):
     self.sessionController.log_debug("Reset stream %s" % self)
     self.notification_center.discard_observer(self, sender=self.stream)
     self.stream = MediaStreamRegistry.VideoStream()
     self.started = False
     self.previous_rx_bytes = 0
     self.previous_tx_bytes = 0
     self.all_rx_bytes = 0
     self.initial_full_screen = False
     self.media_received = False
     self.paused = False
     self.notification_center.add_observer(self, sender=self.stream)
Exemple #6
0
 def new_from_jingle_session(cls, session):
     proposed_stream_types = set([stream.type for stream in session.proposed_streams])
     streams = []
     for stream_type in proposed_stream_types:
         try:
             klass = SIPMediaStreamRegistry.get(stream_type)
         except Exception:
             continue
         streams.append(klass())
     if not streams:
         session.reject('unsupported-applications')
         return None
     session.send_ring_indication()
     instance = cls()
     NotificationCenter().add_observer(instance, sender=session)
     instance._xmpp_identity = session.remote_identity
     instance._sip_identity = session.local_identity
     instance.jingle_session = session
     instance._start_outgoing_sip_session(streams)
     return instance
Exemple #7
0
 def new_from_jingle_session(cls, session):
     proposed_stream_types = set(stream.type for stream in session.proposed_streams)
     streams = []
     for stream_type in proposed_stream_types:
         try:
             klass = SIPMediaStreamRegistry.get(stream_type)
         except Exception:
             continue
         streams.append(klass())
     if not streams:
         session.reject('unsupported-applications')
         return None
     session.send_ring_indication()
     instance = cls()
     NotificationCenter().add_observer(instance, sender=session)
     instance._xmpp_identity = session.remote_identity
     instance._sip_identity = session.local_identity
     instance.jingle_session = session
     instance._start_outgoing_sip_session(streams)
     return instance
 def createStream(self):
     return MediaStreamRegistry.VideoStream()
Exemple #9
0
class ChatSessionHandler(object):
    implements(IObserver)

    sip_identity = WriteOnceAttribute()
    xmpp_identity = WriteOnceAttribute()

    def __init__(self):
        self.started = False
        self.ended = False
        self.sip_session = None
        self.msrp_stream = None
        self._sip_session_timer = None

        self.use_receipts = False
        self.xmpp_session = None
        self._xmpp_message_queue = deque()

        self._pending_msrp_chunks = {}
        self._pending_xmpp_stanzas = {}

    def _set_started(self, value):
        old_value = self.__dict__.get('started', False)
        self.__dict__['started'] = value
        if not old_value and value:
            NotificationCenter().post_notification('ChatSessionDidStart', sender=self)
            self._send_queued_messages()
    def _get_started(self):
        return self.__dict__['started']
    started = property(_get_started, _set_started)
    del _get_started, _set_started

    def _set_xmpp_session(self, session):
        self.__dict__['xmpp_session'] = session
        if session is not None:
            # Reet SIP session timer in case it's active
            if self._sip_session_timer is not None and self._sip_session_timer.active():
                self._sip_session_timer.reset(SESSION_TIMEOUT)
            NotificationCenter().add_observer(self, sender=session)
            session.start()
            # Reet SIP session timer in case it's active
            if self._sip_session_timer is not None and self._sip_session_timer.active():
                self._sip_session_timer.reset(SESSION_TIMEOUT)
    def _get_xmpp_session(self):
        return self.__dict__['xmpp_session']
    xmpp_session = property(_get_xmpp_session, _set_xmpp_session)
    del _get_xmpp_session, _set_xmpp_session

    @classmethod
    def new_from_sip_session(cls, sip_identity, session):
        instance = cls()
        instance.sip_identity = sip_identity
        instance._start_incoming_sip_session(session)
        return instance

    @classmethod
    def new_from_xmpp_stanza(cls, xmpp_identity, recipient):
        instance = cls()
        instance.xmpp_identity = xmpp_identity
        instance._start_outgoing_sip_session(recipient)
        return instance

    @run_in_green_thread
    def _start_incoming_sip_session(self, session):
        self.sip_session = session
        self.msrp_stream = next(stream for stream in session.proposed_streams if stream.type=='chat')
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.sip_session)
        notification_center.add_observer(self, sender=self.msrp_stream)
        self.sip_session.accept([self.msrp_stream])

    @run_in_green_thread
    def _start_outgoing_sip_session(self, target_uri):
        notification_center = NotificationCenter()
        # self.xmpp_identity is our local identity
        from_uri = self.xmpp_identity.uri.as_sip_uri()
        del from_uri.parameters['gr']    # no GRUU in From header
        contact_uri = self.xmpp_identity.uri.as_sip_uri()
        contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
        to_uri = target_uri.as_sip_uri()
        lookup = DNSLookup()
        settings = SIPSimpleSettings()
        account = DefaultAccount()
        if account.sip.outbound_proxy is not None:
            uri = SIPURI(host=account.sip.outbound_proxy.host,
                         port=account.sip.outbound_proxy.port,
                         parameters={'transport': account.sip.outbound_proxy.transport})
        else:
            uri = to_uri
        try:
            routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait()
        except DNSLookupError:
            log.warning('DNS lookup error while looking for %s proxy' % uri)
            notification_center.post_notification('ChatSessionDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
            return
        self.msrp_stream = MediaStreamRegistry().get('chat')()
        route = routes.pop(0)
        from_header = FromHeader(from_uri)
        to_header = ToHeader(to_uri)
        contact_header = ContactHeader(contact_uri)
        self.sip_session = Session(account)
        notification_center.add_observer(self, sender=self.sip_session)
        notification_center.add_observer(self, sender=self.msrp_stream)
        self.sip_session.connect(from_header, to_header, contact_header=contact_header, route=route, streams=[self.msrp_stream])

    def end(self):
        if self.ended:
            return
        if self._sip_session_timer is not None and self._sip_session_timer.active():
            self._sip_session_timer.cancel()
        self._sip_session_timer = None
        notification_center = NotificationCenter()
        if self.sip_session is not None:
            notification_center.remove_observer(self, sender=self.sip_session)
            notification_center.remove_observer(self, sender=self.msrp_stream)
            self.sip_session.end()
            self.sip_session = None
            self.msrp_stream = None
        if self.xmpp_session is not None:
            notification_center.remove_observer(self, sender=self.xmpp_session)
            self.xmpp_session.end()
            self.xmpp_session = None
        self.ended = True
        if self.started:
            notification_center.post_notification('ChatSessionDidEnd', sender=self)
        else:
            notification_center.post_notification('ChatSessionDidFail', sender=self, data=NotificationData(reason='Ended before actually started'))

    def enqueue_xmpp_message(self, message):
        self._xmpp_message_queue.append(message)
        if self.started:
            self._send_queued_messages()

    def _send_queued_messages(self):
        if self._xmpp_message_queue:
            while self._xmpp_message_queue:
                message = self._xmpp_message_queue.popleft()
                if message.body is None:
                    continue
                if not message.use_receipt:
                    success_report = 'no'
                    failure_report = 'no'
                else:
                    success_report = 'yes'
                    failure_report = 'yes'
                sender_uri = message.sender.uri.as_sip_uri()
                sender_uri.parameters['gr'] = encode_resource(sender_uri.parameters['gr'].decode('utf-8'))
                sender = CPIMIdentity(sender_uri)
                self.msrp_stream.send_message(message.body, 'text/plain', sender=sender, message_id=message.id, notify_progress=True, success_report=success_report, failure_report=failure_report)
            self.msrp_stream.send_composing_indication('idle', 30, sender=sender)

    def _inactivity_timeout(self):
        log.msg("Ending SIP session %s due to inactivity" % self.sip_session.call_id)
        self.sip_session.end()

    def handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    def _NH_SIPSessionDidStart(self, notification):
        log.msg("SIP session %s started" % self.sip_session.call_id)
        self._sip_session_timer = reactor.callLater(SESSION_TIMEOUT, self._inactivity_timeout)

        if self.sip_session.direction == 'outgoing':
            # Time to set sip_identity and create the XMPPChatSession
            contact_uri = self.sip_session._invitation.remote_contact_header.uri
            if contact_uri.parameters.get('gr') is not None:
                sip_leg_uri = FrozenURI(contact_uri.user, contact_uri.host, contact_uri.parameters.get('gr'))
            else:
                tmp = self.sip_session.remote_identity.uri
                sip_leg_uri = FrozenURI(tmp.user, tmp.host, generate_sylk_resource())
            self.sip_identity = Identity(sip_leg_uri, self.sip_session.remote_identity.display_name)
            session = XMPPChatSession(local_identity=self.sip_identity, remote_identity=self.xmpp_identity)
            self.xmpp_session = session
            # Session is now established on both ends
            self.started = True
            # Try to wakeup XMPP clients
            self.xmpp_session.send_composing_indication('active')
            self.xmpp_session.send_message(' ', 'text/plain')
        else:
            if self.xmpp_session is not None:
                # Session is now established on both ends
                self.started = True
                # Try to wakeup XMPP clients
                self.xmpp_session.send_composing_indication('active')
                self.xmpp_session.send_message(' ', 'text/plain')
            else:
                # Try to wakeup XMPP clients
                sender = self.sip_identity
                tmp = self.sip_session.local_identity.uri
                recipient_uri = FrozenURI(tmp.user, tmp.host)
                recipient = Identity(recipient_uri)
                xmpp_manager = XMPPManager()
                xmpp_manager.send_stanza(ChatMessage(sender, recipient, ' ', 'text/plain'))
                # Send queued messages
                self._send_queued_messages()

    def _NH_SIPSessionDidEnd(self, notification):
        log.msg("SIP session %s ended" % self.sip_session.call_id)
        notification.center.remove_observer(self, sender=self.sip_session)
        notification.center.remove_observer(self, sender=self.msrp_stream)
        self.sip_session = None
        self.msrp_stream = None
        self.end()

    def _NH_SIPSessionDidFail(self, notification):
        log.msg("SIP session %s failed" % self.sip_session.call_id)
        notification.center.remove_observer(self, sender=self.sip_session)
        notification.center.remove_observer(self, sender=self.msrp_stream)
        self.sip_session = None
        self.msrp_stream = None
        self.end()

    def _NH_SIPSessionNewProposal(self, notification):
        if notification.data.originator == 'remote':
            self.sip_session.reject_proposal()

    def _NH_SIPSessionTransferNewIncoming(self, notification):
        self.sip_session.reject_transfer(403)

    def _NH_ChatStreamGotMessage(self, notification):
        # Notification is sent by the MSRP stream
        message = notification.data.message
        content_type = message.content_type.lower()
        if content_type not in ('text/plain', 'text/html'):
            return
        if content_type == 'text/plain':
            html_body = None
            body = message.body
        else:
            html_body = message.body
            body = None
        if self._sip_session_timer is not None and self._sip_session_timer.active():
            self._sip_session_timer.reset(SESSION_TIMEOUT)
        chunk = notification.data.chunk
        if self.started:
            self.xmpp_session.send_message(body, html_body, message_id=chunk.message_id)
            if self.use_receipts:
                self._pending_msrp_chunks[chunk.message_id] = chunk
            else:
                self.msrp_stream.msrp_session.send_report(chunk, 200, 'OK')
        else:
            sender = self.sip_identity
            recipient_uri = FrozenURI.parse(message.recipients[0].uri)
            recipient = Identity(recipient_uri, message.recipients[0].display_name)
            xmpp_manager = XMPPManager()
            xmpp_manager.send_stanza(ChatMessage(sender, recipient, body, html_body))
            self.msrp_stream.msrp_session.send_report(chunk, 200, 'OK')

    def _NH_ChatStreamGotComposingIndication(self, notification):
        # Notification is sent by the MSRP stream
        if self._sip_session_timer is not None and self._sip_session_timer.active():
            self._sip_session_timer.reset(SESSION_TIMEOUT)
        if not self.started:
            return
        state = None
        if notification.data.state == 'active':
            state = 'composing'
        elif notification.data.state == 'idle':
            state = 'paused'
        if state is not None:
            self.xmpp_session.send_composing_indication(state)

    def _NH_ChatStreamDidDeliverMessage(self, notification):
        if self.started:
            message = self._pending_xmpp_stanzas.pop(notification.data.message_id, None)
            if message is not None:
                self.xmpp_session.send_receipt_acknowledgement(message.id)

    def _NH_ChatStreamDidNotDeliverMessage(self, notification):
        if self.started:
            message = self._pending_xmpp_stanzas.pop(notification.data.message_id, None)
            if message is not None:
                self.xmpp_session.send_error(message, 'TODO', [])    # TODO

    def _NH_XMPPChatSessionDidStart(self, notification):
        if self.sip_session is not None:
            # Session is now established on both ends
            self.started = True

    def _NH_XMPPChatSessionDidEnd(self, notification):
        notification.center.remove_observer(self, sender=self.xmpp_session)
        self.xmpp_session = None
        self.end()

    def _NH_XMPPChatSessionGotMessage(self, notification):
        if self.sip_session is None or self.sip_session.state != 'connected':
            self._xmpp_message_queue.append(notification.data.message)
            return
        if self._sip_session_timer is not None and self._sip_session_timer.active():
            self._sip_session_timer.reset(SESSION_TIMEOUT)
        message = notification.data.message
        sender_uri = message.sender.uri.as_sip_uri()
        del sender_uri.parameters['gr']    # no GRUU in CPIM From header
        sender = CPIMIdentity(sender_uri)
        self.use_receipts = message.use_receipt
        if not message.use_receipt:
            success_report = 'no'
            failure_report = 'no'
        else:
            success_report = 'yes'
            failure_report = 'yes'
            self._pending_xmpp_stanzas[message.id] = message
        # Prefer plaintext
        self.msrp_stream.send_message(message.body, 'text/plain', sender=sender, message_id=message.id, notify_progress=True, success_report=success_report, failure_report=failure_report)
        self.msrp_stream.send_composing_indication('idle', 30, sender=sender)

    def _NH_XMPPChatSessionGotComposingIndication(self, notification):
        if self.sip_session is None or self.sip_session.state != 'connected':
            return
        if self._sip_session_timer is not None and self._sip_session_timer.active():
            self._sip_session_timer.reset(SESSION_TIMEOUT)
        message = notification.data.message
        state = None
        if message.state == 'composing':
            state = 'active'
        elif message.state == 'paused':
            state = 'idle'
        if state is not None:
            sender_uri = message.sender.uri.as_sip_uri()
            del sender_uri.parameters['gr']    # no GRUU in CPIM From header
            sender = CPIMIdentity(sender_uri)
            self.msrp_stream.send_composing_indication(state, 30, sender=sender)
            if message.use_receipt:
                self.xmpp_session.send_receipt_acknowledgement(message.id)

    def _NH_XMPPChatSessionDidDeliverMessage(self, notification):
        chunk = self._pending_msrp_chunks.pop(notification.data.message_id, None)
        if chunk is not None:
            self.msrp_stream.msrp_session.send_report(chunk, 200, 'OK')

    def _NH_XMPPChatSessionDidNotDeliverMessage(self, notification):
        chunk = self._pending_msrp_chunks.pop(notification.data.message_id, None)
        if chunk is not None:
            self.msrp_stream.msrp_session.send_report(chunk, notification.data.code, notification.data.reason)