| |
---|
| | |
---|
| | __author__ = 'jeff@rebeiro.net (Jeff Rebeiro)' |
---|
| | |
---|
| | import HTMLParser |
---|
| | import os |
---|
| | import random |
---|
| | import re |
---|
| | import string |
---|
| | |
---|
| | from twisted.internet import reactor |
---|
| | from twisted.web.resource import Resource |
---|
| | from twisted.web.server import Site |
---|
| | |
---|
| | import common |
---|
| | |
---|
| | CREATE_OBJ = re.compile(r'<u:CreateObject xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">.*<Elements>(.*)</Elements>', re.DOTALL) |
---|
| | CREATE_OBJ_DETAILS = re.compile(r'<dc:title>(.*)</dc:title>.*protocolInfo="\*:\*:(.*):DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_CI=0"') |
---|
| | |
---|
| | X_BACKUP = re.compile(r'<u:X_BACKUP_(\w+) xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">') |
---|
| | CREATE_OBJ = '"urn:schemas-upnp-org:service:ContentDirectory:1#CreateObject"' |
---|
| | CREATE_OBJ_DIDL = re.compile(r'.*<Elements>(?P<didl>.*dc:title>(?P<name>.*)</dc:title.*dc:date>(?P<date>.*)</dc:date.*protocolInfo="\*:\*:(?P<type>.*):DLNA.ORG_PN.*size="(?P<size>\d+)".*)</Elements>.*') |
---|
| | CREATE_OBJ_RESPONSE = '''<?xml version="1.0"?> |
---|
| | <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> |
---|
| | <s:Body> |
---|
| | <u:CreateObjectResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"> |
---|
| | <ObjectID>%(obj_id)s</ObjectID> |
---|
| | <Result><DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp='urn:schemas-upnp-org:metadata-1-0/upnp/' xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/" xmlns:sec="http://www.sec.co.kr/"><item id="%(obj_id)s" parentID="%(parent_id)s" restricted="0" dlna:dlnaManaged="00000004"><dc:title></dc:title><res protocolInfo="http-get:*:%(obj_type)s:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=00D00000000000000000000000000000" importUri="http://%(interface)s:52235/cd/content?didx=0_id=%(obj_id)s" dlna:resumeUpload="1" dlna:uploadedSize="0" size="%(obj_size)s"></res><upnp:class>object.item.imageItem</upnp:class></item></DIDL-Lite></Result> |
---|
| | </u:CreateObjectResponse> |
---|
| | </s:Body> |
---|
| | </s:Envelope>''' |
---|
| | |
---|
| | X_BACKUP_DONE = '"urn:schemas-upnp-org:service:ContentDirectory:1#X_BACKUP_DONE"' |
---|
| | X_BACKUP_START = '"urn:schemas-upnp-org:service:ContentDirectory:1#X_BACKUP_START"' |
---|
| | |
---|
| | X_BACKUP_RESPONSE = '''<?xml version="1.0"?> |
---|
| | <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> |
---|
| | <s:Body> |
---|
| | <u:X_BACKUP_%sResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"/> |
---|
| | </s:Body> |
---|
| | </s:Envelope>''' |
---|
| | |
---|
| | DMS_DESC = '''<?xml version="1.0"?> |
---|
| | DMS_DESC_RESPONSE = '''<?xml version="1.0"?> |
---|
| | <root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:sec="http://www.sec.co.kr/dlna" xmlns:dlna="urn:schemas-dlna-org:device-1-0"> |
---|
| | <specVersion> |
---|
| | <major>1</major> |
---|
| | <minor>0</minor> |
---|
| |
---|
| | </device> |
---|
| | </root>''' |
---|
| | |
---|
| | |
---|
| | class MediaServer(Resource): |
---|
| | |
---|
| | isLeaf = True |
---|
| | class Backup(object): |
---|
| | |
---|
| | backup_objects = {} |
---|
| | |
---|
| | def __init__(self): |
---|
| | self.config = common.LoadOrCreateConfig() |
---|
| | |
---|
| | def _GenerateObjectID(self, obj_date, length=10): |
---|
| | chars = string.letters + string.digits |
---|
| | rand_chars = ''.join(random.choice(chars) for i in xrange(length)) |
---|
| | parent_id = 'UP_%s' % obj_date |
---|
| | obj_id = '%s_%s' % (parent_id, rand_chars) |
---|
| | return (parent_id, obj_id) |
---|
| | |
---|
| | def CreateObject(self, obj_name, obj_date, obj_type, obj_size): |
---|
| | (parent_id, obj_id) = self._GenerateObjectID(obj_date) |
---|
| | self.backup_objects[obj_id] = {'obj_name': obj_name, |
---|
| | 'obj_date': obj_date, |
---|
| | 'obj_type': obj_type, |
---|
| | 'parent_id': parent_id, |
---|
| | 'obj_size': obj_size} |
---|
| | return obj_id |
---|
| | |
---|
| | def FinishBackup(self): |
---|
| | pass |
---|
| | |
---|
| | def GetObjectDetails(self, obj_id): |
---|
| | return self.backup_objects.get(obj_id) |
---|
| | |
---|
| | def StartBackup(self): |
---|
| | pass |
---|
| | |
---|
| | def WriteObject(self, obj_id, data): |
---|
| | obj_details = self.GetObjectDetails(obj_id) |
---|
| | |
---|
| | obj_dir = os.path.join(self.config.get('AUTOBACKUP', 'backup_dir'), |
---|
| | obj_details['obj_date']) |
---|
| | |
---|
| | if not os.path.isdir(obj_dir): |
---|
| | os.makedirs(obj_dir) |
---|
| | |
---|
| | obj_file = os.path.join(obj_dir, obj_details['obj_name']) |
---|
| | |
---|
| | with open(obj_file, 'wb') as f: |
---|
| | f.write(data) |
---|
| | |
---|
| | del(self.backup_objects[obj_id]) |
---|
| | |
---|
| | |
---|
| | class MediaServer(Resource): |
---|
| | |
---|
| | isLeaf = True |
---|
| | |
---|
| | def __init__(self, debug=False): |
---|
| | self.config = common.LoadOrCreateConfig() |
---|
| | self.debug = debug |
---|
| | |
---|
| | def render_GET(self, request): |
---|
| | if self.debug: |
---|
| | print 'Request headers:' |
---|
| | print request.getAllHeaders() |
---|
| | if request.path == '/DMS/SamsungDmsDesc.xml': |
---|
| | return self.GetDMSDescriptionResponse() |
---|
| | else: |
---|
| | print 'Unhandled GET request: %s' % request.path |
---|
| | |
---|
| | def render_POST(self, request): |
---|
| | if self.debug: |
---|
| | print 'Request args:' |
---|
| | print request.args |
---|
| | print 'Request headers:' |
---|
| | print request.getAllHeaders() |
---|
| | if request.path == '/cd/content': |
---|
| | return self.ReceiveUpload(request) |
---|
| | if request.path == '/upnp/control/ContentDirectory1': |
---|
| | return self.GetContentDirectoryResponse(request.content.read()) |
---|
| | |
---|
| | def GetContentDirectoryResponse(self, content): |
---|
| | if X_BACKUP.search(content): |
---|
| | action = X_BACKUP.search(content).group(1) |
---|
| | response = X_BACKUP_RESPONSE % action |
---|
| | print "Response:" |
---|
| | return self.GetContentDirectoryResponse(request) |
---|
| | else: |
---|
| | print 'Unhandled POST request: %s' % request.path |
---|
| | |
---|
| | def GetContentDirectoryResponse(self, request): |
---|
| | soapaction = request.getHeader('soapaction') |
---|
| | |
---|
| | response = '' |
---|
| | |
---|
| | if soapaction == X_BACKUP_START: |
---|
| | response = X_BACKUP_RESPONSE % 'START' |
---|
| | if self.debug: |
---|
| | print 'Response:' |
---|
| | print response |
---|
| | if soapaction == CREATE_OBJ: |
---|
| | request.content.seek(0) |
---|
| | soap_xml = request.content.read() |
---|
| | |
---|
| | m = CREATE_OBJ_DIDL.match(soap_xml) |
---|
| | if m: |
---|
| | obj_name = m.group('name') |
---|
| | obj_date = m.group('date') |
---|
| | obj_type = m.group('type') |
---|
| | obj_size = m.group('size') |
---|
| | |
---|
| | backup = Backup() |
---|
| | obj_id = backup.CreateObject(obj_name, obj_date, obj_type, obj_size) |
---|
| | obj_details = backup.GetObjectDetails(obj_id) |
---|
| | |
---|
| | response = CREATE_OBJ_RESPONSE % { |
---|
| | 'interface': self.config.get('AUTOBACKUP', 'default_interface'), |
---|
| | 'obj_id': obj_id, |
---|
| | 'obj_type': obj_type, |
---|
| | 'obj_size': obj_size, |
---|
| | 'parent_id': obj_details['parent_id']} |
---|
| | if soapaction == X_BACKUP_DONE: |
---|
| | response = X_BACKUP_RESPONSE % 'DONE' |
---|
| | |
---|
| | if self.debug: |
---|
| | print 'Response:' |
---|
| | print response |
---|
| | if CREATE_OBJECT.search(content): |
---|
| | obj_didl = CREATE_OBJ.search(content).group(1) |
---|
| | obj_details = CREATE_OBJ_DETAILS.search(obj_didl).groups() |
---|
| | obj_name = obj_details[0] |
---|
| | obj_type = obj_details[1] |
---|
| | |
---|
| | return response |
---|
| | |
---|
| | def GetDMSDescriptionResponse(self): |
---|
| | response = DMS_DESC % {'friendly_name': self.config.get('AUTOBACKUP', |
---|
| | 'server_name'), |
---|
| | 'uuid': self.config.get('AUTOBACKUP', 'uuid')} |
---|
| | if __name__ == '__main__': |
---|
| | print "Response:" |
---|
| | response = DMS_DESC_RESPONSE % { |
---|
| | 'friendly_name': self.config.get('AUTOBACKUP', 'server_name'), |
---|
| | 'uuid': self.config.get('AUTOBACKUP', 'uuid')} |
---|
| | if self.debug: |
---|
| | print 'Response:' |
---|
| | print response |
---|
| | |
---|
| | return response |
---|
| | |
---|
| | |
---|
| | def StartMediaServer(): |
---|
| | resource = MediaServer() |
---|
| | def ReceiveUpload(self, request): |
---|
| | response = '' |
---|
| | obj_id = request.args['didx'][0].split('=')[1] |
---|
| | backup = Backup() |
---|
| | |
---|
| | data = request.content.read() |
---|
| | backup.WriteObject(obj_id, data) |
---|
| | return response |
---|
| | |
---|
| | |
---|
| | def StartMediaServer(debug=False): |
---|
| | resource = MediaServer(debug=debug) |
---|
| | factory = Site(resource) |
---|
| | reactor.listenTCP(52235, factory) |
---|
| | reactor.run() |
---|
| | |
---|
| | |
---|
| | def main(): |
---|
| | StartMediaServer() |
---|
| | |
---|
| | |
---|
| | if __name__ == "__main__": |
---|
| | StartMediaServer(debug=True) |
---|
| | |
---|
| | |
---|
| | if __name__ == '__main__': |
---|
| | main() |
---|
| | |
---|
| | |