twisted will be restarted after config was change
[enigma2-plugins.git] / webinterface / src / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2
3 from twisted.internet import reactor
4 from twisted.web2 import server, channel, static, resource, stream, http_headers, responsecode, http
5 from twisted.python import util
6 from twisted.python.log import startLogging,discardLogs
7
8 import webif
9 import WebIfConfig  
10 import os
11
12 from Components.config import config, ConfigSubsection, ConfigInteger,ConfigYesNo
13
14 config.plugins.Webinterface = ConfigSubsection()
15 config.plugins.Webinterface.enable = ConfigYesNo(default = True)
16 config.plugins.Webinterface.port = ConfigInteger(80,limits = (1, 999))
17 config.plugins.Webinterface.includehdd = ConfigYesNo(default = False)
18
19 sessions = [ ]
20
21  
22 """
23  set DEBUG to True, if twisted should write logoutput to a file.
24  in normal console output, twisted will print only the first Traceback.
25  is this a bug in twisted or a conflict with enigma2?
26  with this option enabled, twisted will print all TB to the logfile
27  use tail -f <file> to view this log
28 """
29                         
30 DEBUG = False
31 DEBUGFILE= "/tmp/twisted.log"
32
33 # Passwordprotection Test
34 # set it only to True, if you have a patched wrapper.py
35 # see http://twistedmatrix.com/trac/ticket/2041
36 # in /usr/lib/python2.4/site-packages/twisted/web2/auth/wrapper.py
37 # The solution is to change this line
38 #       
39 #       return self.authenticate(req), seg[1:]
40 # into this
41 #       return self.authenticate(req), seg
42 PASSWORDPROTECTION = False
43 PASSWORDPROTECTION_pwd = "root"
44 PASSWORDPROTECTION_mode = "sha"; 
45 # twisted supports more than sha ('md5','md5-sess','sha')
46 # but only sha works for me, but IE 
47 # sha, Firefox=ok, Opera=ok, wget=ok, ie=not ok
48 # md5-sess, firefox=not ok, opera=not ok,wget=ok, ie=not ok
49 # md5 same as md5-sess 
50
51 def stopWebserver():
52         reactor.disconnectAll()
53
54 def restartWebserver():
55         stopWebserver()
56         startWebserver()
57
58 def startWebserver():
59         if config.plugins.Webinterface.enable.value is not True:
60                 print "not starting Werbinterface"
61                 return False
62         if DEBUG:
63                 print "start twisted logfile, writing to %s" % DEBUGFILE 
64                 import sys
65                 startLogging(open(DEBUGFILE,'w'))
66
67         class ScreenPage(resource.Resource):
68                 def __init__(self, path):
69                         self.path = path
70                         
71                         
72                 def render(self, req):
73                         global sessions
74                         if sessions == [ ]:
75                                 return http.Response(responsecode.OK, stream="please wait until enigma has booted")
76
77                         class myProducerStream(stream.ProducerStream):
78                                 closed_callback = None
79
80                                 def close(self):
81                                         if self.closed_callback:
82                                                 self.closed_callback()
83                                         stream.ProducerStream.close(self)
84
85                         if os.path.isfile(self.path):
86                                 s=myProducerStream()
87                                 webif.renderPage(s, self.path, req, sessions[0])  # login?
88                                 return http.Response(responsecode.OK,stream=s)
89                         else:
90                                 return http.Response(responsecode.NOT_FOUND)
91                         
92                 def locateChild(self, request, segments):
93                         path = self.path+'/'+'/'.join(segments)
94                         if path[-1:] == "/":
95                                 path += "index.html"
96                         path +=".xml"
97                         return ScreenPage(path), ()
98                         
99         class Toplevel(resource.Resource):
100                 addSlash = True
101
102                 def render(self, req):
103                         fp = open(util.sibpath(__file__, "web-data")+"/index.html")
104                         s = fp.read()
105                         fp.close()
106                         return http.Response(responsecode.OK, {'Content-type': http_headers.MimeType('text', 'html')},stream=s)
107
108                 child_web = ScreenPage(util.sibpath(__file__, "web")) # "/web/*"
109                 child_webdata = static.File(util.sibpath(__file__, "web-data")) # FIXME: web-data appears as webdata
110
111         toplevel = Toplevel()
112         if config.plugins.Webinterface.includehdd.value:
113                 toplevel.putChild("hdd",static.File("/hdd"))
114         
115         if PASSWORDPROTECTION is False:
116                 site = server.Site(toplevel)
117         else:
118                 from twisted.cred.portal import Portal
119                 from twisted.cred import checkers
120                 from twisted.web2.auth import digest, basic, wrapper
121                 from zope.interface import Interface, implements
122                 from twisted.cred import portal
123                 class IHTTPUser(Interface):
124                         pass
125
126                 class HTTPUser(object):
127                         implements(IHTTPUser)
128
129                 class HTTPAuthRealm(object):
130                         implements(portal.IRealm)
131                         def requestAvatar(self, avatarId, mind, *interfaces):
132                                 if IHTTPUser in interfaces:
133                                         return IHTTPUser, HTTPUser()
134                                 raise NotImplementedError("Only IHTTPUser interface is supported")
135
136                 portal = Portal(HTTPAuthRealm())
137                 checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(root=PASSWORDPROTECTION_pwd)
138                 portal.registerChecker(checker)
139                 root = wrapper.HTTPAuthResource(toplevel,
140                                         (basic.BasicCredentialFactory('DM7025'),digest.DigestCredentialFactory(PASSWORDPROTECTION_mode,'DM7025')),
141                                         portal, (IHTTPUser,))
142                 site = server.Site(root)
143         print "[WebIf] starting Webinterface on port",config.plugins.Webinterface.port.value
144         reactor.listenTCP(config.plugins.Webinterface.port.value, channel.HTTPFactory(site))
145
146         
147 def autostart(reason, **kwargs):
148         if "session" in kwargs:
149                 global sessions
150                 sessions.append(kwargs["session"])
151                 return
152         if reason == 0:
153                 try:
154                         startWebserver()
155                 except ImportError,e:
156                         print "[WebIf] twisted not available, not starting web services",e
157                         
158 def openconfig(session, **kwargs):
159         session.openWithCallback(configCB,WebIfConfig.WebIfConfigScreen)
160
161 def configCB(result):
162         if result is True:
163                 print "[WebIf] config changed"
164                 restartWebserver()
165         else:
166                 print "[WebIf] config not changed"
167                 
168
169 def Plugins(**kwargs):
170         return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart),
171                     PluginDescriptor(name=_("Webinterface"), description=_("Configuration for the Webinterface"),where = [PluginDescriptor.WHERE_PLUGINMENU], icon="plugin.png",fnc = openconfig)]