dbus service: remove unused attribute of cloudeebus.Agent ('srvName')
[contrib/cloudeebus.git] / cloudeebus / cloudeebus.py
1 #!/usr/bin/env python
2
3 # Cloudeebus
4 #
5 # Copyright 2012 Intel Corporation.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 # Luc Yriarte <luc.yriarte@intel.com>
20 # Christophe Guiraud <christophe.guiraud@intel.com>
21 # Frederic Paut <frederic.paut@intel.com>
22 #
23
24
25 import argparse, dbus, json, sys
26
27 from twisted.internet import glib2reactor
28 # Configure the twisted mainloop to be run inside the glib mainloop.
29 # This must be done before importing the other twisted modules
30 glib2reactor.install()
31 from twisted.internet import reactor, defer
32
33 from autobahn.websocket import listenWS
34 from autobahn.wamp import exportRpc, WampServerFactory, WampCraServerProtocol
35
36 from dbus.mainloop.glib import DBusGMainLoop
37
38 import gobject
39 import re
40 import dbus.service
41 gobject.threads_init()
42
43 from dbus import glib
44 glib.init_threads()
45
46 # enable debug log
47 from twisted.python import log
48
49 # XML parser module
50 from xml.etree.ElementTree import XMLParser
51
52 ###############################################################################
53
54 VERSION = "0.5.99"
55 OPENDOOR = False
56 CREDENTIALS = {}
57 WHITELIST = []
58 SERVICELIST = []
59 NETMASK =  []
60
61 ###############################################################################
62 def ipV4ToHex(mask):
63     ## Convert an ip or an IP mask (such as ip/24 or ip/255.255.255.0) in hex value (32bits)
64     maskHex = 0
65     byte = 0
66     if mask.rfind(".") == -1:
67         if (int(mask) < 32):
68             maskHex = (2**(int(mask))-1)
69             maskHex = maskHex << (32-int(mask))
70         else:
71             raise Exception("Illegal mask (larger than 32 bits) " + mask)
72     else:
73         maskField = mask.split(".")
74         # Check if mask has four fields (byte)
75         if len(maskField) != 4:
76             raise Exception("Illegal ip address / mask (should be 4 bytes) " + mask)
77         for maskQuartet in maskField:
78             byte = int(maskQuartet)
79             # Check if each field is really a byte
80             if byte > 255:
81                 raise Exception("Illegal ip address / mask (digit larger than a byte) " + mask)              
82             maskHex += byte
83             maskHex = maskHex << 8
84         maskHex = maskHex >> 8
85     return maskHex
86
87 ###############################################################################
88 class DbusCache:
89     '''
90     Global cache of DBus connexions and signal handlers
91     '''
92     def __init__(self):
93         self.dbusConnexions = {}
94         self.signalHandlers = {}
95
96
97     def reset(self):
98         '''
99         Disconnect signal handlers before resetting cache.
100         '''
101         self.dbusConnexions = {}
102         # disconnect signal handlers
103         for key in self.signalHandlers:
104             self.signalHandlers[key].disconnect()
105         self.signalHandlers = {}
106
107
108     def dbusConnexion(self, busName):
109         if not self.dbusConnexions.has_key(busName):
110             if busName == "session":
111                 self.dbusConnexions[busName] = dbus.SessionBus()
112             elif busName == "system":
113                 self.dbusConnexions[busName] = dbus.SystemBus()
114             else:
115                 raise Exception("Error: invalid bus: %s" % busName)
116         return self.dbusConnexions[busName]
117
118
119
120 ###############################################################################
121 class DbusSignalHandler:
122     '''
123     signal hash id as busName#senderName#objectName#interfaceName#signalName
124     '''
125     def __init__(self, busName, senderName, objectName, interfaceName, signalName):
126         self.id = "#".join([busName, senderName, objectName, interfaceName, signalName])
127         # connect handler to signal
128         self.bus = cache.dbusConnexion(busName)
129         self.bus.add_signal_receiver(self.handleSignal, signalName, interfaceName, senderName, objectName)
130         
131     
132     def disconnect(self):
133         names = self.id.split("#")
134         self.bus.remove_signal_receiver(self.handleSignal, names[4], names[3], names[1], names[2])
135
136
137     def handleSignal(self, *args):
138         '''
139         publish dbus args under topic hash id
140         '''
141         factory.dispatch(self.id, json.dumps(args))
142
143
144
145 ###############################################################################
146 class DbusCallHandler:
147     '''
148     deferred reply to return dbus results
149     '''
150     def __init__(self, method, args):
151         self.pending = False
152         self.request = defer.Deferred()
153         self.method = method
154         self.args = args
155
156
157     def callMethod(self):
158         '''
159         dbus method async call
160         '''
161         self.pending = True
162         self.method(*self.args, reply_handler=self.dbusSuccess, error_handler=self.dbusError)
163         return self.request
164
165
166     def dbusSuccess(self, *result):
167         '''
168         return JSON string result array
169         '''
170         self.request.callback(json.dumps(result))
171         self.pending = False
172
173
174     def dbusError(self, error):
175         '''
176         return dbus error message
177         '''
178         self.request.errback(Exception(error.get_dbus_message()))
179         self.pending = False
180
181
182
183 ################################################################################       
184 class ExecCode:
185     '''
186     Execute DynDBusClass generated code
187     '''
188     def __init__(self, globalCtx, localCtx) :
189         self.exec_string = ""
190         self.exec_code = None
191         self.exec_code_valid = 1
192         self.indent_level = 0
193         self.indent_increment = 1
194         self.line = 0
195         self.localCtx = localCtx
196         self.globalCtx = globalCtx
197         
198
199     def append_stmt(self, stmt) :
200         self.exec_code_valid = 0
201         self.line += 1
202         for x in range(0,self.indent_level):
203             self.exec_string = self.exec_string + ' '            
204         self.exec_string = self.exec_string + stmt + '\n'
205
206     def indent(self) :
207         self.indent_level = self.indent_level + self.indent_increment
208
209     def dedent(self) :
210         self.indent_level = self.indent_level - self.indent_increment
211     
212     # compile : Compile exec_string into exec_code using the builtin
213     # compile function. Skip if already in sync.
214     def compile(self) :
215         if not self.exec_code_valid :
216             self.exec_code = compile(self.exec_string, "<string>", "exec")
217         self.exec_code_valid = True
218
219     def execute(self) :
220         if not self.exec_code_valid :
221             self.compile()
222         exec(self.exec_code, self.globalCtx, self.localCtx)
223
224
225
226 ################################################################################       
227 class XmlCbParser: # The target object of the parser
228     maxDepth = 0
229     depth = 0
230     def __init__(self, dynDBusClass):
231         self.dynDBusClass = dynDBusClass
232         
233     def start(self, tag, attrib):   # Called for each opening tag.
234         if (tag == 'node'):
235             return
236         # Set interface name
237         if (tag == 'interface'):
238             self.dynDBusClass.set_interface(attrib['name'])
239             return
240         # Set method name
241         if (tag == 'method'):
242             self.current = tag
243             self.dynDBusClass.def_method(attrib['name'])
244             return
245         if (tag == 'signal'):
246             self.current = tag
247             self.dynDBusClass.def_signal(attrib['name'])
248             return
249
250         # Set signature (in/out & name) for method
251         if (tag == 'arg'):
252             if (self.current == 'method'):
253                 if (attrib.has_key('direction') == False):
254                     attrib['direction'] = "in"
255                 self.dynDBusClass.add_signature(attrib['name'],
256                                                 attrib['direction'],
257                                                 attrib['type'])
258                 return
259             if (self.current == 'signal'):
260                 if (attrib.has_key('name') == False):
261                     attrib['name'] = 'value'
262                 self.dynDBusClass.add_signature(attrib['name'], 'in',
263                                                 attrib['type'])
264                 return
265     def end(self, tag):             # Called for each closing tag.
266         if (tag == 'method'):
267             self.dynDBusClass.add_dbus_method()
268             self.dynDBusClass.add_body_method()
269             self.dynDBusClass.end_method()
270         if (tag == 'signal'):
271             self.dynDBusClass.add_dbus_signal()
272             self.dynDBusClass.add_body_signal()
273             self.dynDBusClass.end_method()
274            
275     def data(self, data):
276         pass            # We do not need to do anything with data.
277     def close(self):    # Called when all data has been parsed.
278         return self.maxDepth
279
280
281        
282 ###############################################################################
283 def createClassName(objectPath):
284     return re.sub('/', '_', objectPath[1:])
285
286 ################################################################################       
287 class DynDBusClass():
288     def __init__(self, className, globalCtx, localCtx):
289         self.xmlCB = XmlCbParser(self)
290         self.signature = {}
291         self.class_code = ExecCode(globalCtx, localCtx)  
292         self.class_code.indent_increment = 4
293         self.class_code.append_stmt("import dbus")
294         self.class_code.append_stmt("\n")
295         self.class_code.append_stmt("class " + className + "(dbus.service.Object):")
296         self.class_code.indent()
297         
298         ## Overload of __init__ method 
299         self.def_method("__init__")
300         self.add_method("bus, callback=None, objPath='/sample', busName='org.cloudeebus'")
301         self.add_stmt("self.bus = bus")
302         self.add_stmt("self.objPath = objPath")
303         self.add_stmt("self.callback = callback")        
304         self.add_stmt("dbus.service.Object.__init__(self, conn=bus, bus_name=busName)")
305         self.end_method()
306                
307         ## Create 'add_to_connection' method 
308         self.def_method("add_to_connection")
309         self.add_method("connection=None, path=None")
310         self.add_stmt("dbus.service.Object.add_to_connection(self, connection=self.bus, path=self.objPath)")
311         self.end_method()
312                
313         ## Create 'remove_from_connection' method 
314         self.def_method("remove_from_connection")
315         self.add_method("connection=None, path=None")
316         self.add_stmt("dbus.service.Object.remove_from_connection(self, connection=None, path=self.objPath)")
317         self.end_method()
318                
319     def createDBusServiceFromXML(self, xml):
320         self.parser = XMLParser(target=self.xmlCB)
321         self.parser.feed(xml)
322         self.parser.close()
323     
324     def set_interface(self, ifName):
325         self.ifName = ifName
326         
327     def def_method(self, methodName):
328         self.methodToAdd = methodName
329         self.signalToAdd = None
330         self.args_str = str()
331         self.signature = {}
332         self.signature['name'] = str()
333         self.signature['in'] = str()                
334         self.signature['out'] = str()                        
335
336     def def_signal(self, signalName):
337         self.methodToAdd = None
338         self.signalToAdd = signalName
339         self.args_str = str()
340         self.signature = {}
341         self.signature['name'] = str()
342         self.signature['in'] = str()                
343         self.signature['out'] = str()                        
344
345     def add_signature(self, name, direction, signature):
346         if (direction == 'in'):
347             self.signature['in'] += signature
348             if (self.signature['name'] != str()):
349                 self.signature['name'] += ", "
350             self.signature['name'] += name
351         if (direction == 'out'):
352             self.signature['out'] = signature                        
353         
354     def add_method(self, args = None, async_success_cb = None, async_err_cb = None):
355         async_cb_str = str()
356         if (self.methodToAdd != None):
357             name = self.methodToAdd
358         else:
359             name = self.signalToAdd
360         if (args != None):
361             self.args_str = args
362         if (async_success_cb != None):
363             async_cb_str = async_success_cb
364         if (async_err_cb != None):
365             if (async_cb_str != str()):
366                 async_cb_str += ", "
367             async_cb_str += async_err_cb
368                         
369         parameters = self.args_str
370         if (async_cb_str != str()):
371             if (parameters != str()):
372                 parameters += ", "
373             parameters +=async_cb_str       
374         
375         if (parameters != str()):
376             self.class_code.append_stmt("def " + name + "(self, %s):" % parameters)               
377         else:
378             self.class_code.append_stmt("def " + name + "(self):")
379         self.class_code.indent()
380         
381     def end_method(self):
382         self.class_code.append_stmt("\n")
383         self.class_code.dedent()
384         
385     def add_dbus_method(self):
386         decorator = '@dbus.service.method("' + self.ifName + '"'
387         if (self.signature.has_key('in') and self.signature['in'] != str()):
388                 decorator += ", in_signature='" + self.signature['in'] + "'"
389         if (self.signature.has_key('out') and self.signature['out'] != str()):
390                 decorator += ", out_signature='" + self.signature['out'] + "'"
391         decorator += ", async_callbacks=('dbus_async_cb', 'dbus_async_err_cb')"            
392         decorator += ")"
393         self.class_code.append_stmt(decorator)
394         if (self.signature.has_key('name') and self.signature['name'] != str()):
395             self.add_method(self.signature['name'], async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
396         else:
397             self.add_method(async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
398
399     def add_dbus_signal(self):
400         decorator = '@dbus.service.signal("' + self.ifName + '"'
401         if (self.signature.has_key('in') and self.signature['in'] != str()):
402                 decorator += ", signature='" + self.signature['in'] + "'"
403         decorator += ")"            
404         self.class_code.append_stmt(decorator)
405         if (self.signature.has_key('name') and self.signature['name'] != str()):
406             self.add_method(self.signature['name'])
407         else:
408             self.add_method()
409
410     def add_body_method(self):
411         if (self.methodToAdd != None):
412             if (self.args_str != str()):
413                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb, %s)" % self.args_str)
414             else:        
415                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb)")
416
417     def add_body_signal(self):
418         self.class_code.append_stmt("return") ## TODO: Remove and fix with code ad hoc
419         self.class_code.append_stmt("\n")
420
421     def add_stmt(self, stmt) :
422         self.class_code.append_stmt(stmt)
423         
424     def declare(self) :
425         self.class_code.execute()
426
427
428
429 ###############################################################################
430 class CloudeebusService:
431     '''
432     support for sending DBus messages and registering for DBus signals
433     '''
434     def __init__(self, permissions):
435         self.permissions = {};
436         self.permissions['permissions'] = permissions['permissions']
437         self.permissions['authextra'] = permissions['authextra']
438         self.permissions['services'] = permissions['services']
439         self.proxyObjects = {}
440         self.proxyMethods = {}
441         self.pendingCalls = []
442         self.dynDBusClasses = {} # DBus class source code generated dynamically (a list because one by classname)
443         self.services = {}  # DBus service created
444         self.serviceAgents = {} # Instantiated DBus class previously generated dynamically, for now, one by classname
445         self.servicePendingCalls = {} # JS methods called (and waiting for a Success/error response), containing 'methodId', (successCB, errorCB)
446         self.localCtx = locals()
447         self.globalCtx = globals()
448
449
450     def proxyObject(self, busName, serviceName, objectName):
451         '''
452         object hash id as busName#serviceName#objectName
453         '''
454         id = "#".join([busName, serviceName, objectName])
455         if not self.proxyObjects.has_key(id):
456             if not OPENDOOR:
457                 # check permissions, array.index throws exception
458                 self.permissions['permissions'].index(serviceName)
459             bus = cache.dbusConnexion(busName)
460             self.proxyObjects[id] = bus.get_object(serviceName, objectName)
461         return self.proxyObjects[id]
462
463
464     def proxyMethod(self, busName, serviceName, objectName, interfaceName, methodName):
465         '''
466         method hash id as busName#serviceName#objectName#interfaceName#methodName
467         '''
468         id = "#".join([busName, serviceName, objectName, interfaceName, methodName])
469         if not self.proxyMethods.has_key(id):
470             obj = self.proxyObject(busName, serviceName, objectName)
471             self.proxyMethods[id] = obj.get_dbus_method(methodName, interfaceName)
472         return self.proxyMethods[id]
473
474
475     @exportRpc
476     def dbusRegister(self, list):
477         '''
478         arguments: bus, sender, object, interface, signal
479         '''
480         if len(list) < 5:
481             raise Exception("Error: expected arguments: bus, sender, object, interface, signal)")
482         
483         if not OPENDOOR:
484             # check permissions, array.index throws exception
485             self.permissions['permissions'].index(list[1])
486         
487         # check if a handler exists
488         sigId = "#".join(list)
489         if cache.signalHandlers.has_key(sigId):
490             return sigId
491         
492         # create a handler that will publish the signal
493         dbusSignalHandler = DbusSignalHandler(*list)
494         cache.signalHandlers[sigId] = dbusSignalHandler
495         
496         return dbusSignalHandler.id
497
498
499     @exportRpc
500     def dbusSend(self, list):
501         '''
502         arguments: bus, destination, object, interface, message, [args]
503         '''
504         # clear pending calls
505         for call in self.pendingCalls:
506             if not call.pending:
507                 self.pendingCalls.remove(call)
508         
509         if len(list) < 5:
510             raise Exception("Error: expected arguments: bus, destination, object, interface, message, [args])")
511         
512         # parse JSON arg list
513         args = []
514         if len(list) == 6:
515             args = json.loads(list[5])
516         
517         # get dbus proxy method
518         method = self.proxyMethod(*list[0:5])
519         
520         # use a deferred call handler to manage dbus results
521         dbusCallHandler = DbusCallHandler(method, args)
522         self.pendingCalls.append(dbusCallHandler)
523         return dbusCallHandler.callMethod()
524
525
526     @exportRpc
527     def emitSignal(self, list):
528         '''
529         arguments: agentObjectPath, signalName, result (to emit)
530         '''
531         objectPath = list[0]
532         className = re.sub('/', '_', objectPath[1:])
533         signalName = list[1]
534         result = list[2]
535         if (self.serviceAgents.has_key(className) == True):
536             exe_str = "self.serviceAgents['"+ className +"']."+ signalName + "(" + str(result) + ")"
537             eval(exe_str, self.globalCtx, self.localCtx)
538         else:
539             raise Exception("No object path " + objectPath)
540
541     @exportRpc
542     def returnMethod(self, list):
543         '''
544         arguments: methodId, callIndex, success (=true, error otherwise), result (to return)
545         '''
546         methodId = list[0]
547         callIndex = list[1]
548         success = list[2]
549         result = list[3]
550         if (self.servicePendingCalls.has_key(methodId)):
551             cb = self.servicePendingCalls[methodId]['calls'][callIndex]
552             if cb is None:
553                 raise Exception("No pending call " + str(callIndex) + " for methodID " + methodId)
554             if (success):                
555                 successCB = cb["successCB"]
556                 if (result != None):
557                     successCB(result)
558                 else:
559                     successCB()                    
560             else:     
561                 errorCB = cb["errorCB"]        
562                 if (result != None):
563                     errorCB(result)
564                 else:
565                     errorCB()
566             self.servicePendingCalls[methodId]['calls'][callIndex] = None
567             self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] - 1
568             if self.servicePendingCalls[methodId]['count'] == 0:
569                 del self.servicePendingCalls[methodId]
570         else:
571             raise Exception("No methodID " + methodId)
572
573     def srvCB(self, name, objPath, ifName, async_succes_cb, async_error_cb, *args):
574         methodId = self.srvName + "#" + objPath + "#" + ifName + "#" + name
575         cb = { 'successCB': async_succes_cb, 
576                'errorCB': async_error_cb}
577         if methodId not in self.servicePendingCalls:
578             self.servicePendingCalls[methodId] = {'count': 0, 'calls': []}
579             
580         try:
581             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
582         except Exception, e:                
583             args = eval( str(args).replace("dbus.Byte", "dbus.Int16") )
584             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
585                
586         self.servicePendingCalls[methodId]['calls'].append(cb)
587         self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] + 1
588         factory.dispatch(methodId, pendingCallStr)
589                     
590     @exportRpc
591     def serviceAdd(self, list):
592         '''
593         arguments: busName, srvName
594         '''
595         busName = list[0]
596         self.bus =  cache.dbusConnexion( busName )
597         self.srvName = list[1]
598         if not OPENDOOR and (SERVICELIST == [] or SERVICELIST != [] and self.permissions['services'] == None):
599             SERVICELIST.index(self.srvName)
600             
601         if (self.services.has_key(self.srvName) == False):
602             self.services[self.srvName] = dbus.service.BusName(name = self.srvName, bus = self.bus)
603         return self.srvName
604
605     @exportRpc
606     def serviceRelease(self, list):
607         '''
608         arguments: busName, srvName
609         '''
610         self.srvName = list[0]
611         if (self.services.has_key(self.srvName) == True):
612             self.services.pop(self.srvName)
613             return self.srvName
614         else:
615             raise Exception(self.srvName + " does not exist")
616                    
617     @exportRpc
618     def serviceAddAgent(self, list):
619         '''
620         arguments: objectPath, xmlTemplate
621         '''
622         agentObjectPath = list[0]
623         xmlTemplate = list[1]
624         className = createClassName(agentObjectPath)
625         if (self.dynDBusClasses.has_key(className) == False):
626             self.dynDBusClasses[className] = DynDBusClass(className, self.globalCtx, self.localCtx)
627             self.dynDBusClasses[className].createDBusServiceFromXML(xmlTemplate)
628             self.dynDBusClasses[className].declare()
629
630         ## Class already exist, instanciate it if not already instanciated
631         if (self.serviceAgents.has_key(className) == False):
632             self.serviceAgents[className] = eval(className + "(self.bus, callback=self.srvCB, objPath=agentObjectPath, busName=self.srvName)", self.globalCtx, self.localCtx)
633             
634         self.serviceAgents[className].add_to_connection()
635         return (agentObjectPath)
636                     
637     @exportRpc
638     def serviceDelAgent(self, list):
639         '''
640         arguments: objectPath, xmlTemplate
641         '''
642         agentObjectPath = list[0]
643         className = createClassName(agentObjectPath)
644         
645         print 'PY Try to remove ' + className
646
647         if (self.serviceAgents.has_key(className)):
648             self.serviceAgents[className].remove_from_connection()
649             self.serviceAgents.pop(className)
650         else:
651             raise Exception(agentObjectPath + " doesn't exist!")
652         
653         return (agentObjectPath)
654                     
655     @exportRpc
656     def getVersion(self):
657         '''
658         return current version string
659         '''
660         return VERSION
661
662
663
664 ###############################################################################
665 class CloudeebusServerProtocol(WampCraServerProtocol):
666     '''
667     connexion and session authentication management
668     '''
669     
670     def onSessionOpen(self):
671         # CRA authentication options
672         self.clientAuthTimeout = 0
673         self.clientAuthAllowAnonymous = OPENDOOR
674         # CRA authentication init
675         WampCraServerProtocol.onSessionOpen(self)
676     
677     
678     def getAuthPermissions(self, key, extra):
679          return {'permissions': extra.get("permissions", None),
680                  'authextra': extra.get("authextra", None),
681                  'services': extra.get("services", None)}   
682     
683     def getAuthSecret(self, key):
684         secret = CREDENTIALS.get(key, None)
685         if secret is None:
686             return None
687         # secret must be of str type to be hashed
688         return str(secret)
689     
690
691     def onAuthenticated(self, key, permissions):
692         if not OPENDOOR:
693             # check net filter
694             if NETMASK != []:
695                 ipAllowed = False
696                 for netfilter in NETMASK:
697                     ipHex=ipV4ToHex(self.peer.host)
698                     ipAllowed = (ipHex & netfilter['mask']) == netfilter['ipAllowed'] & netfilter['mask']
699                     if ipAllowed:
700                         break
701                 if not ipAllowed:
702                     raise Exception("host " + self.peer.host + " is not allowed!")
703             # check authentication key
704             if key is None:
705                 raise Exception("Authentication failed")
706             # check permissions, array.index throws exception
707             if (permissions['permissions'] != None):
708                 for req in permissions['permissions']:
709                     WHITELIST.index(req);
710             # check allowed service creation, array.index throws exception
711             if (permissions['services'] != None):
712                 for req in permissions['services']:
713                     SERVICELIST.index(req);
714         # create cloudeebus service instance
715         self.cloudeebusService = CloudeebusService(permissions)
716         # register it for RPC
717         self.registerForRpc(self.cloudeebusService)
718         # register for Publish / Subscribe
719         self.registerForPubSub("", True)
720     
721     
722     def connectionLost(self, reason):
723         WampCraServerProtocol.connectionLost(self, reason)
724         if factory.getConnectionCount() == 0:
725             cache.reset()
726
727
728
729 ###############################################################################
730
731 if __name__ == '__main__':
732     
733     cache = DbusCache()
734
735     parser = argparse.ArgumentParser(description='Javascript DBus bridge.')
736     parser.add_argument('-v', '--version', action='store_true', 
737         help='print version and exit')
738     parser.add_argument('-d', '--debug', action='store_true', 
739         help='log debug info on standard output')
740     parser.add_argument('-o', '--opendoor', action='store_true',
741         help='allow anonymous access to all services')
742     parser.add_argument('-p', '--port', default='9000',
743         help='port number')
744     parser.add_argument('-c', '--credentials',
745         help='path to credentials file')
746     parser.add_argument('-w', '--whitelist',
747         help='path to whitelist file (DBus services to use)')
748     parser.add_argument('-s', '--servicelist',
749         help='path to servicelist file (DBus services to export)')
750     parser.add_argument('-n', '--netmask',
751         help='netmask,IP filter (comma separated.) eg. : -n 127.0.0.1,192.168.2.0/24,10.12.16.0/255.255.255.0')
752     
753     args = parser.parse_args(sys.argv[1:])
754
755     if args.version:
756         print("Cloudeebus version " + VERSION)
757         exit(0)
758     
759     if args.debug:
760         log.startLogging(sys.stdout)
761     
762     OPENDOOR = args.opendoor
763     
764     if args.credentials:
765         jfile = open(args.credentials)
766         CREDENTIALS = json.load(jfile)
767         jfile.close()
768     
769     if args.whitelist:
770         jfile = open(args.whitelist)
771         WHITELIST = json.load(jfile)
772         jfile.close()
773         
774     if args.servicelist:
775         jfile = open(args.servicelist)
776         SERVICELIST = json.load(jfile)
777         jfile.close()
778         
779     if args.netmask:
780         iplist = args.netmask.split(",")
781         for ip in iplist:
782             if ip.rfind("/") != -1:
783                 ip=ip.split("/")
784                 ipAllowed = ip[0]
785                 mask = ip[1]
786             else:
787                 ipAllowed = ip
788                 mask = "255.255.255.255" 
789             NETMASK.append( {'ipAllowed': ipV4ToHex(ipAllowed), 'mask' : ipV4ToHex(mask)} )
790
791     uri = "ws://localhost:" + args.port
792     
793     factory = WampServerFactory(uri, debugWamp = args.debug)
794     factory.protocol = CloudeebusServerProtocol
795     factory.setProtocolOptions(allowHixie76 = True)
796     
797     listenWS(factory)
798     
799     DBusGMainLoop(set_as_default=True)
800     
801     reactor.run()