1 # -*- coding: UTF-8 -*-
5 # - better error handling
6 # - use namespace parser
8 from Tools.Import import my_import
10 from Components.Sources.Source import ObsoleteSource
11 from Components.Converter.Converter import Converter
12 from Components.Element import Element
14 from xml.sax import make_parser
15 from xml.sax.handler import ContentHandler, feature_namespaces
16 from xml.sax.saxutils import escape as escape_xml
17 from twisted.python import util
18 from urllib2 import quote
21 #DO NOT REMOVE THIS IMPORT
22 #It IS used (dynamically)
23 from WebScreens import *
24 #DO NOT REMOVE THIS IMPORT
26 from __init__ import decrypt_block
27 from os import urandom
32 # The classes and Function in File handle all ScreenPage-based requests
33 # ScreenPages use enigma2 standard functionality to bring contents to a webfrontend
35 # Like Skins a ScreenPage can consist of several Elements and Converters
37 #===============================================================================
40 # This is the Standard Element for Rendering a "standard" WebElement
41 #===============================================================================
42 class OneTimeElement(Element):
43 def __init__(self, id):
44 Element.__init__(self)
47 def handleCommand(self, args):
48 if ',' in self.source_id:
49 paramlist = self.source_id.split(",")
52 arg = args.get(key, ())
57 list[key] = "".join(arg)
60 self.source.handleCommand(list)
62 for c in args.get(self.source_id, ()):
63 self.source.handleCommand(c)
65 def render(self, request):
66 t = self.source.getHTML(self.source_id)
70 self.suspended = False
84 #===============================================================================
87 # A MacroElement helps using OneTimeElements inside a (Simple)ListFiller Loop
88 #===============================================================================
89 class MacroElement(OneTimeElement):
90 def __init__(self, id, macro_dict, macro_name):
91 OneTimeElement.__init__(self, id)
92 self.macro_dict = macro_dict
93 self.macro_name = macro_name
95 def render(self, request):
96 self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
98 #===============================================================================
101 # In difference to an OneTimeElement a StreamingElement sends an ongoing Stream
102 # of Data. The end of the Streaming is usually when the client disconnects
103 #===============================================================================
104 class StreamingElement(OneTimeElement):
105 def __init__(self, id):
106 OneTimeElement.__init__(self, id)
109 def changed(self, what):
111 self.render(self.request)
113 def setRequest(self, request):
114 self.request = request
116 #===============================================================================
119 # a to-be-filled list item
120 #===============================================================================
122 def __init__(self, name, filternum):
124 self.filternum = filternum
126 #===============================================================================
129 # MacroItem inside a (Simple)ListFiller
130 #===============================================================================
132 def __init__(self, macrodict, macroname):
133 self.macrodict = macrodict
134 self.macroname = macroname
137 #===============================================================================
140 # Returns the String as is
141 #===============================================================================
142 class TextToHTML(Converter):
143 def __init__(self, arg):
144 Converter.__init__(self, arg)
146 def getHTML(self, id):
147 return self.source.text.replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") # encode & etc. here!
149 #===============================================================================
152 # Escapes the given Text to be XML conform
153 #===============================================================================
154 class TextToXML(Converter):
155 def __init__(self, arg):
156 Converter.__init__(self, arg)
158 def getHTML(self, id):
159 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")
161 #===============================================================================
164 # Escapes the given Text so it can be used inside a URL
165 #===============================================================================
166 class TextToURL(Converter):
167 def __init__(self, arg):
168 Converter.__init__(self, arg)
170 def getHTML(self, id):
171 return self.source.text.replace(" ", "%20").replace("+", "%2b").replace("&", "%26").replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8")
173 #===============================================================================
176 # Returns a XML only consisting of <rootElement />
177 #===============================================================================
178 class ReturnEmptyXML(Converter):
179 def __init__(self, arg):
180 Converter.__init__(self, arg)
182 def getHTML(self, id):
183 return "<rootElement />"
185 #===============================================================================
187 # Return simply NOTHING
188 # Useful if you only want to issue a command.
189 #===============================================================================
190 class Null(Converter):
191 def __init__(self, arg):
192 Converter.__init__(self, arg)
194 def getHTML(self, id):
198 #===============================================================================
201 # Transforms a string into a javascript update pattern
202 #===============================================================================
203 class JavascriptUpdate(Converter):
204 def __init__(self, arg):
205 Converter.__init__(self, arg)
207 def getHTML(self, id):
208 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
209 # all other will replace this in JS
210 return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
212 #===============================================================================
215 # The performant 'one-dimensonial listfiller' engine (podlfe)
216 #===============================================================================
217 class SimpleListFiller(Converter):
218 def __init__(self, arg):
219 Converter.__init__(self, arg)
222 l = self.source.simplelist
223 conv_args = self.converter_arguments
226 for element in conv_args:
227 if isinstance(element, basestring):
228 list.append((element, None))
229 elif isinstance(element, ListItem):
230 list.append((element, element.filternum))
231 elif isinstance(element, ListMacroItem):
232 list.append(element.macrodict[element.macroname], None)
234 raise Exception("neither string, ListItem nor ListMacroItem")
237 append = strlist.append
242 for (element, filternum) in list:
243 #filter out "non-displayable" Characters - at the very end, do it the hard way...
244 item = str(item).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
249 append(item.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
251 append(escape_xml(item))
253 append(item.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
257 time = parseint(item) or 0
259 append("%02d:%02d" % (t.tm_hour, t.tm_min))
261 time = parseint(item) or 0
263 append("%d min" % (time / 60))
266 # (this will be done in c++ later!)
268 return ''.join(strlist)
270 text = property(getText)
272 #===============================================================================
273 # the performant 'listfiller'-engine (plfe)
274 #===============================================================================
275 class ListFiller(Converter):
276 def __init__(self, arg):
277 Converter.__init__(self, arg)
278 # print "ListFiller-arg: ",arg
282 lut = self.source.lut
283 conv_args = self.converter_arguments
285 # now build a ["string", 1, "string", 2]-styled list, with indices into the
286 # list to avoid lookup of item name for each entry
288 for element in conv_args:
289 if isinstance(element, basestring):
290 lutlist.append((element, None))
291 elif isinstance(element, ListItem):
292 lutlist.append((lut[element.name], element.filternum))
293 elif isinstance(element, ListMacroItem):
294 lutlist.append((element.macrodict[element.macroname], None))
296 raise Exception("neither string, ListItem nor ListMacroItem")
298 # now, for the huge list, do:
300 append = strlist.append
302 for (element, filternum) in lutlist:
306 #filter out "non-displayable" Characters - at the very end, do it the hard way...
307 curitem = str(item[element]).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
317 append(curitem.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
319 append( escape_xml( curitem ))
321 append(curitem.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
323 append(quote(curitem))
325 from time import localtime
326 time = int(float(curitem)) or 0
328 append("%02d:%02d" % (t.tm_hour, t.tm_min))
330 from time import localtime
331 time = int(float(curitem)) or 0
333 append("%d min" % (time / 60))
336 # (this will be done in c++ later!)
338 return ''.join(strlist)
340 text = property(getText)
342 #===============================================================================
345 # Handles the Content of a Web-Request
346 # It looks up the source, instantiates the Element and Calls the Converter
347 #===============================================================================
348 class webifHandler(ContentHandler):
349 def __init__(self, session, request):
353 self.session = session
355 self.request = request
358 def start_element(self, attrs):
361 wsource = attrs["source"]
363 path = wsource.split('.')
365 scr = self.screen.getRelatedScreen(path[0])
367 print "[webif.py] Parent Screen not found!"
371 source = scr.get(path[0])
373 if isinstance(source, ObsoleteSource):
374 # however, if we found an "obsolete source", issue warning, and resolve the real source.
375 print "WARNING: WEBIF '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
376 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
377 if source.description:
378 print source.description
380 wsource = source.new_source
383 # otherwise, use that source.
386 self.source_id = str(attrs.get("id", wsource))
387 self.is_streaming = "streaming" in attrs
388 self.macro_name = attrs.get("macro") or None
390 def end_element(self):
391 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
392 if not self.is_streaming:
393 if self.macro_name is None:
394 c = OneTimeElement(self.source_id)
396 c = MacroElement(self.source_id, self.macros, self.macro_name)
398 assert self.macro_name is None
399 c = StreamingElement(self.source_id)
401 c.connect(self.source)
403 self.screen.renderer.append(c)
406 def start_convert(self, attrs):
407 ctype = attrs["type"]
409 # TODO: we need something better here
410 if ctype[:4] == "web:": # for now
411 self.converter = eval(ctype[4:])
414 self.converter = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
416 self.converter = my_import('.'.join(("Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype))).__dict__.get(ctype)
419 def end_convert(self):
420 if len(self.sub) == 1:
421 self.sub = self.sub[0]
422 c = self.converter(self.sub)
423 c.connect(self.source)
427 def parse_item(self, attrs):
429 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4, "urlencode": 5, "time": 6, "minutes": 7}[attrs.get("filter", "")]
430 self.sub.append(ListItem(attrs["name"], filter))
432 assert "macro" in attrs, "e2:item must have a name= or macro= attribute!"
433 self.sub.append(ListMacroItem(self.macros, attrs["macro"]))
435 def startElement(self, name, attrs):
436 if name == "e2:screen":
437 if "external_module" in attrs:
438 exec "from " + attrs["external_module"] + " import *"
439 self.screen = eval(attrs["name"])(self.session, self.request) # fixme
440 self.screens.append(self.screen)
443 if name[:3] == "e2:":
446 tag = '<' + name + ''.join([' %s="%s"' % x for x in attrs.items()]) + '>'
447 #tag = tag.encode('utf-8')
451 elif self.mode == 1: # expect "<e2:element>"
452 assert name == "e2:element", "found %s instead of e2:element" % name
453 self.start_element(attrs)
454 elif self.mode == 2: # expect "<e2:convert>"
455 if name[:3] == "e2:":
456 assert name == "e2:convert"
457 self.start_convert(attrs)
461 assert name == "e2:item", "found %s instead of e2:item!" % name
463 self.parse_item(attrs)
465 def endElement(self, name):
466 if name == "e2:screen":
470 tag = "</" + name + ">"
473 elif self.mode == 2 and name[:3] != "e2:":
475 elif self.mode == 2: # closed 'convert' -> sub
477 elif self.mode == 1: # closed 'element'
479 if name[:3] == "e2:":
482 def processingInstruction(self, target, data):
483 self.res.append('<?' + target + ' ' + data + '>')
485 def characters(self, ch):
486 ch = ch.encode('utf-8')
492 def startEntity(self, name):
493 self.res.append('&' + name + ';');
496 for screen in self.screens:
500 print "screen cleanup!"
501 for screen in self.screens:
506 #===============================================================================
509 # Creates the Handler for a Request and calls it
510 # Also ensures that the Handler is finished after the Request is done
511 #===============================================================================
512 def renderPage(request, path, session):
513 # read in the template, create required screens
514 # we don't have persistense yet.
515 # if we had, this first part would only be done once.
516 handler = webifHandler(session, request)
517 parser = make_parser()
518 parser.setFeature(feature_namespaces, 0)
519 parser.setContentHandler(handler)
520 parser.parse(open(util.sibpath(__file__, path)))
522 # by default, we have non-streaming pages
525 # first, apply "commands" (aka. URL argument)
526 for x in handler.res:
527 if isinstance(x, Element):
528 x.handleCommand(request.args)
532 # now, we have a list with static texts mixed
533 # with non-static Elements.
534 # flatten this list, write into the request.
535 for x in handler.res:
536 if isinstance(x, Element):
537 if isinstance(x, StreamingElement):
539 x.setRequest(request)
542 request.write(str(x))
544 # if we met a "StreamingElement", there is at least one
545 # element which wants to output data more than once,
546 # i.e. on host-originated changes.
547 # in this case, don't finish yet, don't cleanup yet,
548 # but instead do that when the client disconnects.
550 requestFinish(handler, request)
553 def requestFinishDeferred(nothing, handler, request):
554 from twisted.internet import reactor
555 reactor.callLater(0, requestFinish, handler, request)
557 d = request.notifyFinish()
559 d.addBoth( requestFinishDeferred, handler, request )
561 #===============================================================================
564 # This has to be/is called at the end of every ScreenPage-based Request
565 #===============================================================================
566 def requestFinish(handler, request):
572 def validate_certificate(cert, key):
573 buf = decrypt_block(cert[8:], key)
576 return buf[36:107] + cert[139:196]
580 xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100))
583 result = xor(random, x)