Always listen on localhost:80 to ensure streaming works.
[vuplus_dvbapp-plugin] / webinterface / src / webif.py
index 188c100..f16100f 100644 (file)
@@ -2,9 +2,6 @@
 Version = '$Header$';
 
 # things to improve:
-#      - nicer code
-#      - screens need to be defined somehow else.
-#        I don't know how, yet. Probably each in an own file.
 #      - better error handling
 #      - use namespace parser
 
@@ -19,35 +16,50 @@ from xml.sax.handler import ContentHandler, feature_namespaces
 from xml.sax.saxutils import escape as escape_xml
 from twisted.python import util
 from urllib2 import quote
+from time import time
 
 #DO NOT REMOVE THIS IMPORT
 #It IS used (dynamically)
 from WebScreens import *
 #DO NOT REMOVE THIS IMPORT
-               
-# implements the 'render'-call.
-# this will act as a downstream_element, like a renderer.
+
+from __init__ import decrypt_block
+from os import urandom
+
+global screen_cache
+screen_cache = {}
+
+# The classes and Function in File handle all ScreenPage-based requests
+# ScreenPages use enigma2 standard functionality to bring contents to a webfrontend
+#
+# Like Skins a ScreenPage can consist of several Elements and Converters
+
+#===============================================================================
+# OneTimeElement
+#
+# This is the Standard Element for Rendering a "standard" WebElement
+#===============================================================================
 class OneTimeElement(Element):
        def __init__(self, id):
                Element.__init__(self)
                self.source_id = id
 
-       # CHECKME: is this ok performance-wise?
        def handleCommand(self, args):
-               if self.source_id.find(",") >= 0:
+               if ',' in self.source_id:
                        paramlist = self.source_id.split(",")
                        list = {}
                        for key in paramlist:
-                               arg = args.get(key, [])
-                               if len(arg) == 0:
+                               arg = args.get(key, ())
+                               Len = len(arg)
+                               if Len == 0:
                                        list[key] = None
-                               elif len(arg) == 1:
+                               elif Len == 1:
                                        list[key] = "".join(arg)
-                               elif len(arg) == 2:
+                               elif Len == 2:
                                        list[key] = arg[0]
                        self.source.handleCommand(list)
                else:
-                       for c in args.get(self.source_id, []):
+                       for c in args.get(self.source_id, ()):
                                self.source.handleCommand(c)
 
        def render(self, request):
@@ -69,6 +81,11 @@ class OneTimeElement(Element):
        def destroy(self):
                pass
 
+#===============================================================================
+# MacroElement
+#
+# A MacroElement helps using OneTimeElements inside a (Simple)ListFiller Loop
+#===============================================================================
 class MacroElement(OneTimeElement):
        def __init__(self, id, macro_dict, macro_name):
                OneTimeElement.__init__(self, id)
@@ -78,6 +95,12 @@ class MacroElement(OneTimeElement):
        def render(self, request):
                self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
 
+#===============================================================================
+# StreamingElement
+#
+# In difference to an OneTimeElement a StreamingElement sends an ongoing Stream
+# of Data. The end of the Streaming is usually when the client disconnects
+#===============================================================================
 class StreamingElement(OneTimeElement):
        def __init__(self, id):
                OneTimeElement.__init__(self, id)
@@ -90,46 +113,80 @@ class StreamingElement(OneTimeElement):
        def setRequest(self, request):
                self.request = request
 
+#===============================================================================
+# ListItem
+#
 # a to-be-filled list item
+#===============================================================================
 class ListItem:
        def __init__(self, name, filternum):
                self.name = name
                self.filternum = filternum
 
+#===============================================================================
+# ListMacroItem
+#
+# MacroItem inside a (Simple)ListFiller
+#===============================================================================
 class ListMacroItem:
        def __init__(self, macrodict, macroname):
                self.macrodict = macrodict
                self.macroname = macroname
 
+
+#===============================================================================
+# TextToHTML
+#
+# Returns the String as is
+#===============================================================================
 class TextToHTML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return self.source.text # encode & etc. here!
+               return self.source.text.replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") # encode & etc. here!
 
+#===============================================================================
+# TextToXML
+#
+# Escapes the given Text to be XML conform
+#===============================================================================
 class TextToXML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return escape_xml(self.source.text).replace("\x19", "").replace("\x1c", "").replace("\x1e", "")
+               return escape_xml(self.source.text).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
 
+#===============================================================================
+# TextToURL
+#
+# Escapes the given Text so it can be used inside a URL
+#===============================================================================
 class TextToURL(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return self.source.text.replace(" ", "%20")
+               return self.source.text.replace(" ", "%20").replace("+", "%2b").replace("&", "%26").replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8")
 
+#===============================================================================
+# ReturnEmptyXML
+# 
+# Returns a XML only consisting of <rootElement />
+#===============================================================================
 class ReturnEmptyXML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return "<rootElement></rootElement>"
+               return "<rootElement />"
 
-# a null-output. Useful if you only want to issue a command.
+#===============================================================================
+# Null
+# Return simply NOTHING
+# Useful if you only want to issue a command.
+#===============================================================================
 class Null(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -137,6 +194,12 @@ class Null(Converter):
        def getHTML(self, id):
                return ""
 
+
+#===============================================================================
+# JavascriptUpdate
+#
+# Transforms a string into a javascript update pattern
+#===============================================================================
 class JavascriptUpdate(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -146,7 +209,11 @@ class JavascriptUpdate(Converter):
                #                all other will replace this in JS
                return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '&deg;'))
 
-# the performant 'one-dimensonial listfiller' engine (podlfe)
+#===============================================================================
+# SimpleListFiller
+#
+# The performant 'one-dimensonial listfiller' engine (podlfe)
+#===============================================================================
 class SimpleListFiller(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -173,16 +240,19 @@ class SimpleListFiller(Converter):
                                item = ""
                                
                        for (element, filternum) in list:
+                               #filter out "non-displayable" Characters - at the very end, do it the hard way...
+                               item = str(item).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
+                               
                                if not filternum:
                                        append(element)
                                elif filternum == 2:
-                                       append(str(item).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
+                                       append(item.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
                                elif filternum == 3:                                    
-                                       append(escape_xml(str(item)))
+                                       append(escape_xml(item))
                                elif filternum == 4:
-                                       append(str(item).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
+                                       append(item.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
                                elif filternum == 5:
-                                       append(quote(str(item)))
+                                       append(quote(item))
                                elif filternum == 6:
                                        time = parseint(item) or 0
                                        t = localtime(time)
@@ -192,16 +262,16 @@ class SimpleListFiller(Converter):
                                        t = localtime(time)
                                        append("%d min" % (time / 60))
                                else:
-                                       append(str(item))
+                                       append(item)
                # (this will be done in c++ later!)
 
                return ''.join(strlist)         
        
        text = property(getText)
-               
-                               
-
+                       
+#===============================================================================
 # the performant 'listfiller'-engine (plfe)
+#===============================================================================
 class ListFiller(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -233,7 +303,8 @@ class ListFiller(Converter):
                                #None becomes ""
                                curitem = ""
                                if filternum:
-                                       curitem = item[element]
+                                       #filter out "non-displayable" Characters - at the very end, do it the hard way...
+                                       curitem = str(item[element]).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
                                        if curitem is None:
                                                curitem = ""
                                else:
@@ -243,13 +314,13 @@ class ListFiller(Converter):
                                if not filternum:
                                        append(element)
                                elif filternum == 2:
-                                       append(str(curitem).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
+                                       append(curitem.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
                                elif filternum == 3:
-                                       append(escape_xml(str(curitem)))
+                                       append( escape_xml( curitem ))
                                elif filternum == 4:
-                                       append(str(curitem).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
+                                       append(curitem.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
                                elif filternum == 5:
-                                       append(quote(str(curitem)))
+                                       append(quote(curitem))
                                elif filternum == 6:
                                        from time import localtime
                                        time = int(float(curitem)) or 0
@@ -261,13 +332,19 @@ class ListFiller(Converter):
                                        t = localtime(time)
                                        append("%d min" % (time / 60))                                  
                                else:
-                                       append(str(curitem))
+                                       append(curitem)
                # (this will be done in c++ later!)
 
                return ''.join(strlist)
 
        text = property(getText)
 
+#===============================================================================
+# webifHandler
+#
+# Handles the Content of a Web-Request
+# It looks up the source, instantiates the Element and Calls the Converter
+#===============================================================================
 class webifHandler(ContentHandler):
        def __init__(self, session, request):
                self.res = [ ]
@@ -334,9 +411,9 @@ class webifHandler(ContentHandler):
                        self.converter = eval(ctype[4:])
                else:
                        try:
-                               self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
+                               self.converter = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
                        except ImportError:
-                               self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
+                               self.converter = my_import('.'.join(("Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype))).__dict__.get(ctype)
                self.sub = [ ]
 
        def end_convert(self):
@@ -357,6 +434,8 @@ class webifHandler(ContentHandler):
 
        def startElement(self, name, attrs):
                if name == "e2:screen":
+                       if "external_module" in attrs:
+                               exec "from " + attrs["external_module"] + " import *"
                        self.screen = eval(attrs["name"])(self.session, self.request) # fixme
                        self.screens.append(self.screen)
                        return
@@ -364,11 +443,8 @@ class webifHandler(ContentHandler):
                if name[:3] == "e2:":
                        self.mode += 1
 
-               tag = [' %s="%s"' % (key, val) for (key, val) in attrs.items()]
-               tag.insert(0, name)
-               tag.insert(0, '<')
-               tag.append('>')
-               tag = ''.join(tag)#.encode('utf-8')
+               tag = '<' + name + ''.join([' %s="%s"' % x for x in attrs.items()]) + '>'
+               #tag = tag.encode('utf-8')
 
                if self.mode == 0:
                        self.res.append(tag)
@@ -427,6 +503,12 @@ class webifHandler(ContentHandler):
                        screen.doClose()
                self.screens = [ ]
 
+#===============================================================================
+# renderPage
+#
+# Creates the Handler for a Request and calls it
+# Also ensures that the Handler is finished after the Request is done
+#===============================================================================
 def renderPage(request, path, session):
        # read in the template, create required screens
        # we don't have persistense yet.
@@ -470,16 +552,36 @@ def renderPage(request, path, session):
        else:   
                def requestFinishDeferred(nothing, handler, request):
                        from twisted.internet import reactor
-                       reactor.callLater(1, requestFinish, handler, request)                           
+                       reactor.callLater(0, requestFinish, handler, request)                           
                
                d = request.notifyFinish()
 
-               d.addErrback( requestFinishDeferred, handler, request )
-               d.addCallback( requestFinishDeferred, handler, request )
+               d.addBoth( requestFinishDeferred, handler, request )
                                                        
-               
+#===============================================================================
+# requestFinish
+#
+# This has to be/is called at the end of every ScreenPage-based Request
+#===============================================================================
 def requestFinish(handler, request):
        handler.cleanup()
        request.finish()        
        
-       del handler
\ No newline at end of file
+       del handler
+
+def validate_certificate(cert, key):
+       buf = decrypt_block(cert[8:], key) 
+       if buf is None:
+               return None
+       return buf[36:107] + cert[139:196]
+
+def get_random():
+       try:
+               xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100))
+               random = urandom(8)
+               x = str(time())[-8:]
+               result = xor(random, x)
+                               
+               return result
+       except:
+               return None