예제 #1
0
파일: muc.py 프로젝트: samykabu/sylkserver
 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)
예제 #2
0
파일: im.py 프로젝트: madhawa/sylkserver
 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')
예제 #3
0
파일: im.py 프로젝트: turbojavaC/sylkserver
 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')
예제 #4
0
파일: muc.py 프로젝트: samykabu/sylkserver
 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)
예제 #5
0
 def __init__(self, local_identity, remote_identity):
     self.local_identity = local_identity
     self.remote_identity = remote_identity
     self.state = None
     self.channel = coros.queue()
     self._proc = None
     from sylk.applications.xmppgateway.xmpp import XMPPManager
     self.xmpp_manager = XMPPManager()
예제 #6
0
파일: muc.py 프로젝트: samykabu/sylkserver
 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)
예제 #7
0
class XMPPIncomingMucSession(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager

        self.xmpp_manager = XMPPManager()

    def start(self):
        NotificationCenter().post_notification("XMPPIncomingMucSessionDidStart", sender=self)
        self._proc = proc.spawn(self._run)
        self.state = "started"

    def end(self):
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification(
            "XMPPIncomingMucSessionDidEnd", sender=self, data=NotificationData(originator="local")
        )
        self.state = "terminated"

    def send_message(self, sender, body, html_body, message_id=None):
        # TODO: timestamp?
        message = GroupChatMessage(sender, self.remote_identity, body, html_body, id=message_id)
        self.xmpp_manager.send_muc_stanza(message)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, GroupChatMessage):
                notification_center.post_notification(
                    "XMPPIncomingMucSessionGotMessage", sender=self, data=NotificationData(message=item)
                )
            elif isinstance(item, MUCAvailabilityPresence):
                if item.available:
                    nickname = item.recipient.uri.resource
                    notification_center.post_notification(
                        "XMPPIncomingMucSessionChangedNickname",
                        sender=self,
                        data=NotificationData(stanza=item, nickname=nickname),
                    )
                else:
                    notification_center.post_notification(
                        "XMPPIncomingMucSessionDidEnd", sender=self, data=NotificationData(originator="local")
                    )
                    self.state = "terminated"
                    break
        self._proc = None
예제 #8
0
 def __init__(self):
     self.xmpp_manager = XMPPManager()
     self.pending_sessions = {}
     self.chat_sessions = set()
     self.media_sessions = set()
     self.s2x_muc_sessions = {}
     self.x2s_muc_sessions = {}
     self.s2x_presence_subscriptions = {}
     self.x2s_presence_subscriptions = {}
     self.s2x_muc_add_participant_handlers = {}
     self.x2s_muc_add_participant_handlers = {}
예제 #9
0
파일: muc.py 프로젝트: samykabu/sylkserver
 def start(self):
     notification_center = NotificationCenter()
     notification_center.add_observer(self, sender=self.session)
     stanza = OutgoingInvitationMessage(self.sender,
                                        self.recipient,
                                        self.inviter,
                                        id='MUC.' + uuid.uuid4().hex)
     xmpp_manager = XMPPManager()
     xmpp_manager.send_muc_stanza(stanza)
     self._timer = reactor.callLater(90, self._timeout)
     notification_center.post_notification(
         'S2XMucInvitationHandlerDidStart', sender=self)
예제 #10
0
class XMPPIncomingMucSession(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    def start(self):
        NotificationCenter().post_notification('XMPPIncomingMucSessionDidStart', sender=self)
        self._proc = proc.spawn(self._run)
        self.state = 'started'

    def end(self):
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification('XMPPIncomingMucSessionDidEnd', sender=self, data=NotificationData(originator='local'))
        self.state = 'terminated'

    def send_message(self, sender, body, html_body, message_id=None):
        # TODO: timestamp?
        message = GroupChatMessage(sender, self.remote_identity, body, html_body, id=message_id)
        self.xmpp_manager.send_muc_stanza(message)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, GroupChatMessage):
                notification_center.post_notification('XMPPIncomingMucSessionGotMessage', sender=self, data=NotificationData(message=item))
            elif isinstance(item, GroupChatSubject):
                notification_center.post_notification('XMPPIncomingMucSessionSubject', sender=self, data=NotificationData(message=item))
            elif isinstance(item, MUCAvailabilityPresence):
                if item.available:
                    nickname = item.recipient.uri.resource
                    notification_center.post_notification('XMPPIncomingMucSessionChangedNickname', sender=self, data=NotificationData(stanza=item, nickname=nickname))
                else:
                    notification_center.post_notification('XMPPIncomingMucSessionDidEnd', sender=self, data=NotificationData(originator='local'))
                    self.state = 'terminated'
                    break
        self._proc = None
예제 #11
0
 def __init__(self, local_identity, remote_identity):
     self.local_identity = local_identity
     self.remote_identity = remote_identity
     self.state = None
     self.channel = coros.queue()
     self._proc = None
     from sylk.applications.xmppgateway.xmpp import XMPPManager
     self.xmpp_manager = XMPPManager()
예제 #12
0
파일: im.py 프로젝트: turbojavaC/sylkserver
    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()
예제 #13
0
 def __init__(self):
     self.xmpp_manager = XMPPManager()
     self.pending_sessions = {}
     self.chat_sessions = set()
     self.media_sessions = set()
     self.s2x_muc_sessions = {}
     self.x2s_muc_sessions = {}
     self.s2x_presence_subscriptions = {}
     self.x2s_presence_subscriptions = {}
     self.s2x_muc_add_participant_handlers = {}
     self.x2s_muc_add_participant_handlers = {}
예제 #14
0
    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)
예제 #15
0
파일: im.py 프로젝트: madhawa/sylkserver
    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()
예제 #16
0
파일: muc.py 프로젝트: samykabu/sylkserver
 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)
예제 #17
0
class XMPPGatewayApplication(SylkApplication):
    implements(IObserver)

    def __init__(self):
        self.xmpp_manager = XMPPManager()
        self.pending_sessions = {}
        self.chat_sessions = set()
        self.media_sessions = set()
        self.s2x_muc_sessions = {}
        self.x2s_muc_sessions = {}
        self.s2x_presence_subscriptions = {}
        self.x2s_presence_subscriptions = {}
        self.s2x_muc_add_participant_handlers = {}
        self.x2s_muc_add_participant_handlers = {}

    def start(self):
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.xmpp_manager)
        notification_center.add_observer(self, name="JingleSessionNewIncoming")
        self.xmpp_manager.start()

    def stop(self):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.xmpp_manager)
        notification_center.add_observer(self, name="JingleSessionNewIncoming")
        self.xmpp_manager.stop()

    def incoming_session(self, session):
        stream_types = set([stream.type for stream in session.proposed_streams])
        if "chat" in stream_types:
            log.msg("New chat session from %s to %s" % (session.remote_identity.uri, session.local_identity.uri))
            self.incoming_chat_session(session)
        elif "audio" in stream_types:
            log.msg("New audio session from %s to %s" % (session.remote_identity.uri, session.local_identity.uri))
            self.incoming_media_session(session)
        else:
            log.msg(
                "New session from %s to %s rejected. Unsupported media: %s "
                % (session.remote_identity.uri, session.local_identity.uri, stream_types)
            )
            session.reject(488)

    def incoming_chat_session(self, session):
        # Check if this session is really an invitation to add a participant to a conference room / muc
        if (
            session.remote_identity.uri.host in self.xmpp_manager.muc_domains
            and "isfocus" in session._invitation.remote_contact_header.parameters
        ):
            try:
                referred_by_uri = SIPURI.parse(session.transfer_info.referred_by)
            except SIPCoreError:
                log.msg("SIP multiparty session invitation %s failed: invalid Referred-By header" % session.call_id)
                session.reject(488)
                return
            muc_uri = FrozenURI(session.remote_identity.uri.user, session.remote_identity.uri.host)
            inviter_uri = FrozenURI(referred_by_uri.user, referred_by_uri.host)
            recipient_uri = FrozenURI(session.local_identity.uri.user, session.local_identity.uri.host)
            sender = Identity(muc_uri)
            recipient = Identity(recipient_uri)
            inviter = Identity(inviter_uri)
            try:
                handler = self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)]
            except KeyError:
                handler = S2XMucInvitationHandler(session, sender, recipient, inviter)
                self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)] = handler
                NotificationCenter().add_observer(self, sender=handler)
                handler.start()
            else:
                log.msg(
                    "SIP multiparty session invitation %s failed: there is another invitation in progress from %s to %s"
                    % (session.call_id, format_uri(inviter_uri, "sip"), format_uri(recipient_uri, "xmpp"))
                )
                session.reject(480)
            return

        # Check domain
        if session.remote_identity.uri.host not in XMPPGatewayConfig.domains:
            log.msg("Session rejected: From domain is not a local XMPP domain")
            session.reject(606, "Not Acceptable")
            return

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

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

        try:
            handler = self.pending_sessions[(sip_leg_uri, xmpp_leg_uri)]
        except KeyError:
            pass
        else:
            # There is another pending session with same identifiers, can't accept this one
            log.msg("Session rejected: other session with same identifiers in progress")
            session.reject(488)
            return

        sip_identity = Identity(sip_leg_uri, session.remote_identity.display_name)
        handler = ChatSessionHandler.new_from_sip_session(sip_identity, session)
        NotificationCenter().add_observer(self, sender=handler)
        key = (sip_leg_uri, xmpp_leg_uri)
        self.pending_sessions[key] = handler

        if xmpp_leg_uri.resource is not None:
            # Incoming session target contained GRUU, so create XMPPChatSession immediately
            xmpp_session = XMPPChatSession(local_identity=handler.sip_identity, remote_identity=Identity(xmpp_leg_uri))
            handler.xmpp_identity = xmpp_session.remote_identity
            handler.xmpp_session = xmpp_session

    def incoming_media_session(self, session):
        if session.remote_identity.uri.host not in self.xmpp_manager.domains | self.xmpp_manager.muc_domains:
            log.msg("Session rejected: From domain is not a local XMPP domain")
            session.reject(403)
            return

        handler = MediaSessionHandler.new_from_sip_session(session)
        if handler is not None:
            NotificationCenter().add_observer(self, sender=handler)

    def incoming_subscription(self, subscribe_request, data):
        from_header = data.headers.get("From", Null)
        to_header = data.headers.get("To", Null)
        if Null in (from_header, to_header):
            subscribe_request.reject(400)
            return

        if XMPPGatewayConfig.log_presence:
            log.msg(
                "SIP subscription from %s to %s"
                % (format_uri(from_header.uri, "sip"), format_uri(to_header.uri, "xmpp"))
            )

        if subscribe_request.event != "presence":
            if XMPPGatewayConfig.log_presence:
                log.msg("SIP subscription rejected: only presence event is supported")
            subscribe_request.reject(489)
            return

        # Check domain
        remote_identity_uri = data.headers["From"].uri
        if remote_identity_uri.host not in XMPPGatewayConfig.domains:
            if XMPPGatewayConfig.log_presence:
                log.msg("SIP subscription rejected: From domain is not a local XMPP domain")
            subscribe_request.reject(606)
            return

        # Get URI representing the SIP side
        sip_leg_uri = FrozenURI(remote_identity_uri.user, remote_identity_uri.host)

        # Get URI representing the XMPP side
        request_uri = data.request_uri
        xmpp_leg_uri = FrozenURI(request_uri.user, request_uri.host)

        try:
            handler = self.s2x_presence_subscriptions[(sip_leg_uri, xmpp_leg_uri)]
        except KeyError:
            sip_identity = Identity(sip_leg_uri, data.headers["From"].display_name)
            xmpp_identity = Identity(xmpp_leg_uri)
            handler = S2XPresenceHandler(sip_identity, xmpp_identity)
            self.s2x_presence_subscriptions[(sip_leg_uri, xmpp_leg_uri)] = handler
            NotificationCenter().add_observer(self, sender=handler)
            handler.start()

        handler.add_sip_subscription(subscribe_request)

    def incoming_referral(self, refer_request, data):
        refer_request.reject(405)

    def incoming_message(self, message_request, data):
        content_type = data.headers.get("Content-Type", Null).content_type
        from_header = data.headers.get("From", Null)
        to_header = data.headers.get("To", Null)
        if Null in (content_type, from_header, to_header):
            message_request.answer(400)
            return
        log.msg("New SIP Message from %s to %s" % (from_header.uri, to_header.uri))

        # Check domain
        if from_header.uri.host not in XMPPGatewayConfig.domains:
            log.msg("Message rejected: From domain is not a local XMPP domain")
            message_request.answer(606)
            return

        if content_type == "message/cpim":
            try:
                cpim_message = CPIMPayload.decode(data.body)
            except CPIMParserError:
                log.msg("Message rejected: CPIM parse error")
                message_request.answer(400)
                return
            else:
                body = cpim_message.content
                content_type = cpim_message.content_type
                sender = cpim_message.sender or from_header
                from_uri = sender.uri
        else:
            body = data.body
            from_uri = from_header.uri
        to_uri = str(to_header.uri)
        message_request.answer(200)
        if from_uri.parameters.get("gr", None) is None:
            from_uri = SIPURI.new(from_uri)
            from_uri.parameters["gr"] = generate_sylk_resource()
        sender = Identity(FrozenURI.parse(from_uri))
        recipient = Identity(FrozenURI.parse(to_uri))
        if content_type in ("text/plain", "text/html"):
            if content_type == "text/plain":
                html_body = None
            else:
                html_body = body
                body = None
            if XMPPGatewayConfig.use_msrp_for_chat:
                message = NormalMessage(sender, recipient, body, html_body, use_receipt=False)
                self.xmpp_manager.send_stanza(message)
            else:
                message = ChatMessage(sender, recipient, body, html_body, use_receipt=False)
                self.xmpp_manager.send_stanza(message)
        elif content_type == IsComposingDocument.content_type:
            if not XMPPGatewayConfig.use_msrp_for_chat:
                try:
                    msg = IsComposingMessage.parse(body)
                except ParserError:
                    pass
                else:
                    state = "composing" if msg.state == "active" else "paused"
                    message = ChatComposingIndication(sender, recipient, state, use_receipt=False)
                    self.xmpp_manager.send_stanza(message)

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

    # Out of band XMPP stanza handling

    @run_in_green_thread
    def _NH_XMPPGotChatMessage(self, notification):
        # This notification is only processed here untill the ChatSessionHandler
        # has both (SIP and XMPP) sessions established
        message = notification.data.message
        sender = message.sender
        recipient = message.recipient
        if XMPPGatewayConfig.use_msrp_for_chat:
            if recipient.uri.resource is None:
                # If recipient resource is not set the session is started from
                # the XMPP side
                sip_leg_uri = FrozenURI.new(recipient.uri)
                xmpp_leg_uri = FrozenURI.new(sender.uri)
                try:
                    handler = self.pending_sessions[(sip_leg_uri, xmpp_leg_uri)]
                    handler.enqueue_xmpp_message(message)
                except KeyError:
                    # Check if we have any already open chat session and dispatch it there
                    try:
                        handler = next(
                            h
                            for h in self.chat_sessions
                            if h.xmpp_identity.uri.user == xmpp_leg_uri.user
                            and h.xmpp_identity.uri.host == xmpp_leg_uri.host
                            and h.sip_identity.uri.user == sip_leg_uri.user
                            and h.sip_identity.uri.host == sip_leg_uri.host
                        )
                    except StopIteration:
                        # Not found, need to create a new handler and a outgoing SIP session
                        xmpp_identity = Identity(xmpp_leg_uri)
                        handler = ChatSessionHandler.new_from_xmpp_stanza(xmpp_identity, sip_leg_uri)
                        key = (sip_leg_uri, xmpp_leg_uri)
                        self.pending_sessions[key] = handler
                        NotificationCenter().add_observer(self, sender=handler)
                    handler.enqueue_xmpp_message(message)
            else:
                # Find handler pending XMPP confirmation
                sip_leg_uri = FrozenURI.new(recipient.uri)
                xmpp_leg_uri = FrozenURI(sender.uri.user, sender.uri.host)
                try:
                    handler = self.pending_sessions[(sip_leg_uri, xmpp_leg_uri)]
                except KeyError:
                    # Find handler pending XMPP confirmation
                    sip_leg_uri = FrozenURI(recipient.uri.user, recipient.uri.host)
                    xmpp_leg_uri = FrozenURI.new(sender.uri)
                    try:
                        handler = self.pending_sessions[(sip_leg_uri, xmpp_leg_uri)]
                    except KeyError:
                        # Try harder, maybe the XMPP client changed his from
                        try:
                            handler = next(
                                h
                                for h in self.chat_sessions
                                if h.xmpp_identity.uri.user == xmpp_leg_uri.user
                                and h.xmpp_identity.uri.host == xmpp_leg_uri.host
                                and h.sip_identity.uri.user == sip_leg_uri.user
                                and h.sip_identity.uri.host == sip_leg_uri.host
                            )
                        except StopIteration:
                            # It's a new XMPP session to a full JID, disregard the full JID and start a new SIP session to the bare JID
                            xmpp_identity = Identity(xmpp_leg_uri)
                            handler = ChatSessionHandler.new_from_xmpp_stanza(xmpp_identity, sip_leg_uri)
                            key = (sip_leg_uri, xmpp_leg_uri)
                            self.pending_sessions[key] = handler
                            NotificationCenter().add_observer(self, sender=handler)
                    handler.enqueue_xmpp_message(message)
                else:
                    # Found handle, create XMPP session and establish session
                    session = XMPPChatSession(local_identity=recipient, remote_identity=sender)
                    handler.enqueue_xmpp_message(message)
                    handler.xmpp_identity = session.remote_identity
                    handler.xmpp_session = session
        else:
            sip_message_sender = SIPMessageSender(message)
            try:
                sip_message_sender.send().wait()
            except SIPMessageError as e:
                # TODO report back an error stanza
                log.error("Error sending SIP Message: %s" % e)

    @run_in_green_thread
    def _NH_XMPPGotNormalMessage(self, notification):
        message = notification.data.message
        sip_message_sender = SIPMessageSender(message)
        try:
            sip_message_sender.send().wait()
        except SIPMessageError as e:
            # TODO report back an error stanza
            log.error("Error sending SIP Message: %s" % e)

    @run_in_green_thread
    def _NH_XMPPGotComposingIndication(self, notification):
        composing_indication = notification.data.composing_indication
        sender = composing_indication.sender
        recipient = composing_indication.recipient
        if not XMPPGatewayConfig.use_msrp_for_chat:
            state = "active" if composing_indication.state == "composing" else "idle"
            body = IsComposingMessage(state=state, refresh=composing_indication.interval or 30).toxml()
            message = NormalMessage(sender, recipient, body, IsComposingDocument.content_type)
            sip_message_sender = SIPMessageSender(message)
            try:
                sip_message_sender.send().wait()
            except SIPMessageError as e:
                # TODO report back an error stanza
                log.error("Error sending SIP Message: %s" % e)

    def _NH_XMPPGotPresenceSubscriptionRequest(self, notification):
        stanza = notification.data.stanza
        # Disregard the resource part, the presence request could be a probe instead of a subscribe
        sender_uri = stanza.sender.uri
        sender_uri_bare = FrozenURI(sender_uri.user, sender_uri.host)
        try:
            handler = self.x2s_presence_subscriptions[(sender_uri_bare, stanza.recipient.uri)]
        except KeyError:
            xmpp_identity = stanza.sender
            xmpp_identity.uri = sender_uri_bare
            sip_identity = stanza.recipient
            handler = X2SPresenceHandler(sip_identity, xmpp_identity)
            self.x2s_presence_subscriptions[(sender_uri_bare, stanza.recipient.uri)] = handler
            notification.center.add_observer(self, sender=handler)
            handler.start()

    def _NH_XMPPGotMucJoinRequest(self, notification):
        stanza = notification.data.stanza
        muc_uri = FrozenURI(stanza.recipient.uri.user, stanza.recipient.uri.host)
        nickname = stanza.recipient.uri.resource
        try:
            handler = self.x2s_muc_sessions[(stanza.sender.uri, muc_uri)]
        except KeyError:
            xmpp_identity = stanza.sender
            sip_identity = stanza.recipient
            sip_identity.uri = muc_uri
            handler = X2SMucHandler(sip_identity, xmpp_identity, nickname)
            handler._first_stanza = stanza
            notification.center.add_observer(self, sender=handler)
            handler.start()
            # Check if there was a pending join request on the SIP side
            try:
                handler = self.s2x_muc_add_participant_handlers[
                    (muc_uri, FrozenURI(stanza.sender.uri.user, stanza.sender.uri.host))
                ]
            except KeyError:
                pass
            else:
                handler.stop()

    def _NH_XMPPGotMucAddParticipantRequest(self, notification):
        sender = notification.data.sender
        recipient = notification.data.recipient
        participant = notification.data.participant
        muc_uri = FrozenURI(recipient.uri.user, recipient.uri.host)
        sender_uri = FrozenURI(sender.uri.user, sender.uri.host)
        participant_uri = FrozenURI(participant.uri.user, participant.uri.host)
        sender = Identity(sender_uri)
        recipient = Identity(muc_uri)
        participant = Identity(participant_uri)
        try:
            handler = self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)]
        except KeyError:
            handler = X2SMucInvitationHandler(sender, recipient, participant)
            self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)] = handler
            notification.center.add_observer(self, sender=handler)
            handler.start()

    # Chat session handling

    def _NH_ChatSessionDidStart(self, notification):
        handler = notification.sender
        log.msg("Chat session established sip:%s <--> xmpp:%s" % (handler.sip_identity.uri, handler.xmpp_identity.uri))
        for k, v in self.pending_sessions.items():
            if v is handler:
                del self.pending_sessions[k]
                break
        self.chat_sessions.add(handler)

    def _NH_ChatSessionDidEnd(self, notification):
        handler = notification.sender
        log.msg("Chat session ended sip:%s <--> xmpp:%s" % (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.chat_sessions.remove(handler)
        notification.center.remove_observer(self, sender=handler)

    def _NH_ChatSessionDidFail(self, notification):
        handler = notification.sender
        uris = None
        for k, v in self.pending_sessions.items():
            if v is handler:
                uris = k
                del self.pending_sessions[k]
                break
        sip_uri, xmpp_uri = uris
        log.msg("Chat session failed sip:%s <--> xmpp:%s (%s)" % (sip_uri, xmpp_uri, notification.data.reason))
        notification.center.remove_observer(self, sender=handler)

    # Presence handling

    def _NH_S2XPresenceHandlerDidStart(self, notification):
        handler = notification.sender
        if XMPPGatewayConfig.log_presence:
            log.msg(
                "Presence flow 0x%x established %s --> %s"
                % (
                    id(handler),
                    format_uri(handler.sip_identity.uri, "sip"),
                    format_uri(handler.xmpp_identity.uri, "xmpp"),
                )
            )
            log.msg(
                "%d SIP --> XMPP and %d XMPP --> SIP presence flows are active"
                % (len(self.s2x_presence_subscriptions.keys()), len(self.x2s_presence_subscriptions.keys()))
            )

    def _NH_S2XPresenceHandlerDidEnd(self, notification):
        handler = notification.sender
        self.s2x_presence_subscriptions.pop((handler.sip_identity.uri, handler.xmpp_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)
        if XMPPGatewayConfig.log_presence:
            log.msg(
                "Presence flow 0x%x ended %s --> %s"
                % (
                    id(handler),
                    format_uri(handler.sip_identity.uri, "sip"),
                    format_uri(handler.xmpp_identity.uri, "xmpp"),
                )
            )
            log.msg(
                "%d SIP --> XMPP and %d XMPP --> SIP presence flows are active"
                % (len(self.s2x_presence_subscriptions.keys()), len(self.x2s_presence_subscriptions.keys()))
            )

    def _NH_X2SPresenceHandlerDidStart(self, notification):
        handler = notification.sender
        if XMPPGatewayConfig.log_presence:
            log.msg(
                "Presence flow 0x%x established %s --> %s"
                % (
                    id(handler),
                    format_uri(handler.xmpp_identity.uri, "xmpp"),
                    format_uri(handler.sip_identity.uri, "sip"),
                )
            )
            log.msg(
                "%d SIP --> XMPP and %d XMPP --> SIP presence flows are active"
                % (len(self.s2x_presence_subscriptions.keys()), len(self.x2s_presence_subscriptions.keys()))
            )

    def _NH_X2SPresenceHandlerDidEnd(self, notification):
        handler = notification.sender
        self.x2s_presence_subscriptions.pop((handler.xmpp_identity.uri, handler.sip_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)
        if XMPPGatewayConfig.log_presence:
            log.msg(
                "Presence flow 0x%x ended %s --> %s"
                % (
                    id(handler),
                    format_uri(handler.xmpp_identity.uri, "xmpp"),
                    format_uri(handler.sip_identity.uri, "sip"),
                )
            )
            log.msg(
                "%d SIP --> XMPP and %d XMPP --> SIP presence flows are active"
                % (len(self.s2x_presence_subscriptions.keys()), len(self.x2s_presence_subscriptions.keys()))
            )

    # MUC handling

    def _NH_X2SMucHandlerDidStart(self, notification):
        handler = notification.sender
        log.msg(
            "Multiparty session established xmpp:%s --> sip:%s" % (handler.xmpp_identity.uri, handler.sip_identity.uri)
        )
        self.x2s_muc_sessions[(handler.xmpp_identity.uri, handler.sip_identity.uri)] = handler

    def _NH_X2SMucHandlerDidEnd(self, notification):
        handler = notification.sender
        log.msg("Multiparty session ended xmpp:%s --> sip:%s" % (handler.xmpp_identity.uri, handler.sip_identity.uri))
        self.x2s_muc_sessions.pop((handler.xmpp_identity.uri, handler.sip_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)

    def _NH_X2SMucInvitationHandlerDidStart(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.msg(
            "%s invited %s to multiparty chat %s"
            % (format_uri(sender_uri, "xmpp"), format_uri(participant_uri), format_uri(muc_uri, "sip"))
        )

    def _NH_X2SMucInvitationHandlerDidEnd(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.msg(
            "%s added %s to multiparty chat %s"
            % (format_uri(sender_uri, "xmpp"), format_uri(participant_uri), format_uri(muc_uri, "sip"))
        )
        del self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_X2SMucInvitationHandlerDidFail(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.msg(
            "%s could not add %s to multiparty chat %s: %s"
            % (
                format_uri(sender_uri, "xmpp"),
                format_uri(participant_uri),
                format_uri(muc_uri, "sip"),
                notification.data.failure,
            )
        )
        del self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_S2XMucInvitationHandlerDidStart(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.msg(
            "%s invited %s to multiparty chat %s"
            % (format_uri(inviter_uri, "sip"), format_uri(recipient_uri, "xmpp"), format_uri(muc_uri, "sip"))
        )

    def _NH_S2XMucInvitationHandlerDidEnd(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.msg(
            "%s added %s to multiparty chat %s"
            % (format_uri(inviter_uri, "sip"), format_uri(recipient_uri, "xmpp"), format_uri(muc_uri, "sip"))
        )
        del self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_S2XMucInvitationHandlerDidFail(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.msg(
            "%s could not add %s to multiparty chat %s: %s"
            % (
                format_uri(inviter_uri, "sip"),
                format_uri(recipient_uri, "xmpp"),
                format_uri(muc_uri, "sip"),
                str(notification.data.failure),
            )
        )
        del self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)]
        notification.center.remove_observer(self, sender=handler)

    # Media sessions

    def _NH_JingleSessionNewIncoming(self, notification):
        session = notification.sender
        handler = MediaSessionHandler.new_from_jingle_session(session)
        if handler is not None:
            notification.center.add_observer(self, sender=handler)

    def _NH_MediaSessionHandlerDidStart(self, notification):
        handler = notification.sender
        log.msg("Media session started sip:%s <--> xmpp:%s" % (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.media_sessions.add(handler)

    def _NH_MediaSessionHandlerDidEnd(self, notification):
        handler = notification.sender
        log.msg("Media session ended sip:%s <--> xmpp:%s" % (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.media_sessions.remove(handler)
        notification.center.remove_observer(self, sender=handler)

    def _NH_MediaSessionHandlerDidFail(self, notification):
        handler = notification.sender
        log.msg("Media session failed sip:%s <--> xmpp:%s" % (handler.sip_identity.uri, handler.xmpp_identity.uri))
        notification.center.remove_observer(self, sender=handler)
예제 #18
0
class XMPPChatSession(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.pending_receipts = {}
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    def start(self):
        NotificationCenter().post_notification('XMPPChatSessionDidStart', sender=self)
        self._proc = proc.spawn(self._run)
        self.state = 'started'

    def end(self):
        self.send_composing_indication('gone')
        self._clear_pending_receipts()
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification('XMPPChatSessionDidEnd', sender=self, data=NotificationData(originator='local'))
        self.state = 'terminated'

    def send_message(self, body, html_body, message_id=None, use_receipt=True):
        message = ChatMessage(self.local_identity, self.remote_identity, body, html_body, id=message_id, use_receipt=use_receipt)
        self.xmpp_manager.send_stanza(message)
        if message_id is not None:
            timer = reactor.callLater(30, self._receipt_timer_expired, message_id)
            self.pending_receipts[message_id] = timer
            NotificationCenter().post_notification('XMPPChatSessionDidSendMessage', sender=self, data=NotificationData(message=message))

    def send_composing_indication(self, state, message_id=None, use_receipt=False):
        message = ChatComposingIndication(self.local_identity, self.remote_identity, state, id=message_id, use_receipt=use_receipt)
        self.xmpp_manager.send_stanza(message)
        if message_id is not None:
            timer = reactor.callLater(30, self._receipt_timer_expired, message_id)
            self.pending_receipts[message_id] = timer
            NotificationCenter().post_notification('XMPPChatSessionDidSendMessage', sender=self, data=NotificationData(message=message))

    def send_receipt_acknowledgement(self, receipt_id):
        message = MessageReceipt(self.local_identity, self.remote_identity, receipt_id)
        self.xmpp_manager.send_stanza(message)

    def send_error(self, stanza, error_type, conditions):
        message = ErrorStanza.from_stanza(stanza, error_type, conditions)
        self.xmpp_manager.send_stanza(message)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, ChatMessage):
                notification_center.post_notification('XMPPChatSessionGotMessage', sender=self, data=NotificationData(message=item))
            elif isinstance(item, ChatComposingIndication):
                if item.state == 'gone':
                    self._clear_pending_receipts()
                    notification_center.post_notification('XMPPChatSessionDidEnd', sender=self, data=NotificationData(originator='remote'))
                    self.state = 'terminated'
                    break
                else:
                    notification_center.post_notification('XMPPChatSessionGotComposingIndication', sender=self, data=NotificationData(message=item))
            elif isinstance(item, MessageReceipt):
                if item.receipt_id in self.pending_receipts:
                    timer = self.pending_receipts.pop(item.receipt_id)
                    timer.cancel()
                    notification_center.post_notification('XMPPChatSessionDidDeliverMessage', sender=self, data=NotificationData(message_id=item.receipt_id))
            elif isinstance(item, ErrorStanza):
                if item.id in self.pending_receipts:
                    timer = self.pending_receipts.pop(item.id)
                    timer.cancel()
                    # TODO: translate cause
                    notification_center.post_notification('XMPPChatSessionDidNotDeliverMessage', sender=self, data=NotificationData(message_id=item.id, code=503, reason='Service Unavailable'))
        self._proc = None

    def _receipt_timer_expired(self, message_id):
        self.pending_receipts.pop(message_id)
        NotificationCenter().post_notification('XMPPChatSessionDidNotDeliverMessage', sender=self, data=NotificationData(message_id=message_id, code=408, reason='Timeout'))

    def _clear_pending_receipts(self):
        notification_center = NotificationCenter()
        while self.pending_receipts:
            message_id, timer = self.pending_receipts.popitem()
            timer.cancel()
            notification_center.post_notification('XMPPChatSessionDidNotDeliverMessage', sender=self, data=NotificationData(message_id=message_id, code=408, reason='Timeout'))
예제 #19
0
class XMPPSubscription(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    @property
    def state(self):
        return self.__dict__['state']

    @state.setter
    def state(self, new_state):
        prev_state = self.__dict__.get('state', None)
        self.__dict__['state'] = new_state
        if prev_state != new_state:
            NotificationCenter().post_notification(
                'XMPPSubscriptionChangedState',
                sender=self,
                data=NotificationData(prev_state=prev_state, state=new_state))

    def start(self):
        NotificationCenter().post_notification('XMPPSubscriptionDidStart',
                                               sender=self)
        self._proc = proc.spawn(self._run)
        self.subscribe()

    def end(self):
        if self.state == 'terminated':
            return
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification(
            'XMPPSubscriptionDidEnd',
            sender=self,
            data=NotificationData(originator='local'))
        self.state = 'terminated'

    def subscribe(self):
        self.state = 'subscribe_sent'
        stanza = SubscriptionPresence(self.local_identity,
                                      self.remote_identity, 'subscribe')
        self.xmpp_manager.send_stanza(stanza)
        # If we are already subscribed we may not receive an answer, send a probe just in case
        self._send_probe()

    def unsubscribe(self):
        self.state = 'unsubscribe_sent'
        stanza = SubscriptionPresence(self.local_identity,
                                      self.remote_identity, 'unsubscribe')
        self.xmpp_manager.send_stanza(stanza)

    def _send_probe(self):
        self.state = 'subscribe_sent'
        stanza = ProbePresence(self.local_identity, self.remote_identity)
        self.xmpp_manager.send_stanza(stanza)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, AvailabilityPresence):
                if self.state == 'subscribe_sent':
                    self.state = 'active'
                notification_center.post_notification(
                    'XMPPSubscriptionGotNotify',
                    sender=self,
                    data=NotificationData(presence=item))
            elif isinstance(item, SubscriptionPresence):
                if self.state == 'subscribe_sent' and item.type == 'subscribed':
                    self.state = 'active'
                elif item.type == 'unsubscribed':
                    prev_state = self.state
                    self.state = 'terminated'
                    if prev_state in ('active', 'unsubscribe_sent'):
                        notification_center.post_notification(
                            'XMPPSubscriptionDidEnd', sender=self)
                    else:
                        notification_center.post_notification(
                            'XMPPSubscriptionDidFail', sender=self)
                    break
        self._proc = None
예제 #20
0
class XMPPIncomingSubscription(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    @property
    def state(self):
        return self.__dict__['state']

    @state.setter
    def state(self, new_state):
        prev_state = self.__dict__.get('state', None)
        self.__dict__['state'] = new_state
        if prev_state != new_state:
            NotificationCenter().post_notification(
                'XMPPIncomingSubscriptionChangedState',
                sender=self,
                data=NotificationData(prev_state=prev_state, state=new_state))

    def start(self):
        NotificationCenter().post_notification(
            'XMPPIncomingSubscriptionDidStart', sender=self)
        self._proc = proc.spawn(self._run)

    def end(self):
        if self.state == 'terminated':
            return
        self.state = 'terminated'
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification(
            'XMPPIncomingSubscriptionDidEnd',
            sender=self,
            data=NotificationData(originator='local'))

    def accept(self):
        self.state = 'active'
        stanza = SubscriptionPresence(self.local_identity,
                                      self.remote_identity, 'subscribed')
        self.xmpp_manager.send_stanza(stanza)

    def reject(self):
        self.state = 'terminating'
        stanza = SubscriptionPresence(self.local_identity,
                                      self.remote_identity, 'unsubscribed')
        self.xmpp_manager.send_stanza(stanza)
        self.end()

    def send_presence(self, stanza):
        self.xmpp_manager.send_stanza(stanza)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, SubscriptionPresence):
                if item.type == 'subscribe':
                    notification_center.post_notification(
                        'XMPPIncomingSubscriptionGotSubscribe', sender=self)
                elif item.type == 'unsubscribe':
                    self.state = 'terminated'
                    notification_center = NotificationCenter()
                    notification_center.post_notification(
                        'XMPPIncomingSubscriptionGotUnsubscribe', sender=self)
                    notification_center.post_notification(
                        'XMPPIncomingSubscriptionDidEnd',
                        sender=self,
                        data=NotificationData(originator='local'))
                    break
            elif isinstance(item, ProbePresence):
                notification_center = NotificationCenter()
                notification_center.post_notification(
                    'XMPPIncomingSubscriptionGotProbe', sender=self)
        self._proc = None
예제 #21
0
class XMPPIncomingSubscription(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    def _set_state(self, new_state):
        prev_state = self.__dict__.get('state', None)
        self.__dict__['state'] = new_state
        if prev_state != new_state:
            NotificationCenter().post_notification('XMPPIncomingSubscriptionChangedState', sender=self, data=NotificationData(prev_state=prev_state, state=new_state))
    def _get_state(self):
        return self.__dict__['state']
    state = property(_get_state, _set_state)
    del _get_state, _set_state

    def start(self):
        NotificationCenter().post_notification('XMPPIncomingSubscriptionDidStart', sender=self)
        self._proc = proc.spawn(self._run)

    def end(self):
        if self.state == 'terminated':
            return
        self.state = 'terminated'
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification('XMPPIncomingSubscriptionDidEnd', sender=self, data=NotificationData(originator='local'))

    def accept(self):
        self.state = 'active'
        stanza = SubscriptionPresence(self.local_identity, self.remote_identity, 'subscribed')
        self.xmpp_manager.send_stanza(stanza)

    def reject(self):
        self.state = 'terminating'
        stanza = SubscriptionPresence(self.local_identity, self.remote_identity, 'unsubscribed')
        self.xmpp_manager.send_stanza(stanza)
        self.end()

    def send_presence(self, stanza):
        self.xmpp_manager.send_stanza(stanza)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, SubscriptionPresence):
                if item.type == 'subscribe':
                    notification_center.post_notification('XMPPIncomingSubscriptionGotSubscribe', sender=self)
                elif item.type == 'unsubscribe':
                    self.state = 'terminated'
                    notification_center = NotificationCenter()
                    notification_center.post_notification('XMPPIncomingSubscriptionGotUnsubscribe', sender=self)
                    notification_center.post_notification('XMPPIncomingSubscriptionDidEnd', sender=self, data=NotificationData(originator='local'))
                    break
            elif isinstance(item, ProbePresence):
                notification_center = NotificationCenter()
                notification_center.post_notification('XMPPIncomingSubscriptionGotProbe', sender=self)
        self._proc = None
예제 #22
0
class XMPPSubscription(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    def _set_state(self, new_state):
        prev_state = self.__dict__.get('state', None)
        self.__dict__['state'] = new_state
        if prev_state != new_state:
            NotificationCenter().post_notification('XMPPSubscriptionChangedState', sender=self, data=NotificationData(prev_state=prev_state, state=new_state))
    def _get_state(self):
        return self.__dict__['state']
    state = property(_get_state, _set_state)
    del _get_state, _set_state

    def start(self):
        NotificationCenter().post_notification('XMPPSubscriptionDidStart', sender=self)
        self._proc = proc.spawn(self._run)
        self.subscribe()

    def end(self):
        if self.state == 'terminated':
            return
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification('XMPPSubscriptionDidEnd', sender=self, data=NotificationData(originator='local'))
        self.state = 'terminated'

    def subscribe(self):
        self.state = 'subscribe_sent'
        stanza = SubscriptionPresence(self.local_identity, self.remote_identity, 'subscribe')
        self.xmpp_manager.send_stanza(stanza)
        # If we are already subscribed we may not receive an answer, send a probe just in case
        self._send_probe()

    def unsubscribe(self):
        self.state = 'unsubscribe_sent'
        stanza = SubscriptionPresence(self.local_identity, self.remote_identity, 'unsubscribe')
        self.xmpp_manager.send_stanza(stanza)

    def _send_probe(self):
        self.state = 'subscribe_sent'
        stanza = ProbePresence(self.local_identity, self.remote_identity)
        self.xmpp_manager.send_stanza(stanza)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, AvailabilityPresence):
                if self.state == 'subscribe_sent':
                    self.state = 'active'
                notification_center.post_notification('XMPPSubscriptionGotNotify', sender=self, data=NotificationData(presence=item))
            elif isinstance(item, SubscriptionPresence):
                if self.state == 'subscribe_sent' and item.type == 'subscribed':
                    self.state = 'active'
                elif item.type == 'unsubscribed':
                    prev_state = self.state
                    self.state = 'terminated'
                    if prev_state in ('active', 'unsubscribe_sent'):
                        notification_center.post_notification('XMPPSubscriptionDidEnd', sender=self)
                    else:
                        notification_center.post_notification('XMPPSubscriptionDidFail', sender=self)
                    break
        self._proc = None
예제 #23
0
class XMPPGatewayApplication(SylkApplication):
    implements(IObserver)

    def __init__(self):
        self.xmpp_manager = XMPPManager()
        self.pending_sessions = {}
        self.chat_sessions = set()
        self.media_sessions = set()
        self.s2x_muc_sessions = {}
        self.x2s_muc_sessions = {}
        self.s2x_presence_subscriptions = {}
        self.x2s_presence_subscriptions = {}
        self.s2x_muc_add_participant_handlers = {}
        self.x2s_muc_add_participant_handlers = {}

    def start(self):
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.xmpp_manager)
        notification_center.add_observer(self, name='JingleSessionNewIncoming')
        self.xmpp_manager.start()

    def stop(self):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.xmpp_manager)
        notification_center.add_observer(self, name='JingleSessionNewIncoming')
        self.xmpp_manager.stop()

    def incoming_session(self, session):
        stream_types = set(
            [stream.type for stream in session.proposed_streams])
        if 'chat' in stream_types:
            log.info('New chat session from %s to %s' %
                     (session.remote_identity.uri, session.local_identity.uri))
            self.incoming_chat_session(session)
        elif 'audio' in stream_types:
            log.info('New audio session from %s to %s' %
                     (session.remote_identity.uri, session.local_identity.uri))
            self.incoming_media_session(session)
        else:
            log.info(
                'New session from %s to %s rejected. Unsupported media: %s ' %
                (session.remote_identity.uri, session.local_identity.uri,
                 stream_types))
            session.reject(488)

    def incoming_chat_session(self, session):
        # Check if this session is really an invitation to add a participant to a conference room / muc
        if session.remote_identity.uri.host in self.xmpp_manager.muc_domains and 'isfocus' in session._invitation.remote_contact_header.parameters:
            try:
                referred_by_uri = SIPURI.parse(
                    session.transfer_info.referred_by)
            except SIPCoreError:
                log.info(
                    "SIP multiparty session invitation %s failed: invalid Referred-By header"
                    % session.call_id)
                session.reject(488)
                return
            muc_uri = FrozenURI(session.remote_identity.uri.user,
                                session.remote_identity.uri.host)
            inviter_uri = FrozenURI(referred_by_uri.user, referred_by_uri.host)
            recipient_uri = FrozenURI(session.local_identity.uri.user,
                                      session.local_identity.uri.host)
            sender = Identity(muc_uri)
            recipient = Identity(recipient_uri)
            inviter = Identity(inviter_uri)
            try:
                handler = self.s2x_muc_add_participant_handlers[(
                    muc_uri, recipient_uri)]
            except KeyError:
                handler = S2XMucInvitationHandler(session, sender, recipient,
                                                  inviter)
                self.s2x_muc_add_participant_handlers[(
                    muc_uri, recipient_uri)] = handler
                NotificationCenter().add_observer(self, sender=handler)
                handler.start()
            else:
                log.info(
                    "SIP multiparty session invitation %s failed: there is another invitation in progress from %s to %s"
                    % (session.call_id, format_uri(inviter_uri, 'sip'),
                       format_uri(recipient_uri, 'xmpp')))
                session.reject(480)
            return

        # Check domain
        if session.remote_identity.uri.host not in XMPPGatewayConfig.domains:
            log.info(
                'Session rejected: From domain is not a local XMPP domain')
            session.reject(606, 'Not Acceptable')
            return

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

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

        try:
            handler = self.pending_sessions[(sip_leg_uri, xmpp_leg_uri)]
        except KeyError:
            pass
        else:
            # There is another pending session with same identifiers, can't accept this one
            log.info(
                'Session rejected: other session with same identifiers in progress'
            )
            session.reject(488)
            return

        sip_identity = Identity(sip_leg_uri,
                                session.remote_identity.display_name)
        handler = ChatSessionHandler.new_from_sip_session(
            sip_identity, session)
        NotificationCenter().add_observer(self, sender=handler)
        key = (sip_leg_uri, xmpp_leg_uri)
        self.pending_sessions[key] = handler

        if xmpp_leg_uri.resource is not None:
            # Incoming session target contained GRUU, so create XMPPChatSession immediately
            xmpp_session = XMPPChatSession(
                local_identity=handler.sip_identity,
                remote_identity=Identity(xmpp_leg_uri))
            handler.xmpp_identity = xmpp_session.remote_identity
            handler.xmpp_session = xmpp_session

    def incoming_media_session(self, session):
        if session.remote_identity.uri.host not in self.xmpp_manager.domains | self.xmpp_manager.muc_domains:
            log.info(
                'Session rejected: From domain is not a local XMPP domain')
            session.reject(403)
            return

        handler = MediaSessionHandler.new_from_sip_session(session)
        if handler is not None:
            NotificationCenter().add_observer(self, sender=handler)

    def incoming_subscription(self, subscribe_request, data):
        from_header = data.headers.get('From', Null)
        to_header = data.headers.get('To', Null)
        if Null in (from_header, to_header):
            subscribe_request.reject(400)
            return

        if XMPPGatewayConfig.log_presence:
            log.info('SIP subscription from %s to %s' % (format_uri(
                from_header.uri, 'sip'), format_uri(to_header.uri, 'xmpp')))

        if subscribe_request.event != 'presence':
            if XMPPGatewayConfig.log_presence:
                log.info(
                    'SIP subscription rejected: only presence event is supported'
                )
            subscribe_request.reject(489)
            return

        # Check domain
        remote_identity_uri = data.headers['From'].uri
        if remote_identity_uri.host not in XMPPGatewayConfig.domains:
            if XMPPGatewayConfig.log_presence:
                log.info(
                    'SIP subscription rejected: From domain is not a local XMPP domain'
                )
            subscribe_request.reject(606)
            return

        # Get URI representing the SIP side
        sip_leg_uri = FrozenURI(remote_identity_uri.user,
                                remote_identity_uri.host)

        # Get URI representing the XMPP side
        request_uri = data.request_uri
        xmpp_leg_uri = FrozenURI(request_uri.user, request_uri.host)

        try:
            handler = self.s2x_presence_subscriptions[(sip_leg_uri,
                                                       xmpp_leg_uri)]
        except KeyError:
            sip_identity = Identity(sip_leg_uri,
                                    data.headers['From'].display_name)
            xmpp_identity = Identity(xmpp_leg_uri)
            handler = S2XPresenceHandler(sip_identity, xmpp_identity)
            self.s2x_presence_subscriptions[(sip_leg_uri,
                                             xmpp_leg_uri)] = handler
            NotificationCenter().add_observer(self, sender=handler)
            handler.start()

        handler.add_sip_subscription(subscribe_request)

    def incoming_referral(self, refer_request, data):
        refer_request.reject(405)

    def incoming_message(self, message_request, data):
        content_type = data.headers.get('Content-Type', Null).content_type
        from_header = data.headers.get('From', Null)
        to_header = data.headers.get('To', Null)
        if Null in (content_type, from_header, to_header):
            message_request.answer(400)
            return
        log.info('New SIP Message from %s to %s' %
                 (from_header.uri, to_header.uri))

        # Check domain
        if from_header.uri.host not in XMPPGatewayConfig.domains:
            log.info(
                'Message rejected: From domain is not a local XMPP domain')
            message_request.answer(606)
            return

        if content_type == 'message/cpim':
            try:
                cpim_message = CPIMPayload.decode(data.body)
            except CPIMParserError:
                log.info('Message rejected: CPIM parse error')
                message_request.answer(400)
                return
            else:
                body = cpim_message.content
                content_type = cpim_message.content_type
                sender = cpim_message.sender or from_header
                from_uri = sender.uri
        else:
            body = data.body
            from_uri = from_header.uri
        to_uri = str(to_header.uri)
        message_request.answer(200)
        if from_uri.parameters.get('gr', None) is None:
            from_uri = SIPURI.new(from_uri)
            from_uri.parameters['gr'] = generate_sylk_resource()
        sender = Identity(FrozenURI.parse(from_uri))
        recipient = Identity(FrozenURI.parse(to_uri))
        if content_type in ('text/plain', 'text/html'):
            if content_type == 'text/plain':
                html_body = None
            else:
                html_body = body
                body = None
            if XMPPGatewayConfig.use_msrp_for_chat:
                message = NormalMessage(sender,
                                        recipient,
                                        body,
                                        html_body,
                                        use_receipt=False)
                self.xmpp_manager.send_stanza(message)
            else:
                message = ChatMessage(sender,
                                      recipient,
                                      body,
                                      html_body,
                                      use_receipt=False)
                self.xmpp_manager.send_stanza(message)
        elif content_type == IsComposingDocument.content_type:
            if not XMPPGatewayConfig.use_msrp_for_chat:
                try:
                    msg = IsComposingMessage.parse(body)
                except ParserError:
                    pass
                else:
                    state = 'composing' if msg.state == 'active' else 'paused'
                    message = ChatComposingIndication(sender,
                                                      recipient,
                                                      state,
                                                      use_receipt=False)
                    self.xmpp_manager.send_stanza(message)

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

    # Out of band XMPP stanza handling

    @run_in_green_thread
    def _NH_XMPPGotChatMessage(self, notification):
        # This notification is only processed here untill the ChatSessionHandler
        # has both (SIP and XMPP) sessions established
        message = notification.data.message
        sender = message.sender
        recipient = message.recipient
        if XMPPGatewayConfig.use_msrp_for_chat:
            if recipient.uri.resource is None:
                # If recipient resource is not set the session is started from
                # the XMPP side
                sip_leg_uri = FrozenURI.new(recipient.uri)
                xmpp_leg_uri = FrozenURI.new(sender.uri)
                try:
                    handler = self.pending_sessions[(sip_leg_uri,
                                                     xmpp_leg_uri)]
                    handler.enqueue_xmpp_message(message)
                except KeyError:
                    # Check if we have any already open chat session and dispatch it there
                    try:
                        handler = next(
                            h for h in self.chat_sessions
                            if h.xmpp_identity.uri.user == xmpp_leg_uri.user
                            and h.xmpp_identity.uri.host == xmpp_leg_uri.host
                            and h.sip_identity.uri.user == sip_leg_uri.user
                            and h.sip_identity.uri.host == sip_leg_uri.host)
                    except StopIteration:
                        # Not found, need to create a new handler and a outgoing SIP session
                        xmpp_identity = Identity(xmpp_leg_uri)
                        handler = ChatSessionHandler.new_from_xmpp_stanza(
                            xmpp_identity, sip_leg_uri)
                        key = (sip_leg_uri, xmpp_leg_uri)
                        self.pending_sessions[key] = handler
                        NotificationCenter().add_observer(self, sender=handler)
                    handler.enqueue_xmpp_message(message)
            else:
                # Find handler pending XMPP confirmation
                sip_leg_uri = FrozenURI.new(recipient.uri)
                xmpp_leg_uri = FrozenURI(sender.uri.user, sender.uri.host)
                try:
                    handler = self.pending_sessions[(sip_leg_uri,
                                                     xmpp_leg_uri)]
                except KeyError:
                    # Find handler pending XMPP confirmation
                    sip_leg_uri = FrozenURI(recipient.uri.user,
                                            recipient.uri.host)
                    xmpp_leg_uri = FrozenURI.new(sender.uri)
                    try:
                        handler = self.pending_sessions[(sip_leg_uri,
                                                         xmpp_leg_uri)]
                    except KeyError:
                        # Try harder, maybe the XMPP client changed his from
                        try:
                            handler = next(
                                h for h in self.chat_sessions
                                if h.xmpp_identity.uri.user ==
                                xmpp_leg_uri.user and h.xmpp_identity.uri.host
                                == xmpp_leg_uri.host and
                                h.sip_identity.uri.user == sip_leg_uri.user and
                                h.sip_identity.uri.host == sip_leg_uri.host)
                        except StopIteration:
                            # It's a new XMPP session to a full JID, disregard the full JID and start a new SIP session to the bare JID
                            xmpp_identity = Identity(xmpp_leg_uri)
                            handler = ChatSessionHandler.new_from_xmpp_stanza(
                                xmpp_identity, sip_leg_uri)
                            key = (sip_leg_uri, xmpp_leg_uri)
                            self.pending_sessions[key] = handler
                            NotificationCenter().add_observer(self,
                                                              sender=handler)
                    handler.enqueue_xmpp_message(message)
                else:
                    # Found handle, create XMPP session and establish session
                    session = XMPPChatSession(local_identity=recipient,
                                              remote_identity=sender)
                    handler.enqueue_xmpp_message(message)
                    handler.xmpp_identity = session.remote_identity
                    handler.xmpp_session = session
        else:
            sip_message_sender = SIPMessageSender(message)
            try:
                sip_message_sender.send().wait()
            except SIPMessageError as e:
                # TODO report back an error stanza
                log.error('Error sending SIP Message: %s' % e)

    @run_in_green_thread
    def _NH_XMPPGotNormalMessage(self, notification):
        message = notification.data.message
        sip_message_sender = SIPMessageSender(message)
        try:
            sip_message_sender.send().wait()
        except SIPMessageError as e:
            # TODO report back an error stanza
            log.error('Error sending SIP Message: %s' % e)

    @run_in_green_thread
    def _NH_XMPPGotComposingIndication(self, notification):
        composing_indication = notification.data.composing_indication
        sender = composing_indication.sender
        recipient = composing_indication.recipient
        if not XMPPGatewayConfig.use_msrp_for_chat:
            state = 'active' if composing_indication.state == 'composing' else 'idle'
            body = IsComposingMessage(state=state,
                                      refresh=composing_indication.interval
                                      or 30).toxml()
            message = NormalMessage(sender, recipient, body,
                                    IsComposingDocument.content_type)
            sip_message_sender = SIPMessageSender(message)
            try:
                sip_message_sender.send().wait()
            except SIPMessageError as e:
                # TODO report back an error stanza
                log.error('Error sending SIP Message: %s' % e)

    def _NH_XMPPGotPresenceSubscriptionRequest(self, notification):
        stanza = notification.data.stanza
        # Disregard the resource part, the presence request could be a probe instead of a subscribe
        sender_uri = stanza.sender.uri
        sender_uri_bare = FrozenURI(sender_uri.user, sender_uri.host)
        try:
            handler = self.x2s_presence_subscriptions[(sender_uri_bare,
                                                       stanza.recipient.uri)]
        except KeyError:
            xmpp_identity = stanza.sender
            xmpp_identity.uri = sender_uri_bare
            sip_identity = stanza.recipient
            handler = X2SPresenceHandler(sip_identity, xmpp_identity)
            self.x2s_presence_subscriptions[(sender_uri_bare,
                                             stanza.recipient.uri)] = handler
            notification.center.add_observer(self, sender=handler)
            handler.start()

    def _NH_XMPPGotMucJoinRequest(self, notification):
        stanza = notification.data.stanza
        muc_uri = FrozenURI(stanza.recipient.uri.user,
                            stanza.recipient.uri.host)
        nickname = stanza.recipient.uri.resource
        try:
            handler = self.x2s_muc_sessions[(stanza.sender.uri, muc_uri)]
        except KeyError:
            xmpp_identity = stanza.sender
            sip_identity = stanza.recipient
            sip_identity.uri = muc_uri
            handler = X2SMucHandler(sip_identity, xmpp_identity, nickname)
            handler._first_stanza = stanza
            notification.center.add_observer(self, sender=handler)
            handler.start()
            # Check if there was a pending join request on the SIP side
            try:
                handler = self.s2x_muc_add_participant_handlers[(
                    muc_uri,
                    FrozenURI(stanza.sender.uri.user, stanza.sender.uri.host))]
            except KeyError:
                pass
            else:
                handler.stop()

    def _NH_XMPPGotMucAddParticipantRequest(self, notification):
        sender = notification.data.sender
        recipient = notification.data.recipient
        participant = notification.data.participant
        muc_uri = FrozenURI(recipient.uri.user, recipient.uri.host)
        sender_uri = FrozenURI(sender.uri.user, sender.uri.host)
        participant_uri = FrozenURI(participant.uri.user, participant.uri.host)
        sender = Identity(sender_uri)
        recipient = Identity(muc_uri)
        participant = Identity(participant_uri)
        try:
            handler = self.x2s_muc_add_participant_handlers[(muc_uri,
                                                             participant_uri)]
        except KeyError:
            handler = X2SMucInvitationHandler(sender, recipient, participant)
            self.x2s_muc_add_participant_handlers[(muc_uri,
                                                   participant_uri)] = handler
            notification.center.add_observer(self, sender=handler)
            handler.start()

    # Chat session handling

    def _NH_ChatSessionDidStart(self, notification):
        handler = notification.sender
        log.info('Chat session established sip:%s <--> xmpp:%s' %
                 (handler.sip_identity.uri, handler.xmpp_identity.uri))
        for k, v in self.pending_sessions.items():
            if v is handler:
                del self.pending_sessions[k]
                break
        self.chat_sessions.add(handler)

    def _NH_ChatSessionDidEnd(self, notification):
        handler = notification.sender
        log.info('Chat session ended sip:%s <--> xmpp:%s' %
                 (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.chat_sessions.remove(handler)
        notification.center.remove_observer(self, sender=handler)

    def _NH_ChatSessionDidFail(self, notification):
        handler = notification.sender
        uris = None
        for k, v in self.pending_sessions.items():
            if v is handler:
                uris = k
                del self.pending_sessions[k]
                break
        sip_uri, xmpp_uri = uris
        log.info('Chat session failed sip:%s <--> xmpp:%s (%s)' %
                 (sip_uri, xmpp_uri, notification.data.reason))
        notification.center.remove_observer(self, sender=handler)

    # Presence handling

    def _NH_S2XPresenceHandlerDidStart(self, notification):
        handler = notification.sender
        if XMPPGatewayConfig.log_presence:
            log.info('Presence flow 0x%x established %s --> %s' %
                     (id(handler), format_uri(handler.sip_identity.uri, 'sip'),
                      format_uri(handler.xmpp_identity.uri, 'xmpp')))
            log.info(
                '%d SIP --> XMPP and %d XMPP --> SIP presence flows are active'
                % (len(self.s2x_presence_subscriptions),
                   len(self.x2s_presence_subscriptions)))

    def _NH_S2XPresenceHandlerDidEnd(self, notification):
        handler = notification.sender
        self.s2x_presence_subscriptions.pop(
            (handler.sip_identity.uri, handler.xmpp_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)
        if XMPPGatewayConfig.log_presence:
            log.info('Presence flow 0x%x ended %s --> %s' %
                     (id(handler), format_uri(handler.sip_identity.uri, 'sip'),
                      format_uri(handler.xmpp_identity.uri, 'xmpp')))
            log.info(
                '%d SIP --> XMPP and %d XMPP --> SIP presence flows are active'
                % (len(self.s2x_presence_subscriptions),
                   len(self.x2s_presence_subscriptions)))

    def _NH_X2SPresenceHandlerDidStart(self, notification):
        handler = notification.sender
        if XMPPGatewayConfig.log_presence:
            log.info(
                'Presence flow 0x%x established %s --> %s' %
                (id(handler), format_uri(handler.xmpp_identity.uri, 'xmpp'),
                 format_uri(handler.sip_identity.uri, 'sip')))
            log.info(
                '%d SIP --> XMPP and %d XMPP --> SIP presence flows are active'
                % (len(self.s2x_presence_subscriptions),
                   len(self.x2s_presence_subscriptions)))

    def _NH_X2SPresenceHandlerDidEnd(self, notification):
        handler = notification.sender
        self.x2s_presence_subscriptions.pop(
            (handler.xmpp_identity.uri, handler.sip_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)
        if XMPPGatewayConfig.log_presence:
            log.info(
                'Presence flow 0x%x ended %s --> %s' %
                (id(handler), format_uri(handler.xmpp_identity.uri, 'xmpp'),
                 format_uri(handler.sip_identity.uri, 'sip')))
            log.info(
                '%d SIP --> XMPP and %d XMPP --> SIP presence flows are active'
                % (len(self.s2x_presence_subscriptions),
                   len(self.x2s_presence_subscriptions)))

    # MUC handling

    def _NH_X2SMucHandlerDidStart(self, notification):
        handler = notification.sender
        log.info('Multiparty session established xmpp:%s --> sip:%s' %
                 (handler.xmpp_identity.uri, handler.sip_identity.uri))
        self.x2s_muc_sessions[(handler.xmpp_identity.uri,
                               handler.sip_identity.uri)] = handler

    def _NH_X2SMucHandlerDidEnd(self, notification):
        handler = notification.sender
        log.info('Multiparty session ended xmpp:%s --> sip:%s' %
                 (handler.xmpp_identity.uri, handler.sip_identity.uri))
        self.x2s_muc_sessions.pop(
            (handler.xmpp_identity.uri, handler.sip_identity.uri), None)
        notification.center.remove_observer(self, sender=handler)

    def _NH_X2SMucInvitationHandlerDidStart(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.info('%s invited %s to multiparty chat %s' %
                 (format_uri(sender_uri, 'xmpp'), format_uri(participant_uri),
                  format_uri(muc_uri, 'sip')))

    def _NH_X2SMucInvitationHandlerDidEnd(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.info('%s added %s to multiparty chat %s' %
                 (format_uri(sender_uri, 'xmpp'), format_uri(participant_uri),
                  format_uri(muc_uri, 'sip')))
        del self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_X2SMucInvitationHandlerDidFail(self, notification):
        handler = notification.sender
        sender_uri = handler.sender.uri
        muc_uri = handler.recipient.uri
        participant_uri = handler.participant.uri
        log.info('%s could not add %s to multiparty chat %s: %s' %
                 (format_uri(sender_uri, 'xmpp'), format_uri(participant_uri),
                  format_uri(muc_uri, 'sip'), notification.data.failure))
        del self.x2s_muc_add_participant_handlers[(muc_uri, participant_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_S2XMucInvitationHandlerDidStart(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.info(
            "%s invited %s to multiparty chat %s" %
            (format_uri(inviter_uri, 'sip'), format_uri(
                recipient_uri, 'xmpp'), format_uri(muc_uri, 'sip')))

    def _NH_S2XMucInvitationHandlerDidEnd(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.info(
            '%s added %s to multiparty chat %s' %
            (format_uri(inviter_uri, 'sip'), format_uri(
                recipient_uri, 'xmpp'), format_uri(muc_uri, 'sip')))
        del self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)]
        notification.center.remove_observer(self, sender=handler)

    def _NH_S2XMucInvitationHandlerDidFail(self, notification):
        handler = notification.sender
        muc_uri = handler.sender.uri
        inviter_uri = handler.inviter.uri
        recipient_uri = handler.recipient.uri
        log.info(
            '%s could not add %s to multiparty chat %s: %s' %
            (format_uri(inviter_uri, 'sip'), format_uri(recipient_uri, 'xmpp'),
             format_uri(muc_uri, 'sip'), str(notification.data.failure)))
        del self.s2x_muc_add_participant_handlers[(muc_uri, recipient_uri)]
        notification.center.remove_observer(self, sender=handler)

    # Media sessions

    def _NH_JingleSessionNewIncoming(self, notification):
        session = notification.sender
        handler = MediaSessionHandler.new_from_jingle_session(session)
        if handler is not None:
            notification.center.add_observer(self, sender=handler)

    def _NH_MediaSessionHandlerDidStart(self, notification):
        handler = notification.sender
        log.info('Media session started sip:%s <--> xmpp:%s' %
                 (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.media_sessions.add(handler)

    def _NH_MediaSessionHandlerDidEnd(self, notification):
        handler = notification.sender
        log.info('Media session ended sip:%s <--> xmpp:%s' %
                 (handler.sip_identity.uri, handler.xmpp_identity.uri))
        self.media_sessions.remove(handler)
        notification.center.remove_observer(self, sender=handler)

    def _NH_MediaSessionHandlerDidFail(self, notification):
        handler = notification.sender
        log.info('Media session failed sip:%s <--> xmpp:%s' %
                 (handler.sip_identity.uri, handler.xmpp_identity.uri))
        notification.center.remove_observer(self, sender=handler)
예제 #24
0
class XMPPChatSession(object):
    local_identity = WriteOnceAttribute()
    remote_identity = WriteOnceAttribute()

    def __init__(self, local_identity, remote_identity):
        self.local_identity = local_identity
        self.remote_identity = remote_identity
        self.state = None
        self.pending_receipts = {}
        self.channel = coros.queue()
        self._proc = None
        from sylk.applications.xmppgateway.xmpp import XMPPManager
        self.xmpp_manager = XMPPManager()

    def start(self):
        NotificationCenter().post_notification('XMPPChatSessionDidStart',
                                               sender=self)
        self._proc = proc.spawn(self._run)
        self.state = 'started'

    def end(self):
        self.send_composing_indication('gone')
        self._clear_pending_receipts()
        self._proc.kill()
        self._proc = None
        NotificationCenter().post_notification(
            'XMPPChatSessionDidEnd',
            sender=self,
            data=NotificationData(originator='local'))
        self.state = 'terminated'

    def send_message(self, body, html_body, message_id=None, use_receipt=True):
        message = ChatMessage(self.local_identity,
                              self.remote_identity,
                              body,
                              html_body,
                              id=message_id,
                              use_receipt=use_receipt)
        self.xmpp_manager.send_stanza(message)
        if message_id is not None:
            timer = reactor.callLater(30, self._receipt_timer_expired,
                                      message_id)
            self.pending_receipts[message_id] = timer
            NotificationCenter().post_notification(
                'XMPPChatSessionDidSendMessage',
                sender=self,
                data=NotificationData(message=message))

    def send_composing_indication(self,
                                  state,
                                  message_id=None,
                                  use_receipt=False):
        message = ChatComposingIndication(self.local_identity,
                                          self.remote_identity,
                                          state,
                                          id=message_id,
                                          use_receipt=use_receipt)
        self.xmpp_manager.send_stanza(message)
        if message_id is not None:
            timer = reactor.callLater(30, self._receipt_timer_expired,
                                      message_id)
            self.pending_receipts[message_id] = timer
            NotificationCenter().post_notification(
                'XMPPChatSessionDidSendMessage',
                sender=self,
                data=NotificationData(message=message))

    def send_receipt_acknowledgement(self, receipt_id):
        message = MessageReceipt(self.local_identity, self.remote_identity,
                                 receipt_id)
        self.xmpp_manager.send_stanza(message)

    def send_error(self, stanza, error_type, conditions):
        message = ErrorStanza.from_stanza(stanza, error_type, conditions)
        self.xmpp_manager.send_stanza(message)

    def _run(self):
        notification_center = NotificationCenter()
        while True:
            item = self.channel.wait()
            if isinstance(item, ChatMessage):
                notification_center.post_notification(
                    'XMPPChatSessionGotMessage',
                    sender=self,
                    data=NotificationData(message=item))
            elif isinstance(item, ChatComposingIndication):
                if item.state == 'gone':
                    self._clear_pending_receipts()
                    notification_center.post_notification(
                        'XMPPChatSessionDidEnd',
                        sender=self,
                        data=NotificationData(originator='remote'))
                    self.state = 'terminated'
                    break
                else:
                    notification_center.post_notification(
                        'XMPPChatSessionGotComposingIndication',
                        sender=self,
                        data=NotificationData(message=item))
            elif isinstance(item, MessageReceipt):
                if item.receipt_id in self.pending_receipts:
                    timer = self.pending_receipts.pop(item.receipt_id)
                    timer.cancel()
                    notification_center.post_notification(
                        'XMPPChatSessionDidDeliverMessage',
                        sender=self,
                        data=NotificationData(message_id=item.receipt_id))
            elif isinstance(item, ErrorStanza):
                if item.id in self.pending_receipts:
                    timer = self.pending_receipts.pop(item.id)
                    timer.cancel()
                    # TODO: translate cause
                    notification_center.post_notification(
                        'XMPPChatSessionDidNotDeliverMessage',
                        sender=self,
                        data=NotificationData(message_id=item.id,
                                              code=503,
                                              reason='Service Unavailable'))
        self._proc = None

    def _receipt_timer_expired(self, message_id):
        self.pending_receipts.pop(message_id)
        NotificationCenter().post_notification(
            'XMPPChatSessionDidNotDeliverMessage',
            sender=self,
            data=NotificationData(message_id=message_id,
                                  code=408,
                                  reason='Timeout'))

    def _clear_pending_receipts(self):
        notification_center = NotificationCenter()
        while self.pending_receipts:
            message_id, timer = self.pending_receipts.popitem()
            timer.cancel()
            notification_center.post_notification(
                'XMPPChatSessionDidNotDeliverMessage',
                sender=self,
                data=NotificationData(message_id=message_id,
                                      code=408,
                                      reason='Timeout'))