Commit 4f9c9341 authored by Michael Weinrich's avatar Michael Weinrich
Browse files

modules renamed, classes divided in parts for client and server devices

parent fea684ce
......@@ -8,27 +8,32 @@ import louie
from twisted.internet import reactor, defer
from coherence.base import Coherence
#from coherence.upnp.core.devices.control_point import ControlPoint
from coherence import log
#from coherence.extern.logger import Logger
#log = Logger('UPnT')
from upntest.testhost import TestHost
from upntest.host import Host
"""The main class of the whole test suite"""
class UPnT(log.Loggable):
logCategory = 'UPnT'
known_hosts = {}
runningDeferreds = []
shutdown_signal = None
def __init__(self, config={}):
log.init()
self.known_hosts = {}
self.runningDeferreds = []
self.shutdown_signal = None
self._hosts_to_remove = []
self._config = config
self._coherence = Coherence(self._config);
#if config.get('test_control_point', False):
#self._control_point = ControlPoint(self._coherence)
self.info('connecting signals...')
louie.connect(self.datagramReceived, 'UPnT.ssdp_datagram_received', louie.Any, weak=False)
louie.connect(self.datagramReceived, 'UPnT.msearch_datagram_received', louie.Any, weak=False)
......@@ -42,24 +47,30 @@ class UPnT(log.Loggable):
reactor.addSystemEventTrigger('before', 'shutdown', self.waitForShutdown)
def datagramReceived(self, data, ip, port):
"""debug output for received datagrams"""
#print '\nDatagram received from %s:%s' % (ip, port)
#print 'Datagram data:\n%s' % data
"""
Check if host exists in host list. If not create it and call the
validation method.
"""
if not self.known_hosts.has_key(ip):
self.known_hosts[ip] = TestHost(ip, self._config)
self.known_hosts[ip] = Host(ip, self._config)
self.info('Host added', ip)
self.debug('Calling dispatchDiscoveryPacketData')
try:
self.known_hosts[ip].dispatchDiscoveryPacketData(data)
except Exception:
pass
#try:
self.known_hosts[ip].dispatchDiscoveryPacketData(data)
#except Exception, error:
# self.warning(error)
# GUI update signal
louie.send('UPnT.host_discovery_end', None, ip)
def hostRemoved(self, ip):
"""Remove host from the known hosts list"""
self.known_hosts.pop(ip)
"""
Remove host from the known hosts list
"""
self.debug('hostRemoved(%r)' % ip)
self._hosts_to_remove.append(ip)
def removeHosts(self):
self.known_hosts = None
......
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2007, Michael Weinrich <testsuite@michael-weinrich.de>
from coherence import log
import louie
from twisted.internet import reactor
class ServerEventMessageChecks(log.Loggable):
"""
This class contains event message checks for client devices, mainly the
control point kind of devices. This way this class checks messages sent by a
server device like a MediaServer for instance.
"""
logCategory = 'UPnT_ClientEventMessageChecks'
def __init__(self, service):
self.notify_seq = {}
self._service = service
self._host = ''
self.isHostAvailable()
def isHostAvailable(self):
if self._service.parentDevice is None:
reactor.callLater(2, self.isHostAvailable)
else:
self._host = self._service.parentDevice.host.ip
louie.connect(self.checkEventMessage, 'UPnT.event.message_received', louie.Any, weak=False)
self.debug('ClientEventMessageCheck initialized for service %r' % str(self._service))
def checkEventMessage(self, packet_data):
self.debug('checkEventMessage (%r)' % packet_data)
[command, header, body] = splitData(packet_data)
if command['method'] == 'notify':
self.checkNotifyMessage(command, header, body)
else:
self.debug(packet_data)
def calcNextEventSeq(self, service_host, service_url, service_sid):
"""
Check for the next sequence id. If none is stored, create a new entry
and start counting at 0. Return the new value.
"""
self.debug('calcNextEventSeq (%r, %r, %r)' % (service_host, service_url, service_sid))
service_ident = service_host + service_url
# chekc for dict index
if not self.notify_seq.has_key(service_ident):
self.notify_seq[service_ident] = {}
if not self.notify_seq[service_ident].has_key(service_sid):
self.notify_seq[service_ident][service_sid] = 0
# calc new value, wrap to one if value too big (UDA S.67)
self.notify_seq[service_ident][service_sid] += 1
if self.notify_seq[service_ident][service_sid] > 429496729:
self.notify_seq[service_ident][service_sid] = 1
def removeEventSeq(self, service):
"""
Remove the sequence number from the dictionary so that it starts again
at 0 when some device subscribes the next time
"""
self.debug('removeEventSeq (%r)' % self.notify_seq)
#if service.scpdUrl == self._service.scpdUrl:
#del self.notify_seq[self._host + self._service.event]
def checkNotifyMessage(self, command, header, body):
self.debug('checkNotifyMessage')
# HOST header check
if not header.has_key('host'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: HOST header missing',
[command['path'], header['host']]
)
# ACCEPT-LANGUAGE header shouldn't be here
if header.has_key('accept-language'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: ACCEPT-LANGUAGE header found, not allowed for unsubscribe message',
[command['path'], header['host']]
)
# CONTENT-LENGTH header check
if not header.has_key('content-length'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: CONTENT-LENGTH header missing',
[command['path'], header['host']]
)
elif header['nt'] != len(body):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: Wrong CONTENT-LENGTH header value (%r, should be %r)' % (header['content-length'], len(body)),
[command['path'], header['host']]
)
# CONTENT-TYPE header check
if not header.has_key('content-type'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: CONTENT-TYPE header missing',
[command['path'], header['host']]
)
elif header['nt'] != 'text/xml':
louie.send(
'UPnT.event.notify_incorrect',
None,
"Notify: Wrong CONTENT-TYPE header value (%r, should be 'text/xml')" % header['nt'],
[command['path'], header['host']]
)
# NT header check
if not header.has_key('nt'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: NT header missing',
[command['path'], header['host']]
)
elif header['nt'] != 'upnp:event':
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: Wrong NT header value (%r)' % header['nt'],
[command['path'], header['host']]
)
# NTS header check
if not header.has_key('nts'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: NTS header missing',
[command['path'], header['host']]
)
elif header['nts'] != 'upnp:propchange':
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: Wrong NTS header value (%r)' % header['nt'],
[command['path'], header['host']]
)
# SID header check
if not header.has_key('sid'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: SID header missing',
[command['path'], header['host']]
)
elif not header['sid'].startswith('uuid:'):
louie.send(
'UPnT.event.notify_incorrect',
None,
"Notify: SID header has to start with 'uuid:' (%r)" % header['sid'],
[command['path'], header['host']]
)
# SEQ header check
if not header.has_key('seq'):
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: SEQ header missing',
[command['path'], header['host']]
)
else:
self.calcNextEventSeq(header['host'], command['path'], header['sid'])
service_ident = header['host'] + command['path']
if header['seq'] != self.notify_seq[service_ident][header['sid']]:
louie.send(
'UPnT.event.notify_incorrect',
None,
"Notify: SEQ header has to be incremented starting with 0 (%r, should be %r)" % (header['seq'], self.notify_seq[service_ident]),
[command['path'], header['host']]
)
# TIMEOUT header check
if not header.has_key('timeout'):
louie.send(
'UPnT.infoMessage',
None,
'A subscribe message should include a TIMEOUT header (%s)' % command['path']
)
else:
value = header['timeout'].split('-')
if value[0].strip() == 'Second':
try:
i = int(value[1].strip())
except ValueError:
if value[1].strip() != 'infinite':
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: Wrong value for TIMEOUT header',
header['timeout'],
[command['path'], header['host']]
)
else:
louie.send(
'UPnT.event.notify_incorrect',
None,
'Notify: Wrong value for TIMEOUT header',
header['timeout'],
[command['path'], header['host']]
)
class ClientEventMessageChecks(log.Loggable):
"""
This class contains event message checks for server devices like
MediaServer and MediaRenderer for instance. This way this class checks
messages sent by a control point for validity.
"""
logCategory = 'UPnT_ServerEventMessageChecks'
def __init__(self, service):
self._service = service
self._host = ''
self.isHostAvailable()
def isHostAvailable(self):
if self._service.parentDevice is None:
reactor.callLater(2, self.isHostAvailable)
else:
self._host = self._service.parentDevice.host.ip
self.debug('ServerEventMessageCheck initialized for service %r' % str(self._service))
louie.connect(self.checkEventMessage, 'UPnT.event.message_received', louie.Any, weak=False)
def checkEventMessage(self, packet_data):
self.debug('checkEventMessage (%r)' % packet_data)
[command, header, body] = splitData(packet_data)
host = header['host'].split(':')[0].strip()
#if not (command['path'] == self._service.controlUrl and \
# host ==
if command['method'] == 'unsubscribe':
self.checkUnsubscribeMessage(command, header, body)
elif command['method'] == 'subscribe' and header.has_key('SID'):
self.checkRenewalMessage(command, header, body)
elif command['method'] == 'subscribe' and header.has_key('NT') and \
header.has_key('callback'):
self.checkSubscriptionMessage(command, header, body)
else:
self.debug(packet_data)
def checkSubscriptionMessage(self, command, header, body):
self.debug('checkSubscriptionMessage')
# HOST header check
if not header.has_key('host'):
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: HOST header missing',
[command['path'], header['host']]
)
# CALLBACK header check
if not header.has_key('callback'):
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: CALLBACK header missing',
[command['path'], header['host']]
)
# NT header check
if not header.has_key('nt'):
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: NT header missing',
[command['path'], header['host']]
)
elif header['nt'] != 'upnp:event':
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: Wrong NT header value (%r)' % header['nt'],
[command['path'], header['host']]
)
# SID header shouldn't be here
if header.has_key('sid'):
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: SID header found, not allowed for subscription message',
[command['path'], header['host']]
)
# TIMEOUT header check
if not header.has_key('timeout'):
louie.send(
'UPnT.infoMessage',
None,
'A subscribe message should include a TIMEOUT header (%s)' % command['path']
)
else:
value = header['timeout'].split('-')
if value[0].strip() == 'Second':
try:
i = int(value[1].strip())
except ValueError:
if value[1].strip() != 'infinite':
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: Wrong value for TIMEOUT header (%r)' % header['timeout'],
[command['path'], header['host']]
)
else:
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: Wrong value for TIMEOUT header (%r)' % header['timeout'],
[command['path'], header['host']]
)
# message shouldn't have body content
if len(body) > 0:
louie.send(
'UPnT.event.subscription_incorrect',
None,
'Subscription: Conctent found in body (%r)' % body,
[command['path'], header['host']]
)
def checkRenewalMessage(self, command, header, body):
self.debug('checkRenewalMessage')
# HOST header check
if not header.has_key('host'):
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Renewal: HOST header missing',
[command['path'], header['host']]
)
# CALLBACK header shouldn't be here
if header.has_key('callback'):
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Renewal: CALLBACK header found, not allowed for renewal message',
[command['path'], header['host']]
)
# NT header shouldn't be here
if header.has_key('nt'):
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Renewal: NT header header found, not allowed for renewal message',
[command['path'], header['host']]
)
# SID header check
if not header.has_key('sid'):
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Renewal: SID header missing',
[command['path'], header['host']]
)
elif not header['sid'].startswith('uuid:'):
louie.send(
'UPnT.event.renewal_incorrect',
None,
"Renewal: SID header has to start with 'uuid:'",
[command['path'], header['host']]
)
# TIMEOUT header check
if not header.has_key('timeout'):
louie.send(
'UPnT.infoMessage',
None,
'A subscribe message should include a TIMEOUT header (%s)' % command['path']
)
else:
value = header['timeout'].split('-')
if value[0].strip() == 'Second':
try:
i = int(value[1].strip())
except ValueError:
if value[1].strip() != 'infinite':
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Renewal: Wrong value for TIMEOUT header',
header['timeout'],
[command['path'], header['host']]
)
else:
louie.send(
'UPnT.event.renewal_incorrect',
None,
'Wrong value for TIMEOUT header',
header['timeout'],
[command['path'], header['host']]
)
def checkUnsubscribeMessage(self, command, header, body):
self.debug('checkUnsubscribeMessage')
# HOST header check
if not header.has_key('host'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
'Unsubscribe: HOST header missing',
[command['path'], header['host']]
)
# CALLBACK header shouldn't be here
if header.has_key('callback'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
'Unsubscribe: CALLBACK header found, not allowed for unsubscribe message',
[command['path'], header['host']]
)
# NT header shouldn't be here
if header.has_key('nt'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
'Unsubscribe: NT header header found, not allowed for unsubscribe message',
[command['path'], header['host']]
)
# SID header check
if not header.has_key('sid'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
'Unsubscribe: SID header missing',
[command['path'], header['host']]
)
elif not header['sid'].startswith('uuid:'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
"Unsubscribe: SID header has to start with 'uuid:'",
[command['path'], header['host']]
)
# TIMEOUT header shouldn't be here
if header.has_key('timeout'):
louie.send(
'UPnT.event.unsubscribe_incorrect',
None,
'Unsubscribe: TIMEOUT header header found, not allowed for unsubscribe message',
[command['path'], header['host']]
)
def splitData(self, packet_data):
[header, body] = packet_data.split('\r\n\r\n', 1)
lines = header.split('\r\n')
#split first line to get the command, requested resource and protocol version
cmd = string.split(lines[0], ' ')
command = {'method': cmd[0].lower(), 'path': cmd[1].lower(), 'protocol': cmd[2].lower()}
#format all lines but the first and transform it into a dictionary
lines = map(lambda x: x.replace(': ', ':', 1), lines[1:])
lines = filter(lambda x: len(x) > 0, lines)
headers = [string.split(x, ':', 1) for x in lines]
headers = dict(map(lambda x: (x[0].lower().strip(), x[1].strip()), headers))
return [command, headers, body]
......@@ -12,18 +12,16 @@ from twisted.internet import reactor, defer
import louie
from upntest.testdevice import TestDevice
from upntest.testservice import TestService
from upntest.serverdevice import ServerDevice
from upntest.service import Service
from coherence.upnp.core import utils
from coherence import log
#from coherence.extern.logger import Logger
#log = Logger('UPnT.TestHost')
class TestHost(log.Loggable):
class Host(log.Loggable):
"""helper class to keep track of hosts and their behaviour"""
logCategory = 'UPnT_TestHost'
logCategory = 'UPnT_Host'
def __init__(self, ip, config):
......@@ -33,57 +31,40 @@ class TestHost(log.Loggable):
self._devices = {}
self._services = {}
self._create_dev_tree_func = None
#overall behaviour indicators
self._discovery_packets_valid = True
self._last_announcement = 0
#hints for compliance to recommended behaviour
self._ssdp_max_age_to_low = False
self._mx_value_to_high = False
self._date_header_not_sent = False
def getIp(self):
return self._ip
ip = property(getIp)
def dispatchDiscoveryPacketData(self, data):
self.debug('dispatchDiscoveryPacketData')
[header, payload] = data.split('\r\n\r\n', 1)
if data.lower().startswith('m-search'):
self.info('Packet is of type M_SEARCH')