Mercurial > libervia-web
view libervia.tac @ 142:f6aeeb753c06
browser side: ultra-minimalist native DOM implementation
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 09 Dec 2012 23:26:55 +0100 |
parents | 09a512d9a0c0 |
children | 8635bc9db9bf |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ Libervia: a Salut à Toi frontend Copyright (C) 2011, 2012 Jérôme Poisson <goffi@goffi.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ #You need do adapt the following consts to your server _REG_EMAIL_FROM = "NOREPLY@libervia.org" _REG_EMAIL_SERVER = "localhost" _REG_ADMIN_EMAIL = "goffi@goffi.org" _NEW_ACCOUNT_SERVER = "localhost" _NEW_ACCOUNT_DOMAIN = "tazar.int" _NEW_ACCOUNT_RESOURCE = "libervia" from twisted.application import internet, service from twisted.internet import glib2reactor glib2reactor.install() from twisted.internet import reactor, defer from twisted.mail.smtp import sendmail from twisted.web import server from twisted.web import error as weberror from twisted.web.static import File from twisted.web.resource import Resource, NoResource from twisted.python.components import registerAdapter from twisted.words.protocols.jabber.jid import JID from txjsonrpc.web import jsonrpc from txjsonrpc import jsonrpclib from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService from email.mime.text import MIMEText from logging import debug, info, warning, error import re, glob import os.path, sys import tempfile, shutil, uuid from server_side.blog import MicroBlog from zope.interface import Interface, Attribute, implements TIMEOUT = 10 #Session's time out, after that the user will be disconnected LIBERVIA_DIR = "output/" MEDIA_DIR = "media/" AVATARS_DIR = "avatars/" CARDS_DIR = "games/cards/tarot" class ISATSession(Interface): profile = Attribute("Sat profile") jid = Attribute("JID associated with the profile") class SATSession(object): implements(ISATSession) def __init__(self, session): self.profile = None self.jid = None class LiberviaSession(server.Session): sessionTimeout = TIMEOUT def __init__(self, *args, **kwargs): self.__lock = False server.Session.__init__(self, *args, **kwargs) def lock(self): """Prevent session from expiring""" self.__lock = True self._expireCall.reset(sys.maxint) def unlock(self): """Allow session to expire again, and touch it""" self.__lock = False self.touch() def touch(self): if not self.__lock: server.Session.touch(self) class ProtectedFile(File): """A File class which doens't show directory listing""" def directoryListing(self): return NoResource() class SATActionIDHandler(object): """Manage SàT action action_id lifecycle""" ID_LIFETIME = 30 #after this time (in seconds), action_id will be suppressed and action result will be ignored def __init__(self): self.waiting_ids = {} def waitForId(self, callback, action_id, profile, *args, **kwargs): """Wait for an action result @param callback: method to call when action gave a result back @param action_id: action_id to wait for @param profile: %(doc_profile)s @param *args: additional argument to pass to callback @param **kwargs: idem""" action_tuple = (action_id, profile) self.waiting_ids[action_tuple] = (callback, args, kwargs) reactor.callLater(self.ID_LIFETIME, self.purgeID, action_tuple) def purgeID(self, action_tuple): """Called when an action_id has not be handled in time""" if action_tuple in self.waiting_ids: warning ("action of action_id %s [%s] has not been managed, action_id is now ignored" % action_tuple) del self.waiting_ids[action_tuple] def actionResultCb(self, answer_type, action_id, data, profile): """Manage the actionResult signal""" action_tuple = (action_id, profile) if action_tuple in self.waiting_ids: callback, args, kwargs = self.waiting_ids[action_tuple] del self.waiting_ids[action_tuple] callback(answer_type, action_id, data, *args, **kwargs) class MethodHandler(jsonrpc.JSONRPC): def __init__(self, sat_host): jsonrpc.JSONRPC.__init__(self) self.sat_host=sat_host def render(self, request): self.session = request.getSession() profile = ISATSession(self.session).profile if not profile: #user is not identified, we return a jsonrpc fault parsed = jsonrpclib.loads(request.content.read()) fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc')) return jsonrpc.JSONRPC.render(self, request) def jsonrpc_getProfileJid(self): """Return the jid of the profile""" sat_session = ISATSession(self.session) profile = sat_session.profile sat_session.jid = JID(self.sat_host.bridge.getParamA("JabberID", "Connection", profile_key=profile)) return sat_session.jid.full() def jsonrpc_getContacts(self): """Return all passed args.""" profile = ISATSession(self.session).profile return self.sat_host.bridge.getContacts(profile) def jsonrpc_addContact(self, entity, name, groups): """Subscribe to contact presence, and add it to the given groups""" profile = ISATSession(self.session).profile self.sat_host.bridge.addContact(entity, profile) self.sat_host.bridge.updateContact(entity, name, groups, profile) def jsonrpc_delContact(self, entity): """Remove contact from contacts list""" profile = ISATSession(self.session).profile self.sat_host.bridge.delContact(entity, profile) def jsonrpc_updateContact(self, entity, name, groups): """Update contact's roster item""" profile = ISATSession(self.session).profile self.sat_host.bridge.updateContact(entity, name, groups, profile) def jsonrpc_subscription(self, sub_type, entity, name, groups): """Confirm (or infirm) subscription, and setup user roster in case of subscription""" profile = ISATSession(self.session).profile self.sat_host.bridge.subscription(sub_type, entity, profile) if sub_type == 'subscribed': self.sat_host.bridge.updateContact(entity, name, groups, profile) def jsonrpc_getWaitingSub(self): """Return list of room already joined by user""" profile = ISATSession(self.session).profile return self.sat_host.bridge.getWaitingSub(profile) def jsonrpc_setStatus(self, status): """Change the status""" profile = ISATSession(self.session).profile self.sat_host.bridge.setPresence('', '', 0, {'':status}, profile) def jsonrpc_sendMessage(self, to_jid, msg, subject, type): """send message""" profile = ISATSession(self.session).profile return self.sat_host.bridge.sendMessage(to_jid, msg, subject, type, profile) def jsonrpc_sendMblog(self, raw_text): """Parse raw_text of the microblog box, and send message consequently""" profile = ISATSession(self.session).profile match = re.match(r'@(.+?): *(.*$)', raw_text) if match: recip = match.group(1) text = match.group(2) #if recip == '@' and text: # #This text if for the public microblog # return self.sat_host.bridge.sendPersonalEvent("MICROBLOG", {'content':text}, profile) if recip == '@' and text: #This text if for the public microblog print "sending public blog" return self.sat_host.bridge.sendGroupBlog("PUBLIC", [], text, profile) else: print "sending group blog" return self.sat_host.bridge.sendGroupBlog("GROUP", [recip], text, profile) def jsonrpc_getLastMblogs(self, publisher_jid, max_item): """Get last microblogs posted by a contact @param publisher_jid: jid of the publisher @param max_item: number of items to ask @return list of microblog data (dict)""" profile = ISATSession(self.session).profile d = defer.Deferred() self.sat_host.bridge.getLastGroupBlogs(publisher_jid, max_item, profile, callback=d.callback, errback=d.errback) return d def jsonrpc_getMassiveLastMblogs(self, publishers_type, publishers_list, max_item): """Get lasts microblogs posted by several contacts at once @param publishers_type: one of "ALL", "GROUP", "JID" @param publishers_list: list of publishers type (empty list of all, list of groups or list of jids) @param max_item: number of items to ask @return: dictionary key=publisher's jid, value=list of microblog data (dict)""" profile = ISATSession(self.session).profile d = defer.Deferred() self.sat_host.bridge.getMassiveLastGroupBlogs(publishers_type, publishers_list, max_item, profile, callback=d.callback, errback=d.errback) self.sat_host.bridge.massiveSubscribeGroupBlogs(publishers_type, publishers_list, profile) return d def jsonrpc_getPresenceStatus(self): """Get Presence information for connected contacts""" profile = ISATSession(self.session).profile return self.sat_host.bridge.getPresenceStatus(profile) def jsonrpc_getHistory(self, from_jid, to_jid, size, between): """Return history for the from_jid/to_jid couple""" sat_session = ISATSession(self.session) profile = sat_session.profile sat_jid = sat_session.jid if not sat_jid: error("No jid saved for this profile") return {} if JID(from_jid).userhost() != sat_jid.userhost() and JID(to_jid).userhost() != sat_jid.userhost(): error("Trying to get history from a different jid, maybe a hack attempt ?") return {} d = defer.Deferred() self.sat_host.bridge.getHistory(from_jid, to_jid, size, between, profile, callback=d.callback, errback=d.errback) def show(result_dbus): result = [] for line in result_dbus: #XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types # and txJsonRPC doesn't accept D-Bus types, resulting in a empty query timestamp, from_jid, to_jid, message, mess_type = line result.append((float(timestamp), unicode(from_jid), unicode(to_jid), unicode(message), unicode(mess_type))) return result d.addCallback(show) return d def jsonrpc_joinMUC(self, room_jid, nick): """Join a Multi-User Chat room""" profile = ISATSession(self.session).profile try: room_jid = JID(room_jid) except: warning('Invalid room jid') return self.sat_host.bridge.joinMUC(room_jid.userhost(), nick, {}, profile) def jsonrpc_getRoomsJoined(self): """Return list of room already joined by user""" profile = ISATSession(self.session).profile return self.sat_host.bridge.getRoomsJoined(profile) def jsonrpc_launchTarotGame(self, other_players): """Create a room, invite the other players and start a Tarot game""" profile = ISATSession(self.session).profile self.sat_host.bridge.tarotGameLaunch(other_players, profile) def jsonrpc_getTarotCardsPaths(self): """Give the path of all the tarot cards""" _join = os.path.join _media_dir = _join(self.sat_host.media_dir,'') return map(lambda x: _join(MEDIA_DIR, x[len(_media_dir):]),glob.glob(_join(_media_dir,CARDS_DIR,'*_*.png'))); def jsonrpc_tarotGameReady(self, player, referee): """Tell to the server that we are ready to start the game""" profile = ISATSession(self.session).profile self.sat_host.bridge.tarotGameReady(player, referee) def jsonrpc_tarotGameContratChoosed(self, player_nick, referee, contrat): """Tell to the server that we are ready to start the game""" profile = ISATSession(self.session).profile self.sat_host.bridge.tarotGameContratChoosed(player_nick, referee, contrat, profile) def jsonrpc_tarotGamePlayCards(self, player_nick, referee, cards): """Tell to the server the cards we want to put on the table""" profile = ISATSession(self.session).profile self.sat_host.bridge.tarotGamePlayCards(player_nick, referee, cards, profile) def jsonrpc_launchRadioCollective(self, invited): """Create a room, invite people, and start a radio collective""" profile = ISATSession(self.session).profile self.sat_host.bridge.radiocolLaunch(invited, profile) def jsonrpc_getEntityData(self, jid, keys): """Get cached data for an entit @param jid: jid of contact from who we want data @param keys: name of data we want (list) @return: requested data""" profile = ISATSession(self.session).profile return self.sat_host.bridge.getEntityData(jid, keys, profile) class Register(jsonrpc.JSONRPC): """This class manage the registration procedure with SàT It provide an api for the browser, check password and setup the web server""" def __init__(self, sat_host): jsonrpc.JSONRPC.__init__(self) self.sat_host=sat_host self.profiles_waiting={} self.request=None def getWaitingRequest(self, profile): """Tell if a profile is trying to log in""" if self.profiles_waiting.has_key(profile): return self.profiles_waiting[profile] else: return None def render(self, request): """ Render method with some hacks: - if login is requested, try to login with form data - except login, every method is jsonrpc - user doesn't need to be authentified for isRegistered, but must be for all other methods """ if request.postpath==['login']: return self.login(request) _session = request.getSession() parsed = jsonrpclib.loads(request.content.read()) if parsed.get("method")!="isRegistered": #if we don't call login or isRegistered, we need to be identified profile = ISATSession(_session).profile if not profile: #user is not identified, we return a jsonrpc fault fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc')) self.request = request return jsonrpc.JSONRPC.render(self, request) def login(self, request): """ this method is called with the POST information from the registering form it test if the password is ok, and log in if it's the case, else it return an error @param request: request of the register formulaire, must have "login" and "password" as arguments @return: A constant indicating the state: - BAD REQUEST: something is wrong in the request (bad arguments, profile_key for login) - AUTH ERROR: either the profile or the password is wrong - ALREADY WAITING: a request has already be made for this profile - server.NOT_DONE_YET: the profile is being processed, the return value will be given by self._logged or self._logginError """ try: if request.args['submit_type'][0] == 'login': _login = request.args['login'][0] if _login.startswith('@'): raise Exception('No profile_key allowed') _pass = request.args['login_password'][0] elif request.args['submit_type'][0] == 'register': return self._registerNewAccount(request.args) else: raise Exception('Unknown submit type') except KeyError: return "BAD REQUEST" _profile_check = self.sat_host.bridge.getProfileName(_login) def profile_pass_cb(_profile_pass): if not _profile_check or _profile_check != _login or _profile_pass != _pass: request.write("AUTH ERROR") request.finish() return if self.profiles_waiting.has_key(_login): request.write("ALREADY WAITING") request.finish() return if self.sat_host.bridge.isConnected(_login): request.write(self._logged(_login, request, finish=False)) request.finish() return self.profiles_waiting[_login] = request self.sat_host.bridge.connect(_login) def profile_pass_errback(ignore): error("INTERNAL ERROR: can't check profile password") request.write("AUTH ERROR") request.finish() d = defer.Deferred() self.sat_host.bridge.asyncGetParamA("Password", "Connection", profile_key=_login, callback=d.callback, errback=d.errback) d.addCallbacks(profile_pass_cb, profile_pass_errback) return server.NOT_DONE_YET def _postAccountCreation(self, answer_type, id, data, profile): """Called when a account has just been created, setup stuff has microblog access""" def _connected(ignore): mblog_d = defer.Deferred() self.sat_host.bridge.setMicroblogAccess("open", profile, lambda: mblog_d.callback(None), mblog_d.errback) mblog_d.addBoth(lambda ignore: self.sat_host.bridge.disconnect(profile)) d = defer.Deferred() self.sat_host.bridge.asyncConnect(profile, lambda: d.callback(None), d.errback) d.addCallback(_connected) def _registerNewAccount(self, args): """Create a new account, or return error @param args: dict of args as given by the form @return: "REGISTRATION" in case of success""" #TODO: must be moved in SàT core try: profile = login = args['register_login'][0] password = args['register_password'][0] #FIXME: password is ignored so far email = args['email'][0] except KeyError: return "BAD REQUEST" if not re.match(r'^[a-z0-9_-]+$', login, re.IGNORECASE) or \ not re.match(r'^.+@.+\..+', email, re.IGNORECASE): return "BAD REQUEST" #_charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire _charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)] import random random.seed() password = ''.join([random.choice(_charset) for i in range(15)]) if login in self.sat_host.bridge.getProfilesList(): #FIXME: must use a deferred + create a new profile check method return "ALREADY EXISTS" #we now create the profile self.sat_host.bridge.createProfile(login) #FIXME: values must be in a config file instead of hardcoded self.sat_host.bridge.setParam("JabberID", "%s@%s/%s" % (login, _NEW_ACCOUNT_DOMAIN, _NEW_ACCOUNT_RESOURCE), "Connection", profile) self.sat_host.bridge.setParam("Server", _NEW_ACCOUNT_SERVER, "Connection", profile) self.sat_host.bridge.setParam("Password", password, "Connection", profile) #and the account action_id = self.sat_host.bridge.registerNewAccount(login, password, email, _NEW_ACCOUNT_DOMAIN, 5222) self.sat_host.action_handler.waitForId(self._postAccountCreation, action_id, profile) #time to send the email _email_host = _REG_EMAIL_SERVER _email_from = _REG_EMAIL_FROM def email_ok(ignore): print ("Account creation email sent to %s" % email) def email_ko(ignore): #TODO: return error code to user error ("Failed to send email to %s" % email) body = (u"""Welcome to Libervia, a Salut à Toi project part /!\\ WARNING, THIS IS ONLY A TECHNICAL DEMO, DON'T USE THIS ACCOUNT FOR ANY SERIOUS PURPOSE /!\\ Here are your connection informations: login: %(login)s password: %(password)s Your Jabber ID (JID) is: %(jid)s Any feedback welcome Cheers Goffi""" % { 'login': login, 'password': password, 'jid':"%s@%s" % (login, _NEW_ACCOUNT_DOMAIN) }).encode('utf-8') msg = MIMEText(body, 'plain', 'UTF-8') msg['Subject'] = 'Libervia account created' msg['From'] = _email_from msg['To'] = email d = sendmail(_email_host, _email_from, email, msg.as_string()) d.addCallbacks(email_ok, email_ko) #email to the administrator body = (u"""New account created: %(login)s [%(email)s]""" % { 'login': login, 'email': email }).encode('utf-8') msg = MIMEText(body, 'plain', 'UTF-8') msg['Subject'] = 'Libervia new account created' msg['From'] = _email_from msg['To'] = _REG_ADMIN_EMAIL d = sendmail(_email_host, _email_from, _REG_ADMIN_EMAIL, msg.as_string()) d.addCallbacks(email_ok, email_ko) return "REGISTRATION" def __cleanWaiting(self, login): """Remove login from waiting queue""" try: del self.profiles_waiting[login] except KeyError: pass def _logged(self, profile, request, finish=True): """Set everything when a user just logged and return "LOGGED" to the requester""" def result(answer): if finish: request.write(answer) request.finish() else: return answer self.__cleanWaiting(profile) _session = request.getSession() sat_session = ISATSession(_session) if sat_session.profile: error (('/!\\ Session has already a profile, this should NEVER happen !')) return result('SESSION_ACTIVE') sat_session.profile = profile self.sat_host.prof_connected.add(profile) def onExpire(): try: #We purge the queue del self.sat_host.signal_handler.queue[profile] except KeyError: pass #and now we disconnect the profile self.sat_host.bridge.disconnect(profile) _session.notifyOnExpire(onExpire) d = defer.Deferred() return result('LOGGED') def _logginError(self, login, request, error_type): """Something went wrong during loggin, return an error""" self.__cleanWaiting(login) return error_type def jsonrpc_isConnected(self): _session = self.request.getSession() profile = ISATSession(_session).profile return self.sat_host.bridge.isConnected(profile) def jsonrpc_connect(self): _session = self.request.getSession() profile = ISATSession(_session).profile if self.profiles_waiting.has_key(profile): raise jsonrpclib.Fault('1','Already waiting') #FIXME: define some standard error codes for libervia self.profiles_waiting[profile] = self.request self.sat_host.bridge.connect(profile) return server.NOT_DONE_YET def jsonrpc_isRegistered(self): """Tell if the user is already registered""" _session = self.request.getSession() profile = ISATSession(_session).profile return bool(profile) class SignalHandler(jsonrpc.JSONRPC): def __init__(self, sat_host): Resource.__init__(self) self.register=None self.sat_host=sat_host self.signalDeferred = {} self.queue = {} def plugRegister(self, register): self.register = register def jsonrpc_getSignals(self): """Keep the connection alive until a signal is received, then send it @return: (signal, *signal_args)""" _session = self.request.getSession() profile = ISATSession(_session).profile if profile in self.queue: #if we have signals to send in queue if self.queue[profile]: return self.queue[profile].pop(0) else: #the queue is empty, we delete the profile from queue del self.queue[profile] _session.lock() #we don't want the session to expire as long as this connection is active def unlock(ignore): _session.unlock() self.signalDeferred[profile] = defer.Deferred() self.request.notifyFinish().addBoth(unlock) return self.signalDeferred[profile] def getGenericCb(self, function_name): """Return a generic function which send all params to signalDeferred.callback function must have profile as last argument""" def genericCb(*args): profile = args[-1] if not profile in self.sat_host.prof_connected: return if profile in self.signalDeferred: self.signalDeferred[profile].callback((function_name,args[:-1])) del self.signalDeferred[profile] else: if not self.queue.has_key(profile): self.queue[profile] = [] self.queue[profile].append((function_name, args[:-1])) return genericCb def connected(self, profile): assert(self.register) #register must be plugged request = self.register.getWaitingRequest(profile) if request: self.register._logged(profile, request) def connectionError(self, error_type, profile): assert(self.register) #register must be plugged request = self.register.getWaitingRequest(profile) if request: #The user is trying to log in if error_type == "AUTH_ERROR": _error_t = "AUTH ERROR" else: _error_t = "UNKNOWN" self.register._logginError(profile, request, _error_t) def render(self, request): """ Render method wich reject access if user is not identified """ _session = request.getSession() parsed = jsonrpclib.loads(request.content.read()) profile = ISATSession(_session).profile if not profile: #user is not identified, we return a jsonrpc fault fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc')) self.request = request return jsonrpc.JSONRPC.render(self, request) class UploadManager(Resource): """This class manage the upload of a file It redirect the stream to SàT core backend""" #XXX: only used for RadioCol so far isLeaf = True def __init__(self, sat_host): self.sat_host=sat_host self.upload_dir = tempfile.mkdtemp() self.sat_host.addCleanup(shutil.rmtree, self.upload_dir) def getTmpDir(self): return self.upload_dir def render(self, request): """ Render method with some hacks: - if login is requested, try to login with form data - except login, every method is jsonrpc - user doesn't need to be authentified for isRegistered, but must be for all other methods """ filename = "%s.ogg" % str(uuid.uuid4()) #XXX: chromium doesn't seem to play song without the .ogg extension, even with audio/ogg mime-type filepath = os.path.join(self.upload_dir, filename) with open(filepath,'w') as f: f.write(request.args['song'][0]) profile = ISATSession(request.getSession()).profile self.sat_host.bridge.radiocolSongAdded(request.args['referee'][0], filepath, profile) return "OK" class Libervia(service.Service): def __init__(self): self._cleanup = [] root = ProtectedFile(LIBERVIA_DIR) self.signal_handler = SignalHandler(self) _register = Register(self) _upload = UploadManager(self) self.signal_handler.plugRegister(_register) self.sessions = {} #key = session value = user self.prof_connected = set() #Profiles connected self.action_handler = SATActionIDHandler() ## bridge ## try: self.bridge=DBusBridgeFrontend() except BridgeExceptionNoService: print(u"Can't connect to SàT backend, are you sure it's launched ?") sys.exit(1) self.bridge.register("connected", self.signal_handler.connected) self.bridge.register("connectionError", self.signal_handler.connectionError) self.bridge.register("actionResult", self.action_handler.actionResultCb) #core for signal_name in ['presenceUpdate', 'newMessage', 'subscribe', 'contactDeleted', 'newContact', 'entityDataUpdated']: self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name)) #plugins for signal_name in ['personalEvent', 'roomJoined', 'roomUserJoined', 'roomUserLeft', 'tarotGameStarted', 'tarotGameNew', 'tarotGameChooseContrat', 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore', 'radiocolStarted', 'radiocolPreload', 'radiocolPlay', 'radiocolNoUpload', 'radiocolUploadOk', 'radiocolSongRejected']: self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name), "plugin") self.media_dir = self.bridge.getConfig('','media_dir') self.local_dir = self.bridge.getConfig('','local_dir') root.putChild('json_signal_api', self.signal_handler) root.putChild('json_api', MethodHandler(self)) root.putChild('register_api', _register) root.putChild('upload', _upload) root.putChild('blog', MicroBlog(self)) root.putChild('css', ProtectedFile("server_css/")) root.putChild(os.path.dirname(MEDIA_DIR), ProtectedFile(self.media_dir)) root.putChild(os.path.dirname(AVATARS_DIR), ProtectedFile(os.path.join(self.local_dir, AVATARS_DIR))) root.putChild('radiocol', ProtectedFile(_upload.getTmpDir(), defaultType="audio/ogg")) #We cheat for PoC because we know we are on the same host, so we use directly upload dir self.site = server.Site(root) self.site.sessionFactory = LiberviaSession def addCleanup(self, callback, *args, **kwargs): """Add cleaning method to call when service is stopped cleaning method will be called in reverse order of they insertion @param callback: callable to call on service stop @param *args: list of arguments of the callback @param **kwargs: list of keyword arguments of the callback""" self._cleanup.insert(0, (callback, args, kwargs)) def startService(self): reactor.listenTCP(8080, self.site) def stopService(self): print "launching cleaning methods" for callback, args, kwargs in self._cleanup: callback(*args, **kwargs) def run(self): reactor.run() def stop(self): reactor.stop() registerAdapter(SATSession, server.Session, ISATSession) application = service.Application('Libervia') service = Libervia() service.setServiceParent(application)