webinterface: drop plugin-local translations, bump version to 1.8.0
[enigma2-plugins.git] / webinterface / src / plugin.py
1 Version = '$Header$';
2
3 from enigma import eConsoleAppContainer, eTPM
4 from Plugins.Plugin import PluginDescriptor
5
6 from Components.config import config, ConfigBoolean, ConfigSubsection, ConfigInteger, ConfigYesNo, ConfigText, ConfigOnOff
7 from Components.Network import iNetworkInfo
8 from Screens.MessageBox import MessageBox
9 from WebIfConfig import WebIfConfigScreen
10 from WebChilds.Toplevel import getToplevel
11 from Tools.HardwareInfo import HardwareInfo
12
13 from Tools.Directories import copyfile, resolveFilename, SCOPE_PLUGINS, SCOPE_CONFIG
14 from Tools.IO import saveFile
15 from Tools.Log import Log
16
17 from twisted.internet import reactor, ssl
18 from twisted.internet.error import CannotListenError
19 from twisted.web import server, http, util, static, resource
20
21 from zope.interface import Interface, implements
22 from socket import gethostname as socket_gethostname
23 from OpenSSL import SSL, crypto
24 from time import gmtime
25 from os.path import isfile as os_isfile, exists as os_exists
26
27 from __init__ import __version__, decrypt_block
28 from webif import get_random, validate_certificate
29
30 import random, uuid, time, hashlib
31
32 from netaddr import IPNetwork
33
34 tpm = eTPM()
35 rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?']
36 hw = HardwareInfo()
37 #CONFIG INIT
38
39 #init the config
40 config.plugins.Webinterface = ConfigSubsection()
41 config.plugins.Webinterface.enabled = ConfigYesNo(default=True)
42 config.plugins.Webinterface.show_in_extensionsmenu = ConfigYesNo(default = False)
43 config.plugins.Webinterface.allowzapping = ConfigYesNo(default=True)
44 config.plugins.Webinterface.includemedia = ConfigYesNo(default=False)
45 config.plugins.Webinterface.autowritetimer = ConfigYesNo(default=False)
46 config.plugins.Webinterface.loadmovielength = ConfigYesNo(default=True)
47 config.plugins.Webinterface.version = ConfigText(__version__) # used to make the versioninfo accessible enigma2-wide, not confgurable in GUI.
48
49 config.plugins.Webinterface.http = ConfigSubsection()
50 config.plugins.Webinterface.http.enabled = ConfigYesNo(default=True)
51 config.plugins.Webinterface.http.port = ConfigInteger(default = 80, limits=(1, 65535) )
52 config.plugins.Webinterface.http.auth = ConfigYesNo(default=True)
53
54 config.plugins.Webinterface.https = ConfigSubsection()
55 config.plugins.Webinterface.https.enabled = ConfigYesNo(default=True)
56 config.plugins.Webinterface.https.port = ConfigInteger(default = 443, limits=(1, 65535) )
57 config.plugins.Webinterface.https.auth = ConfigYesNo(default=True)
58
59 config.plugins.Webinterface.streamauth = ConfigYesNo(default=False)
60 config.plugins.Webinterface.localauth = ConfigOnOff(default=False)
61
62 config.plugins.Webinterface.anti_hijack = ConfigOnOff(default=True)
63 config.plugins.Webinterface.extended_security = ConfigOnOff(default=True)
64
65 global running_defered, waiting_shutdown, toplevel
66
67 running_defered = []
68 waiting_shutdown = 0
69 toplevel = None
70 server.VERSION = "Enigma2 WebInterface Server $Revision$".replace("$Revi", "").replace("sion: ", "").replace("$", "")
71
72 KEY_FILE = resolveFilename(SCOPE_CONFIG, "key.pem")
73 CERT_FILE = resolveFilename(SCOPE_CONFIG, "cert.pem")
74
75 #===============================================================================
76 # Helperclass to close running Instances of the Webinterface
77 #===============================================================================
78 class Closer:
79         counter = 0
80         def __init__(self, session, callback=None, l2k=None):
81                 self.callback = callback
82                 self.session = session
83                 self.l2k = l2k
84 #===============================================================================
85 # Closes all running Instances of the Webinterface
86 #===============================================================================
87         def stop(self):
88                 global running_defered
89                 for d in running_defered:
90                         print "[Webinterface] stopping interface on ", d.interface, " with port", d.port
91                         x = d.stopListening()
92
93                         try:
94                                 x.addCallback(self.isDown)
95                                 self.counter += 1
96                         except AttributeError:
97                                 pass
98                 running_defered = []
99                 if self.counter < 1:
100                         if self.callback is not None:
101                                 self.callback(self.session, self.l2k)
102
103 #===============================================================================
104 # #Is it already down?
105 #===============================================================================
106         def isDown(self, s):
107                 self.counter -= 1
108                 if self.counter < 1:
109                         if self.callback is not None:
110                                 self.callback(self.session, self.l2k)
111
112 def installCertificates(session):
113         if not os_exists(CERT_FILE) \
114                         or not os_exists(KEY_FILE):
115                 print "[Webinterface].installCertificates :: Generating SSL key pair and CACert"
116                 # create a key pair
117                 k = crypto.PKey()
118                 k.generate_key(crypto.TYPE_RSA, 2048)
119
120                 # create a self-signed cert
121                 cert = crypto.X509()
122                 cert.get_subject().C = "DE"
123                 cert.get_subject().ST = "Home"
124                 cert.get_subject().L = "Home"
125                 cert.get_subject().O = "Dreambox"
126                 cert.get_subject().OU = "STB"
127                 cert.get_subject().CN = socket_gethostname()
128                 cert.set_serial_number(random.randint(1000000,1000000000))
129                 cert.set_notBefore("20120101000000Z");
130                 cert.set_notAfter("20301231235900Z")
131                 cert.set_issuer(cert.get_subject())
132                 cert.set_pubkey(k)
133                 print "[Webinterface].installCertificates :: Signing SSL key pair with new CACert"
134                 cert.sign(k, 'sha256')
135
136                 try:
137                         print "[Webinterface].installCertificates ::  Installing newly generated certificate and key pair"
138                         saveFile(CERT_FILE, crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
139                         saveFile(KEY_FILE, crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
140                 except IOError, e:
141                         #Disable https
142                         config.plugins.Webinterface.https.enabled.value = False
143                         config.plugins.Webinterface.https.enabled.save()
144                         #Inform the user
145                         session.open(MessageBox, "Couldn't install generated SSL-Certifactes for https access\nHttps access is disabled!", MessageBox.TYPE_ERROR)
146
147
148 #===============================================================================
149 # restart the Webinterface for all configured Interfaces
150 #===============================================================================
151 def restartWebserver(session, l2k):
152         try:
153                 del session.mediaplayer
154                 del session.messageboxanswer
155         except NameError:
156                 pass
157         except AttributeError:
158                 pass
159
160         global running_defered
161         if len(running_defered) > 0:
162                 Closer(session, startWebserver, l2k).stop()
163         else:
164                 startWebserver(session, l2k)
165
166 #===============================================================================
167 # start the Webinterface for all configured Interfaces
168 #===============================================================================
169 def startWebserver(session, l2k):
170         global running_defered
171         global toplevel
172
173         session.mediaplayer = None
174         session.messageboxanswer = None
175         if toplevel is None:
176                 toplevel = getToplevel(session)
177
178         errors = ""
179
180         if config.plugins.Webinterface.enabled.value is not True:
181                 print "[Webinterface] is disabled!"
182
183         else:
184                 # IF SSL is enabled we need to check for the certs first
185                 # If they're not there we'll exit via return here
186                 # and get called after Certificates are installed properly
187                 if config.plugins.Webinterface.https.enabled.value:
188                         installCertificates(session)
189
190                 # Listen on all Interfaces
191
192                 #HTTP
193                 port = config.plugins.Webinterface.http.port.value
194                 auth = config.plugins.Webinterface.http.auth.value
195                 if config.plugins.Webinterface.http.enabled.value is True:
196                         ret = startServerInstance(session, port, useauth=auth, l2k=l2k)
197                         if not ret:
198                                 errors = "%s port %i\n" %(errors, port)
199                         else:
200                                 registerBonjourService('http', port)
201
202                 #Streaming requires listening on localhost:80 no matter what, ensure it its available
203                 if config.plugins.Webinterface.http.port.value != 80 or not config.plugins.Webinterface.http.enabled.value:
204                         #LOCAL HTTP Connections (Streamproxy)
205                         local4 = "127.0.0.1"
206                         local4mapped = "::ffff:127.0.0.1"
207                         local6 = "::1"
208
209                         ret = startServerInstance(session, 80, useauth=auth, l2k=l2k, ipaddress=local4)
210                         if not ret:
211                                 errors = "%s%s:%i\n" %(errors, local4, 80)
212                         ret = startServerInstance(session, 80, useauth=auth, l2k=l2k, ipaddress=local4mapped, ipaddress2=local6)
213                         #ip6 is optional
214 #                       if not ret:
215 #                               errors = "%s%s/%s:%i\n" %(errors, local4mapped, local6, 80)
216
217                 #HTTPS
218                 if config.plugins.Webinterface.https.enabled.value is True:
219                         sport = config.plugins.Webinterface.https.port.value
220                         sauth = config.plugins.Webinterface.https.auth.value
221
222                         ret = startServerInstance(session, sport, useauth=sauth, l2k=l2k, usessl=True)
223                         if not ret:
224                                 errors = "%s%s:%i\n" %(errors, "0.0.0.0 / ::", sport)
225                         else:
226                                 registerBonjourService('https', sport)
227
228                 if errors:
229                         session.open(MessageBox, "Webinterface - Couldn't listen on:\n %s" % (errors), type=MessageBox.TYPE_ERROR, timeout=30)
230
231 #===============================================================================
232 # stop the Webinterface for all configured Interfaces
233 #===============================================================================
234 def stopWebserver(session):
235         try:
236                 del session.mediaplayer
237                 del session.messageboxanswer
238         except NameError:
239                 pass
240         except AttributeError:
241                 pass
242
243         global running_defered
244         if len(running_defered) > 0:
245                 Closer(session).stop()
246
247 #===============================================================================
248 # startServerInstance
249 # Starts an Instance of the Webinterface
250 # on given ipaddress, port, w/o auth, w/o ssl
251 #===============================================================================
252 def startServerInstance(session, port, useauth=False, l2k=None, usessl=False, ipaddress="::", ipaddress2=None):
253         l3k = None
254         l3c = tpm.getData(eTPM.DT_LEVEL3_CERT)
255
256         if l3c is None:
257                 return False
258
259         l3k = validate_certificate(l3c, l2k)
260         if l3k is None:
261                 return False
262
263         random = get_random()
264         if random is None:
265                 return False
266
267         value = tpm.computeSignature(random)
268         result = decrypt_block(value, l3k)
269
270         if result is None:
271                 return False
272         else:
273                 if result [80:88] != random:
274                         return False
275
276         if useauth:
277 # HTTPAuthResource handles the authentication for every Resource you want it to
278                 root = HTTPAuthResource(toplevel, "Enigma2 WebInterface")
279                 site = server.Site(root)
280         else:
281                 root = HTTPRootResource(toplevel)
282                 site = server.Site(root)
283
284         result = False
285
286         def logFail(addr, exception=None):
287                 print "[Webinterface] FAILED to listen on %s:%i auth=%s ssl=%s" % (addr, port, useauth, usessl)
288                 if exception:
289                         print exception
290
291         if usessl:
292                 ctx = ChainedOpenSSLContextFactory(KEY_FILE, CERT_FILE)
293                 try:
294                         d = reactor.listenSSL(port, site, ctx, interface=ipaddress)
295                         result = True
296                         running_defered.append(d)
297                 except CannotListenError as e:
298                         logFail(ipaddress, e)
299                 if ipaddress2:
300                         try:
301                                 d = reactor.listenSSL(port, site, ctx, interface=ipaddress2)
302                                 result = True
303                                 running_defered.append(d)
304                         except CannotListenError as e:
305                                 logFail(ipaddress2, e)
306         else:
307                 try:
308                         d = reactor.listenTCP(port, site, interface=ipaddress)
309                         result = True
310                         running_defered.append(d)
311                 except CannotListenError as e:
312                         logFail(ipaddress, e)
313                 if ipaddress2:
314                         try:
315                                 d = reactor.listenTCP(port, site, interface=ipaddress2)
316                                 result = True
317                                 running_defered.append(d)
318                         except CannotListenError as e:
319                                 logFail(ipaddress2, e)
320         
321         print "[Webinterface] started on %s:%i auth=%s ssl=%s" % (ipaddress, port, useauth, usessl)
322         return result
323
324         #except Exception, e:
325                 #print "[Webinterface] starting FAILED on %s:%i!" % (ipaddress, port), e
326                 #return False
327
328 class ChainedOpenSSLContextFactory(ssl.DefaultOpenSSLContextFactory):
329         def __init__(self, privateKeyFileName, certificateChainFileName, sslmethod=SSL.SSLv23_METHOD):
330                 self.privateKeyFileName = privateKeyFileName
331                 self.certificateChainFileName = certificateChainFileName
332                 self.sslmethod = sslmethod
333                 self.cacheContext()
334
335         def cacheContext(self):
336                 ctx = SSL.Context(self.sslmethod)
337                 ctx.set_options(SSL.OP_NO_SSLv3|SSL.OP_NO_SSLv2)
338                 ctx.use_certificate_chain_file(self.certificateChainFileName)
339                 ctx.use_privatekey_file(self.privateKeyFileName)
340                 self._context = ctx
341
342 class SimpleSession(object):
343         def __init__(self, expires=0):
344                 self._id = "0"
345                 self._expires = time.time() + expires if expires > 0 else 0
346
347         def _generateId(self):
348                 if config.plugins.Webinterface.extended_security.value:
349                         self._id = str ( uuid.uuid4() )
350                 else:
351                         self._id = "0"
352
353         def _getId(self):
354                 if self.expired():
355                         self._generateId()
356                 return self._id
357
358         def expired(self):
359                 expired = False
360                 if config.plugins.Webinterface.extended_security.value:
361                         expired = self._expires > 0 and self._expires < time.time()
362                         expired = expired or self._id == "0"
363                 else:
364                         expired = self._id != "0"
365                 return expired
366
367         id = property(_getId)
368
369 #Every request made will pass this Resource (as it is the root resource)
370 #Any "global" checks should be done here
371 class HTTPRootResource(resource.Resource):
372         SESSION_PROTECTED_PATHS = ['/web/', '/opkg', '/ipkg']
373         SESSION_EXCEPTIONS = [
374                 '/web/epgsearch.rss', '/web/movielist.m3u', '/web/movielist.rss', '/web/services.m3u', '/web/session',
375                 '/web/stream.m3u', '/web/stream', '/web/streamcurrent.m3u', '/web/strings.js', '/web/ts.m3u']
376
377         def __init__(self, res):
378                 print "[HTTPRootResource}.__init__"
379                 resource.Resource.__init__(self)
380                 self.resource = res
381                 self.sessionInvalidResource = resource.ErrorPage(http.PRECONDITION_FAILED, "Precondition failed!", "sessionid is missing, invalid or expired!")
382                 self._sessions = {}
383
384         def getClientToken(self, request):
385                 ip = request.getClientIP()
386                 ua = request.getHeader("User-Agent") or "Default UA"
387                 return hashlib.sha1("%s/%s" %(ip, ua)).hexdigest()
388
389         def isSessionValid(self, request):
390                 session = self._sessions.get( self.getClientToken(request), None )
391                 if session is None or session.expired():
392                         session = SimpleSession()
393                         key = self.getClientToken(request)
394                         print "[HTTPRootResource].isSessionValid :: created session with id '%s' for client with token '%s'" %(session.id, key)
395                         self._sessions[ key ] = session
396
397                 request.enigma2_session = session
398
399                 if config.plugins.Webinterface.extended_security.value and not request.path in self.SESSION_EXCEPTIONS:
400                         protected = False
401                         for path in self.SESSION_PROTECTED_PATHS:
402                                 if request.path.startswith(path):
403                                         protected = True
404
405                         if protected:
406                                 rsid = request.args.get('sessionid', None)
407                                 if rsid:
408                                         rsid = rsid[0]
409                                 return session and session.id == rsid
410
411                 return True
412
413         def render(self, request):
414                 #enable SAMEORIGIN policy for iframes
415                 if config.plugins.Webinterface.anti_hijack.value:
416                         request.setHeader("X-Frame-Options", "SAMEORIGIN")
417
418                 if self.isSessionValid(request):
419                         return self.resource.render(request)
420                 else:
421                         return self.sessionInvalidResource.render(request)
422
423         def getChildWithDefault(self, path, request):
424                 #enable SAMEORIGIN policy for iframes
425                 if config.plugins.Webinterface.anti_hijack.value:
426                         request.setHeader("X-Frame-Options", "SAMEORIGIN")
427
428                 if self.isSessionValid(request):
429                         return self.resource.getChildWithDefault(path, request)
430                 else:
431                         print "[Webinterface.HTTPRootResource.render] !!! session invalid !!!"
432                         return self.sessionInvalidResource
433
434 #===============================================================================
435 # HTTPAuthResource
436 # Handles HTTP Authorization for a given Resource
437 #===============================================================================
438 class HTTPAuthResource(HTTPRootResource):
439         LOCALHOSTS = (IPNetwork("127.0.0.1"), IPNetwork("::1"))
440
441         def __init__(self, res, realm):
442                 HTTPRootResource.__init__(self, res)
443                 self.realm = realm
444                 self.authorized = False
445                 self.unauthorizedResource = resource.ErrorPage(http.UNAUTHORIZED, "Access denied", "Authentication credentials invalid!")
446                 self._localNetworks = []
447
448         def _assignLocalNetworks(self, ifaces):
449                         if self._localNetworks:
450                                 return
451                         self._localNetworks = []
452                         #LAN
453                         for key, iface in ifaces.iteritems():
454                                 if iface.ipv4.address != "0.0.0.0":
455                                         v4net = IPNetwork("%s/%s" %(iface.ipv4.address, iface.ipv4.netmask))
456                                         self._localNetworks.append(v4net)
457                                 if iface.ipv6.address != "::":
458                                         v6net = IPNetwork("%s/%s" %(iface.ipv6.address, iface.ipv6.netmask))
459                                         self._localNetworks.append(v6net)
460                         Log.w(self._localNetworks)
461
462         def unauthorized(self, request):
463                 request.setHeader('WWW-authenticate', 'Basic realm="%s"' % self.realm)
464                 request.setResponseCode(http.UNAUTHORIZED)
465                 return self.unauthorizedResource
466
467         def _isLocalClient(self, clientip):
468                 if self._isLocalHost(clientip):
469                         return True
470                 for lnw in self._localNetworks:
471                         if self._networkContains(lnw, clientip):
472                                 return True
473                 return False
474
475         def _isLocalHost(self, clientip):
476                 for host in self.LOCALHOSTS:
477                         if self._networkContains(host, clientip):
478                                 return True
479                 return False
480
481         def _networkContains(self, network, ip):
482                 if network.__contains__(ip):
483                         return True
484                 try:
485                         # You may get an ipv6 noted ipv4 address like "::ffff:192.168.0.2"
486                         # In that case it won't match the ipv4 local network so we have to try converting it to plain ipv4
487                         if network.__contains__(ip.ipv4()):
488                                 return True
489                 except:
490                         pass
491                 return False
492
493         def isAuthenticated(self, request):
494                 self._assignLocalNetworks(iNetworkInfo.getConfiguredInterfaces())
495                 if request.transport:
496                         host = IPNetwork(request.transport.getPeer().host)
497                         #If streamauth is disabled allow all acces from localhost
498                         if not config.plugins.Webinterface.streamauth.value:
499                                 if self._isLocalHost(host.ip):
500                                         Log.i("Streaming auth is disabled - Bypassing Authcheck because host '%s' is local!" %host)
501                                         return True
502                         if not config.plugins.Webinterface.localauth.value:
503                                 if self._isLocalClient(host.ip):
504                                         Log.i("Local auth is disabled - Bypassing Authcheck because host '%s' is local!" %host)
505                                         return True
506
507                 # get the Session from the Request
508                 http_session = request.getSession().sessionNamespaces
509
510                 # if the auth-information has not yet been stored to the http_session
511                 if not http_session.has_key('authenticated'):
512                         if request.getUser() and request.getPassword():
513                                 http_session['authenticated'] = check_passwd(request.getUser(), request.getPassword())
514                         else:
515                                 http_session['authenticated'] = False
516
517                 #if the auth-information already is in the http_session
518                 else:
519                         if http_session['authenticated'] is False:
520                                 http_session['authenticated'] = check_passwd(request.getUser(), request.getPassword() )
521
522                 #return the current authentication status
523                 return http_session['authenticated']
524
525 #===============================================================================
526 # Call render of self.resource (if authenticated)
527 #===============================================================================
528         def render(self, request):
529                 if self.isAuthenticated(request) is True:
530                         return HTTPRootResource.render(self, request)
531                 else:
532                         print "[Webinterface.HTTPAuthResource.render] !!! unauthorized !!!"
533                         return self.unauthorized(request).render(request)
534
535 #===============================================================================
536 # Override to call getChildWithDefault of self.resource (if authenticated)
537 #===============================================================================
538         def getChildWithDefault(self, path, request):
539                 if self.isAuthenticated(request) is True:
540                         return HTTPRootResource.getChildWithDefault(self, path, request)
541                 else:
542                         print "[Webinterface.HTTPAuthResource.getChildWithDefault] !!! unauthorized !!!"
543                         return self.unauthorized(request)
544
545 # Password verfication stuff
546 from crypt import crypt
547 from pwd import getpwnam
548 from spwd import getspnam
549
550
551 def check_passwd(name, passwd):
552         cryptedpass = None
553         try:
554                 cryptedpass = getpwnam(name)[1]
555         except:
556                 return False
557
558         #shadowed or not, that's the questions here
559         if cryptedpass == 'x' or cryptedpass == '*':
560                 try:
561                         cryptedpass = getspnam(name)[1]
562                 except:
563                         return False
564
565         if cryptedpass == '':
566                 return True
567
568         return crypt(passwd, cryptedpass) == cryptedpass
569
570 global_session = None
571
572 #===============================================================================
573 # sessionstart
574 # Actions to take place on Session start
575 #===============================================================================
576 def sessionstart(reason, session):
577         global global_session
578         global_session = session
579         networkstart(True, session)
580
581
582 def registerBonjourService(protocol, port):
583         try:
584                 from Plugins.Extensions.Bonjour.Bonjour import bonjour
585
586                 service = bonjour.buildService(protocol, port)
587                 bonjour.registerService(service, True)
588                 print "[WebInterface.registerBonjourService] Service for protocol '%s' with port '%i' registered!" %(protocol, port)
589                 return True
590
591         except ImportError, e:
592                 print "[WebInterface.registerBonjourService] %s" %e
593                 return False
594
595 def unregisterBonjourService(protocol):
596         try:
597                 from Plugins.Extensions.Bonjour.Bonjour import bonjour
598
599                 bonjour.unregisterService(protocol)
600                 print "[WebInterface.unregisterBonjourService] Service for protocol '%s' unregistered!" %(protocol)
601                 return True
602
603         except ImportError, e:
604                 print "[WebInterface.unregisterBonjourService] %s" %e
605                 return False
606
607 def checkBonjour():
608         if ( not config.plugins.Webinterface.http.enabled.value ) or ( not config.plugins.Webinterface.enabled.value ):
609                 unregisterBonjourService('http')
610         if ( not config.plugins.Webinterface.https.enabled.value ) or ( not config.plugins.Webinterface.enabled.value ):
611                 unregisterBonjourService('https')
612
613 #===============================================================================
614 # networkstart
615 # Actions to take place after Network is up (startup the Webserver)
616 #===============================================================================
617 #def networkstart(reason, **kwargs):
618 def networkstart(reason, session):
619         l2r = False
620         l2k = None
621         l2c = tpm.getData(eTPM.DT_LEVEL2_CERT)
622
623         if l2c is None:
624                 return
625
626         l2k = validate_certificate(l2c, rootkey)
627         if l2k is None:
628                 return
629
630         if reason is True:
631                 startWebserver(session, l2k)
632                 checkBonjour()
633
634         elif reason is False:
635                 stopWebserver(session)
636                 checkBonjour()
637
638 def openconfig(session, **kwargs):
639         session.openWithCallback(configCB, WebIfConfigScreen)
640
641 def menu_config(menuid, **kwargs):
642         if menuid == "network":
643                 return [(_("Webinterface"), openconfig, "webif", 60)]
644         else:
645                 return []
646
647 def configCB(result, session):
648         l2r = False
649         l2k = None
650         l2c = tpm.getData(eTPM.DT_LEVEL2_CERT)
651
652         if l2c is None:
653                 return
654
655         l2k = validate_certificate(l2c, rootkey)
656         if l2k is None:
657                 return
658
659         if result:
660                 print "[WebIf] config changed"
661                 restartWebserver(session, l2k)
662                 checkBonjour()
663         else:
664                 print "[WebIf] config not changed"
665
666 def Plugins(**kwargs):
667         p = PluginDescriptor(where=[PluginDescriptor.WHERE_SESSIONSTART], fnc=sessionstart)
668         p.weight = 100 #webif should start as last plugin
669         list = [p,
670 #                       PluginDescriptor(where=[PluginDescriptor.WHERE_NETWORKCONFIG_READ], fnc=networkstart),
671                         PluginDescriptor(name=_("Webinterface"), description=_("Configuration for the Webinterface"),
672                                                         where=PluginDescriptor.WHERE_MENU, icon="plugin.png", fnc=menu_config)]
673         if config.plugins.Webinterface.show_in_extensionsmenu.value:
674                 list.append(PluginDescriptor(name="Webinterface", description=_("Configuration for the Webinterface"),
675                         where=PluginDescriptor.WHERE_EXTENSIONSMENU, icon="plugin.png", fnc=openconfig))
676         return list