# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
#
# Copyright (C) 2006 Fluendo, S.A. (www.fluendo.com).
# Copyright 2006, Frank Scholz <coherence@beebits.net>
import socket
import time
from twisted.internet import reactor
from twisted.internet import task
from twisted.internet.protocol import DatagramProtocol
import coherence.extern.louie as louie
from coherence.upnp.core import utils
SSDP_PORT = 1900
SSDP_ADDR = '239.255.255.250'
from coherence import log
[docs]class MSearch(DatagramProtocol, log.LogAble):
logCategory = 'msearch'
def __init__(self, ssdp_server, test=False):
log.LogAble.__init__(self)
self.ssdp_server = ssdp_server
if not test:
self.port = reactor.listenUDP(0, self)
self.double_discover_loop = task.LoopingCall(self.double_discover)
self.double_discover_loop.start(120.0)
[docs] def datagramReceived(self, data, xxx_todo_changeme):
(host, port) = xxx_todo_changeme
if isinstance(data, bytes):
data = data.decode('utf-8')
cmd, headers = utils.parse_http_response(data)
self.info('datagramReceived from %s:%d, protocol %s code %s', host,
port, cmd[0], cmd[1])
if cmd[0].startswith('HTTP/1.') and cmd[1] == '200':
self.msg('for %r', headers['usn'])
if not self.ssdp_server.isKnown(headers['usn']):
self.info('register as remote %s, %s, %s', headers['usn'],
headers['st'], headers['location'])
self.ssdp_server.register(
'remote',
headers['usn'], headers['st'],
headers['location'],
headers['server'],
headers['cache-control'],
host=host)
else:
self.ssdp_server.known[headers['usn']][
'last-seen'] = time.time()
self.debug('updating last-seen for %r', headers['usn'])
# make raw data available
# send out the signal after we had a chance to register the device
louie.send('UPnP.SSDP.datagram_received', None, data, host, port)
[docs] def double_discover(self):
" Because it's worth it (with UDP's reliability) "
self.info('send out discovery for ssdp:all')
self.discover()
self.discover()
[docs] def discover(self):
req = ['M-SEARCH * HTTP/1.1',
'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT),
'MAN: "ssdp:discover"',
'MX: 5',
'ST: ssdp:all',
'', '']
req = '\r\n'.join(req).encode('ascii')
try:
self.transport.write(req, (SSDP_ADDR, SSDP_PORT))
except socket.error as msg:
self.info("failure sending out the discovery message: %r", msg)