Esempio n. 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])
Esempio n. 2
0
 def _start_outgoing_sip_session(self, streams):
     notification_center = NotificationCenter()
     # self.xmpp_identity is our local identity on the SIP side
     from_uri = self.xmpp_identity.uri.as_sip_uri()
     from_uri.parameters.pop('gr', None)    # no GRUU in From header
     to_uri = self.sip_identity.uri.as_sip_uri()
     to_uri.parameters.pop('gr', None)      # no GRUU in To header
     # TODO: need to fix GRUU in the proxy
     #contact_uri = self.xmpp_identity.uri.as_sip_uri()
     #contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
     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('MedialSessionHandlerDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
         return
     route = routes.pop(0)
     from_header = FromHeader(from_uri)
     to_header = ToHeader(to_uri)
     self.sip_session = Session(account)
     notification_center.add_observer(self, sender=self.sip_session)
     self.sip_session.connect(from_header, to_header, route=route, streams=streams)
Esempio n. 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.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)
Esempio n. 4
0
    def start(self):
        notification_center = NotificationCenter()
        self.greenlet = api.getcurrent()
        settings = SIPSimpleSettings()
        account = AccountManager().sylkserver_account
        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 = self.destination_uri
        lookup = DNSLookup()
        try:
            routes = lookup.lookup_sip_proxy(
                uri, settings.sip.transport_list).wait()
        except DNSLookupError:
            notification_center.post_notification(
                'OutgoingFileTransferHandlerDidFail', sender=self)
            return

        self.session = Session(account)
        self.stream = FileTransferStream(self.file_selector, 'sendonly')
        notification_center.add_observer(self, sender=self.session)
        notification_center.add_observer(self, sender=self.stream)
        from_header = FromHeader(self.sender_uri, u'SIPStache File Transfer')
        to_header = ToHeader(self.destination_uri)
        transport = routes[0].transport
        parameters = {} if transport == 'udp' else {'transport': transport}
        contact_header = ContactHeader(
            SIPURI(user=self.sender_uri.user,
                   host=SIPConfig.local_ip.normalized,
                   port=getattr(Engine(), '%s_port' % transport),
                   parameters=parameters))
        extra_headers = []
        if ThorNodeConfig.enabled:
            extra_headers.append(Header('Thor-Scope', 'sipstache-file'))
        extra_headers.append(
            Header('X-Originator-From', str(self.destination_uri)))
        self.session.connect(from_header,
                             to_header,
                             contact_header=contact_header,
                             routes=routes,
                             streams=[self.stream],
                             is_focus=False,
                             extra_headers=extra_headers)
        notification_center.post_notification(
            'OutgoingFileTransferHandlerDidStart', sender=self)
Esempio n. 5
0
 def _start_outgoing_sip_session(self, streams):
     notification_center = NotificationCenter()
     # self.xmpp_identity is our local identity on the SIP side
     from_uri = self.xmpp_identity.uri.as_sip_uri()
     from_uri.parameters.pop('gr', None)    # no GRUU in From header
     to_uri = self.sip_identity.uri.as_sip_uri()
     to_uri.parameters.pop('gr', None)      # no GRUU in To header
     # TODO: need to fix GRUU in the proxy
     #contact_uri = self.xmpp_identity.uri.as_sip_uri()
     #contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
     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('MedialSessionHandlerDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
         return
     route = routes.pop(0)
     from_header = FromHeader(from_uri)
     to_header = ToHeader(to_uri)
     self.sip_session = Session(account)
     notification_center.add_observer(self, sender=self.sip_session)
     self.sip_session.connect(from_header, to_header, route=route, streams=streams)
Esempio n. 6
0
    def start(self):
        notification_center = NotificationCenter()
        self.greenlet = api.getcurrent()
        settings = SIPSimpleSettings()
        account = AccountManager().sylkserver_account
        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 = self.destination_uri
        lookup = DNSLookup()
        try:
            routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait()
        except DNSLookupError:
            notification_center.post_notification('OutgoingFileTransferHandlerDidFail', sender=self)
            return

        self.session = Session(account)
        self.stream = FileTransferStream(self.file_selector, 'sendonly')
        notification_center.add_observer(self, sender=self.session)
        notification_center.add_observer(self, sender=self.stream)
        from_header = FromHeader(self.sender_uri, u'SIPStache File Transfer')
        to_header = ToHeader(self.destination_uri)
        transport = routes[0].transport
        parameters = {} if transport=='udp' else {'transport': transport}
        contact_header = ContactHeader(SIPURI(user=self.sender_uri.user, host=SIPConfig.local_ip.normalized, port=getattr(Engine(), '%s_port' % transport), parameters=parameters))
        extra_headers = []
        if ThorNodeConfig.enabled:
            extra_headers.append(Header('Thor-Scope', 'sipstache-file'))
        extra_headers.append(Header('X-Originator-From', str(self.destination_uri)))
        self.session.connect(from_header, to_header, contact_header=contact_header, routes=routes, streams=[self.stream], is_focus=False, extra_headers=extra_headers)
        notification_center.post_notification('OutgoingFileTransferHandlerDidStart', sender=self)
Esempio n. 7
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])
Esempio n. 8
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
     )
Esempio n. 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 _get_started(self):
        return self.__dict__['started']

    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()

    started = property(_get_started, _set_started)
    del _get_started, _set_started

    def _get_xmpp_session(self):
        return self.__dict__['xmpp_session']

    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)

    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):
        sender = None
        while self._xmpp_message_queue:
            message = self._xmpp_message_queue.popleft()
            if message.body is None:
                continue
            sender_uri = message.sender.uri.as_sip_uri()
            sender_uri.parameters['gr'] = encode_resource(
                sender_uri.parameters['gr'].decode('utf-8'))
            sender = ChatIdentity(sender_uri)
            self.msrp_stream.send_message(message.body,
                                          'text/plain',
                                          sender=sender,
                                          message_id=str(message.id),
                                          notify_progress=message.use_receipt)
        if sender:
            self.msrp_stream.send_composing_indication('idle',
                                                       30,
                                                       sender=sender)

    def _inactivity_timeout(self):
        log.info("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.info("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.info("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.info("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.content
        else:
            html_body = message.content
            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 = ChatIdentity(sender_uri)
        self.use_receipts = message.use_receipt
        if not message.use_receipt:
            notify_progress = False
        else:
            notify_progress = True
            self._pending_xmpp_stanzas[message.id] = message
        # Prefer plaintext
        self.msrp_stream.send_message(message.body,
                                      'text/plain',
                                      sender=sender,
                                      message_id=str(message.id),
                                      notify_progress=notify_progress)
        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 = ChatIdentity(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)
Esempio n. 10
0
class MediaSessionHandler(object):
    implements(IObserver)

    def __init__(self):
        self.started = False
        self.ended = False

        self._sip_identity = None
        self._xmpp_identity = None

        self._audio_bidge = AudioConference()
        self.sip_session = None
        self.jingle_session = None

    @classmethod
    def new_from_sip_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 = JingleMediaStreamRegistry.get(stream_type)
            except Exception:
                continue
            streams.append(klass())
        if not streams:
            session.reject(488)
            return None
        session.send_ring_indication()
        instance = cls()
        NotificationCenter().add_observer(instance, sender=session)
        # Get URI representing the SIP side
        contact_uri = 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 = session.remote_identity.uri
            sip_leg_uri = FrozenURI(tmp.user, tmp.host,
                                    generate_sylk_resource())
        instance._sip_identity = Identity(sip_leg_uri)
        # Get URI representing the XMPP side
        request_uri = session.request_uri
        remote_resource = request_uri.parameters.get('gr', None)
        if remote_resource is not None:
            try:
                remote_resource = decode_resource(remote_resource)
            except (TypeError, UnicodeError):
                remote_resource = None
        xmpp_leg_uri = FrozenURI(request_uri.user, request_uri.host,
                                 remote_resource)
        instance._xmpp_identity = Identity(xmpp_leg_uri)
        instance.sip_session = session
        instance._start_outgoing_jingle_session(streams)
        return instance

    @classmethod
    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

    @property
    def sip_identity(self):
        return self._sip_identity

    @property
    def xmpp_identity(self):
        return self._xmpp_identity

    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(
                'MediaSessionHandlerDidStart', sender=self)

    def _get_started(self):
        return self.__dict__['started']

    started = property(_get_started, _set_started)
    del _get_started, _set_started

    @run_in_green_thread
    def _start_outgoing_sip_session(self, streams):
        notification_center = NotificationCenter()
        # self.xmpp_identity is our local identity on the SIP side
        from_uri = self.xmpp_identity.uri.as_sip_uri()
        from_uri.parameters.pop('gr', None)  # no GRUU in From header
        to_uri = self.sip_identity.uri.as_sip_uri()
        to_uri.parameters.pop('gr', None)  # no GRUU in To header
        # TODO: need to fix GRUU in the proxy
        #contact_uri = self.xmpp_identity.uri.as_sip_uri()
        #contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
        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(
                'MedialSessionHandlerDidFail',
                sender=self,
                data=NotificationData(reason='DNS lookup error'))
            return
        route = routes.pop(0)
        from_header = FromHeader(from_uri)
        to_header = ToHeader(to_uri)
        self.sip_session = Session(account)
        notification_center.add_observer(self, sender=self.sip_session)
        self.sip_session.connect(from_header,
                                 to_header,
                                 route=route,
                                 streams=streams)

    @run_in_green_thread
    def _start_outgoing_jingle_session(self, streams):
        if self.xmpp_identity.uri.resource is not None:
            self.sip_session.reject()
            return
        xmpp_manager = XMPPManager()
        local_jid = self.sip_identity.uri.as_xmpp_jid()
        remote_jid = self.xmpp_identity.uri.as_xmpp_jid()

        # If this was an invitation to a conference, use the information in the Referred-By header
        if self.sip_identity.uri.host in xmpp_manager.muc_domains and self.sip_session.transfer_info and self.sip_session.transfer_info.referred_by:
            try:
                referred_by_uri = SIPURI.parse(
                    self.sip_session.transfer_info.referred_by)
            except SIPCoreError:
                self.sip_session.reject(488)
                return
            else:
                inviter_uri = FrozenURI(referred_by_uri.user,
                                        referred_by_uri.host)
                local_jid = inviter_uri.as_xmpp_jid()

        # Use disco to gather potential JIDs to call
        d = xmpp_manager.disco_client_protocol.requestItems(remote_jid,
                                                            sender=local_jid)
        try:
            items = block_on(d)
        except Exception:
            items = []
        if not items:
            self.sip_session.reject(480)
            return

        # Check which items support Jingle
        valid = []
        for item in items:
            d = xmpp_manager.disco_client_protocol.requestInfo(
                item.entity,
                nodeIdentifier=item.nodeIdentifier,
                sender=local_jid)
            try:
                info = block_on(d)
            except Exception:
                continue
            if jingle.NS_JINGLE in info.features and jingle.NS_JINGLE_APPS_RTP in info.features:
                valid.append(item.entity)
        if not valid:
            self.sip_session.reject(480)
            return

        # TODO: start multiple sessions?
        self._xmpp_identity = Identity(FrozenURI.parse(valid[0]))

        notification_center = NotificationCenter()
        if self.sip_identity.uri.host in xmpp_manager.muc_domains:
            self.jingle_session = JingleSession(
                xmpp_manager.jingle_coin_protocol)
        else:
            self.jingle_session = JingleSession(xmpp_manager.jingle_protocol)
        notification_center.add_observer(self, sender=self.jingle_session)
        self.jingle_session.connect(self.sip_identity,
                                    self.xmpp_identity,
                                    streams,
                                    is_focus=self.sip_session.remote_focus)

    def end(self):
        if self.ended:
            return
        notification_center = NotificationCenter()
        if self.sip_session is not None:
            notification_center.remove_observer(self, sender=self.sip_session)
            if self.sip_session.direction == 'incoming' and not self.started:
                self.sip_session.reject()
            else:
                self.sip_session.end()
            self.sip_session = None
        if self.jingle_session is not None:
            notification_center.remove_observer(self,
                                                sender=self.jingle_session)
            if self.jingle_session.direction == 'incoming' and not self.started:
                self.jingle_session.reject()
            else:
                self.jingle_session.end()
            self.jingle_session = None
        self.ended = True
        if self.started:
            notification_center.post_notification('MediaSessionHandlerDidEnd',
                                                  sender=self)
        else:
            notification_center.post_notification('MediaSessionHandlerDidFail',
                                                  sender=self)

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

    def _NH_SIPSessionDidStart(self, notification):
        log.info("SIP session %s started" % self.sip_session.call_id)
        if self.sip_session.direction == 'outgoing':
            # Time to accept the Jingle session and bridge them together
            try:
                audio_stream = next(stream
                                    for stream in self.sip_session.streams
                                    if stream.type == 'audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
            self.jingle_session.accept(self.jingle_session.proposed_streams,
                                       is_focus=self.sip_session.remote_focus)
        else:
            # Both sessions have been accepted now
            self.started = True
            try:
                audio_stream = next(stream
                                    for stream in self.sip_session.streams
                                    if stream.type == 'audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)

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

    def _NH_SIPSessionDidFail(self, notification):
        log.info("SIP session %s failed (%s)" %
                 (self.sip_session.call_id, notification.data.reason))
        notification.center.remove_observer(self, sender=self.sip_session)
        self.sip_session = 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_SIPSessionDidChangeHoldState(self, notification):
        if notification.data.originator == 'remote':
            if notification.data.on_hold:
                self.jingle_session.hold()
            else:
                self.jingle_session.unhold()

    def _NH_SIPSessionGotConferenceInfo(self, notification):
        self.jingle_session._send_conference_info(
            notification.data.conference_info.toxml())

    def _NH_JingleSessionDidStart(self, notification):
        log.info("Jingle session %s started" % notification.sender.id)
        if self.jingle_session.direction == 'incoming':
            # Both sessions have been accepted now
            self.started = True
            try:
                audio_stream = next(stream
                                    for stream in self.jingle_session.streams
                                    if stream.type == 'audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
        else:
            # Time to accept the Jingle session and bridge them together
            try:
                audio_stream = next(stream
                                    for stream in self.jingle_session.streams
                                    if stream.type == 'audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
            self.sip_session.accept(self.sip_session.proposed_streams)

    def _NH_JingleSessionDidEnd(self, notification):
        log.info("Jingle session %s ended" % notification.sender.id)
        notification.center.remove_observer(self, sender=self.jingle_session)
        self.jingle_session = None
        self.end()

    def _NH_JingleSessionDidFail(self, notification):
        log.info("Jingle session %s failed (%s)" %
                 (notification.sender.id, notification.data.reason))
        notification.center.remove_observer(self, sender=self.jingle_session)
        self.jingle_session = None
        self.end()

    def _NH_JingleSessionDidChangeHoldState(self, notification):
        if notification.data.originator == 'remote':
            if notification.data.on_hold:
                self.sip_session.hold()
            else:
                self.sip_session.unhold()
Esempio n. 11
0
class IncomingReferralHandler(object):
    implements(IObserver)

    def __init__(self, refer_request, data):
        self._refer_request = refer_request
        self._refer_headers = data.headers
        self.room_uri = data.request_uri
        self.room_uri_str = "%s@%s" % (self.room_uri.user, self.room_uri.host)
        self.refer_to_uri = re.sub("<|>", "", data.headers.get("Refer-To").uri)
        self.method = data.headers.get("Refer-To").parameters.get("method", "INVITE").upper()
        self.session = None
        self.streams = []

    def start(self):
        if not self.refer_to_uri.startswith(("sip:", "sips:")):
            self.refer_to_uri = "sip:%s" % self.refer_to_uri
        try:
            self.refer_to_uri = SIPURI.parse(self.refer_to_uri)
        except SIPCoreError:
            log.msg("Room %s - failed to add %s" % (self.room_uri_str, self.refer_to_uri))
            self._refer_request.reject(488)
            return
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self._refer_request)
        if self.method == "INVITE":
            self._refer_request.accept()
            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 = self.refer_to_uri
            lookup = DNSLookup()
            notification_center.add_observer(self, sender=lookup)
            lookup.lookup_sip_proxy(uri, settings.sip.transport_list)
        elif self.method == "BYE":
            log.msg(
                "Room %s - %s removed %s from the room"
                % (self.room_uri_str, self._refer_headers.get("From").uri, self.refer_to_uri)
            )
            self._refer_request.accept()
            conference_application = ConferenceApplication()
            conference_application.remove_participant(self.refer_to_uri, self.room_uri)
            self._refer_request.end(200)
        else:
            self._refer_request.reject(488)

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

    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
        )

    def _NH_DNSLookupDidFail(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)

    def _NH_SIPSessionGotRingIndication(self, notification):
        if self._refer_request is not None:
            self._refer_request.send_notify(180)

    def _NH_SIPSessionGotProvisionalResponse(self, notification):
        if self._refer_request is not None:
            self._refer_request.send_notify(notification.data.code, notification.data.reason)

    def _NH_SIPSessionDidStart(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(200)
        conference_application = ConferenceApplication()
        conference_application.add_participant(self.session, self.room_uri)
        log.msg("Room %s - %s added %s" % (self.room_uri_str, self._refer_headers.get("From").uri, self.refer_to_uri))
        self.session = None
        self.streams = []

    def _NH_SIPSessionDidFail(self, notification):
        log.msg("Room %s - failed to add %s: %s" % (self.room_uri_str, self.refer_to_uri, notification.data.reason))
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(notification.data.code or 500, notification.data.reason or notification.data.code)
        self.session = None
        self.streams = []

    def _NH_SIPSessionDidEnd(self, notification):
        # If any stream fails to start we won't get SIPSessionDidFail, we'll get here instead
        log.msg("Room %s - failed to add %s" % (self.room_uri_str, self.refer_to_uri))
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(200)
        self.session = None
        self.streams = []

    def _NH_SIPIncomingReferralDidEnd(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
        self._refer_request = None
Esempio n. 12
0
class MediaSessionHandler(object):
    implements(IObserver)

    def __init__(self):
        self.started = False
        self.ended = False

        self._sip_identity = None
        self._xmpp_identity = None

        self._audio_bidge = AudioConference()
        self.sip_session = None
        self.jingle_session = None

    @classmethod
    def new_from_sip_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 = JingleMediaStreamRegistry.get(stream_type)
            except Exception:
                continue
            streams.append(klass())
        if not streams:
            session.reject(488)
            return None
        session.send_ring_indication()
        instance = cls()
        NotificationCenter().add_observer(instance, sender=session)
        # Get URI representing the SIP side
        contact_uri = 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 = session.remote_identity.uri
            sip_leg_uri = FrozenURI(tmp.user, tmp.host, generate_sylk_resource())
        instance._sip_identity = Identity(sip_leg_uri)
        # Get URI representing the XMPP side
        request_uri = session.request_uri
        remote_resource = request_uri.parameters.get('gr', None)
        if remote_resource is not None:
            try:
                remote_resource = decode_resource(remote_resource)
            except (TypeError, UnicodeError):
                remote_resource = None
        xmpp_leg_uri = FrozenURI(request_uri.user, request_uri.host, remote_resource)
        instance._xmpp_identity = Identity(xmpp_leg_uri)
        instance.sip_session = session
        instance._start_outgoing_jingle_session(streams)
        return instance

    @classmethod
    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

    @property
    def sip_identity(self):
        return self._sip_identity

    @property
    def xmpp_identity(self):
        return self._xmpp_identity

    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('MediaSessionHandlerDidStart', sender=self)
    def _get_started(self):
        return self.__dict__['started']
    started = property(_get_started, _set_started)
    del _get_started, _set_started

    @run_in_green_thread
    def _start_outgoing_sip_session(self, streams):
        notification_center = NotificationCenter()
        # self.xmpp_identity is our local identity on the SIP side
        from_uri = self.xmpp_identity.uri.as_sip_uri()
        from_uri.parameters.pop('gr', None)    # no GRUU in From header
        to_uri = self.sip_identity.uri.as_sip_uri()
        to_uri.parameters.pop('gr', None)      # no GRUU in To header
        # TODO: need to fix GRUU in the proxy
        #contact_uri = self.xmpp_identity.uri.as_sip_uri()
        #contact_uri.parameters['gr'] = encode_resource(contact_uri.parameters['gr'].decode('utf-8'))
        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('MedialSessionHandlerDidFail', sender=self, data=NotificationData(reason='DNS lookup error'))
            return
        route = routes.pop(0)
        from_header = FromHeader(from_uri)
        to_header = ToHeader(to_uri)
        self.sip_session = Session(account)
        notification_center.add_observer(self, sender=self.sip_session)
        self.sip_session.connect(from_header, to_header, route=route, streams=streams)

    @run_in_green_thread
    def _start_outgoing_jingle_session(self, streams):
        if self.xmpp_identity.uri.resource is not None:
            self.sip_session.reject()
            return
        xmpp_manager = XMPPManager()
        local_jid = self.sip_identity.uri.as_xmpp_jid()
        remote_jid = self.xmpp_identity.uri.as_xmpp_jid()

        # If this was an invitation to a conference, use the information in the Referred-By header
        if self.sip_identity.uri.host in xmpp_manager.muc_domains and self.sip_session.transfer_info and self.sip_session.transfer_info.referred_by:
            try:
                referred_by_uri = SIPURI.parse(self.sip_session.transfer_info.referred_by)
            except SIPCoreError:
                self.sip_session.reject(488)
                return
            else:
                inviter_uri = FrozenURI(referred_by_uri.user, referred_by_uri.host)
                local_jid = inviter_uri.as_xmpp_jid()

        # Use disco to gather potential JIDs to call
        d = xmpp_manager.disco_client_protocol.requestItems(remote_jid, sender=local_jid)
        try:
            items = block_on(d)
        except Exception:
            items = []
        if not items:
            self.sip_session.reject(480)
            return

        # Check which items support Jingle
        valid = []
        for item in items:
            d = xmpp_manager.disco_client_protocol.requestInfo(item.entity, nodeIdentifier=item.nodeIdentifier, sender=local_jid)
            try:
                info = block_on(d)
            except Exception:
                continue
            if jingle.NS_JINGLE in info.features and jingle.NS_JINGLE_APPS_RTP in info.features:
                valid.append(item.entity)
        if not valid:
            self.sip_session.reject(480)
            return

        # TODO: start multiple sessions?
        self._xmpp_identity = Identity(FrozenURI.parse(valid[0]))

        notification_center = NotificationCenter()
        if self.sip_identity.uri.host in xmpp_manager.muc_domains:
            self.jingle_session = JingleSession(xmpp_manager.jingle_coin_protocol)
        else:
            self.jingle_session = JingleSession(xmpp_manager.jingle_protocol)
        notification_center.add_observer(self, sender=self.jingle_session)
        self.jingle_session.connect(self.sip_identity, self.xmpp_identity, streams, is_focus=self.sip_session.remote_focus)

    def end(self):
        if self.ended:
            return
        notification_center = NotificationCenter()
        if self.sip_session is not None:
            notification_center.remove_observer(self, sender=self.sip_session)
            if self.sip_session.direction == 'incoming' and not self.started:
                self.sip_session.reject()
            else:
                self.sip_session.end()
            self.sip_session = None
        if self.jingle_session is not None:
            notification_center.remove_observer(self, sender=self.jingle_session)
            if self.jingle_session.direction == 'incoming' and not self.started:
                self.jingle_session.reject()
            else:
                self.jingle_session.end()
            self.jingle_session = None
        self.ended = True
        if self.started:
            notification_center.post_notification('MediaSessionHandlerDidEnd', sender=self)
        else:
            notification_center.post_notification('MediaSessionHandlerDidFail', sender=self)

    @run_in_twisted_thread
    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)
        if self.sip_session.direction == 'outgoing':
            # Time to accept the Jingle session and bridge them together
            try:
                audio_stream = next(stream for stream in self.sip_session.streams if stream.type=='audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
            self.jingle_session.accept(self.jingle_session.proposed_streams, is_focus=self.sip_session.remote_focus)
        else:
            # Both sessions have been accepted now
            self.started = True
            try:
                audio_stream = next(stream for stream in self.sip_session.streams if stream.type=='audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)

    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)
        self.sip_session = None
        self.end()

    def _NH_SIPSessionDidFail(self, notification):
        log.msg("SIP session %s failed (%s)" % (self.sip_session.call_id, notification.data.reason))
        notification.center.remove_observer(self, sender=self.sip_session)
        self.sip_session = 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_SIPSessionDidChangeHoldState(self, notification):
        if notification.data.originator == 'remote':
            if notification.data.on_hold:
                self.jingle_session.hold()
            else:
                self.jingle_session.unhold()

    def _NH_SIPSessionGotConferenceInfo(self, notification):
        self.jingle_session._send_conference_info(notification.data.conference_info.toxml())

    def _NH_JingleSessionDidStart(self, notification):
        log.msg("Jingle session %s started" % notification.sender.id)
        if self.jingle_session.direction == 'incoming':
            # Both sessions have been accepted now
            self.started = True
            try:
                audio_stream = next(stream for stream in self.jingle_session.streams if stream.type=='audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
        else:
            # Time to accept the Jingle session and bridge them together
            try:
                audio_stream = next(stream for stream in self.jingle_session.streams if stream.type=='audio')
            except StopIteration:
                pass
            else:
                self._audio_bidge.add(audio_stream)
            self.sip_session.accept(self.sip_session.proposed_streams)

    def _NH_JingleSessionDidEnd(self, notification):
        log.msg("Jingle session %s ended" % notification.sender.id)
        notification.center.remove_observer(self, sender=self.jingle_session)
        self.jingle_session = None
        self.end()

    def _NH_JingleSessionDidFail(self, notification):
        log.msg("Jingle session %s failed (%s)" % (notification.sender.id, notification.data.reason))
        notification.center.remove_observer(self, sender=self.jingle_session)
        self.jingle_session = None
        self.end()

    def _NH_JingleSessionDidChangeHoldState(self, notification):
        if notification.data.originator == 'remote':
            if notification.data.on_hold:
                self.sip_session.hold()
            else:
                self.sip_session.unhold()
Esempio n. 13
0
class IncomingReferralHandler(object):
    implements(IObserver)

    def __init__(self, refer_request, data):
        self._refer_request = refer_request
        self._refer_headers = data.headers
        self.room_uri = data.request_uri
        self.room_uri_str = '%s@%s' % (self.room_uri.user, self.room_uri.host)
        self.refer_to_uri = re.sub('<|>', '', data.headers.get('Refer-To').uri)
        self.method = data.headers.get('Refer-To').parameters.get(
            'method', 'INVITE').upper()
        self.session = None
        self.streams = []

    def start(self):
        if not self.refer_to_uri.startswith(('sip:', 'sips:')):
            self.refer_to_uri = 'sip:%s' % self.refer_to_uri
        try:
            self.refer_to_uri = SIPURI.parse(self.refer_to_uri)
        except SIPCoreError:
            log.info('Room %s - failed to add %s' %
                     (self.room_uri_str, self.refer_to_uri))
            self._refer_request.reject(488)
            return
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self._refer_request)
        if self.method == 'INVITE':
            self._refer_request.accept()
            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 = self.refer_to_uri
            lookup = DNSLookup()
            notification_center.add_observer(self, sender=lookup)
            lookup.lookup_sip_proxy(uri, settings.sip.transport_list)
        elif self.method == 'BYE':
            log.info('Room %s - %s removed %s from the room' %
                     (self.room_uri_str, self._refer_headers.get('From').uri,
                      self.refer_to_uri))
            self._refer_request.accept()
            conference_application = ConferenceApplication()
            conference_application.remove_participant(self.refer_to_uri,
                                                      self.room_uri)
            self._refer_request.end(200)
        else:
            self._refer_request.reject(488)

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

    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 _NH_DNSLookupDidFail(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)

    def _NH_SIPSessionGotRingIndication(self, notification):
        if self._refer_request is not None:
            self._refer_request.send_notify(180)

    def _NH_SIPSessionGotProvisionalResponse(self, notification):
        if self._refer_request is not None:
            self._refer_request.send_notify(notification.data.code,
                                            notification.data.reason)

    def _NH_SIPSessionDidStart(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(200)
        conference_application = ConferenceApplication()
        conference_application.add_participant(self.session, self.room_uri)
        log.info('Room %s - %s added %s' %
                 (self.room_uri_str, self._refer_headers.get('From').uri,
                  self.refer_to_uri))
        self.session = None
        self.streams = []

    def _NH_SIPSessionDidFail(self, notification):
        log.info(
            'Room %s - failed to add %s: %s' %
            (self.room_uri_str, self.refer_to_uri, notification.data.reason))
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(
                notification.data.code or 500, notification.data.reason
                or notification.data.code)
        self.session = None
        self.streams = []

    def _NH_SIPSessionDidEnd(self, notification):
        # If any stream fails to start we won't get SIPSessionDidFail, we'll get here instead
        log.info('Room %s - failed to add %s' %
                 (self.room_uri_str, self.refer_to_uri))
        notification.center.remove_observer(self, sender=notification.sender)
        if self._refer_request is not None:
            self._refer_request.end(200)
        self.session = None
        self.streams = []

    def _NH_SIPIncomingReferralDidEnd(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
        self._refer_request = None
Esempio n. 14
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)
Esempio n. 15
0
class OutgoingFileTransferHandler(object):
    implements(IObserver)

    def __init__(self, sender_uri, destination_uri, file_selector):
        self.sender_uri = sender_uri
        self.destination_uri = destination_uri
        self.file_selector = file_selector
        self.session = None
        self.stream = None
        self.timer = None
        self.success = False

    @run_in_green_thread
    def start(self):
        notification_center = NotificationCenter()
        self.greenlet = api.getcurrent()
        settings = SIPSimpleSettings()
        account = AccountManager().sylkserver_account
        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 = self.destination_uri
        lookup = DNSLookup()
        try:
            routes = lookup.lookup_sip_proxy(
                uri, settings.sip.transport_list).wait()
        except DNSLookupError:
            notification_center.post_notification(
                'OutgoingFileTransferHandlerDidFail', sender=self)
            return

        self.session = Session(account)
        self.stream = FileTransferStream(self.file_selector, 'sendonly')
        notification_center.add_observer(self, sender=self.session)
        notification_center.add_observer(self, sender=self.stream)
        from_header = FromHeader(self.sender_uri, u'SIPStache File Transfer')
        to_header = ToHeader(self.destination_uri)
        transport = routes[0].transport
        parameters = {} if transport == 'udp' else {'transport': transport}
        contact_header = ContactHeader(
            SIPURI(user=self.sender_uri.user,
                   host=SIPConfig.local_ip.normalized,
                   port=getattr(Engine(), '%s_port' % transport),
                   parameters=parameters))
        extra_headers = []
        if ThorNodeConfig.enabled:
            extra_headers.append(Header('Thor-Scope', 'sipstache-file'))
        extra_headers.append(
            Header('X-Originator-From', str(self.destination_uri)))
        self.session.connect(from_header,
                             to_header,
                             contact_header=contact_header,
                             routes=routes,
                             streams=[self.stream],
                             is_focus=False,
                             extra_headers=extra_headers)
        notification_center.post_notification(
            'OutgoingFileTransferHandlerDidStart', sender=self)

    def stop(self):
        if self.session is not None:
            self.session.end()

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

    def _NH_FileTransferStreamDidFinish(self, notification):
        self.success = True
        if self.timer is None:
            self.timer = reactor.callLater(2, self.session.end)

    def _NH_SIPSessionDidEnd(self, notification):
        if self.timer is not None and self.timer.active():
            self.timer.cancel()
        self.timer = None
        notification.center.remove_observer(self, sender=self.stream)
        notification.center.remove_observer(self, sender=self.session)
        self.session = None
        self.stream = None
        if self.success:
            notification.center.post_notification(
                'OutgoingFileTransferHandlerDidFail', sender=self)
        else:
            notification.center.post_notification(
                'OutgoingFileTransferHandlerDidEnd', sender=self)

    _NH_SIPSessionDidFail = _NH_SIPSessionDidEnd
Esempio n. 16
0
class X2SMucHandler(object):
    implements(IObserver)

    sip_identity = WriteOnceAttribute()
    xmpp_identity = WriteOnceAttribute()

    def __init__(self, sip_identity, xmpp_identity, nickname):
        self.sip_identity = sip_identity
        self.xmpp_identity = xmpp_identity
        self.nickname = nickname
        self._xmpp_muc_session = None
        self._sip_session = None
        self._msrp_stream = None
        self._first_stanza = None
        self._pending_nicknames_map = {
        }  # map message ID of MSRP NICKNAME chunk to corresponding stanza
        self._pending_messages_map = {
        }  # map message ID of MSRP SEND chunk to corresponding stanza
        self._participants = set()  # set of (URI, nickname) tuples
        self.ended = False

    def start(self):
        notification_center = NotificationCenter()
        self._xmpp_muc_session = XMPPIncomingMucSession(
            local_identity=self.sip_identity,
            remote_identity=self.xmpp_identity)
        notification_center.add_observer(self, sender=self._xmpp_muc_session)
        self._xmpp_muc_session.start()
        notification_center.post_notification('X2SMucHandlerDidStart',
                                              sender=self)
        self._start_sip_session()

    def end(self):
        if self.ended:
            return
        notification_center = NotificationCenter()
        if self._xmpp_muc_session is not None:
            notification_center.remove_observer(self,
                                                sender=self._xmpp_muc_session)
            # Send indication that the user has been kicked from the room
            sender = Identity(
                FrozenURI(self.sip_identity.uri.user,
                          self.sip_identity.uri.host, self.nickname))
            stanza = MUCAvailabilityPresence(sender,
                                             self.xmpp_identity,
                                             available=False)
            stanza.jid = self.xmpp_identity
            stanza.muc_statuses.append('307')
            xmpp_manager = XMPPManager()
            xmpp_manager.send_muc_stanza(stanza)
            self._xmpp_muc_session.end()
            self._xmpp_muc_session = None
        if self._sip_session is not None:
            notification_center.remove_observer(self, sender=self._sip_session)
            self._sip_session.end()
            self._sip_session = None
        self.ended = True
        notification_center.post_notification('X2SMucHandlerDidEnd',
                                              sender=self)

    @run_in_green_thread
    def _start_sip_session(self):
        # 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 = self.sip_identity.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)
            self.end()
            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 = NotificationCenter()
        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 handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    def _NH_SIPSessionDidStart(self, notification):
        log.info("SIP multiparty session %s started" %
                 self._sip_session.call_id)
        if not self._sip_session.remote_focus or not self._msrp_stream.nickname_allowed:
            self.end()
            return
        message_id = self._msrp_stream.set_local_nickname(self.nickname)
        self._pending_nicknames_map[message_id] = (self.nickname,
                                                   self._first_stanza)
        self._first_stanza = None

    def _NH_SIPSessionDidEnd(self, notification):
        log.info("SIP multiparty 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.info("SIP multiparty 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_SIPSessionGotConferenceInfo(self, notification):
        # Translate to XMPP payload
        xmpp_manager = XMPPManager()
        own_uri = FrozenURI(self.xmpp_identity.uri.user,
                            self.xmpp_identity.uri.host)
        conference_info = notification.data.conference_info
        new_participants = set()
        for user in conference_info.users:
            user_uri = FrozenURI.parse(user.entity if user.entity.startswith((
                'sip:', 'sips:')) else 'sip:' + user.entity)
            nickname = user.display_text.value if user.display_text else user.entity
            new_participants.add((user_uri, nickname))
        # Remove participants that are no longer in the room
        for uri, nickname in self._participants - new_participants:
            sender = Identity(
                FrozenURI(self.sip_identity.uri.user,
                          self.sip_identity.uri.host, nickname))
            stanza = MUCAvailabilityPresence(sender,
                                             self.xmpp_identity,
                                             available=False)
            xmpp_manager.send_muc_stanza(stanza)
        # Send presence for current participants
        for uri, nickname in new_participants:
            if uri == own_uri:
                continue
            sender = Identity(
                FrozenURI(self.sip_identity.uri.user,
                          self.sip_identity.uri.host, nickname))
            stanza = MUCAvailabilityPresence(sender,
                                             self.xmpp_identity,
                                             available=True)
            stanza.jid = Identity(uri)
            xmpp_manager.send_muc_stanza(stanza)
        self._participants = new_participants
        # Send own status last
        sender = Identity(
            FrozenURI(self.sip_identity.uri.user, self.sip_identity.uri.host,
                      self.nickname))
        stanza = MUCAvailabilityPresence(sender,
                                         self.xmpp_identity,
                                         available=True)
        stanza.jid = self.xmpp_identity
        stanza.muc_statuses.append('110')
        xmpp_manager.send_muc_stanza(stanza)

    def _NH_ChatStreamGotMessage(self, notification):
        # Notification is sent by the MSRP stream
        if not self._xmpp_muc_session:
            return
        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.content
        else:
            html_body = message.content
            body = None
        resource = message.sender.display_name or str(message.sender.uri)
        sender = Identity(
            FrozenURI(self.sip_identity.uri.user, self.sip_identity.uri.host,
                      resource))
        self._xmpp_muc_session.send_message(sender,
                                            body,
                                            html_body,
                                            message_id='MUC.' +
                                            uuid.uuid4().hex)
        self._msrp_stream.msrp_session.send_report(notification.data.chunk,
                                                   200, 'OK')

    def _NH_ChatStreamDidSetNickname(self, notification):
        # Notification is sent by the MSRP stream
        nickname, stanza = self._pending_nicknames_map.pop(
            notification.data.message_id)
        self.nickname = nickname

    def _NH_ChatStreamDidNotSetNickname(self, notification):
        # Notification is sent by the MSRP stream
        nickname, stanza = self._pending_nicknames_map.pop(
            notification.data.message_id)
        error_stanza = MUCErrorPresence.from_stanza(stanza, 'cancel',
                                                    [('conflict', STANZAS_NS)])
        xmpp_manager = XMPPManager()
        xmpp_manager.send_muc_stanza(error_stanza)

    def _NH_ChatStreamDidDeliverMessage(self, notification):
        # Echo back the message to the sender
        stanza = self._pending_messages_map.pop(notification.data.message_id)
        stanza.sender, stanza.recipient = stanza.recipient, stanza.sender
        stanza.sender.uri = FrozenURI(stanza.sender.uri.user,
                                      stanza.sender.uri.host, self.nickname)
        xmpp_manager = XMPPManager()
        xmpp_manager.send_muc_stanza(stanza)

    def _NH_ChatStreamDidNotDeliverMessage(self, notification):
        self._pending_messages_map.pop(notification.data.message_id)

    def _NH_XMPPIncomingMucSessionDidEnd(self, notification):
        notification.center.remove_observer(self,
                                            sender=self._xmpp_muc_session)
        self._xmpp_muc_session = None
        self.end()

    def _NH_XMPPIncomingMucSessionGotMessage(self, notification):
        if not self._sip_session:
            return
        message = notification.data.message
        sender_uri = message.sender.uri.as_sip_uri()
        del sender_uri.parameters['gr']  # no GRUU in CPIM From header
        sender = ChatIdentity(sender_uri, display_name=self.nickname)
        message_id = self._msrp_stream.send_message(message.body,
                                                    'text/plain',
                                                    sender=sender)
        self._pending_messages_map[message_id] = message
        # Message will be echoed back to the sender on ChatStreamDidDeliverMessage

    def _NH_XMPPIncomingMucSessionChangedNickname(self, notification):
        if not self._sip_session:
            return
        nickname = notification.data.nickname
        try:
            message_id = self._msrp_stream.set_local_nickname(nickname)
        except ChatStreamError:
            return
        self._pending_nicknames_map[message_id] = (nickname,
                                                   notification.data.stanza)

    def _NH_XMPPIncomingMucSessionSubject(self, notification):
        if not self._sip_session:
            return
        message = notification.data.message
        sender_uri = message.sender.uri.as_sip_uri()
        del sender_uri.parameters['gr']  # no GRUU in CPIM From header
        sender = ChatIdentity(sender_uri, display_name=self.nickname)
        message_id = self._msrp_stream.send_message(
            'Conference title set to: %s' % message.body,
            'text/plain',
            sender=sender)
        self._pending_messages_map[message_id] = message
Esempio n. 17
0
class OutgoingFileTransferHandler(object):
    implements(IObserver)

    def __init__(self, sender_uri, destination_uri, file_selector):
        self.sender_uri = sender_uri
        self.destination_uri = destination_uri
        self.file_selector = file_selector
        self.session = None
        self.stream = None
        self.timer = None
        self.success = False

    @run_in_green_thread
    def start(self):
        notification_center = NotificationCenter()
        self.greenlet = api.getcurrent()
        settings = SIPSimpleSettings()
        account = AccountManager().sylkserver_account
        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 = self.destination_uri
        lookup = DNSLookup()
        try:
            routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait()
        except DNSLookupError:
            notification_center.post_notification('OutgoingFileTransferHandlerDidFail', sender=self)
            return

        self.session = Session(account)
        self.stream = FileTransferStream(self.file_selector, 'sendonly')
        notification_center.add_observer(self, sender=self.session)
        notification_center.add_observer(self, sender=self.stream)
        from_header = FromHeader(self.sender_uri, u'SIPStache File Transfer')
        to_header = ToHeader(self.destination_uri)
        transport = routes[0].transport
        parameters = {} if transport=='udp' else {'transport': transport}
        contact_header = ContactHeader(SIPURI(user=self.sender_uri.user, host=SIPConfig.local_ip.normalized, port=getattr(Engine(), '%s_port' % transport), parameters=parameters))
        extra_headers = []
        if ThorNodeConfig.enabled:
            extra_headers.append(Header('Thor-Scope', 'sipstache-file'))
        extra_headers.append(Header('X-Originator-From', str(self.destination_uri)))
        self.session.connect(from_header, to_header, contact_header=contact_header, routes=routes, streams=[self.stream], is_focus=False, extra_headers=extra_headers)
        notification_center.post_notification('OutgoingFileTransferHandlerDidStart', sender=self)

    def stop(self):
        if self.session is not None:
            self.session.end()

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

    def _NH_FileTransferStreamDidFinish(self, notification):
        self.success = True
        if self.timer is None:
            self.timer = reactor.callLater(2, self.session.end)

    def _NH_SIPSessionDidEnd(self, notification):
        if self.timer is not None and self.timer.active():
            self.timer.cancel()
        self.timer = None
        notification.center.remove_observer(self, sender=self.stream)
        notification.center.remove_observer(self, sender=self.session)
        self.session = None
        self.stream = None
        if self.success:
            notification.center.post_notification('OutgoingFileTransferHandlerDidFail', sender=self)
        else:
            notification.center.post_notification('OutgoingFileTransferHandlerDidEnd', sender=self)

    _NH_SIPSessionDidFail = _NH_SIPSessionDidEnd