Exemple #1
0
    def process_child(self, ch, name, request):
        self.debug(f'process_child: {name} [child: {ch}, request: {request}]')
        if ch is not None:
            self.info(f'Child found {ch}')
            if request.method == b'GET' or request.method == b'HEAD':
                headers = request.getAllHeaders()
                if b'content-length' in headers:
                    self.warning(
                        f'{request.method} request with content-length ' +
                        f'{headers[b"content-length"]} header - sanitizing')
                    # Fix missing headers for some Samsung TVs (see issue #20)
                    try:
                        del request.received_headers[b'content-length']
                    except AttributeError:
                        request.received_headers = headers
                        del request.received_headers[b'content-length']
                self.debug('data')
                if len(request.content.getvalue()) > 0:
                    # shall we remove that?
                    # can we remove that?
                    self.warning(f'{request.method} request with ' +
                                 f'{len(request.content.getvalue())} ' +
                                 f'bytes of message-body - sanitizing')
                    request.content = StringIO()

            if hasattr(ch, 'location'):
                self.debug(f'we have a location ' +
                           f'{isinstance(ch.location, resource.Resource)}')
                if isinstance(ch.location, ReverseProxyResource) or isinstance(
                        ch.location, resource.Resource):
                    # self.info(f'getChild proxy {name} to {ch.location.uri}')
                    self.prepare_connection(request)
                    self.prepare_headers(ch, request)
                    return ch.location
            try:
                p = ch.get_path()
            except TypeError:
                return self.list_content(name, ch, request)
            except Exception as msg:
                self.debug(f'error accessing items path {msg}')
                self.debug(traceback.format_exc())
                return self.list_content(name, ch, request)
            if p is not None and os.path.exists(p):
                self.info(f'accessing path {p}')
                self.prepare_connection(request)
                self.prepare_headers(ch, request)
                ch = StaticFile(p)
            else:
                self.debug(f'accessing path {p} failed')
                return self.list_content(name, ch, request)

        if ch is None:
            p = util.sibpath(__file__.encode('ascii'), name)
            self.debug(f'checking if msroot is file: {p}')
            if os.path.exists(p):
                ch = StaticFile(p)
        self.info(f'MSRoot ch {ch}')
        return ch
Exemple #2
0
    def process_child(self, ch, name, request):
        if ch is not None:
            self.info('Child found %s', ch)
            if (request.method == 'GET' or request.method == 'HEAD'):
                headers = request.getAllHeaders()
                if 'content-length' in headers:
                    self.warning(
                        '%s request with content-length %s header - sanitizing',
                        request.method, headers['content-length'])
                    del request.received_headers['content-length']
                self.debug('data', )
                if len(request.content.getvalue()) > 0:
                    """ shall we remove that?
                        can we remove that?
                    """
                    self.warning(
                        '%s request with %d bytes of message-body - sanitizing',
                        request.method, len(request.content.getvalue()))
                    request.content = StringIO()

            if hasattr(ch, "location"):
                self.debug("we have a location %s",
                           isinstance(ch.location, resource.Resource))
                if (isinstance(ch.location, ReverseProxyResource)
                        or isinstance(ch.location, resource.Resource)):
                    #self.info('getChild proxy %s to %s' % (name, ch.location.uri))
                    self.prepare_connection(request)
                    self.prepare_headers(ch, request)
                    return ch.location
            try:
                p = ch.get_path()
            except TypeError:
                return self.list_content(name, ch, request)
            except Exception as msg:
                self.debug("error accessing items path %r", msg)
                self.debug(traceback.format_exc())
                return self.list_content(name, ch, request)
            if p != None and os.path.exists(p):
                self.info("accessing path %r", p)
                self.prepare_connection(request)
                self.prepare_headers(ch, request)
                ch = StaticFile(p)
            else:
                self.debug("accessing path %r failed", p)
                return self.list_content(name, ch, request)

        if ch is None:
            p = util.sibpath(__file__, name)
            if os.path.exists(p):
                ch = StaticFile(p)
        self.info('MSRoot ch %s', ch)
        return ch
Exemple #3
0
    def render_GET(self, request):
        self.info(f'render GET {request}')
        request.setResponseCode(200)
        if self.contentType is not None:
            request.setHeader(b'Content-Type', self.contentType)
        request.write(b'')

        headers = request.getAllHeaders()
        if ('connection' in headers and headers['connection'] == 'close'):
            pass

        self.start(request)
        return server.NOT_DONE_YET
Exemple #4
0
 def prepare_headers(self, ch, request):
     request.setHeader('transferMode.dlna.org', request._dlna_transfermode)
     if hasattr(ch, 'item') and hasattr(ch.item, 'res'):
         if ch.item.res[0].protocolInfo is not None:
             additional_info = ch.item.res[0].get_additional_info()
             if additional_info != '*':
                 request.setHeader('contentFeatures.dlna.org',
                                   additional_info)
             elif 'getcontentfeatures.dlna.org' in request.getAllHeaders():
                 request.setHeader(
                     'contentFeatures.dlna.org',
                     "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"
                 )
Exemple #5
0
    def render_GET(self, request):
        self.info('render GET %r', request)
        request.setResponseCode(200)
        if hasattr(self, 'contentType'):
            request.setHeader('Content-Type', self.contentType)
        request.write('')

        headers = request.getAllHeaders()
        if ('connection' in headers and headers['connection'] == 'close'):
            pass

        self.start(request)
        return server.NOT_DONE_YET
Exemple #6
0
    def render_GET(self, request):
        self.info(f'render GET {request}')
        request.setResponseCode(200)
        if hasattr(self, 'contentType'):
            request.setHeader(b'Content-Type', self.contentType)
        request.write(b'')

        headers = request.getAllHeaders()
        if ('connection' in headers and headers['connection'] == 'close'):
            pass
        if self.requests:
            if self.streamheader:
                self.debug('writing streamheader')
                for h in self.streamheader:
                    request.write(h.data)
            self.requests.append(request)
        else:
            self.parse_pipeline()
            self.start(request)
        return server.NOT_DONE_YET
Exemple #7
0
    def getChildWithDefault(self, path, request):
        self.info(
            f'{self.server.device_type} getChildWithDefault, '
            f'{request.method}, {path}, {request.uri} {request.client}'
        )
        headers = request.getAllHeaders()
        self.debug(f'\t-> headers are: {headers}')
        if not isinstance(path, bytes):
            path = path.encode('ascii')
        if path.endswith(b'\''):
            self.warning(f'\t modified wrong path from {path} to {path[:-1]}')
            path = path[:-1]
        self.debug(f'\t-> path is: {path} [{type(path)}]')

        try:
            if (
                b'getcontentfeatures.dlna.org' in headers
                and headers[b'getcontentfeatures.dlna.org'] != b'1'
            ):
                request.setResponseCode(400)
                return static.Data(
                    b'<html><p>wrong value for '
                    b'getcontentFeatures.dlna.org</p></html>',
                    'text/html',
                )
        except Exception as e1:
            self.error(f'MSRoot.getChildWithDefault: {e1}')

        if request.method == b'HEAD':
            if b'getcaptioninfo.sec' in headers:
                self.warning(f'requesting srt file for id {path}')
                ch = self.store.get_by_id(path)
                try:
                    location = ch.get_path()
                    caption = ch.caption
                    if caption is None:
                        raise KeyError
                    request.setResponseCode(200)
                    request.setHeader(b'CaptionInfo.sec', caption)
                    return static.Data(b'', 'text/html')
                except Exception as e2:
                    self.error(
                        f'MSRoot.getChildWithDefault (method: HEAD): {e2}'
                    )
                    print(traceback.format_exc())
                    request.setResponseCode(404)
                    return static.Data(
                        b'<html><p>the requested srt file '
                        b'was not found</p></html>',
                        'text/html',
                    )

        try:
            request._dlna_transfermode = headers[b'transfermode.dlna.org']
        except KeyError:
            request._dlna_transfermode = b'Streaming'
        if request.method in (b'GET', b'HEAD'):
            if COVER_REQUEST_INDICATOR.match(request.uri.decode('utf-8')):
                self.info(f'request cover for id {path}')

                def got_item(ch):
                    if ch is not None:
                        request.setResponseCode(200)
                        file = ch.get_cover()
                        if file and os.path.exists(file):
                            self.info(f'got cover {file}')
                            return StaticFile(file)
                    request.setResponseCode(404)
                    return static.Data(
                        b'<html><p>cover requested not found</p></html>',
                        'text/html',
                    )

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_item)
                dfr.isLeaf = True
                return dfr

            if ATTACHMENT_REQUEST_INDICATOR.match(request.uri.decode('utf-8')):
                self.info(f'request attachment {request.args} for id {path}')

                def got_attachment(ch):
                    try:
                        # FIXME same as below
                        if 'transcoded' in request.args:
                            if (
                                self.server.coherence.config.get(
                                    'transcoding', 'no'
                                )
                                == 'yes'
                            ):
                                format = request.args['transcoded'][0]
                                type = request.args['type'][0]
                                self.info(
                                    f'request transcoding {format} {type}'
                                )
                                try:
                                    from coherence.transcoder import (
                                        TranscoderManager,
                                    )

                                    manager = TranscoderManager(
                                        self.server.coherence
                                    )
                                    return manager.select(
                                        format,
                                        ch.item.attachments[
                                            request.args['attachment'][0]
                                        ],
                                    )
                                except Exception:
                                    self.debug(traceback.format_exc())
                                request.setResponseCode(404)
                                return static.Data(
                                    b'<html><p>the requested transcoded file '
                                    b'was not found</p></html>',
                                    'text/html',
                                )
                            else:
                                request.setResponseCode(404)
                                return static.Data(
                                    b'<html><p>This MediaServer '
                                    b'doesn\'t support transcoding</p></html>',
                                    'text/html',
                                )
                        else:
                            return ch.item.attachments[
                                request.args['attachment'][0]
                            ]
                    except Exception:
                        request.setResponseCode(404)
                        return static.Data(
                            b'<html><p>the requested attachment '
                            b'was not found</p></html>',
                            'text/html',
                        )

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_attachment)
                dfr.isLeaf = True
                return dfr

        if request.method in (
            b'GET',
            b'HEAD',
        ) and TRANSCODED_REQUEST_INDICATOR.match(request.uri.decode('utf-8')):
            self.info(
                f'request transcoding to '
                f'{request.uri.split(b"/")[-1]} for id {path}'
            )
            if self.server.coherence.config.get('transcoding', 'no') == 'yes':

                def got_stuff_to_transcode(ch):
                    # FIXME create a generic transcoder class
                    # and sort the details there
                    format = request.uri.split(b'/')[
                        -1
                    ]  # request.args['transcoded'][0]
                    uri = ch.get_path()
                    try:
                        from coherence.transcoder import TranscoderManager

                        manager = TranscoderManager(self.server.coherence)
                        return manager.select(format, uri)
                    except Exception:
                        self.debug(traceback.format_exc())
                        request.setResponseCode(404)
                        return static.Data(
                            b'<html><p>the requested transcoded file '
                            b'was not found</p></html>',
                            'text/html',
                        )

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_stuff_to_transcode)
                dfr.isLeaf = True
                return dfr

            request.setResponseCode(404)
            return static.Data(
                b'<html><p>This MediaServer '
                b'doesn\'t support transcoding</p></html>',
                'text/html',
            )

        if request.method == b'POST' and request.uri.endswith(b'?import'):
            d = self.import_file(path, request)
            if isinstance(d, defer.Deferred):
                d.addBoth(self.import_response, path)
                d.isLeaf = True
                return d
            return self.import_response(None, path)

        if (
            b'user-agent' in headers
            and (
                headers[b'user-agent'].find(b'Xbox/') in [0, None]
                or headers[b'user-agent'].startswith(  # XBox  # wmp11
                    b'''Mozilla/4.0 (compatible; UPnP/1.0; Windows'''
                )
            )
            and path in [b'description-1.xml', b'description-2.xml']
        ):
            self.info(
                'XBox/WMP alert, we need to '
                'simulate a Windows Media Connect server'
            )
            if b'xbox-description-1.xml' in self.children:
                self.msg('returning xbox-description-1.xml')
                return self.children[b'xbox-description-1.xml']

        # resource http://XXXX/<deviceID>/config
        # configuration for the given device
        # accepted methods:
        # GET, HEAD:
        #       returns the configuration data (in XML format)
        # POST: stop the current device and restart it
        #       with the posted configuration data
        if path in (b'config'):
            backend = self.server.backend
            backend_type = backend.__class__.__name__

            def constructConfigData(backend):
                msg = '<plugin active="yes">'
                msg += '<backend>' + to_string(backend_type) + '</backend>'
                for key, value in list(backend.config.items()):
                    msg += (
                        '<' + to_string(key) + '>'
                        + to_string(value)
                        + '</' + to_string(key) + '>'
                    )
                msg += '</plugin>'
                return to_bytes(msg)

            if request.method in (b'GET', b'HEAD'):
                # the client wants to retrieve the
                #  configuration parameters for the backend
                msg = constructConfigData(backend)
                request.setResponseCode(200)
                return static.Data(msg, 'text/xml')
            elif request.method in (b'POST'):
                # the client wants to update the configuration parameters
                # for the backend we relaunch the backend with the
                # new configuration (after content validation)

                def convert_elementtree_to_dict(root):
                    active = False
                    for name, value in list(root.items()):
                        if name == 'active':
                            if value in ('yes'):
                                active = True
                        break
                    if active is False:
                        return None
                    dict = {}
                    for element in root.getchildren():
                        key = element.tag
                        text = element.text
                        if key != 'backend':
                            dict[key] = text
                    return dict

                new_config = None
                try:
                    element_tree = etree.fromstring(request.content.getvalue())
                    new_config = convert_elementtree_to_dict(element_tree)
                    self.server.coherence.remove_plugin(self.server)
                    self.warning(
                        f'{backend.name} {self.server.device_type} ({backend})'
                        f' with id {str(self.server.uuid)[5:]} deactivated'
                    )
                    if new_config is None:
                        msg = '<plugin active="no"/>'
                    else:
                        new_backend = self.server.coherence.add_plugin(
                            backend_type, **new_config
                        )
                        if self.server.coherence.writeable_config():
                            self.server.coherence.store_plugin_config(
                                new_backend.uuid, new_config
                            )
                            msg = (
                                '<html><p>Device restarted. Config file '
                                + 'has been modified with posted data.</p>'
                                + '</html>'
                            )  # constructConfigData(new_backend)
                        else:
                            msg = (
                                '<html><p>Device restarted. '
                                + 'Config file not modified</p>'
                                + '</html>'
                            )  # constructConfigData(new_backend)
                    request.setResponseCode(202)
                    return static.Data(msg.encode('ascii'), 'text/html')
                except SyntaxError as e:
                    request.setResponseCode(400)
                    return static.Data(
                        f'<html>'
                        f'<p>Invalid data posted:<BR>{e}</p>'
                        f'</html>'.encode('ascii'),
                        'text/html',
                    )
            else:
                # invalid method requested
                request.setResponseCode(405)
                return static.Data(
                    b'<html><p>This resource does not allow '
                    + b'the requested HTTP method</p></html>',
                    'text/html',
                )

        if path in self.children:
            return self.children[path]
        if request.uri == b'/':
            return self
        return self.getChild(path, request)
Exemple #8
0
    def getChildWithDefault(self, path, request):
        self.info('%s getChildWithDefault, %s, %s, %s %s',
                  self.server.device_type, request.method, path, request.uri,
                  request.client)
        headers = request.getAllHeaders()
        self.msg(request.getAllHeaders())

        try:
            if headers['getcontentfeatures.dlna.org'] != '1':
                request.setResponseCode(400)
                return static.Data(
                    '<html><p>wrong value for getcontentFeatures.dlna.org</p></html>',
                    'text/html')
        except:
            pass

        if request.method == 'HEAD':
            if 'getcaptioninfo.sec' in headers:
                self.warning("requesting srt file for id %s", path)
                ch = self.store.get_by_id(path)
                try:
                    location = ch.get_path()
                    caption = ch.caption
                    if caption is None:
                        raise KeyError
                    request.setResponseCode(200)
                    request.setHeader('CaptionInfo.sec', caption)
                    return static.Data('', 'text/html')
                except:
                    print(traceback.format_exc())
                    request.setResponseCode(404)
                    return static.Data(
                        '<html><p>the requested srt file was not found</p></html>',
                        'text/html')

        try:
            request._dlna_transfermode = headers['transfermode.dlna.org']
        except KeyError:
            request._dlna_transfermode = 'Streaming'
        if request.method in ('GET', 'HEAD'):
            if COVER_REQUEST_INDICATOR.match(request.uri):
                self.info("request cover for id %s", path)

                def got_item(ch):
                    if ch is not None:
                        request.setResponseCode(200)
                        file = ch.get_cover()
                        if file and os.path.exists(file):
                            self.info("got cover %s", file)
                            return StaticFile(file)
                    request.setResponseCode(404)
                    return static.Data(
                        '<html><p>cover requested not found</p></html>',
                        'text/html')

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_item)
                dfr.isLeaf = True
                return dfr

            if ATTACHMENT_REQUEST_INDICATOR.match(request.uri):
                self.info("request attachment %r for id %s", request.args,
                          path)

                def got_attachment(ch):
                    try:
                        #FIXME same as below
                        if 'transcoded' in request.args:
                            if self.server.coherence.config.get(
                                    'transcoding', 'no') == 'yes':
                                format = request.args['transcoded'][0]
                                type = request.args['type'][0]
                                self.info("request transcoding %r %r", format,
                                          type)
                                try:
                                    from coherence.transcoder import TranscoderManager
                                    manager = TranscoderManager(
                                        self.server.coherence)
                                    return manager.select(
                                        format, ch.item.attachments[
                                            request.args['attachment'][0]])
                                except:
                                    self.debug(traceback.format_exc())
                                request.setResponseCode(404)
                                return static.Data(
                                    '<html><p>the requested transcoded file was not found</p></html>',
                                    'text/html')
                            else:
                                request.setResponseCode(404)
                                return static.Data(
                                    "<html><p>This MediaServer doesn't support transcoding</p></html>",
                                    'text/html')
                        else:
                            return ch.item.attachments[
                                request.args['attachment'][0]]
                    except:
                        request.setResponseCode(404)
                        return static.Data(
                            '<html><p>the requested attachment was not found</p></html>',
                            'text/html')

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_attachment)
                dfr.isLeaf = True
                return dfr

        if request.method in ('GET',
                              'HEAD') and TRANSCODED_REQUEST_INDICATOR.match(
                                  request.uri):
            self.info("request transcoding to %s for id %s",
                      request.uri.split('/')[-1], path)
            if self.server.coherence.config.get('transcoding', 'no') == 'yes':

                def got_stuff_to_transcode(ch):
                    #FIXME create a generic transcoder class and sort the details there
                    format = request.uri.split('/')[
                        -1]  # request.args['transcoded'][0]
                    uri = ch.get_path()
                    try:
                        from coherence.transcoder import TranscoderManager
                        manager = TranscoderManager(self.server.coherence)
                        return manager.select(format, uri)
                    except:
                        self.debug(traceback.format_exc())
                        request.setResponseCode(404)
                        return static.Data(
                            '<html><p>the requested transcoded file was not found</p></html>',
                            'text/html')

                dfr = defer.maybeDeferred(self.store.get_by_id, path)
                dfr.addCallback(got_stuff_to_transcode)
                dfr.isLeaf = True
                return dfr

            request.setResponseCode(404)
            return static.Data(
                "<html><p>This MediaServer doesn't support transcoding</p></html>",
                'text/html')

        if request.method == 'POST' and request.uri.endswith('?import'):
            d = self.import_file(path, request)
            if isinstance(d, defer.Deferred):
                d.addBoth(self.import_response, path)
                d.isLeaf = True
                return d
            return self.import_response(None, path)

        if ('user-agent' in headers
                and (headers['user-agent'].find('Xbox/') == 0 or  # XBox
                     headers['user-agent'].startswith(
                         """Mozilla/4.0 (compatible; UPnP/1.0; Windows"""))
                and  # wmp11
                path in ['description-1.xml', 'description-2.xml']):
            self.info(
                'XBox/WMP alert, we need to simulate a Windows Media Connect server'
            )
            if 'xbox-description-1.xml' in self.children:
                self.msg('returning xbox-description-1.xml')
                return self.children['xbox-description-1.xml']

        # resource http://XXXX/<deviceID>/config
        # configuration for the given device
        # accepted methods:
        # GET, HEAD: returns the configuration data (in XML format)
        # POST: stop the current device and restart it with the posted configuration data
        if path in ('config'):
            backend = self.server.backend
            backend_type = backend.__class__.__name__

            def constructConfigData(backend):
                msg = "<plugin active=\"yes\">"
                msg += "<backend>" + backend_type + "</backend>"
                for key, value in list(backend.config.items()):
                    msg += "<" + key + ">" + value + "</" + key + ">"
                msg += "</plugin>"
                return msg

            if request.method in ('GET', 'HEAD'):
                # the client wants to retrieve the configuration parameters for the backend
                msg = constructConfigData(backend)
                request.setResponseCode(200)
                return static.Data(msg, 'text/xml')
            elif request.method in ('POST'):
                # the client wants to update the configuration parameters for the backend
                # we relaunch the backend with the new configuration (after content validation)

                def convert_elementtree_to_dict(root):
                    active = False
                    for name, value in list(root.items()):
                        if name == 'active':
                            if value in ('yes'):
                                active = True
                        break
                    if active is False:
                        return None
                    dict = {}
                    for element in root.getchildren():
                        key = element.tag
                        text = element.text
                        if key != 'backend':
                            dict[key] = text
                    return dict

                new_config = None
                try:
                    element_tree = etree.fromstring(request.content.getvalue())
                    new_config = convert_elementtree_to_dict(element_tree)
                    self.server.coherence.remove_plugin(self.server)
                    self.warning("%s %s (%s) with id %s desactivated",
                                 backend.name, self.server.device_type,
                                 backend,
                                 str(self.server.uuid)[5:])
                    if new_config is None:
                        msg = "<plugin active=\"no\"/>"
                    else:
                        new_backend = self.server.coherence.add_plugin(
                            backend_type, **new_config)
                        if self.server.coherence.writeable_config():
                            self.server.coherence.store_plugin_config(
                                new_backend.uuid, new_config)
                            msg = "<html><p>Device restarted. Config file has been modified with posted data.</p></html>"  # constructConfigData(new_backend)
                        else:
                            msg = "<html><p>Device restarted. Config file not modified</p></html>"  # constructConfigData(new_backend)
                    request.setResponseCode(202)
                    return static.Data(msg, 'text/html')  # 'text/xml')
                except SyntaxError as e:
                    request.setResponseCode(400)
                    return static.Data(
                        "<html><p>Invalid data posted:<BR>%s</p></html>" % e,
                        'text/html')
            else:
                # invalid method requested
                request.setResponseCode(405)
                return static.Data(
                    "<html><p>This resource does not allow the requested HTTP method</p></html>",
                    'text/html')

        if path in self.children:
            return self.children[path]
        if request.uri == '/':
            return self
        return self.getChild(path, request)