示例#1
0
    def init_incoming(self, stanza):
        self._id = stanza.jingle.sid
        self._local_identity = Identity(FrozenURI.parse(stanza.recipient))
        self._remote_identity = Identity(FrozenURI.parse(stanza.sender))
        self._local_jid = self._local_identity.uri.as_xmpp_jid()
        self._remote_jid = self._remote_identity.uri.as_xmpp_jid()

        remote_sdp = jingle_to_sdp(stanza.jingle)
        try:
            self._sdp_negotiator = SDPNegotiator.create_with_remote_offer(remote_sdp)
        except SIPCoreError, e:
            self._fail(originator='local', reason='general-error', description=str(e))
            return
示例#2
0
    def init_incoming(self, stanza):
        self._id = stanza.jingle.sid
        self._local_identity = Identity(FrozenURI.parse(stanza.recipient))
        self._remote_identity = Identity(FrozenURI.parse(stanza.sender))
        self._local_jid = self._local_identity.uri.as_xmpp_jid()
        self._remote_jid = self._remote_identity.uri.as_xmpp_jid()

        remote_sdp = jingle_to_sdp(stanza.jingle)
        try:
            self._sdp_negotiator = SDPNegotiator.create_with_remote_offer(remote_sdp)
        except SIPCoreError, e:
            self._fail(originator='local', reason='general-error', description=str(e))
            return
示例#3
0
    def init_incoming(self, stanza):
        self._id = stanza.jingle.sid
        self._local_identity = Identity(FrozenURI.parse(stanza.recipient))
        self._remote_identity = Identity(FrozenURI.parse(stanza.sender))
        self._local_jid = self._local_identity.uri.as_xmpp_jid()
        self._remote_jid = self._remote_identity.uri.as_xmpp_jid()

        remote_sdp = jingle_to_sdp(stanza.jingle)
        try:
            self._sdp_negotiator = SDPNegotiator.create_with_remote_offer(
                remote_sdp)
        except SIPCoreError as e:
            self._fail(originator='local',
                       reason='general-error',
                       description=str(e))
            return

        self.proposed_streams = []
        for index, media_stream in enumerate(remote_sdp.media):
            if media_stream.port != 0:
                for stream_type in MediaStreamRegistry:
                    try:
                        stream = stream_type.new_from_sdp(
                            self, remote_sdp, index)
                    except InvalidStreamError:
                        break
                    except UnknownStreamError:
                        continue
                    else:
                        stream.index = index
                        self.proposed_streams.append(stream)
                        break

        if self.proposed_streams:
            self.direction = 'incoming'
            self.state = 'incoming'
            NotificationCenter().post_notification(
                'JingleSessionNewIncoming',
                sender=self,
                data=NotificationData(streams=self.proposed_streams))
        else:
            self._fail(originator='local', reason='unsupported-applications')
示例#4
0
    def _OH_ProcessRemoteOperation(self, operation):
        notification = operation.notification
        stanza = notification.data.stanza
        if notification.name == 'XMPPGotJingleSessionTerminate':
            if self.state not in ('incoming', 'connecting', 'connected_pending_accept', 'connected'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None
            # Session ended remotely
            prev_state = self.state
            self.state = 'terminated'
            if prev_state == 'incoming':
                reason = stanza.jingle.reason.value if stanza.jingle.reason else 'cancel'
                notification.center.post_notification('JingleSessionDidFail', self, NotificationData(originator='remote', reason=reason))
            else:
                notification.center.post_notification('JingleSessionWillEnd', self, NotificationData(originator='remote'))
                streams = self.proposed_streams if prev_state == 'connecting' else self.streams
                for stream in streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self.end_time = datetime.now()
                notification.center.post_notification('JingleSessionDidEnd', self, NotificationData(originator='remote'))
            self._channel.send_exception(proc.ProcExit)
        elif notification.name == 'XMPPGotJingleSessionInfo':
            info = stanza.jingle.info
            if not info:
                return
            if info == 'ringing':
                if self.state not in ('connecting', 'connected_pending_accept'):
                    return
                notification.center.post_notification('JingleSessionGotRingIndication', self)
            elif info in ('hold', 'unhold'):
                if self.state != 'connected':
                    return
                notification.center.post_notification('JingleSessionDidChangeHoldState', self, NotificationData(originator='remote', on_hold=info=='hold', partial=False))
        elif notification.name == 'XMPPGotJingleDescriptionInfo':
            if self.state != 'connecting':
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError:
                # The description-info stanza may have been just a parameter change, not a full 'SDP'
                return

            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            del self._pending_transport_info_stanzas[:]

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart', sender=self)
            stream_map = dict((stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError), e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local', reason='failed-application', description=error)
            else:
                self.state = 'connected_pending_accept'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                # Hold the streams to prevent real RTP from flowing
                for stream in self.streams:
                    stream.hold()
示例#5
0
                notification.center.post_notification('JingleSessionDidStart', self, NotificationData(streams=self.streams))
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password
            del self._pending_transport_info_stanzas[:]

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError, e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self._fail(originator='remote', reason='incompatible-parameters', description=str(e))
                return

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote
示例#6
0
    def _OH_ProcessRemoteOperation(self, operation):
        notification = operation.notification
        stanza = notification.data.stanza
        if notification.name == 'XMPPGotJingleSessionTerminate':
            if self.state not in ('incoming', 'connecting',
                                  'connected_pending_accept', 'connected'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None
            # Session ended remotely
            prev_state = self.state
            self.state = 'terminated'
            if prev_state == 'incoming':
                reason = stanza.jingle.reason.value if stanza.jingle.reason else 'cancel'
                notification.center.post_notification(
                    'JingleSessionDidFail', self,
                    NotificationData(originator='remote', reason=reason))
            else:
                notification.center.post_notification(
                    'JingleSessionWillEnd', self,
                    NotificationData(originator='remote'))
                streams = self.proposed_streams if prev_state == 'connecting' else self.streams
                for stream in streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self.end_time = datetime.now()
                notification.center.post_notification(
                    'JingleSessionDidEnd', self,
                    NotificationData(originator='remote'))
            self._channel.send_exception(proc.ProcExit)
        elif notification.name == 'XMPPGotJingleSessionInfo':
            info = stanza.jingle.info
            if not info:
                return
            if info == 'ringing':
                if self.state not in ('connecting',
                                      'connected_pending_accept'):
                    return
                notification.center.post_notification(
                    'JingleSessionGotRingIndication', self)
            elif info in ('hold', 'unhold'):
                if self.state != 'connected':
                    return
                notification.center.post_notification(
                    'JingleSessionDidChangeHoldState', self,
                    NotificationData(originator='remote',
                                     on_hold=info == 'hold',
                                     partial=False))
        elif notification.name == 'XMPPGotJingleDescriptionInfo':
            if self.state != 'connecting':
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content
                                   if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError:
                # The description-info stanza may have been just a parameter change, not a full 'SDP'
                return

            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            del self._pending_transport_info_stanzas[:]

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart',
                                                  sender=self)
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [
                stream for stream in self.proposed_streams
                if stream.index >= len(local_sdp.media)
            ]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError) as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local',
                           reason='failed-application',
                           description=error)
            else:
                self.state = 'connected_pending_accept'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                # Hold the streams to prevent real RTP from flowing
                for stream in self.streams:
                    stream.hold()
        elif notification.name == 'XMPPGotJingleSessionAccept':
            if self.state not in ('connecting', 'connected_pending_accept'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            if self.state == 'connected_pending_accept':
                # We already negotiated ICE and media is 'flowing' (not really because streams are on hold)
                # unhold the streams and pretend the session just started
                for stream in self.streams:
                    stream.unhold()
                self.state = 'connected'
                notification.center.post_notification(
                    'JingleSessionDidStart', self,
                    NotificationData(streams=self.streams))
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content
                                   if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password
            del self._pending_transport_info_stanzas[:]

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self._fail(originator='remote',
                           reason='incompatible-parameters',
                           description=str(e))
                return

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart',
                                                  sender=self)
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [
                stream for stream in self.proposed_streams
                if stream.index >= len(local_sdp.media)
            ]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError) as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local',
                           reason='failed-application',
                           description=error)
            else:
                self.state = 'connected'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                notification.center.post_notification(
                    'JingleSessionDidStart', self,
                    NotificationData(streams=self.streams))
        elif notification.name == 'XMPPGotJingleTransportInfo':
            if self.state != 'connecting':
                # ICE trickling not supported yet, so only accept candidates before accept
                return

            for c in stanza.jingle.content:
                content = next(content for content in stanza.jingle.content
                               if content.name == c.name)
                content.transport.candidates.extend(c.transport.candidates)
                if isinstance(content.transport,
                              jingle.IceUdpTransport) or isinstance(
                                  content.transport, jingle.RawUdpTransport):
                    for cand in content.transport.candidates:
                        if cand.port == 0:
                            continue

                        idx = "%s:%s:%s" % (cand.protocol, cand.ip, cand.port)
                        if idx in self.candidates:
                            continue

                        self.candidates.add(idx)
                        self._pending_transport_info_stanzas.append(stanza)
示例#7
0
                notification.center.post_notification('JingleSessionDidStart', self, NotificationData(streams=self.streams))
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password
            del self._pending_transport_info_stanzas[:]

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError, e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self._fail(originator='remote', reason='incompatible-parameters', description=str(e))
                return

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote