Ejemplo n.º 1
0
 def get_connector(self):
     if self.client_relay is not None:
         return RelayConnection(self.client_relay,
                                'active',
                                logger=self.client_logger)
     else:
         return DirectConnector(logger=self.client_logger)
Ejemplo n.º 2
0
class MSRPStreamBase(object, metaclass=MediaStreamType):

    # Attributes that need to be defined by each MSRP stream type
    type = None
    priority = None
    msrp_session_class = None

    media_type = None
    accept_types = None
    accept_wrapped_types = None

    # These attributes are always False for any MSRP stream
    hold_supported = False
    on_hold = False
    on_hold_by_local = False
    on_hold_by_remote = False

    def __new__(cls, *args, **kw):
        if cls is MSRPStreamBase:
            raise TypeError("MSRPStreamBase cannot be instantiated directly")
        return object.__new__(cls)

    def __init__(self, direction='sendrecv'):
        self.direction = direction
        self.greenlet = None
        self.local_media = None
        self.remote_media = None
        self.msrp = None  # Placeholder for the MSRPTransport that will be set when started
        self.msrp_connector = None
        self.cpim_enabled = None  # Boolean value. None means it was not negotiated yet
        self.session = None
        self.msrp_session = None
        self.shutting_down = False
        self.local_role = None
        self.remote_role = None
        self.transport = None
        self.remote_accept_types = None
        self.remote_accept_wrapped_types = None

        self._initialize_done = False
        self._done = False
        self._failure_reason = None

    @property
    def local_uri(self):
        msrp = self.msrp or self.msrp_connector
        return msrp.local_uri if msrp is not None else None

    def _create_local_media(self, uri_path):
        transport = "TCP/TLS/MSRP" if uri_path[-1].use_tls else "TCP/MSRP"
        attributes = []
        path = " ".join(str(uri) for uri in uri_path)

        attributes.append(SDPAttribute(b"path", path.encode()))
        if self.direction not in [None, 'sendrecv']:
            attributes.append(SDPAttribute(self.direction.encode(), b''))
        if self.accept_types is not None:
            a_types = " ".join(self.accept_types)
            attributes.append(SDPAttribute(b"accept-types", a_types.encode()))
        if self.accept_wrapped_types is not None:
            a_w_types = " ".join(self.accept_wrapped_types)
            attributes.append(
                SDPAttribute(b"accept-wrapped-types", a_w_types.encode()))
        attributes.append(
            SDPAttribute(
                b"setup",
                self.local_role.encode() if self.local_role else None))
        local_ip = uri_path[-1].host
        connection = SDPConnection(local_ip.encode())
        return SDPMediaStream(self.media_type.encode(),
                              uri_path[-1].port or 2855,
                              transport.encode(),
                              connection=connection,
                              formats=[b"*"],
                              attributes=attributes)

    # The public API (the IMediaStream interface)

    # noinspection PyUnusedLocal
    def get_local_media(self, remote_sdp=None, index=0):
        return self.local_media

    def new_from_sdp(self, session, remote_sdp, stream_index):
        raise NotImplementedError

    @run_in_green_thread
    def initialize(self, session, direction):
        self.greenlet = api.getcurrent()
        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self)
        try:
            self.session = session
            self.transport = self.session.account.msrp.transport
            outgoing = direction == 'outgoing'
            logger = NotificationProxyLogger()
            if self.session.account is BonjourAccount():
                if outgoing:
                    self.msrp_connector = DirectConnector(logger=logger)
                    self.local_role = 'active'
                else:
                    if self.transport == 'tls' and None in (
                            self.session.account.tls_credentials.cert,
                            self.session.account.tls_credentials.key):
                        raise MSRPStreamError(
                            "Cannot accept MSRP connection without a TLS certificate"
                        )
                    self.msrp_connector = DirectAcceptor(logger=logger)
                    self.local_role = 'passive'
            else:
                if self.session.account.msrp.connection_model == 'relay':
                    if not outgoing and self.remote_role in ('actpass',
                                                             'passive'):
                        # 'passive' not allowed by the RFC but play nice for interoperability. -Saul
                        self.msrp_connector = DirectConnector(
                            logger=logger, use_sessmatch=True)
                        self.local_role = 'active'
                    elif outgoing and not self.session.account.nat_traversal.use_msrp_relay_for_outbound:
                        self.msrp_connector = DirectConnector(
                            logger=logger, use_sessmatch=True)
                        self.local_role = 'active'
                    else:
                        if self.session.account.nat_traversal.msrp_relay is None:
                            relay_host = relay_port = None
                        else:
                            if self.transport != self.session.account.nat_traversal.msrp_relay.transport:
                                raise MSRPStreamError(
                                    "MSRP relay transport conflicts with MSRP transport setting"
                                )
                            relay_host = self.session.account.nat_traversal.msrp_relay.host
                            relay_port = self.session.account.nat_traversal.msrp_relay.port
                        relay = MSRPRelaySettings(
                            domain=self.session.account.uri.host.decode(),
                            username=self.session.account.uri.user.decode(),
                            password=self.session.account.credentials.password.
                            decode(),
                            host=relay_host,
                            port=relay_port,
                            use_tls=self.transport == 'tls')
                        self.msrp_connector = RelayConnection(
                            relay,
                            'passive',
                            logger=logger,
                            use_sessmatch=True)
                        self.local_role = 'actpass' if outgoing else 'passive'
                else:
                    if not outgoing and self.remote_role in ('actpass',
                                                             'passive'):
                        # 'passive' not allowed by the RFC but play nice for interoperability. -Saul
                        self.msrp_connector = DirectConnector(
                            logger=logger, use_sessmatch=True)
                        self.local_role = 'active'
                    else:
                        if not outgoing and self.transport == 'tls' and None in (
                                self.session.account.tls_credentials.cert,
                                self.session.account.tls_credentials.key):
                            raise MSRPStreamError(
                                "Cannot accept MSRP connection without a TLS certificate"
                            )
                        self.msrp_connector = DirectAcceptor(
                            logger=logger, use_sessmatch=True)
                        self.local_role = 'actpass' if outgoing else 'passive'
            full_local_path = self.msrp_connector.prepare(local_uri=URI(
                host=host.default_ip,
                port=0,
                use_tls=self.transport == 'tls',
                credentials=self.session.account.tls_credentials))
            self.local_media = self._create_local_media(full_local_path)
        except (CertificateError, CertificateAuthorityError,
                CertificateExpiredError, CertificateSecurityError,
                CertificateRevokedError) as e:
            reason = "%s for %s" % (e.error, e.certificate.subject.CN.lower())
            notification_center.post_notification(
                'MediaStreamDidNotInitialize',
                sender=self,
                data=NotificationData(reason=reason))
        except Exception as e:
            notification_center.post_notification(
                'MediaStreamDidNotInitialize',
                sender=self,
                data=NotificationData(reason=str(e)))
        else:
            notification_center.post_notification('MediaStreamDidInitialize',
                                                  sender=self)
        finally:
            self._initialize_done = True
            self.greenlet = None

    # noinspection PyUnusedLocal
    @run_in_green_thread
    def start(self, local_sdp, remote_sdp, stream_index):
        self.greenlet = api.getcurrent()
        notification_center = NotificationCenter()
        context = 'sdp_negotiation'
        try:
            remote_media = remote_sdp.media[stream_index]
            self.remote_media = remote_media
            self.remote_accept_types = remote_media.attributes.getfirst(
                b'accept-types', b'').decode().split()
            self.remote_accept_wrapped_types = remote_media.attributes.getfirst(
                b'accept-wrapped-types', b'').decode().split()
            self.cpim_enabled = contains_mime_type(
                self.accept_types, 'message/cpim') and contains_mime_type(
                    self.remote_accept_types, 'message/cpim')
            remote_uri_path = remote_media.attributes.getfirst(b'path')
            if remote_uri_path is None:
                raise AttributeError(
                    "remote SDP media does not have 'path' attribute")
            full_remote_path = [
                URI.parse(uri) for uri in remote_uri_path.decode().split()
            ]
            remote_transport = 'tls' if full_remote_path[0].use_tls else 'tcp'
            if self.transport != remote_transport:
                raise MSRPStreamError(
                    "remote transport ('%s') different from local transport ('%s')"
                    % (remote_transport, self.transport))
            if isinstance(self.session.account,
                          Account) and self.local_role == 'actpass':
                remote_setup = remote_media.attributes.getfirst(
                    'setup', 'passive')
                if remote_setup == 'passive':
                    # If actpass is offered connectors are always started as passive
                    # We need to switch to active if the remote answers with passive
                    if self.session.account.msrp.connection_model == 'relay':
                        self.msrp_connector.mode = 'active'
                    else:
                        local_uri = self.msrp_connector.local_uri
                        logger = self.msrp_connector.logger
                        self.msrp_connector = DirectConnector(
                            logger=logger, use_sessmatch=True)
                        self.msrp_connector.prepare(local_uri)
            context = 'start'
            self.msrp = self.msrp_connector.complete(full_remote_path)
            if self.msrp_session_class is not None:
                self.msrp_session = self.msrp_session_class(
                    self.msrp,
                    accept_types=self.accept_types,
                    on_incoming_cb=self._handle_incoming,
                    automatic_reports=False)
            self.msrp_connector = None
        except Exception as e:
            self._failure_reason = str(e)
            traceback.print_exc()
            notification_center.post_notification(
                'MediaStreamDidFail',
                sender=self,
                data=NotificationData(context=context,
                                      reason=self._failure_reason))
        else:
            notification_center.post_notification('MediaStreamDidStart',
                                                  sender=self)
        finally:
            self.greenlet = None

    def deactivate(self):
        self.shutting_down = True

    @run_in_green_thread
    def end(self):
        if self._done:
            return
        self._done = True
        notification_center = NotificationCenter()
        if not self._initialize_done:
            # we are in the middle of initialize()
            try:
                msrp_connector = self.msrp_connector
                if self.greenlet is not None:
                    api.kill(self.greenlet)
                if msrp_connector is not None:
                    msrp_connector.cleanup()
            finally:
                notification_center.post_notification(
                    'MediaStreamDidNotInitialize',
                    sender=self,
                    data=NotificationData(reason='Interrupted'))
                notification_center.remove_observer(self, sender=self)
                self.msrp_connector = None
                self.greenlet = None
        else:
            notification_center.post_notification('MediaStreamWillEnd',
                                                  sender=self)
            msrp = self.msrp
            msrp_session = self.msrp_session
            msrp_connector = self.msrp_connector
            try:
                if self.greenlet is not None:
                    api.kill(self.greenlet)
                if msrp_session is not None:
                    msrp_session.shutdown()
                elif msrp is not None:
                    msrp.loseConnection(wait=False)
                if msrp_connector is not None:
                    msrp_connector.cleanup()
            finally:
                notification_center.post_notification(
                    'MediaStreamDidEnd',
                    sender=self,
                    data=NotificationData(error=self._failure_reason))
                notification_center.remove_observer(self, sender=self)
                self.msrp = None
                self.msrp_session = None
                self.msrp_connector = None
                self.session = None
                self.greenlet = None

    # noinspection PyMethodMayBeStatic,PyUnusedLocal
    def validate_update(self, remote_sdp, stream_index):
        return True  # TODO

    def update(self, local_sdp, remote_sdp, stream_index):
        pass  # TODO

    def hold(self):
        pass

    def unhold(self):
        pass

    def reset(self, stream_index):
        pass

    # Internal IObserver interface

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

    # Internal message handlers

    def _handle_incoming(self, chunk=None, error=None):
        notification_center = NotificationCenter()
        if error is not None:
            if self.shutting_down and isinstance(error.value, ConnectionDone):
                return
            self._failure_reason = error.getErrorMessage()
            notification_center.post_notification(
                'MediaStreamDidFail',
                sender=self,
                data=NotificationData(context='reading',
                                      reason=self._failure_reason))
        elif chunk is not None:
            method_handler = getattr(self, '_handle_%s' % chunk.method, None)
            if method_handler is not None:
                method_handler(chunk)

    def _handle_REPORT(self, chunk):
        pass

    def _handle_SEND(self, chunk):
        pass
Ejemplo n.º 3
0
 def start(self, local_sdp, remote_sdp, stream_index):
     self.greenlet = api.getcurrent()
     notification_center = NotificationCenter()
     context = 'sdp_negotiation'
     try:
         remote_media = remote_sdp.media[stream_index]
         self.remote_media = remote_media
         self.remote_accept_types = remote_media.attributes.getfirst(
             b'accept-types', b'').decode().split()
         self.remote_accept_wrapped_types = remote_media.attributes.getfirst(
             b'accept-wrapped-types', b'').decode().split()
         self.cpim_enabled = contains_mime_type(
             self.accept_types, 'message/cpim') and contains_mime_type(
                 self.remote_accept_types, 'message/cpim')
         remote_uri_path = remote_media.attributes.getfirst(b'path')
         if remote_uri_path is None:
             raise AttributeError(
                 "remote SDP media does not have 'path' attribute")
         full_remote_path = [
             URI.parse(uri) for uri in remote_uri_path.decode().split()
         ]
         remote_transport = 'tls' if full_remote_path[0].use_tls else 'tcp'
         if self.transport != remote_transport:
             raise MSRPStreamError(
                 "remote transport ('%s') different from local transport ('%s')"
                 % (remote_transport, self.transport))
         if isinstance(self.session.account,
                       Account) and self.local_role == 'actpass':
             remote_setup = remote_media.attributes.getfirst(
                 'setup', 'passive')
             if remote_setup == 'passive':
                 # If actpass is offered connectors are always started as passive
                 # We need to switch to active if the remote answers with passive
                 if self.session.account.msrp.connection_model == 'relay':
                     self.msrp_connector.mode = 'active'
                 else:
                     local_uri = self.msrp_connector.local_uri
                     logger = self.msrp_connector.logger
                     self.msrp_connector = DirectConnector(
                         logger=logger, use_sessmatch=True)
                     self.msrp_connector.prepare(local_uri)
         context = 'start'
         self.msrp = self.msrp_connector.complete(full_remote_path)
         if self.msrp_session_class is not None:
             self.msrp_session = self.msrp_session_class(
                 self.msrp,
                 accept_types=self.accept_types,
                 on_incoming_cb=self._handle_incoming,
                 automatic_reports=False)
         self.msrp_connector = None
     except Exception as e:
         self._failure_reason = str(e)
         traceback.print_exc()
         notification_center.post_notification(
             'MediaStreamDidFail',
             sender=self,
             data=NotificationData(context=context,
                                   reason=self._failure_reason))
     else:
         notification_center.post_notification('MediaStreamDidStart',
                                               sender=self)
     finally:
         self.greenlet = None
Ejemplo n.º 4
0
 def initialize(self, session, direction):
     self.greenlet = api.getcurrent()
     notification_center = NotificationCenter()
     notification_center.add_observer(self, sender=self)
     try:
         self.session = session
         self.transport = self.session.account.msrp.transport
         outgoing = direction == 'outgoing'
         logger = NotificationProxyLogger()
         if self.session.account is BonjourAccount():
             if outgoing:
                 self.msrp_connector = DirectConnector(logger=logger)
                 self.local_role = 'active'
             else:
                 if self.transport == 'tls' and None in (
                         self.session.account.tls_credentials.cert,
                         self.session.account.tls_credentials.key):
                     raise MSRPStreamError(
                         "Cannot accept MSRP connection without a TLS certificate"
                     )
                 self.msrp_connector = DirectAcceptor(logger=logger)
                 self.local_role = 'passive'
         else:
             if self.session.account.msrp.connection_model == 'relay':
                 if not outgoing and self.remote_role in ('actpass',
                                                          'passive'):
                     # 'passive' not allowed by the RFC but play nice for interoperability. -Saul
                     self.msrp_connector = DirectConnector(
                         logger=logger, use_sessmatch=True)
                     self.local_role = 'active'
                 elif outgoing and not self.session.account.nat_traversal.use_msrp_relay_for_outbound:
                     self.msrp_connector = DirectConnector(
                         logger=logger, use_sessmatch=True)
                     self.local_role = 'active'
                 else:
                     if self.session.account.nat_traversal.msrp_relay is None:
                         relay_host = relay_port = None
                     else:
                         if self.transport != self.session.account.nat_traversal.msrp_relay.transport:
                             raise MSRPStreamError(
                                 "MSRP relay transport conflicts with MSRP transport setting"
                             )
                         relay_host = self.session.account.nat_traversal.msrp_relay.host
                         relay_port = self.session.account.nat_traversal.msrp_relay.port
                     relay = MSRPRelaySettings(
                         domain=self.session.account.uri.host.decode(),
                         username=self.session.account.uri.user.decode(),
                         password=self.session.account.credentials.password.
                         decode(),
                         host=relay_host,
                         port=relay_port,
                         use_tls=self.transport == 'tls')
                     self.msrp_connector = RelayConnection(
                         relay,
                         'passive',
                         logger=logger,
                         use_sessmatch=True)
                     self.local_role = 'actpass' if outgoing else 'passive'
             else:
                 if not outgoing and self.remote_role in ('actpass',
                                                          'passive'):
                     # 'passive' not allowed by the RFC but play nice for interoperability. -Saul
                     self.msrp_connector = DirectConnector(
                         logger=logger, use_sessmatch=True)
                     self.local_role = 'active'
                 else:
                     if not outgoing and self.transport == 'tls' and None in (
                             self.session.account.tls_credentials.cert,
                             self.session.account.tls_credentials.key):
                         raise MSRPStreamError(
                             "Cannot accept MSRP connection without a TLS certificate"
                         )
                     self.msrp_connector = DirectAcceptor(
                         logger=logger, use_sessmatch=True)
                     self.local_role = 'actpass' if outgoing else 'passive'
         full_local_path = self.msrp_connector.prepare(local_uri=URI(
             host=host.default_ip,
             port=0,
             use_tls=self.transport == 'tls',
             credentials=self.session.account.tls_credentials))
         self.local_media = self._create_local_media(full_local_path)
     except (CertificateError, CertificateAuthorityError,
             CertificateExpiredError, CertificateSecurityError,
             CertificateRevokedError) as e:
         reason = "%s for %s" % (e.error, e.certificate.subject.CN.lower())
         notification_center.post_notification(
             'MediaStreamDidNotInitialize',
             sender=self,
             data=NotificationData(reason=reason))
     except Exception as e:
         notification_center.post_notification(
             'MediaStreamDidNotInitialize',
             sender=self,
             data=NotificationData(reason=str(e)))
     else:
         notification_center.post_notification('MediaStreamDidInitialize',
                                               sender=self)
     finally:
         self._initialize_done = True
         self.greenlet = None
Ejemplo n.º 5
0
# Copyright (C) 2008-2009 AG Projects. See LICENSE for details.
#

from msrplib.connect import DirectConnector
from msrplib.protocol import URI
from msrplib.trafficlog import Logger
from msrplib.session import GreenMSRPSession

from twisted.internet import reactor
del reactor  # let eventlib know we want twisted-based hub

logger = Logger()
local_uri = URI(session_id='client', use_tls=False)
remote_uri = URI(session_id='server', use_tls=False)
connector = DirectConnector(logger=logger)
connector.prepare(local_uri)
transport = connector.complete([remote_uri])
session = GreenMSRPSession(transport)

session.send_message('hi', 'text/plain')
logger.info('received: %s', session.receive_chunk().data)
session.shutdown()