# -*- coding: utf-8 -*-
##############################################################################
# 2011 E2OpenPlugins #
# #
# This file is open source software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License version 2 as #
# published by the Free Software Foundation. #
# #
##############################################################################
import os
import imp
import logging
from twisted.web import server, http, resource
from Cheetah.Template import Template
from defaults import VIEWS_PATH
from utilities import mangle_host_header_port
OWIF_PREFIX = 'P_'
#: HTTP 404 Not Found response content
FOUR_O_FOUR = """
<html><head><title>Open Webif</title></head>
<body><h1>Error 404: Page not found</h1><br/>
The requested URL was not found on this server.</body></html>
"""
#: template for simple XML result
TEMPLATE_E2_SIMPLE_XML_RESULT = "web/e2simplexmlresult"
#: template for enigma2 event list
TEMPLATE_E2_EVENT_LIST = "web/e2eventlist"
#: template for enigma2 service list
TEMPLATE_E2_SERVICE_LIST = "web/e2servicelist"
#: template for enigma2 tags
TEMPLATE_E2_TAGS = "web/e2tags"
#: template aliases
TEMPLATE_ALIASES = {
"web/loadepg": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/epgbouquet": TEMPLATE_E2_EVENT_LIST,
"web/getservices": TEMPLATE_E2_SERVICE_LIST,
"web/gettags": TEMPLATE_E2_TAGS,
"web/epgservicenow": TEMPLATE_E2_EVENT_LIST,
"web/timeraddbyeventid": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/mediaplayerplay": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/streamsubservices": TEMPLATE_E2_SERVICE_LIST,
"web/timertogglestatus": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/subservices": TEMPLATE_E2_SERVICE_LIST,
"web/timerlistwrite": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/recordnow": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/epgmulti": TEMPLATE_E2_EVENT_LIST,
"web/epgservicenext": TEMPLATE_E2_EVENT_LIST,
"web/message": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/pluginlistread": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/moviedelete": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/timeradd": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/removelocation": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/timerdelete": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/moviemove": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/mediaplayercmd": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/zap": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/movierename": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/parentcontrollist": TEMPLATE_E2_SERVICE_LIST,
"web/saveepg": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/epgsearch": TEMPLATE_E2_EVENT_LIST,
"web/mediaplayeradd": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/mediaplayerwrite": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/movietags": TEMPLATE_E2_TAGS,
"web/messageanswer": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/servicelistreload": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/mediaplayerload": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/epgservice": TEMPLATE_E2_EVENT_LIST,
"web/timerchange": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/timercleanup": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/mediaplayerremove": TEMPLATE_E2_SIMPLE_XML_RESULT,
"web/epgsimilar": TEMPLATE_E2_EVENT_LIST,
"web/epgnext": TEMPLATE_E2_EVENT_LIST,
"web/epgnownext": TEMPLATE_E2_EVENT_LIST,
"web/epgnow": TEMPLATE_E2_EVENT_LIST,
}
CONTENT_TYPE_X_MPEGURL = 'application/x-mpegurl'
CONTENT_TYPE_HTML = 'text/html'
CONTENT_TYPE_TEXT = 'text/plain'
CONTENT_TYPE_JSON = 'application/json; charset=utf-8'
[docs]def error404(request):
"""
HTTP 404 Not Found response.
Args:
request (twisted.web.server.Request): HTTP request object
Returns:
HTTP 404 Not Found response
"""
request.setHeader("content-type", CONTENT_TYPE_HTML)
request.setResponseCode(http.NOT_FOUND)
request.write(FOUR_O_FOUR)
request.finish()
[docs]class BaseController(resource.Resource):
"""
Basic HTTP requests controller.
"""
isLeaf = False
[docs] def __init__(self, path="", **kwargs):
"""
Constructor
Args:
path (basestring): Base path
session: enigma2 Session instance
"""
resource.Resource.__init__(self)
self.path = path
self.session = kwargs.get("session")
self.log = logging.getLogger(__name__)
self.content_type = None
self.verbose = 0
[docs] def loadTemplate(self, template_trunk_relpath, module, args):
"""
Try to generate template contents by trying to load optimised bytecode,
python sourcefile and `.tmpl` file (in that order).
Args:
template_trunk_relpath (basestring): template filename trunk
module (basestring): module name
args (dict): template parameters
Returns:
str: template content
"""
if self.verbose > 10:
self.log.debug(
"template_trunk_relpath={!r} module={!r} args={!r}".format(
template_trunk_relpath, module, args))
trunk = '/'.join((VIEWS_PATH, template_trunk_relpath))
template_file = None
for ext in ('pyo', 'py', 'tmpl'):
candy = '.'.join((trunk, ext))
if os.path.isfile(candy):
template_file = candy
break
if template_file is None:
return None
# self.log.debug(">> {!r}".format(template_file))
if template_file[-1] in ('o', 'y'):
if template_file.endswith("o"):
template = imp.load_compiled(module, template_file)
else:
template = imp.load_source(module, template_file)
mod = getattr(template, module, None)
if callable(mod):
return str(mod(searchList=args))
else:
return str(Template(file=template_file, searchList=[args]))
return None
def getChild(self, path, request):
return self.__class__(self.session, path)
def render(self, request):
if self.verbose:
fmt = "{scheme}://{netloc}{path} " \
"accessed by {client}{via} {r_args}"
args = mangle_host_header_port(request.getHeader('host'))
args['path'] = request.path
try:
args['client'] = request.transport.getPeer()
except Exception as exc:
self.log.error(exc)
args['client'] = request.getClient()
args['via'] = ''
header = request.getHeader("X-Forwarded-For")
if header:
args['via'] = " ({!r})".format(header.split(",")[-1].strip())
args['r_args'] = request.args
self.log.info(fmt.format(**args))
if self.verbose > 4:
self.log.debug(request.getAllHeaders())
# cache data
path = self.path
if self.path == "":
self.path = "index"
self.path = self.path.replace(".", "")
owif_callback_name = OWIF_PREFIX + self.path
func = getattr(self, owif_callback_name, None)
if self.verbose > 10:
self.log.info('{!r} {!r}'.format(owif_callback_name, func))
if callable(func):
data = func(request)
if data is None:
self.log.warning('{!r} {!r} returned None'.format(
owif_callback_name, func))
error404(request)
return server.NOT_DONE_YET
if self.content_type:
request.setHeader("content-type", self.content_type)
if self.content_type == CONTENT_TYPE_X_MPEGURL:
request.write(data)
request.finish()
elif isinstance(data, str):
request.setHeader("content-type", CONTENT_TYPE_TEXT)
request.write(data)
request.finish()
else:
tmpl_trunk = request.path
template_module_name = self.path
if tmpl_trunk[-1] == "/":
tmpl_trunk += "index"
elif tmpl_trunk[-5:] != "index" and self.path == "index":
tmpl_trunk += "/index"
tmpl_trunk = tmpl_trunk.strip("/")
tmpl_trunk = tmpl_trunk.replace(".", "")
if tmpl_trunk in TEMPLATE_ALIASES:
the_alias = TEMPLATE_ALIASES[tmpl_trunk]
template_module_name = os.path.basename(the_alias)
if self.verbose > 10:
self.log.warning("Template alias {!r} -> {!r}".format(
tmpl_trunk, the_alias))
tmpl_trunk = the_alias
# out => content
out = self.loadTemplate(tmpl_trunk, template_module_name, data)
if out is None:
self.log.error(
"Template not loadable for {!r} (page {!r})".format(
owif_callback_name, request.uri))
error404(request)
else:
request.write(out)
request.finish()
else:
self.log.error("Callback {!r} for page {!r} not found".format(
owif_callback_name, request.uri))
error404(request)
# restore cached data
self.path = path
return server.NOT_DONE_YET