Esempio n. 1
0
    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))
Esempio n. 2
0
    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))
Esempio n. 3
0
    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))
Esempio n. 4
0
    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))
Esempio n. 5
0
 def onClose(self, clean, code, reason):
     if self.connection_handler is None:
         # Very early connection closed, onOpen wasn't even called
         return
     log.msg('Connection from %s closed' % self.peer)
     self.factory.connections.discard(self)
     self.connection_handler.stop()
     self.connection_handler = None
Esempio n. 6
0
def _send_push_notification(payload):
    if GeneralConfig.firebase_server_key:
        try:
            r = yield agent.request('POST', FIREBASE_API_URL, headers, StringProducer(payload))
        except Exception, e:
            log.msg('Error sending Firebase message: %s', e)
        else:
            if r.code != 200:
                log.warn('Error sending Firebase message: %s' % r.phrase)
Esempio n. 7
0
 def onConnect(self, request):
     self.peer = request.peer
     if SYLK_WS_PROTOCOL not in request.protocols:
         log.msg('Rejecting connection from %s, remote does not support our sub-protocol' % self.peer)
         raise HttpException(406, 'No compatible protocol specified')
     if not self.backend.ready:
         log.msg('Rejecting connection from %s, backend is not connected' % self.peer)
         raise HttpException(503, 'Backend is not connected')
     return SYLK_WS_PROTOCOL
Esempio n. 8
0
    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))
Esempio n. 9
0
    def start(self):
        ws_url = "ws" + server.url[4:] + "/webrtcgateway/ws"
        self.factory = SylkWebSocketServerFactory(
            ws_url, protocols=[SYLK_WS_PROTOCOL], server="SylkServer/%s" % sylk_version, debug=False
        )
        self.factory.setProtocolOptions(
            allowedOrigins=GeneralConfig.web_origins,
            autoPingInterval=GeneralConfig.websocket_ping_interval,
            autoPingTimeout=GeneralConfig.websocket_ping_interval / 2,
        )
        self.factory.ws_logger = self.ws_logger

        self.web = WebRTCGatewayWeb(self.factory)
        server.register_resource("webrtcgateway", self.web.resource())

        log.msg("WebSocket handler started at %s" % ws_url)
        log.msg("Allowed web origins: %s" % ", ".join(GeneralConfig.web_origins))
        log.msg("Allowed SIP domains: %s" % ", ".join(GeneralConfig.sip_domains))
        log.msg("Using Janus API: %s" % JanusConfig.api_url)

        self.ws_logger.start()

        self.backend = JanusBackend()
        self.backend.start()

        self.factory.backend = self.backend
Esempio n. 10
0
    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))
Esempio n. 11
0
 def onClose(self, wasClean, code, reason):
     if self.ready_event is None:
         # Very early connection closed, onOpen wasn't even called
         return
     log.msg('Connection from %s closed' % self.transport.getPeer())
     self.factory.connections.discard(self)
     if self.ready_event.is_set():
         assert self.janus_session_id is not None
         self.backend.janus_stop_keepalive(self.janus_session_id)
         self.backend.janus_destroy_session(self.janus_session_id)
     if self.proc is not None:
         self.proc.kill()
         self.proc = None
     # cleanup
     self.ready_event.clear()
     self.accounts_map.clear()
     self.account_handles_map.clear()
     self.sessions_map.clear()
     self.session_handles_map.clear()
     self.janus_session_id = None
Esempio n. 12
0
    def _OH_account_add(self, data):
        transaction = data.get('transaction', None)
        if transaction is None:
            log.warn('Transaction not specified!')
            return

        try:
            try:
                account = data['account']
                password = data['password']
            except KeyError:
                raise APIError('Invalid parameters: "account" and "password" must be specified')

            if account in self.accounts_map:
                log.warn('Account %s already added' % account)
                data = dict(sylkrtc='error', transaction=transaction, error='Account already added')
                self._send_data(json.dumps(data))
                return

            # Validate URI
            uri = 'sip:%s' % account
            try:
                sip_uri = SIPURI.parse(uri)
            except SIPCoreError:
                raise APIError('Invalid account specified: %s' % account)
            if not {'*', sip_uri.host}.intersection(GeneralConfig.sip_domains):
                raise APIError('SIP domain not allowed: %s' % sip_uri.host)

            # Create and store our mapping
            account_info = AccountInfo(account, password)
            self.accounts_map[account_info.id] = account_info

            data = dict(sylkrtc='ack', transaction=transaction)
            self._send_data(json.dumps(data))
            log.msg('Account %s added' % account)
        except APIError, e:
            log.error('account_add: %s' % e)
            data = dict(sylkrtc='error', transaction=transaction, error=str(e))
            self._send_data(json.dumps(data))
Esempio n. 13
0
 def onConnect(self, request):
     log.msg('Incoming connection from %s (origin %s)' % (request.peer, request.origin))
     if SYLK_WS_PROTOCOL not in request.protocols:
         log.msg('Rejecting connection, remote does not support our sub-protocol')
         raise http.HttpException(http.NOT_ACCEPTABLE[0], 'No compatible protocol specified')
     if not self.backend.ready:
         log.msg('Rejecting connection, backend is not connected')
         raise http.HttpException(http.SERVICE_UNAVAILABLE[0], 'Backend is not connected')
     return SYLK_WS_PROTOCOL
Esempio n. 14
0
    def start(self):
        ws_url = 'ws' + server.url[4:] + '/webrtcgateway/ws'
        self.factory = SylkWebSocketServerFactory(ws_url, protocols=[SYLK_WS_PROTOCOL], server='SylkServer/%s' % sylk_version, debug=False)
        self.factory.setProtocolOptions(allowedOrigins=GeneralConfig.web_origins)
        self.factory.ws_logger = self.ws_logger

        self.web = WebRTCGatewayWeb(self.factory)
        server.register_resource('webrtcgateway', self.web.resource())

        log.msg('WebSocket handler started at %s' % ws_url)
        log.msg('Allowed web origins: %s' % ', '.join(GeneralConfig.web_origins))
        log.msg('Allowed SIP domains: %s' % ', '.join(GeneralConfig.sip_domains))
        log.msg('Using Janus API: %s' % JanusConfig.api_url)

        self.ws_logger.start()

        self.backend = JanusBackend()
        self.backend.start()

        self.factory.backend = self.backend
Esempio n. 15
0
    def start(self):
        ws_url = 'ws' + server.url[4:] + '/webrtcgateway/ws'
        self.factory = SylkWebSocketServerFactory(ws_url, protocols=[SYLK_WS_PROTOCOL], server='SylkServer/%s' % sylk_version, debug=False)
        self.factory.setProtocolOptions(allowedOrigins=GeneralConfig.web_origins,
                                        autoPingInterval=GeneralConfig.websocket_ping_interval,
                                        autoPingTimeout=GeneralConfig.websocket_ping_interval/2)
        self.factory.ws_logger = self.ws_logger

        self.web = WebRTCGatewayWeb(self.factory)
        server.register_resource('webrtcgateway', self.web.resource())

        log.msg('WebSocket handler started at %s' % ws_url)
        log.msg('Allowed web origins: %s' % ', '.join(GeneralConfig.web_origins))
        log.msg('Allowed SIP domains: %s' % ', '.join(GeneralConfig.sip_domains))
        log.msg('Using Janus API: %s' % JanusConfig.api_url)

        self.ws_logger.start()

        self.backend = JanusBackend()
        self.backend.start()

        self.factory.backend = self.backend
Esempio n. 16
0
 def _NH_JanusBackendDisconnected(self, notification):
     log.msg('Janus backend connection down: %s' % notification.data.reason)
     self.connection = Null
Esempio n. 17
0
 def _NH_JanusBackendConnected(self, notification):
     assert self.connection is Null
     self.connection = notification.sender
     log.msg('Janus backend connection up')
     self.factory.resetDelay()
Esempio n. 18
0
 def incoming_session(self, session):
     log.msg(u'New incoming session %s from %s rejected' % (session.call_id, IdentityFormatter.format(session.remote_identity)))
     session.reject(403)
Esempio n. 19
0
 def incoming_session(self, session):
     log.msg(u'New incoming session %s from %s rejected' % (session.call_id, IdentityFormatter.format(session.remote_identity)))
     session.reject(403)
Esempio n. 20
0
 def _NH_JanusBackendDisconnected(self, notification):
     log.msg('Janus backend connection down: %s' % notification.data.reason)
     self.connection = Null
Esempio n. 21
0
 def onOpen(self):
     log.msg('Connection from %s open' % self.peer)
     self.factory.connections.add(self)
     self.connection_handler = ConnectionHandler(self)
     self.connection_handler.start()
Esempio n. 22
0
    def _OH_janus_event(self, data):
        handle_id = data['handle_id']
        event_type = data['event_type']
        event = data['event']

        if event_type == 'event':
            event_data = event['plugindata']['data']
            if 'result' in event_data:
                jsep = event.get('jsep', None)
                event_type = event_data['result']['event']
                if event_type in ('registering', 'registered', 'registration_failed', 'incomingcall'):
                    # skip 'registered' events from session handles
                    if event_type == 'registered' and event_data['result']['register_sent'] in (False, 'false'):
                        return
                    # account event
                    try:
                        account_info = self.account_handles_map[handle_id]
                    except KeyError:
                        log.warn('Could not find account for handle ID %s' % handle_id)
                        return
                    if event_type == 'incomingcall':
                        originator = SIP_PREFIX_RE.sub('', event_data['result']['username'])
                        jsep = event.get('jsep', None)
                        assert jsep is not None
                        session_id = uuid.uuid4().hex
                        session = JanusSessionInfo(session_id)
                        session.janus_handle_id = handle_id
                        session.init_incoming(account_info.id, originator)
                        self.sessions_map[session_id] = session
                        self.session_handles_map[handle_id] = session
                        data = dict(sylkrtc='account_event',
                                    account=account_info.id,
                                    session=session_id,
                                    event='incoming_session',
                                    data=dict(originator=originator, sdp=jsep['sdp']))
                        log.msg('Incoming session %s %s <-> %s created' % (session.id, session.remote_identity, session.local_identity))
                    else:
                        registration_state = event_type
                        if registration_state == 'registration_failed':
                            registration_state = 'failed'
                        if account_info.registration_state == registration_state:
                            return
                        account_info.registration_state = registration_state
                        registration_data = dict(state=registration_state)
                        if registration_state == 'failed':
                            code = event_data['result']['code']
                            reason = event_data['result']['reason']
                            registration_data['reason'] = '%d %s' % (code, reason)
                        data = dict(sylkrtc='account_event',
                                    account=account_info.id,
                                    event='registration_state',
                                    data=registration_data)
                        log.msg('Account %s registration state changed to %s' % (account_info.id, registration_state))
                    self._send_data(json.dumps(data))
                elif event_type in ('calling', 'accepted', 'hangup'):
                    # session event
                    try:
                        session_info = self.session_handles_map[handle_id]
                    except KeyError:
                        log.warn('Could not find session for handle ID %s' % handle_id)
                        return
                    if event_type == 'hangup' and session_info.state == 'terminated':
                        return
                    if event_type == 'calling':
                        session_info.state = 'progress'
                    elif event_type == 'accepted':
                        session_info.state = 'accepted'
                    elif event_type == 'hangup':
                        session_info.state = 'terminated'
                    data = dict(sylkrtc='session_event',
                                session=session_info.id,
                                event='state',
                                data=dict(state=session_info.state))
                    log.msg('%s session %s state: %s' % (session_info.direction.title(), session_info.id, session_info.state))
                    if session_info.state == 'accepted' and session_info.direction == 'outgoing':
                        assert jsep is not None
                        data['data']['sdp'] = jsep['sdp']
                    elif session_info.state == 'terminated':
                        code = event_data['result'].get('code', 0)
                        reason = event_data['result'].get('reason', 'Unknown')
                        reason = '%d %s' % (code, reason)
                        data['data']['reason'] = reason
                    self._send_data(json.dumps(data))
                    if session_info.state == 'terminated':
                        self._cleanup_session(session_info)
                        log.msg('%s session %s %s <-> %s terminated (%s)' % (session_info.direction.title(),
                                                                             session_info.id,
                                                                             session_info.local_identity,
                                                                             session_info.remote_identity,
                                                                             reason))
                elif event_type in ('ack', 'hangingup'):
                    # ignore
                    pass
                else:
                    log.warn('Unexpected event type: %s' % event_type)
            else:
                log.warn('Unexpected event: %s' % event)
        elif event_type == 'webrtcup':
            try:
                session_info = self.session_handles_map[handle_id]
            except KeyError:
                log.msg('Could not find session for handle ID %s' % handle_id)
                return
            session_info.state = 'established'
            data = dict(sylkrtc='session_event',
                        session=session_info.id,
                        event='state',
                        data=dict(state=session_info.state))
            log.msg('%s session %s state: %s' % (session_info.direction.title(), session_info.id, session_info.state))
            self._send_data(json.dumps(data))
        elif event_type == 'hangup':
            try:
                session_info = self.session_handles_map[handle_id]
            except KeyError:
                log.msg('Could not find session for handle ID %s' % handle_id)
                return
            if session_info.state != 'terminated':
                session_info.state = 'terminated'
                code = event.get('code', 0)
                reason = event.get('reason', 'Unknown')
                reason = '%d %s' % (code, reason)
                data = dict(sylkrtc='session_event',
                            session=session_info.id,
                            event='state',
                            data=dict(state=session_info.state, reason=reason))
                log.msg('%s session %s state: %s' % (session_info.direction.title(), session_info.id, session_info.state))
                self._send_data(json.dumps(data))
                self._cleanup_session(session_info)
                log.msg('%s session %s %s <-> %s terminated (%s)' % (session_info.direction.title(),
                                                                     session_info.id,
                                                                     session_info.local_identity,
                                                                     session_info.remote_identity,
                                                                     reason))
        elif event_type in ('media', 'declining'):
            # ignore
            pass
        else:
            log.warn('Received unexpected event type: %s' % event_type)
Esempio n. 23
0
 def _NH_JanusBackendConnected(self, notification):
     assert self.connection is Null
     self.connection = notification.sender
     log.msg('Janus backend connection up')
     self.factory.resetDelay()
Esempio n. 24
0
 def start(self):
     host, port = GeneralConfig.http_management_interface
     self.listener = reactor.listenTCP(port, Site(self.app.resource()), interface=host)
     log.msg('Admin web handler started at http://%s:%d' % (host, port))