# HG changeset patch # User Goffi # Date 1255985152 -7200 # Node ID c49345fd77376d25692c3da2f4a2628b52dcaf9d # Parent a06a151fc31ff5bec8104f3d981fb0e4ad37a91d refactoring: moved sat to sat.tac, now a twisted application so we can use twistd. diff -r a06a151fc31f -r c49345fd7737 sat --- a/sat Sun Oct 18 23:20:45 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,374 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -""" -SAT: a jabber client -Copyright (C) 2009 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 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - - -from twisted.internet import glib2reactor, protocol -glib2reactor.install() - -from twisted.words.protocols.jabber import client, jid, xmlstream, error -from twisted.words.xish import domish - -from twisted.internet import reactor -import pdb - -from sat_bridge.DBus import DBusBridge -import logging -from logging import debug, info, error - -import signal, sys -import os.path - -from tools.memory import Memory -from glob import glob - - -### logging configuration FIXME: put this elsewhere ### -logging.basicConfig(level=logging.DEBUG, - format='%(message)s') -### - - - - -class SAT: - - def __init__(self): - self.reactor=reactor - self.memory=Memory() - self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future - self.connected=False #FIXME: use twisted var instead - - self._iq_cb_map = {} #callback called when ns is found on IQ - self._waiting_conf = {} #callback called when a confirmation is received - self._progress_cb_map = {} #callback called when a progress is requested (key = progress id) - self.plugins = {} - - self.bridge=DBusBridge() - self.bridge.register("connect", self.connect) - self.bridge.register("disconnect", self.disconnect) - self.bridge.register("getContacts", self.memory.getContacts) - self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus) - self.bridge.register("sendMessage", self.sendMessage) - self.bridge.register("setParam", self.setParam) - self.bridge.register("getParam", self.memory.getParam) - self.bridge.register("getParams", self.memory.getParams) - self.bridge.register("getParamsCategories", self.memory.getParamsCategories) - self.bridge.register("getHistory", self.memory.getHistory) - self.bridge.register("setPresence", self.setPresence) - self.bridge.register("addContact", self.addContact) - self.bridge.register("delContact", self.delContact) - self.bridge.register("isConnected", self.isConnected) - self.bridge.register("confirmationAnswer", self.confirmationAnswer) - self.bridge.register("getProgress", self.getProgress) - - self._import_plugins() - self.connect() - - - def _import_plugins(self): - """Import all plugins found in plugins directory""" - #TODO: manage dependencies - plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename,glob ("plugins/plugin*.py"))] - - for plug in plug_lst: - plug_path = 'plugins.'+plug - __import__(plug_path) - mod = sys.modules[plug_path] - plug_info = mod.PLUGIN_INFO - info ("importing plugin: %s", plug_info['name']) - self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self) - - def connect(self): - print "Connect !" - if (self.connected): - info("already connected !") - return - info("Connecting...") - self.me = jid.JID(self.memory.getParamV("JabberID", "Connection")) - self.factory = client.XMPPClientFactory(self.me, self.memory.getParamV("Password", "Connection")) - self.factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,self.authd) - self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT,self.failed) - reactor.connectTCP(self.memory.getParamV("Server", "Connection"), 5222, self.factory) - self.connectionStatus="online" #TODO: check if connection is OK - self.connected=True #TODO: use startedConnecting and clientConnectionLost of XMPPClientFactory - - def disconnect(self): - if (not self.connected): - info("not connected !") - return - info("Disconnecting...") - - def run(self): - debug("running app") - reactor.run() - - def stop(self): - debug("stopping app") - reactor.stop() - - def authd(self,xmlstream): - self.xmlstream=xmlstream - roster=client.IQ(xmlstream,'get') - roster.addElement(('jabber:iq:roster', 'query')) - roster.addCallback(self.rosterCb) - roster.send() - debug("server = %s",self.memory.getParamV("Server", "Connection")) - - ###FIXME: tmp disco ### - self.memory.registerFeature("http://jabber.org/protocol/disco#info") - self.disco(self.memory.getParamV("Server", "Connection"), self.serverDisco) - - - #we now send our presence status - self.setPresence(status="Online") - - # add a callback for the messages - xmlstream.addObserver('/message', self.gotMessage) - xmlstream.addObserver('/presence', self.presenceCb) - xmlstream.addObserver("/iq[@type='set' or @type='get']", self.iqCb) - #reactor.callLater(2,self.sendFile,"goffi2@jabber.goffi.int/Psi", "/tmp/fakefile") - - def sendMessage(self,to,msg,type='chat'): - #FIXME: check validity of recipient - debug("Sending jabber message to %s...", to) - message = domish.Element(('jabber:client','message')) - message["to"] = jid.JID(to).full() - message["from"] = self.me.full() - message["type"] = type - message.addElement("body", "jabber:client", msg) - self.xmlstream.send(message) - self.memory.addToHistory(self.me, self.me, jid.JID(to), message["type"], unicode(msg)) - self.bridge.newMessage(message['from'], unicode(msg), to=message['to']) #We send back the message, so all clients are aware of it - - def setParam(self, name, value, namespace): - """set wanted paramater and notice observers""" - info ("setting param: %s=%s in namespace %s", name, value, namespace) - self.memory.setParam(name, value, namespace) - self.bridge.paramUpdate(name, value, namespace) - - def setRoster(self, to): - """Add a contact to roster list""" - to_jid=jid.JID(to) - roster=client.IQ(self.xmlstream,'set') - query=roster.addElement(('jabber:iq:roster', 'query')) - item=query.addElement("item") - item.attributes["jid"]=to_jid.userhost() - roster.send() - #TODO: check IQ result - - def delRoster(self, to): - """Remove a contact from roster list""" - to_jid=jid.JID(to) - roster=client.IQ(self.xmlstream,'set') - query=roster.addElement(('jabber:iq:roster', 'query')) - item=query.addElement("item") - item.attributes["jid"]=to_jid.userhost() - item.attributes["subscription"]="remove" - roster.send() - #TODO: check IQ result - - - def failed(self,xmlstream): - debug("failed: %s", xmlstream.getErrorMessage()) - debug("failed: %s", dir(xmlstream)) - - def isConnected(self): - return self.connected - - ## jabber methods ## - - def disco (self, item, callback, node=None): - """XEP-0030 Service discovery Feature.""" - disco=client.IQ(self.xmlstream,'get') - disco["from"]=self.me.full() - disco["to"]=item - disco.addElement(('http://jabber.org/protocol/disco#info', 'query')) - disco.addCallback(callback) - disco.send() - - - def setPresence(self, to="", type="", show="", status="", priority=0): - """Send our presence information""" - presence = domish.Element(('jabber:client', 'presence')) - if not type in ["", "unavailable", "subscribed", "subscribe", - "unsubscribe", "unsubscribed", "prob", "error"]: - error("Type error !") - #TODO: throw an error - return - - if to: - presence.attributes["to"]=to - if type: - presence.attributes["type"]=type - - for element in ["show", "status", "priority"]: - if locals()[element]: - presence.addElement(element).addContent(unicode(locals()[element])) - - self.xmlstream.send(presence) - - def addContact(self, to): - """Add a contact in roster list""" - to_jid=jid.JID(to) - self.setRoster(to_jid.userhost()) - self.setPresence(to_jid.userhost(), "subscribe") - - def delContact(self, to): - """Remove contact from roster list""" - to_jid=jid.JID(to) - self.delRoster(to_jid.userhost()) - self.bridge.contactDeleted(to) - - def gotMessage(self,message): - debug (u"got_message from: %s", message["from"]) - for e in message.elements(): - if e.name == "body": - self.bridge.newMessage(message["from"], e.children[0]) - self.memory.addToHistory(self.me, jid.JID(message["from"]), self.me, "chat", e.children[0]) - break - - ## callbacks ## - - def add_IQ_cb(self, ns, cb): - """Add an IQ callback on namespace ns""" - debug ("Registered callback for namespace %s", ns) - self._iq_cb_map[ns]=cb - - def iqCb(self, stanza): - info ("iqCb") - debug ("="*20) - debug ("DEBUG:\n") - debug (stanza.toXml().encode('utf-8')) - debug ("="*20) - #FIXME: temporary ugly code - uri = stanza.firstChildElement().uri - if self._iq_cb_map.has_key(uri): - self._iq_cb_map[uri](stanza) - #TODO: manage errors stanza - - def presenceCb(self, elem): - info ("presence update for [%s]", elem.getAttribute("from")) - debug("\n\nXML=\n%s\n\n", elem.toXml()) - presence={} - presence["jid"]=elem.getAttribute("from") - presence["type"]=elem.getAttribute("type") or "" - presence["show"]="" - presence["status"]="" - presence["priority"]=0 - - for item in elem.elements(): - if presence.has_key(item.name): - presence[item.name]=item.children[0] - - ### we check if the status is not about subscription ### - #TODO: check that from jid is one we wants to subscribe (ie: check a recent subscription asking) - if jid.JID(presence["jid"]).userhost()!=self.me.userhost(): - if presence["type"]=="subscribed": - debug ("subscription answer") - elif presence["type"]=="unsubscribed": - debug ("unsubscription answer") - elif presence["type"]=="subscribe": - #FIXME: auto answer for subscribe request, must be checked ! - debug ("subscription request") - self.setPresence(to=presence["jid"], type="subscribed") - else: - #We keep presence information only if it is not for subscription - self.memory.addPresenceStatus(presence["jid"], presence["type"], presence["show"], - presence["status"], int(presence["priority"])) - - #now it's time to notify frontends - self.bridge.presenceUpdate(presence["jid"], presence["type"], presence["show"], - presence["status"], int(presence["priority"])) - - def rosterCb(self,roster): - for contact in roster.firstChildElement().elements(): - info ("new contact in roster list: %s", contact['jid']) - #and now the groups - groups=[] - for group in contact.elements(): - if group.name!="group": - error("Unexpected element !") - break - groups.append(str(group)) - self.memory.addContact(contact['jid'], contact.attributes, groups) - self.bridge.newContact(contact['jid'], contact.attributes, groups) - - def serverDisco(self, disco): - """xep-0030 Discovery Protocol.""" - for element in disco.firstChildElement().elements(): - if element.name=="feature": - debug ("Feature dectetee: %s",element["var"]) - self.server_features.append(element["var"]) - elif element.name=="identity": - debug ("categorie= %s",element["category"]) - debug ("features= %s",self.server_features) - - ## Generic HMI ## - - def askConfirmation(self, id, type, data, cb): - """Add a confirmation callback""" - if self._waiting_conf.has_key(id): - error ("Attempt to register two callbacks for the same confirmation") - else: - self._waiting_conf[id] = cb - self.bridge.askConfirmation(type, id, data) - - - def confirmationAnswer(self, id, accepted, data): - """Called by frontends to answer confirmation requests""" - debug ("Received confirmation answer for id [%s]: %s", id, "accepted" if accepted else "refused") - if not self._waiting_conf.has_key(id): - error ("Received an unknown confirmation") - else: - cb = self._waiting_conf[id] - del self._waiting_conf[id] - cb(id, accepted, data) - - def registerProgressCB(self, id, CB): - """Register a callback called when progress is requested for id""" - self._progress_cb_map[id] = CB - - def removeProgressCB(self, id): - """Remove a progress callback""" - if not self._progress_cb_map.has_key(id): - error ("Trying to remove an unknow progress callback") - else: - del self._progress_cb_map[id] - - def getProgress(self, id): - """Return a dict with progress information - data['position'] : current possition - data['size'] : end_position - """ - data = {} - try: - self._progress_cb_map[id](data) - except KeyError: - pass - #debug("Requested progress for unknown id") - return data - - -app=SAT() #TODO: tmp, use twisted way instead -app.run() - -app.memory.save() #FIXME: not the best place -debug("Good Bye") diff -r a06a151fc31f -r c49345fd7737 sat.tac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sat.tac Mon Oct 19 22:45:52 2009 +0200 @@ -0,0 +1,381 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +SAT: a jabber client +Copyright (C) 2009 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + + +from twisted.application import internet, service +from twisted.internet import glib2reactor, protocol +glib2reactor.install() + +from twisted.words.protocols.jabber import client, jid, xmlstream, error +from twisted.words.xish import domish + +from twisted.internet import reactor +import pdb + +from sat_bridge.DBus import DBusBridge +import logging +from logging import debug, info, error + +import signal, sys +import os.path + +from tools.memory import Memory +from glob import glob + + +### logging configuration FIXME: put this elsewhere ### +logging.basicConfig(level=logging.DEBUG, + format='%(message)s') +### + + + + +class SAT: + + def __init__(self): + #self.reactor=reactor + self.memory=Memory() + self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future + self.connected=False #FIXME: use twisted var instead + + self._iq_cb_map = {} #callback called when ns is found on IQ + self._waiting_conf = {} #callback called when a confirmation is received + self._progress_cb_map = {} #callback called when a progress is requested (key = progress id) + self.plugins = {} + + self.bridge=DBusBridge() + self.bridge.register("connect", self.connect) + self.bridge.register("disconnect", self.disconnect) + self.bridge.register("getContacts", self.memory.getContacts) + self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus) + self.bridge.register("sendMessage", self.sendMessage) + self.bridge.register("setParam", self.setParam) + self.bridge.register("getParam", self.memory.getParam) + self.bridge.register("getParams", self.memory.getParams) + self.bridge.register("getParamsCategories", self.memory.getParamsCategories) + self.bridge.register("getHistory", self.memory.getHistory) + self.bridge.register("setPresence", self.setPresence) + self.bridge.register("addContact", self.addContact) + self.bridge.register("delContact", self.delContact) + self.bridge.register("isConnected", self.isConnected) + self.bridge.register("confirmationAnswer", self.confirmationAnswer) + self.bridge.register("getProgress", self.getProgress) + + self._import_plugins() + self.connect() + + + def _import_plugins(self): + """Import all plugins found in plugins directory""" + #TODO: manage dependencies + plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename,glob ("plugins/plugin*.py"))] + + for plug in plug_lst: + plug_path = 'plugins.'+plug + __import__(plug_path) + mod = sys.modules[plug_path] + plug_info = mod.PLUGIN_INFO + info ("importing plugin: %s", plug_info['name']) + self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self) + + def connect(self): + print "connecting..." + + def getService(self): + print "GetService !" + """if (self.connected): + info("already connected !") + return""" + info("Getting client...") + self.me = jid.JID(self.memory.getParamV("JabberID", "Connection")) + self.factory = client.XMPPClientFactory(self.me, self.memory.getParamV("Password", "Connection")) + self.factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,self.authd) + self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT,self.failed) + self.connectionStatus="online" #TODO: check if connection is OK + self.connected=True #TODO: use startedConnecting and clientConnectionLost of XMPPClientFactory + return internet.TCPClient(self.memory.getParamV("Server", "Connection"), 5222, self.factory) + + def disconnect(self): + if (not self.connected): + info("not connected !") + return + info("Disconnecting...") + + def run(self): + debug("running app") + reactor.run() + + def stop(self): + debug("stopping app") + reactor.stop() + + def authd(self,xmlstream): + self.xmlstream=xmlstream + roster=client.IQ(xmlstream,'get') + roster.addElement(('jabber:iq:roster', 'query')) + roster.addCallback(self.rosterCb) + roster.send() + debug("server = %s",self.memory.getParamV("Server", "Connection")) + + ###FIXME: tmp disco ### + self.memory.registerFeature("http://jabber.org/protocol/disco#info") + self.disco(self.memory.getParamV("Server", "Connection"), self.serverDisco) + + + #we now send our presence status + self.setPresence(status="Online") + + # add a callback for the messages + xmlstream.addObserver('/message', self.gotMessage) + xmlstream.addObserver('/presence', self.presenceCb) + xmlstream.addObserver("/iq[@type='set' or @type='get']", self.iqCb) + #reactor.callLater(2,self.sendFile,"goffi2@jabber.goffi.int/Psi", "/tmp/fakefile") + + def sendMessage(self,to,msg,type='chat'): + #FIXME: check validity of recipient + debug("Sending jabber message to %s...", to) + message = domish.Element(('jabber:client','message')) + message["to"] = jid.JID(to).full() + message["from"] = self.me.full() + message["type"] = type + message.addElement("body", "jabber:client", msg) + self.xmlstream.send(message) + self.memory.addToHistory(self.me, self.me, jid.JID(to), message["type"], unicode(msg)) + self.bridge.newMessage(message['from'], unicode(msg), to=message['to']) #We send back the message, so all clients are aware of it + + def setParam(self, name, value, namespace): + """set wanted paramater and notice observers""" + info ("setting param: %s=%s in namespace %s", name, value, namespace) + self.memory.setParam(name, value, namespace) + self.bridge.paramUpdate(name, value, namespace) + + def setRoster(self, to): + """Add a contact to roster list""" + to_jid=jid.JID(to) + roster=client.IQ(self.xmlstream,'set') + query=roster.addElement(('jabber:iq:roster', 'query')) + item=query.addElement("item") + item.attributes["jid"]=to_jid.userhost() + roster.send() + #TODO: check IQ result + + def delRoster(self, to): + """Remove a contact from roster list""" + to_jid=jid.JID(to) + roster=client.IQ(self.xmlstream,'set') + query=roster.addElement(('jabber:iq:roster', 'query')) + item=query.addElement("item") + item.attributes["jid"]=to_jid.userhost() + item.attributes["subscription"]="remove" + roster.send() + #TODO: check IQ result + + + def failed(self,xmlstream): + debug("failed: %s", xmlstream.getErrorMessage()) + debug("failed: %s", dir(xmlstream)) + + def isConnected(self): + return self.connected + + ## jabber methods ## + + def disco (self, item, callback, node=None): + """XEP-0030 Service discovery Feature.""" + disco=client.IQ(self.xmlstream,'get') + disco["from"]=self.me.full() + disco["to"]=item + disco.addElement(('http://jabber.org/protocol/disco#info', 'query')) + disco.addCallback(callback) + disco.send() + + + def setPresence(self, to="", type="", show="", status="", priority=0): + """Send our presence information""" + presence = domish.Element(('jabber:client', 'presence')) + if not type in ["", "unavailable", "subscribed", "subscribe", + "unsubscribe", "unsubscribed", "prob", "error"]: + error("Type error !") + #TODO: throw an error + return + + if to: + presence.attributes["to"]=to + if type: + presence.attributes["type"]=type + + for element in ["show", "status", "priority"]: + if locals()[element]: + presence.addElement(element).addContent(unicode(locals()[element])) + + self.xmlstream.send(presence) + + def addContact(self, to): + """Add a contact in roster list""" + to_jid=jid.JID(to) + self.setRoster(to_jid.userhost()) + self.setPresence(to_jid.userhost(), "subscribe") + + def delContact(self, to): + """Remove contact from roster list""" + to_jid=jid.JID(to) + self.delRoster(to_jid.userhost()) + self.bridge.contactDeleted(to) + + def gotMessage(self,message): + debug (u"got_message from: %s", message["from"]) + for e in message.elements(): + if e.name == "body": + self.bridge.newMessage(message["from"], e.children[0]) + self.memory.addToHistory(self.me, jid.JID(message["from"]), self.me, "chat", e.children[0]) + break + + ## callbacks ## + + def add_IQ_cb(self, ns, cb): + """Add an IQ callback on namespace ns""" + debug ("Registered callback for namespace %s", ns) + self._iq_cb_map[ns]=cb + + def iqCb(self, stanza): + info ("iqCb") + debug ("="*20) + debug ("DEBUG:\n") + debug (stanza.toXml().encode('utf-8')) + debug ("="*20) + #FIXME: temporary ugly code + uri = stanza.firstChildElement().uri + if self._iq_cb_map.has_key(uri): + self._iq_cb_map[uri](stanza) + #TODO: manage errors stanza + + def presenceCb(self, elem): + info ("presence update for [%s]", elem.getAttribute("from")) + debug("\n\nXML=\n%s\n\n", elem.toXml()) + presence={} + presence["jid"]=elem.getAttribute("from") + presence["type"]=elem.getAttribute("type") or "" + presence["show"]="" + presence["status"]="" + presence["priority"]=0 + + for item in elem.elements(): + if presence.has_key(item.name): + presence[item.name]=item.children[0] + + ### we check if the status is not about subscription ### + #TODO: check that from jid is one we wants to subscribe (ie: check a recent subscription asking) + if jid.JID(presence["jid"]).userhost()!=self.me.userhost(): + if presence["type"]=="subscribed": + debug ("subscription answer") + elif presence["type"]=="unsubscribed": + debug ("unsubscription answer") + elif presence["type"]=="subscribe": + #FIXME: auto answer for subscribe request, must be checked ! + debug ("subscription request") + self.setPresence(to=presence["jid"], type="subscribed") + else: + #We keep presence information only if it is not for subscription + self.memory.addPresenceStatus(presence["jid"], presence["type"], presence["show"], + presence["status"], int(presence["priority"])) + + #now it's time to notify frontends + self.bridge.presenceUpdate(presence["jid"], presence["type"], presence["show"], + presence["status"], int(presence["priority"])) + + def rosterCb(self,roster): + for contact in roster.firstChildElement().elements(): + info ("new contact in roster list: %s", contact['jid']) + #and now the groups + groups=[] + for group in contact.elements(): + if group.name!="group": + error("Unexpected element !") + break + groups.append(str(group)) + self.memory.addContact(contact['jid'], contact.attributes, groups) + self.bridge.newContact(contact['jid'], contact.attributes, groups) + + def serverDisco(self, disco): + """xep-0030 Discovery Protocol.""" + for element in disco.firstChildElement().elements(): + if element.name=="feature": + debug ("Feature dectetee: %s",element["var"]) + self.server_features.append(element["var"]) + elif element.name=="identity": + debug ("categorie= %s",element["category"]) + debug ("features= %s",self.server_features) + + ## Generic HMI ## + + def askConfirmation(self, id, type, data, cb): + """Add a confirmation callback""" + if self._waiting_conf.has_key(id): + error ("Attempt to register two callbacks for the same confirmation") + else: + self._waiting_conf[id] = cb + self.bridge.askConfirmation(type, id, data) + + + def confirmationAnswer(self, id, accepted, data): + """Called by frontends to answer confirmation requests""" + debug ("Received confirmation answer for id [%s]: %s", id, "accepted" if accepted else "refused") + if not self._waiting_conf.has_key(id): + error ("Received an unknown confirmation") + else: + cb = self._waiting_conf[id] + del self._waiting_conf[id] + cb(id, accepted, data) + + def registerProgressCB(self, id, CB): + """Register a callback called when progress is requested for id""" + self._progress_cb_map[id] = CB + + def removeProgressCB(self, id): + """Remove a progress callback""" + if not self._progress_cb_map.has_key(id): + error ("Trying to remove an unknow progress callback") + else: + del self._progress_cb_map[id] + + def getProgress(self, id): + """Return a dict with progress information + data['position'] : current possition + data['size'] : end_position + """ + data = {} + try: + self._progress_cb_map[id](data) + except KeyError: + pass + #debug("Requested progress for unknown id") + return data + + +application = service.Application('SàT') +sat = SAT() +service = sat.getService() +service.setServiceParent(application) + + +#app.memory.save() #FIXME: not the best place +#debug("Good Bye")