def _OH_session_trickle(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: session = data['session'] candidates = data['candidates'] except KeyError: raise APIError('Invalid parameters: "session" and "candidates" must be specified') try: session_info = self.sessions_map[session] except KeyError: raise APIError('Unknown session specified: %s' % session) if session_info.state == 'terminated': raise APIError('Session is terminated') block_on(self.backend.janus_trickle(self.janus_session_id, session_info.janus_handle_id, candidates)) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('Trickled ICE candidate(s) for session %s' % session) except APIError, e: log.error('session-trickle: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def delete_messages(self, local_uri=None, remote_uri=None, date=None, after_date=None, before_date=None, media_type=None): block_on(self.chat_history.delete_messages(local_uri=local_uri, remote_uri=remote_uri, date=date, after_date=after_date, before_date=before_date, media_type=media_type)) block_on(self.session_history.delete_entries(local_uri=local_uri, remote_uri=remote_uri, after_date=after_date, before_date=before_date)) self.search_text = None self.search_uris = None self.search_local = None self.refreshViewer()
def _OH_session_terminate(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: session = data['session'] except KeyError: raise APIError('Invalid parameters: "session" must be specified') try: session_info = self.sessions_map[session] except KeyError: raise APIError('Unknown session specified: %s' % session) if session_info.state not in ('connecting', 'progress', 'accepted', 'established'): raise APIError('Invalid state for session terminate: \"%s\"' % session_info.state) if session_info.direction == 'incoming' and session_info.state == 'connecting': data = {'request': 'decline', 'code': 486} else: data = {'request': 'hangup'} block_on(self.backend.janus_message(self.janus_session_id, session_info.janus_handle_id, data)) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('%s terminated session %s' % (session_info.account_id, session)) except APIError, e: log.error('session-terminate: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def test_block_on_success(self): from twisted.internet import reactor d = reactor.resolver.getHostByName('www.google.com') ip = block_on(d) assert len(ip.split('.')) == 4, ip ip2 = block_on(d) assert ip == ip2, (ip, ip2)
def _OH_session_answer(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: session = data['session'] sdp = data['sdp'] except KeyError: raise APIError('Invalid parameters: "session" and "sdp" must be specified') try: session_info = self.sessions_map[session] except KeyError: raise APIError('Unknown session specified: %s' % session) if session_info.direction != 'incoming': raise APIError('Cannot answer outgoing session') if session_info.state != 'connecting': raise APIError('Invalid state for session answer') data = {'request': 'accept'} jsep = {'type': 'answer', 'sdp': sdp} block_on(self.backend.janus_message(self.janus_session_id, session_info.janus_handle_id, data, jsep)) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('%s answered session %s' % (session_info.account_id, session)) except APIError, e: log.error('session-answer: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def _OH_account_unregister(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: account = data['account'] except KeyError: raise APIError('Invalid parameters: "account" must be specified') try: account_info = self.accounts_map[account] except KeyError: raise APIError('Unknown account specified: %s' % account) handle_id = account_info.janus_handle_id if handle_id is not None: block_on(self.backend.janus_detach(self.janus_session_id, handle_id)) self.backend.janus_set_event_handler(handle_id, None) account_info.janus_handle_id = None self.account_handles_map.pop(handle_id) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('Account %s will unregister' % account) except APIError, e: log.error('account-unregister: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def do_cleanup(): if self.janus_session_id is None: # The connection was closed, there is noting to do here return self.sessions_map.pop(session.id) if session.direction == 'outgoing': # Destroy plugin handle for outgoing sessions. For incoming ones it's the # same as the account handle, so don't block_on(self.backend.janus_detach(self.janus_session_id, session.janus_handle_id)) self.backend.janus_set_event_handler(session.janus_handle_id, None) self.session_handles_map.pop(session.janus_handle_id)
def _start_outgoing_jingle_session(self, streams): if self.xmpp_identity.uri.resource is not None: self.sip_session.reject() return xmpp_manager = XMPPManager() local_jid = self.sip_identity.uri.as_xmpp_jid() remote_jid = self.xmpp_identity.uri.as_xmpp_jid() # If this was an invitation to a conference, use the information in the Referred-By header if self.sip_identity.uri.host in xmpp_manager.muc_domains and self.sip_session.transfer_info and self.sip_session.transfer_info.referred_by: try: referred_by_uri = SIPURI.parse(self.sip_session.transfer_info.referred_by) except SIPCoreError: self.sip_session.reject(488) return else: inviter_uri = FrozenURI(referred_by_uri.user, referred_by_uri.host) local_jid = inviter_uri.as_xmpp_jid() # Use disco to gather potential JIDs to call d = xmpp_manager.disco_client_protocol.requestItems(remote_jid, sender=local_jid) try: items = block_on(d) except Exception: items = [] if not items: self.sip_session.reject(480) return # Check which items support Jingle valid = [] for item in items: d = xmpp_manager.disco_client_protocol.requestInfo(item.entity, nodeIdentifier=item.nodeIdentifier, sender=local_jid) try: info = block_on(d) except Exception: continue if jingle.NS_JINGLE in info.features and jingle.NS_JINGLE_APPS_RTP in info.features: valid.append(item.entity) if not valid: self.sip_session.reject(480) return # TODO: start multiple sessions? self._xmpp_identity = Identity(FrozenURI.parse(valid[0])) notification_center = NotificationCenter() if self.sip_identity.uri.host in xmpp_manager.muc_domains: self.jingle_session = JingleSession(xmpp_manager.jingle_coin_protocol) else: self.jingle_session = JingleSession(xmpp_manager.jingle_protocol) notification_center.add_observer(self, sender=self.jingle_session) self.jingle_session.connect(self.sip_identity, self.xmpp_identity, streams, is_focus=self.sip_session.remote_focus)
def _shutdown(self): if self.disconnecting: return self.disconnecting = True self.dns_monitor.cancel() if self.advertiser: self.advertiser.cancel() if self.shutdown_message: self._publish(self.shutdown_message) requests = [conn.protocol.unsubscribe(*self.topics) for conn in self.connections] d = defer.DeferredList([request.deferred for request in requests]) block_on(d) self._disconnect_all()
class JanusPluginHandle(object): backend = JanusBackend() plugin = None def __init__(self, session, event_handler): if self.plugin is None: raise TypeError( 'Cannot instantiate {0.__class__.__name__} with no associated plugin' .format(self)) response = block_on(self.backend.attach_plugin( session.id, self.plugin)) # type: janus.SuccessResponse self.id = response.data.id self.session = session self.backend.set_event_handler(self.id, event_handler) def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): self.detach() def detach(self): try: block_on(self.backend.detach_plugin(self.session.id, self.id)) except JanusError as e: log.warning('could not detach Janus plugin: %s', e) self.backend.set_event_handler(self.id, None) def message(self, body, jsep=None, async=False): deferred = self.backend.message(self.session.id, self.id, body, jsep) return deferred if async else block_on(deferred)
def _OH_account_register(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: account = data['account'] except KeyError: raise APIError('Invalid parameters: "account" must be specified') try: account_info = self.accounts_map[account] except KeyError: raise APIError('Unknown account specified: %s' % account) proxy = self._lookup_sip_proxy(account) handle_id = account_info.janus_handle_id if handle_id is not None: # Destroy the existing plugin handle block_on(self.backend.janus_detach(self.janus_session_id, handle_id)) self.backend.janus_set_event_handler(handle_id, None) self.account_handles_map.pop(handle_id) account_info.janus_handle_id = None # Create a plugin handle handle_id = block_on(self.backend.janus_attach(self.janus_session_id, 'janus.plugin.sip')) self.backend.janus_set_event_handler(handle_id, self._handle_janus_event) account_info.janus_handle_id = handle_id self.account_handles_map[handle_id] = account_info data = {'request': 'register', 'username': account_info.uri, 'ha1_secret': account_info.password, 'proxy': proxy} block_on(self.backend.janus_message(self.janus_session_id, handle_id, data)) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('Account %s will register' % account) except APIError, e: log.error('account-register: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def __init__(self, session, event_handler): if self.plugin is None: raise TypeError( 'Cannot instantiate {0.__class__.__name__} with no associated plugin' .format(self)) response = block_on(self.backend.attach_plugin( session.id, self.plugin)) # type: janus.SuccessResponse self.id = response.data.id self.session = session self.backend.set_event_handler(self.id, event_handler)
def _create_janus_session(self): if self.ready_event.is_set(): data = dict(sylkrtc='event', event='ready') self._send_data(json.dumps(data)) return try: self.janus_session_id = block_on(self.backend.janus_create_session()) self.backend.janus_start_keepalive(self.janus_session_id) except Exception, e: log.warn('Error creating session, disconnecting: %s' % e) self.disconnect(3000, unicode(e)) return
def _OH_session_create(self, data): transaction = data.get('transaction', None) if transaction is None: log.warn('Transaction not specified!') return try: try: account = data['account'] session = data['session'] uri = data['uri'] sdp = data['sdp'] except KeyError: raise APIError('Invalid parameters: "account", "session", "uri" and "sdp" must be specified') try: account_info = self.accounts_map[account] except KeyError: raise APIError('Unknown account specified: %s' % account) if session in self.sessions_map: raise APIError('Session ID (%s) already in use' % session) # Create a new plugin handle and 'register' it, without actually doing so handle_id = block_on(self.backend.janus_attach(self.janus_session_id, 'janus.plugin.sip')) self.backend.janus_set_event_handler(handle_id, self._handle_janus_event) try: proxy = self._lookup_sip_proxy(account_info.id) except DNSLookupError: block_on(self.backend.janus_detach(self.janus_session_id, handle_id)) self.backend.janus_set_event_handler(handle_id, None) raise APIError('DNS lookup error') account_uri = 'sip:%s' % account_info.id data = {'request': 'register', 'username': account_uri, 'ha1_secret': account_info.password, 'proxy': proxy, 'send_register': False} block_on(self.backend.janus_message(self.janus_session_id, handle_id, data)) session_info = JanusSessionInfo(session) session_info.janus_handle_id = handle_id session_info.init_outgoing(account, uri) self.sessions_map[session_info.id] = session_info self.session_handles_map[handle_id] = session_info data = {'request': 'call', 'uri': 'sip:%s' % SIP_PREFIX_RE.sub('', uri)} jsep = {'type': 'offer', 'sdp': sdp} block_on(self.backend.janus_message(self.janus_session_id, handle_id, data, jsep)) data = dict(sylkrtc='ack', transaction=transaction) self._send_data(json.dumps(data)) log.msg('Outgoing session %s from %s to %s created' % (session, account, uri)) except APIError, e: log.error('session-create: %s' % e) data = dict(sylkrtc='error', transaction=transaction, error=str(e)) self._send_data(json.dumps(data))
def detach(self): try: block_on(self.backend.detach_plugin(self.session.id, self.id)) except JanusError as e: log.warning('could not detach Janus plugin: %s', e) self.backend.set_event_handler(self.id, None)
def test_block_on_already_succeed(self): d = defer.succeed('hey corotwine') res = block_on(d) assert res == 'hey corotwine', ` res `
def __init__(self): response = block_on( self.backend.create_session()) # type: janus.SuccessResponse self.id = response.data.id
self.detach() def detach(self): try: block_on(self.backend.detach_plugin(self.session.id, self.id)) except JanusError as e: log.warning('could not detach Janus plugin: %s', e) self.backend.set_event_handler(self.id, None) def message(self, body, jsep=None, async=False): deferred = self.backend.message(self.session.id, self.id, body, jsep) return deferred if async else block_on(deferred) def trickle(self, candidates, async=False): deferred = self.backend.trickle(self.session.id, self.id, candidates) return deferred if async else block_on(deferred) class GenericPluginHandle(JanusPluginHandle): def __init__(self, plugin, session, event_handler): self.plugin = plugin super(GenericPluginHandle, self).__init__(session, event_handler) class SIPPluginHandle(JanusPluginHandle): plugin = 'janus.plugin.sip' def register(self, account, proxy=None): self.message(janus.SIPRegister(proxy=proxy, **account.user_data)) def unregister(self):
def _OH_AcceptOperation(self, operation): if self.state != 'incoming': return notification_center = NotificationCenter() settings = SIPSimpleSettings() streams = operation.streams for stream in self.proposed_streams: if stream in streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='incoming') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 remote_sdp = self._sdp_negotiator.current_remote local_ip = SIPConfig.local_ip.normalized local_sdp = SDPSession(local_ip, connection=SDPConnection(local_ip), name=settings.user_agent) stream_map = dict((stream.index, stream) for stream in self.proposed_streams) for index, media in enumerate(remote_sdp.media): stream = stream_map.get(index, None) if stream is not None: media = stream.get_local_media(remote_sdp=remote_sdp, index=index) else: media = SDPMediaStream.new(media) media.port = 0 media.attributes = [] local_sdp.media.append(media) try: self._sdp_negotiator.set_local_answer(local_sdp) self._sdp_negotiator.negotiate() except SIPCoreError, e: self._fail(originator='local', reason='incompatible-parameters', description=str(e)) return self.local_focus = operation.is_focus notification_center.post_notification('JingleSessionWillStart', sender=self) # Get active SDPs (negotiator may make changes) local_sdp = self._sdp_negotiator.active_local remote_sdp = self._sdp_negotiator.active_remote # Build the payload and send it over payload = sdp_to_jingle(local_sdp) payload.sid = self._id if self.local_focus: payload.conference_info = jingle.ConferenceInfo(True) stanza = self._protocol.sessionAccept(self._local_jid, self._remote_jid, payload) d = self._send_stanza(stanza) block_on(d) wait_count = 0 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.get(index, None) if stream is not None: if remote_media.port: wait_count += 1 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() with api.timeout(self.media_stream_timeout): while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1
def _OH_ConnectOperation(self, operation): if self.state is not None: return settings = SIPSimpleSettings() notification_center = NotificationCenter() self.direction = 'outgoing' self.state = 'connecting' self.proposed_streams = operation.streams self.local_focus = operation.is_focus self._id = random_id() self._local_identity = operation.sender self._remote_identity = operation.recipient self._local_jid = self._local_identity.uri.as_xmpp_jid() self._remote_jid = self._remote_identity.uri.as_xmpp_jid() notification_center.post_notification( 'JingleSessionNewOutgoing', self, NotificationData(streams=operation.streams)) for stream in self.proposed_streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='outgoing') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 # Build local SDP and negotiator local_ip = SIPConfig.local_ip.normalized local_sdp = SDPSession(local_ip, connection=SDPConnection(local_ip), name=settings.user_agent) for index, stream in enumerate(self.proposed_streams): stream.index = index media = stream.get_local_media(remote_sdp=None, index=index) local_sdp.media.append(media) self._sdp_negotiator = SDPNegotiator.create_with_local_offer( local_sdp) # Build the payload and send it over payload = sdp_to_jingle(local_sdp) payload.sid = self._id if self.local_focus: payload.conference_info = jingle.ConferenceInfo(True) stanza = self._protocol.sessionInitiate(self._local_jid, self._remote_jid, payload) d = self._send_stanza(stanza) block_on(d) except (MediaStreamDidNotInitializeError, MediaStreamDidFailError, IqTimeoutError, StanzaError, SIPCoreError) as e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() if isinstance(e, IqTimeoutError): error = 'timeout sending IQ stanza' elif isinstance(e, StanzaError): error = str(e.condition) elif isinstance(e, SIPCoreError): error = str(e) else: error = 'media stream failed: %s' % e.data.reason self.state = 'terminated' NotificationCenter().post_notification('JingleSessionDidFail', sender=self, data=NotificationData( originator='local', reason=error)) self._channel.send_exception(proc.ProcExit) else: self._timer = reactor.callLater(settings.sip.invite_timeout, self.end)
def _OH_ConnectOperation(self, operation): if self.state is not None: return settings = SIPSimpleSettings() notification_center = NotificationCenter() self.direction = 'outgoing' self.state = 'connecting' self.proposed_streams = operation.streams self.local_focus = operation.is_focus self._id = random_id() self._local_identity = operation.sender self._remote_identity = operation.recipient self._local_jid = self._local_identity.uri.as_xmpp_jid() self._remote_jid = self._remote_identity.uri.as_xmpp_jid() notification_center.post_notification('JingleSessionNewOutgoing', self, NotificationData(streams=operation.streams)) for stream in self.proposed_streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='outgoing') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 # Build local SDP and negotiator local_ip = SIPConfig.local_ip.normalized local_sdp = SDPSession(local_ip, connection=SDPConnection(local_ip), name=settings.user_agent) for index, stream in enumerate(self.proposed_streams): stream.index = index media = stream.get_local_media(remote_sdp=None, index=index) local_sdp.media.append(media) self._sdp_negotiator = SDPNegotiator.create_with_local_offer(local_sdp) # Build the payload and send it over payload = sdp_to_jingle(local_sdp) payload.sid = self._id if self.local_focus: payload.conference_info = jingle.ConferenceInfo(True) stanza = self._protocol.sessionInitiate(self._local_jid, self._remote_jid, payload) d = self._send_stanza(stanza) block_on(d) except (MediaStreamDidNotInitializeError, MediaStreamDidFailError, IqTimeoutError, StanzaError, SIPCoreError), e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() if isinstance(e, IqTimeoutError): error = 'timeout sending IQ stanza' elif isinstance(e, StanzaError): error = str(e.condition) elif isinstance(e, SIPCoreError): error = str(e) else: error = 'media stream failed: %s' % e.data.reason self.state = 'terminated' NotificationCenter().post_notification('JingleSessionDidFail', sender=self, data=NotificationData(originator='local', reason=error)) self._channel.send_exception(proc.ProcExit)
def _OH_AcceptOperation(self, operation): if self.state != 'incoming': return notification_center = NotificationCenter() settings = SIPSimpleSettings() streams = operation.streams for stream in self.proposed_streams: if stream in streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='incoming') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 remote_sdp = self._sdp_negotiator.current_remote local_ip = SIPConfig.local_ip.normalized local_sdp = SDPSession(local_ip, connection=SDPConnection(local_ip), name=settings.user_agent) stream_map = dict( (stream.index, stream) for stream in self.proposed_streams) for index, media in enumerate(remote_sdp.media): stream = stream_map.get(index, None) if stream is not None: media = stream.get_local_media(remote_sdp=remote_sdp, index=index) else: media = SDPMediaStream.new(media) media.port = 0 media.attributes = [] local_sdp.media.append(media) try: self._sdp_negotiator.set_local_answer(local_sdp) self._sdp_negotiator.negotiate() except SIPCoreError as e: self._fail(originator='local', reason='incompatible-parameters', description=str(e)) return self.local_focus = operation.is_focus notification_center.post_notification('JingleSessionWillStart', sender=self) # Get active SDPs (negotiator may make changes) local_sdp = self._sdp_negotiator.active_local remote_sdp = self._sdp_negotiator.active_remote # Build the payload and send it over payload = sdp_to_jingle(local_sdp) payload.sid = self._id if self.local_focus: payload.conference_info = jingle.ConferenceInfo(True) stanza = self._protocol.sessionAccept(self._local_jid, self._remote_jid, payload) d = self._send_stanza(stanza) block_on(d) wait_count = 0 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.get(index, None) if stream is not None: if remote_media.port: wait_count += 1 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() with api.timeout(self.media_stream_timeout): while wait_count > 0: notification = operation.channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1 except (MediaStreamDidNotInitializeError, MediaStreamDidFailError, api.TimeoutError, IqTimeoutError, StanzaError) 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' elif isinstance(e, IqTimeoutError): error = 'timeout sending IQ stanza' elif isinstance(e, StanzaError): error = str(e.condition) 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))