Exemplo n.º 1
0
    def __init__(self,
                 ssdp,
                 start=True,
                 interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR,
                 ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)
Exemplo n.º 2
0
    def __init__(self, parent_udn, service, event_reload_time,
                 force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False
Exemplo n.º 3
0
    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)
Exemplo n.º 4
0
    def __init__(self, parent_udn, service, event_reload_time, force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False
Exemplo n.º 5
0
    def __init__(self, control_point, start=True):
        """ Constructor for the MulticastEventListener class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.control_point = control_point
        if start:
            self.start()
Exemplo n.º 6
0
    def __init__(self, parent_udn, service, event_reload_time, force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        


        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        
        
        
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False
Exemplo n.º 7
0
    def __init__(self,
                 server_name,
                 xml_description_filename,
                 max_age=1800,
                 receive_notify=True,
                 http_version="1.1",
                 search_type="sspd:all",
                 additional_headers={}):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        self.receive_notify = receive_notify
        self.running = False
        self.http_version = http_version
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.additional_headers = additional_headers
        self.search_type = search_type
        self.udp_transport = UDPTransport()
        self.udp_listener = UDPListener(SSDP_ADDR,
                                        SSDP_PORT,
                                        data_callback=self._datagram_received)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Exemplo n.º 8
0
    def __init__(self,
                 server_name,
                 xml_description_filename,
                 max_age=1800,
                 receive_notify=True,
                 udp_listener=''):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        log.debug("max_age: %s", max_age)
        self.receive_notify = receive_notify
        self.running = False
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.udp_transport = UDPTransport()
        if udp_listener == '':
            self.udp_listener = UDPListener(
                SSDP_ADDR, SSDP_PORT, data_callback=self._datagram_received)
        else:
            self.udp_listener = None
            udp_listener.subscribe(self)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Exemplo n.º 9
0
class MulticastEventController:

    msg_already_started = 'tried to start() MulticastEventController when already started'
    msg_already_stopped = 'tried to stop() MulticastEventController when already stopped'

    def __init__(self, parent_udn, service, event_reload_time, force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False

    def send_variables(self):
        if not self.eventing_variables:
            return

        #FIXME - fix BOOTID.UPNP.ORG
        notify_msg = ['NOTIFY * HTTP/1.0',
                'HOST: %s:%d' % (UPnPDefaults.MULTICAST_EVENT_ADDR,
                                 UPnPDefaults.MULTICAST_EVENT_PORT),
                'CONTENT-TYPE: text/xml; charset="utf-8"',
                'USN: %s::%s' % (str(self.parent_udn), self.service.service_type),
                'SVCID: %s' % (str(self.service.id)),
                'NT: upnp:event',
                'NTS: upnp:propchange',
                'SEQ: %d' % (self.event_key),
                'LVL: upnp:/info',
                'BOOTID.UPNP.ORG: 0']

        body = self._build_message_body(self.eventing_variables)

        notify_msg.append('CONTENT-LENGTH: %d' % len(body))
        # Empyt line
        notify_msg.append('')
        notify_msg.append(body)

        self.udp_transport.send_data('\r\n'.join(notify_msg), UPnPDefaults.MULTICAST_EVENT_ADDR,
                                     UPnPDefaults.MULTICAST_EVENT_PORT)
        self.event_key_increment()
        self.eventing_variables = {}

    def _update_variable(self, name, value):
        if self.force_event_reload:
            self.eventing_variables[name] = value
            self.send_variables()
            return

        if name in self.eventing_variables and \
            self.eventing_variables[name] != value:
            self.send_variables()

        self.eventing_variables[name] = value

    def _build_message_body(self, variables):
        log.debug("Building multicast message body to variables: %s" 
                  % str(variables))
        preamble = """<?xml version="1.0"?>"""
        return '%s%s' % (preamble, build_notify_message_body(variables))

    def event_key_increment(self):
        self.event_key += 1
        if self.event_key > 4294967295:
            self.event_key = 1

    def is_running(self):
        """ Returns True if the listener is running.

        @rtype: boolean
        """
        if not self.force_event_reload:
            self._is_running = self.l_call.is_running()
        return self._is_running

    def start(self):
        """ Starts the listener.
        """
        if not self.is_running():
            for name, state_var in list(self.service.get_variables().items()):
                if state_var.send_events and state_var.multicast:
                    state_var.subscribe_for_update(self._update_variable)

            if not self.force_event_reload:
                self.l_call.start(self.event_reload_time)
            self._is_running = True
            log.debug('Multicast event controller started with event reload time: %d'
                      % self.event_reload_time)
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('Multicast event controller stopped')
            for name, state_var in list(self.service.get_variables().items()):
                if state_var.send_events and state_var.multicast:
                    state_var.unsubscribe_for_update(self._update_variable)

            if not self.force_event_reload:
                self.l_call.stop()
                reactor.rem_after_stop_func(self.stop)
            self._is_running = False
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self._cleanup()

    def _cleanup(self):
        """ Clean up references.
        """
        self.service = None
        self.udp_transport = None
        self.eventing_variables = None
        self.l_call = None
Exemplo n.º 10
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"

        @type interval: float
        @type search_type: string
        """
        if not self.is_running():
            self.listen_udp.start()
            self.loopcall._args = (search_type, )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s", search_type)
        self.discover(search_type)
        self.discover(search_type)

    def discover(self, type="ssdp:all"):
        """ Mounts and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        req = ['M-SEARCH * HTTP/1.1',
                'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
                'MAN: "ssdp:discover"',
                'MX: 5',
                'ST: ' + type, '', '']
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0] == 'HTTP/1.1' and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    self.ssdp._register(headers['usn'],
                                        headers['st'],
                                        headers['location'],
                                        headers['server'],
                                        headers['cache-control'])
Exemplo n.º 11
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self,
                 ssdp,
                 start=True,
                 interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR,
                 ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self,
              interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE,
              http_version="1.1",
              man='"ssdp:discover"',
              mx=1,
              additionals={}):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"
        @param http_version: http version for m-search (default is 1.1)
        @param man: man field for m-search (default is ssdp:discover)
        @param mx: mx field for m-search (default is 1)
        @param additionals: dict containing additional field to be appended
			    in the end of the m-search message (default is
			    a empty dictionary)

        @type interval: float
        @type search_type: string
        @type http_version: string
        @type man: string
        @type mx: int
        @type additionals: dict
        """
        if not self.is_running():
            self.ssdp.search_type = search_type
            self.listen_udp.start()
            self.loopcall._args = (
                search_type,
                http_version,
                man,
                mx,
                additionals,
            )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self,
                        search_type=DEFAULT_SEARCH_TYPE,
                        http_version="1.1",
                        man='"ssdp:discover"',
                        mx=1,
                        additionals={}):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s, HTTP_VERSION=%s, MAN=%s, \
		 MX=%d, additionals=%s" % (search_type, http_version, man, mx, additionals))
        self.discover(search_type, http_version, man, mx, additionals)

    def discover(self,
                 search_type=DEFAULT_SEARCH_TYPE,
                 http_version="1.1",
                 man='"ssdp:discover"',
                 mx=1,
                 additionals={}):
        """ Builds and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        if (mx > 120):
            mx = 120
        elif (mx < 1):
            mx = 1
        req = [
            'M-SEARCH * HTTP/%s' % http_version,
            'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
            'MAN: %s' % man,
            'MX: %s' % mx,
            'ST: %s' % search_type
        ]
        append = req.append
        [append('%s: %s' % (k, v)) for k, v in additionals.items()]
        append('')
        append('')
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0].startswith('HTTP/1.') and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    default_fields_name = [
                        "usn", "st", "location", "server", "cache-control",
                        "ext"
                    ]
                    default_header = {}
                    for field in default_fields_name:
                        default_header[field] = headers.pop(field, "")
                    self.ssdp.register(default_header['usn'],
                                       default_header['st'],
                                       default_header['location'],
                                       default_header['server'],
                                       default_header['cache-control'],
                                       "remote", headers)
Exemplo n.º 12
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.search_type = DEFAULT_SEARCH_TYPE
        self.udp_transport = UDPTransport()
#        self.listen_udp = UDPListener(ssdp_addr, ssdp_port,
        self.listen_udp = UDPListener(ssdp_addr, 2149,      # WMP is not picked up if 1900 is used for source
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"

        @type interval: float
        @type search_type: string
        """

#        interval = 30.0
        
        if not self.is_running():
            self.search_type = search_type
            self.listen_udp.start()
            
#            print ">>>>>>>>> interval: " + str(interval)
            
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
#        print "<<<<<<<<< start double discover >>>>>>>>>"
        self.discover(search_type)
        self.discover(search_type)
#        print "<<<<<<<<< end double discover >>>>>>>>>"

    def discover(self, type="ssdp:all"):
#    def discover(self, type="upnp:rootdevice"):
        """ Mounts and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        
#        type = "urn:schemas-upnp-org:device:MediaServer:1"
        type = "upnp:rootdevice"
        
#        req = ['M-SEARCH * HTTP/1.1',
#                'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
#                'MAN: "ssdp:discover"',
#                'MX: 5',
#                'ST: ' + type, '', '']
#        req = '\r\n'.join(req)
        req = ['M-SEARCH * HTTP/1.1',
                'HOST:%s:%d' % (self.ssdp_addr, self.ssdp_port),
                'MAN:"ssdp:discover"',
#                'Host:%s:%d' % (self.ssdp_addr, self.ssdp_port),
#                'Man:"ssdp:discover"',
                'MX:5',
                'ST:' + type, '', '', '']
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
#        print "datagram_received start"
        cmd, headers = parse_http_response(data)
        if cmd[0] == 'HTTP/1.1' and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
#                    print "_datagram_received _register"
#                    print "_datagram_received headers: " + str(headers)
                    self.ssdp._register(headers['usn'],
                                        headers['st'],
                                        headers['location'],
                                        headers['server'],
                                        headers['cache-control'])
Exemplo n.º 13
0
class MulticastEventController:

    msg_already_started = 'tried to start() MulticastEventController when already started'
    msg_already_stopped = 'tried to stop() MulticastEventController when already stopped'

    def __init__(self, parent_udn, service, event_reload_time,
                 force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False

    def send_variables(self):
        if not self.eventing_variables:
            return

        #FIXME - fix BOOTID.UPNP.ORG
        notify_msg = [
            'NOTIFY * HTTP/1.0',
            'HOST: %s:%d' % (UPnPDefaults.MULTICAST_EVENT_ADDR,
                             UPnPDefaults.MULTICAST_EVENT_PORT),
            'CONTENT-TYPE: text/xml; charset="utf-8"',
            'USN: %s::%s' % (str(self.parent_udn), self.service.service_type),
            'SVCID: %s' % (str(self.service.id)), 'NT: upnp:event',
            'NTS: upnp:propchange',
            'SEQ: %d' % (self.event_key), 'LVL: upnp:/info',
            'BOOTID.UPNP.ORG: 0'
        ]

        body = self._build_message_body(self.eventing_variables)

        notify_msg.append('CONTENT-LENGTH: %d' % len(body))
        # Empyt line
        notify_msg.append('')
        notify_msg.append(body)

        self.udp_transport.send_data('\r\n'.join(notify_msg),
                                     UPnPDefaults.MULTICAST_EVENT_ADDR,
                                     UPnPDefaults.MULTICAST_EVENT_PORT)
        self.event_key_increment()
        self.eventing_variables = {}

    def _update_variable(self, name, value):
        if self.force_event_reload:
            self.eventing_variables[name] = value
            self.send_variables()
            return

        if self.eventing_variables.has_key(name) and \
            self.eventing_variables[name] != value:
            self.send_variables()

        self.eventing_variables[name] = value

    def _build_message_body(self, variables):
        log.debug("Building multicast message body to variables: %s" %
                  str(variables))
        preamble = """<?xml version="1.0"?>"""
        return '%s%s' % (preamble, build_notify_message_body(variables))

    def event_key_increment(self):
        self.event_key += 1
        if self.event_key > 4294967295:
            self.event_key = 1

    def is_running(self):
        """ Returns True if the listener is running.

        @rtype: boolean
        """
        if not self.force_event_reload:
            self._is_running = self.l_call.is_running()
        return self._is_running

    def start(self):
        """ Starts the listener.
        """
        if not self.is_running():
            for name, state_var in self.service.get_variables().items():
                if state_var.send_events and state_var.multicast:
                    state_var.subscribe_for_update(self._update_variable)

            if not self.force_event_reload:
                self.l_call.start(self.event_reload_time)
            self._is_running = True
            log.debug(
                'Multicast event controller started with event reload time: %d'
                % self.event_reload_time)
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('Multicast event controller stopped')
            for name, state_var in self.service.get_variables().items():
                if state_var.send_events and state_var.multicast:
                    state_var.unsubscribe_for_update(self._update_variable)

            if not self.force_event_reload:
                self.l_call.stop()
                reactor.rem_after_stop_func(self.stop)
            self._is_running = False
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self._cleanup()

    def _cleanup(self):
        """ Clean up references.
        """
        self.service = None
        self.udp_transport = None
        self.eventing_variables = None
        self.l_call = None
Exemplo n.º 14
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR, ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE, http_version="1.1",
	      man='"ssdp:discover"', mx=1, additionals={}):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"
        @param http_version: http version for m-search (default is 1.1)
        @param man: man field for m-search (default is ssdp:discover)
        @param mx: mx field for m-search (default is 1)
        @param additionals: dict containing additional field to be appended
			    in the end of the m-search message (default is
			    a empty dictionary)

        @type interval: float
        @type search_type: string
        @type http_version: string
        @type man: string
        @type mx: int
        @type additionals: dict
        """
        if not self.is_running():
	    self.ssdp.search_type = search_type
            self.listen_udp.start()
            self.loopcall._args = (search_type, http_version, man, mx,
	    			   additionals, )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE,
    			http_version="1.1", man='"ssdp:discover"', mx=1,
			additionals={}):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s, HTTP_VERSION=%s, MAN=%s, \
		 MX=%d, additionals=%s" % (search_type, http_version, man, mx,
		 			   additionals))
        self.discover(search_type, http_version, man, mx, additionals)

    def discover(self, search_type=DEFAULT_SEARCH_TYPE, http_version="1.1",
    		 man='"ssdp:discover"', mx=1, additionals={}):
        """ Builds and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
	if (mx > 120):
	    mx = 120
	elif (mx < 1):
	    mx = 1
        req = ['M-SEARCH * HTTP/%s' % http_version,
               'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
               'MAN: %s' % man,
               'MX: %s' % mx,
               'ST: %s' % search_type]
	append = req.append
	[append('%s: %s' % (k, v)) for k, v in additionals.items()]
	append('')
	append('')
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0].startswith('HTTP/1.') and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    default_fields_name = ["usn", "st", "location", "server",
                                           "cache-control", "ext"]
                    default_header = {}
                    for field in default_fields_name:
                        default_header[field] = headers.pop(field, "")
                    self.ssdp.register(default_header['usn'],
                                       default_header['st'],
                                       default_header['location'],
                                       default_header['server'],
                                       default_header['cache-control'],
                                       "remote", headers)