|
- # Licensed under the MIT license
- # http://opensource.org/licenses/mit-license.php
-
- # (c) 2005, Tim Potter <tpot@samba.org>
-
- #
- # Implementation of SSDP server under Twisted Python.
- #
-
- import string
-
- from twisted.python import log
- from twisted.internet.protocol import DatagramProtocol
-
- # TODO: Is there a better way of hooking the SSDPServer into a reactor
- # without having to know the default SSDP port and multicast address?
- # There must be a twisted idiom for doing this.
-
- SSDP_PORT = 1900
- SSDP_ADDR = '239.255.255.250'
-
- # TODO: Break out into a HTTPOverUDP class and implement
- # process_SEARCH(), process_NOTIFY() methods. Create UPNP specific
- # class to handle services etc.
-
- class SSDPServer(DatagramProtocol):
- """A class implementing a SSDP server. The notifyReceived and
- searchReceived methods are called when the appropriate type of
- datagram is received by the server."""
-
- elements = {}
- known = {}
-
- def datagramReceived(self, data, (host, port)):
- """Handle a received multicast datagram."""
-
- # Break up message in to command and headers
-
- data = string.replace(data, '\r', '')
- data = string.replace(data, ': ', ':')
- lines = string.split(data, '\n')
- lines = filter(lambda x: len(x) > 0, lines)
- cmd = string.split(lines[0], ' ')
-
- headers = dict([string.split(x, ':', 1) for x in lines[1:]])
-
- # TODO: datagram may contain a payload, i.e Content-Length
- # header is > 0. Maybe use some twisted HTTP object to do the
- # parsing for us.
-
- # SSDP discovery
-
- if cmd[0] == 'M-SEARCH' and cmd[1] == '*':
- self.discoveryRequest(headers, (host, port))
-
- # SSDP presence
-
- if cmd[0] == 'NOTIFY' and cmd[1] == '*':
- self.notifyReceived(headers, (host, port))
-
- def discoveryRequest(self, headers, (host, port)):
- """Process a discovery request. The response must be sent to
- the address specified by (host, port)."""
-
- log.msg('Discovery request for %s' % headers['ST'])
-
- # Do we know about this service?
-
- if not self.known.has_key(headers['ST']):
- return
-
- # Generate a response
-
- response = []
- response.append('HTTP/1.1 200 OK')
-
- for k, v in self.known[headers['ST']].items():
- response.append('%s: %s' % (k, v))
-
- log.msg('responding with: %s' % response)
-
- self.transport.write(
- string.join(response, '\r\n') + '\r\n\r\n', (host, port))
-
- def register(self, usn, st, location):
- """Register a service or device that this SSDP server will
- respond to."""
-
- log.msg('Registering %s' % st)
-
- self.known[st] = {}
- self.known[st]['USN'] = usn
- self.known[st]['LOCATION'] = location
- self.known[st]['ST'] = st
- self.known[st]['EXT'] = ''
- self.known[st]['SERVER'] = 'Twisted, UPnP/1.0, python-upnp'
- self.known[st]['CACHE-CONTROL'] = 'max-age=1800'
-
- def notifyReceived(self, headers, (host, port)):
- """Process a presence announcement. We just remember the
- details of the SSDP service announced."""
-
- if headers['NTS'] == 'ssdp:alive':
-
- if not self.elements.has_key(headers['NT']):
-
- # Register device/service
-
- self.elements[headers['NT']] = {}
- self.elements[headers['NT']]['USN'] = headers['USN']
- self.elements[headers['NT']]['host'] = (host, port)
-
- log.msg('Detected presence for %s' % headers['NT'])
-
- elif headers['NTS'] == 'ssdp:byebye':
-
- if self.elements.has_key(headers['NT']):
-
- # Unregister device/service
-
- del(self.elements[headers['NT']])
-
- log.msg('Detected absence for %s' % headers['NT'])
-
- else:
- log.msg('Unknown subtype %s for notification type %s' %
- (headers['NTS'], headers['NT']))
-
- def findService(self, name):
- """Return information about a service registered over SSDP."""
-
- # TODO: Implement me.
-
- # TODO: Send out a discovery request if we haven't registered
- # a presence announcement.
-
- def findDevice(self, name):
- """Return information about a device registered over SSDP."""
-
- # TODO: Implement me.
-
- # TODO: Send out a discovery request if we haven't registered
- # a presence announcement.
|