@@ -27,7 +27,9 @@ 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."""
# not used yet
stdheaders = [ ('Server', 'Twisted, UPnP/1.0, python-upnp'), ]
elements = {}
known = {}
@@ -36,37 +38,40 @@ class SSDPServer(DatagramProtocol):
# 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)
log.msg('respond to: %s:%d' % (host, port))
header, payload = data.split('\r\n\r\n')
lines = header.split('\r\n')
cmd = string.split(lines[0], ' ')
lines = map(lambda x: x.replace(': ', ':', 1), lines[1:])
lines = filter(lambda x: len(x) > 0, lines)
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
headers = [string.split(x, ':', 1) for x in lines[1:]]
headers = dict(map(lambda x: (x[0].lower(), x[1]), headers))
if cmd[0] == 'M-SEARCH' and cmd[1] == '*':
# SSDP discovery
self.discoveryRequest(headers, (host, port))
# SSDP presence
if cmd[0] == 'NOTIFY' and cmd[1] == '*':
elif cmd[0] == 'NOTIFY' and cmd[1] == '*':
# SSDP presence
self.notifyReceived(headers, (host, port))
else:
log.msg('Unknown SSDP command %s %s' % cmd)
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 '])
log.msg('Discovery request for %s' % headers['st '])
# Do we know about this service?
if not self.known.has_key(headers['ST']):
if headers['st'] == 'ssdp:all':
for i in self.known:
hcopy = dict(headers.iteritems())
hcopy['st'] = i
self.discoveryRequest(hcopy, (host, post))
return
if not self.known.has_key(headers['st']):
return
# Generate a response
@@ -74,11 +79,12 @@ class SSDPServer(DatagramProtocol):
response = []
response.append('HTTP/1.1 200 OK')
for k, v in self.known[headers['ST ']].items():
for k, v in self.known[headers['st ']].items():
response.append('%s: %s' % (k, v))
log.msg('responding with: %s' % response)
# TODO: we should wait random(headers['mx'])
self.transport.write(
string.join(response, '\r\n') + '\r\n\r\n', (host, port))
@@ -95,36 +101,54 @@ class SSDPServer(DatagramProtocol):
self.known[st]['EXT'] = ''
self.known[st]['SERVER'] = 'Twisted, UPnP/1.0, python-upnp'
self.known[st]['CACHE-CONTROL'] = 'max-age=1800'
self.doNotify(st)
def doNotify(self, st):
"""Do notification"""
log.msg('Sending alive notification for %s' % st)
resp = [ 'NOTIFY * HTTP/1.1',
'Host: %s:%d' % (SSDP_ADDR, SSDP_PORT),
'NTS: ssdp:alive',
]
stcpy = dict(self.known[st].iteritems())
stcpy['NT'] = stcpy['ST']
del stcpy['ST']
resp.extend(map(lambda x: ': '.join(x), stcpy.iteritems()))
log.msg(repr(resp))
self.transport.write(
string.join(resp, '\r\n') + '\r\n\r\n', (SSDP_ADDR, SSDP_PORT))
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 headers['nts '] == 'ssdp:alive':
if not self.elements.has_key(headers['NT']):
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)
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'])
log.msg('Detected presence for %s' % headers['nt '])
elif headers['NTS'] == 'ssdp:byebye':
elif headers['nts '] == 'ssdp:byebye':
if self.elements.has_key(headers['NT ']):
if self.elements.has_key(headers['nt ']):
# Unregister device/service
del(self.elements[headers['NT ']])
del(self.elements[headers['nt ']])
log.msg('Detected absence for %s' % headers['NT '])
log.msg('Detected absence for %s' % headers['nt '])
else:
log.msg('Unknown subtype %s for notification type %s' %
(headers['NTS'], headers['NT ']))
(headers['nts'], headers['nt ']))
def findService(self, name):
"""Return information about a service registered over SSDP."""