changeset 64:d46f849664aa

SàT: multi-profile, plugins updated - core: 2 new convenient methods: getJidNStream and getClient - new param in plugin info: "handler" to know if there is a handler to plug on profiles clients - plugins with handler now use an other class which is returned to profile client with the new method "getHandler" and pluged when connecting
author Goffi <goffi@goffi.org>
date Sat, 30 Jan 2010 16:17:33 +1100
parents 0db25931b60d
children d35c5edab53f
files frontends/quick_frontend/quick_app.py frontends/sat_bridge_frontend/DBus.py frontends/wix/main_window.py plugins/plugin_xep_0054.py plugins/plugin_xep_0065.py plugins/plugin_xep_0077.py plugins/plugin_xep_0096.py plugins/plugin_xep_0100.py sat.tac tools/memory.py
diffstat 10 files changed, 175 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/quick_frontend/quick_app.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/frontends/quick_frontend/quick_app.py	Sat Jan 30 16:17:33 2010 +1100
@@ -133,7 +133,7 @@
             self.CM.update(from_jid, 'show', show)
             self.CM.update(from_jid, 'statuses', statuses)
             self.CM.update(from_jid, 'groups', groups)
-            cache = self.bridge.getProfileCache(from_jid)
+            cache = self.bridge.getCardCache(from_jid)
             if cache.has_key('nick'): 
                 self.CM.update(from_jid, 'nick', cache['nick'])
             if cache.has_key('avatar'): 
--- a/frontends/sat_bridge_frontend/DBus.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/frontends/sat_bridge_frontend/DBus.py	Sat Jan 30 16:17:33 2010 +1100
@@ -58,23 +58,23 @@
     def sendMessage(self, to, message):
         return self.db_comm_iface.sendMessage(to, message)
     
-    def sendFile(self, to, path):
-        return self.db_comm_iface.sendFile(to, path)
+    def sendFile(self, to, path, profile_key='@DEFAULT@'):
+        return self.db_comm_iface.sendFile(to, path, profile_key)
 
     def findGateways(self, target):
         return self.db_comm_iface.findGateways(target)
 
-    def getProfile(self, target):
-        return self.db_comm_iface.getProfile(target)
+    def getCard(self, target, profile_key='@DEFAULT@'):
+        return self.db_comm_iface.getCard(target, profile_key)
 
-    def getProfileCache(self, target):
-        return self.db_comm_iface.getProfileCache(target)
+    def getCardCache(self, target):
+        return self.db_comm_iface.getCardCache(target)
 
     def getAvatarFile(self, hash):
         return self.db_comm_iface.getAvatarFile(hash)
 
-    def in_band_register(self, target):
-        return self.db_comm_iface.in_band_register(target)
+    def in_band_register(self, target, profile_key='@DEFAULT@'):
+        return self.db_comm_iface.in_band_register(target, profile_key)
     
     def setPresence(self, to="", show="", priority=0, statuses={}):
         return self.db_comm_iface.setPresence(to, show, priority, statuses)
--- a/frontends/wix/main_window.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/frontends/wix/main_window.py	Sat Jan 30 16:17:33 2010 +1100
@@ -521,7 +521,7 @@
             dlg.ShowModal()
             dlg.Destroy()
             return
-        id = self.bridge.getProfile(target.short) 
+        id = self.bridge.getCard(target.short) 
         self.current_action_ids.add(id)
         self.current_action_ids_cb[id] = self.onProfileReceived
    
--- a/plugins/plugin_xep_0054.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/plugins/plugin_xep_0054.py	Sat Jan 30 16:17:33 2010 +1100
@@ -58,11 +58,11 @@
 "protocols": ["XEP-0054", "XEP-0153"],
 "dependencies": [],
 "main": "XEP_0054",
+"handler": "yes",
 "description": """Implementation of vcard-temp"""
 }
 
-class XEP_0054(XMPPHandler):
-    implements(iwokkel.IDisco)
+class XEP_0054():
 
     def __init__(self, host):
         info("Plugin XEP_0054 initialization")
@@ -71,9 +71,12 @@
         self.vcard_cache = host.memory.getPrivate("vcard_cache") or {}  #used to store nicknames and avatar, key = jid
         if not os.path.exists(self.avatar_path):
             os.makedirs(self.avatar_path)
-        host.bridge.addMethod("getProfile", ".communication", in_sign='s', out_sign='s', method=self.getProfile)
+        host.bridge.addMethod("getCard", ".communication", in_sign='ss', out_sign='s', method=self.getCard)
         host.bridge.addMethod("getAvatarFile", ".communication", in_sign='s', out_sign='s', method=self.getAvatarFile)
-        host.bridge.addMethod("getProfileCache", ".communication", in_sign='s', out_sign='a{ss}', method=self.getProfileCache)
+        host.bridge.addMethod("getCardCache", ".communication", in_sign='s', out_sign='a{ss}', method=self.getCardCache)
+
+    def getHandler(self):
+        return XEP_0054_handler(self)  
    
     def update_cache(self, jid, name, value):
         """update cache value
@@ -101,15 +104,6 @@
             return None
 
 
-    def connectionInitialized(self):
-        self.xmlstream.addObserver(VCARD_UPDATE, self.update)
-    
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
-        return [disco.DiscoFeature(NS_VCARD)]
-
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
-        return []
-
     def save_photo(self, photo_xml):
         """Parse a <PHOTO> elem and save the picture"""
         for elem in photo_xml.elements():
@@ -177,14 +171,18 @@
         error ("Can't find VCard of %s" % failure.value.stanza['from'])
         self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}) #FIXME: maybe an error message would be best
   
-    def getProfile(self, target):
+    def getCard(self, target, profile_key='@DEFAULT@'):
         """Ask server for VCard
         @param target: jid from which we want the VCard
         @result: id to retrieve the profile"""
+        current_jid, xmlstream = self.host.getJidNStream(profile_key)
+        if not xmlstream:
+            error ('Asking profile for an non-existant or not connected profile')
+            return ""
         to_jid = jid.JID(target)
         debug("Asking for %s's VCard" % to_jid.userhost())
-        reg_request=IQ(self.host.xmlstream,'get')
-        reg_request["from"]=self.host.me.full()
+        reg_request=IQ(xmlstream,'get')
+        reg_request["from"]=current_jid.full()
         reg_request["to"] = to_jid.userhost()
         query=reg_request.addElement('vCard', NS_VCARD)
         reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err)
@@ -201,7 +199,7 @@
             return ""
         return filename
 
-    def getProfileCache(self, target):
+    def getCardCache(self, target):
         """Request for cached values of profile 
         return the cached nickname and avatar if exists, else get VCard
         """
@@ -215,6 +213,24 @@
             result['avatar'] = avatar
         return result
 
+
+
+class XEP_0054_handler(XMPPHandler):
+    implements(iwokkel.IDisco)
+   
+    def __init__(self, plugin_parent):
+        self.plugin_parent = plugin_parent
+        self.host = plugin_parent.host
+
+    def connectionInitialized(self):
+        self.xmlstream.addObserver(VCARD_UPDATE, self.update)
+    
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
+        return [disco.DiscoFeature(NS_VCARD)]
+
+    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+        return []
+    
     def update(self, presence):
         """Request for VCard's nickname
         return the cached nickname if exists, else get VCard
@@ -224,8 +240,7 @@
         for elem in x_elem.elements():
             if elem.name == 'photo':
                 hash = str(elem)
-                old_avatar = self.get_cache(to_jid, 'avatar')
+                old_avatar = self.plugin_parent.get_cache(to_jid, 'avatar')
                 if not old_avatar or old_avatar != hash:
                     debug('New avatar found, requesting vcard')
-                    self.getProfile(to_jid.userhost())
-
+                    self.plugin_parent.getCard(to_jid.userhost(), self.parent.profile)
--- a/plugins/plugin_xep_0065.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/plugins/plugin_xep_0065.py	Sat Jan 30 16:17:33 2010 +1100
@@ -85,6 +85,7 @@
 "type": "XEP",
 "protocols": ["XEP-0065"],
 "main": "XEP_0065",
+"handler": "yes",
 "description": """Implementation of SOCKS5 Bytestreams"""
 }
 
@@ -312,7 +313,7 @@
             debug("Saving file in %s.", self.data["dest_path"])
             self.dest_file = open(self.data["dest_path"], 'w')
             self.state = STATE_TARGET_READY
-            self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id)
+            self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id, self.xmlstream)
 
 
         except struct.error, why:
@@ -450,8 +451,7 @@
         debug ("Socks 5 client connection lost (reason: %s)", reason)
 
 
-class XEP_0065(XMPPHandler):
-    implements(iwokkel.IDisco)
+class XEP_0065():
     
     params = """
     <params>
@@ -480,20 +480,12 @@
         info("Launching Socks5 Stream server on port %d", port)
         reactor.listenTCP(port, self.server_factory)
     
+    def getHandler(self):
+        return XEP_0065_handler(self)  
+   
     def getExternalIP(self):
         """Return IP visible from outside, by asking to a website"""
         return getPage("http://www.goffi.org/sat_tools/get_ip.php")
-    
-    def connectionInitialized(self):
-        self.xmlstream.addObserver(BS_REQUEST, self.getFile)
-
-
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
-        return [disco.DiscoFeature(NS_BS)]
-
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
-        return []
-
 
     def setData(self, data, id):
         self.data = data
@@ -507,8 +499,10 @@
         self.server_factory.protocol.filesize = size
         self.server_factory.protocol.transfert_id = id
 
-    def getFile(self, iq):
+    def getFile(self, iq, profile_key='@DEFAULT@'):
         """Get file using byte stream"""
+        client = self.host.getClient(profile_key)
+        assert(client)
         iq.handled = True
         SI_elem = iq.firstChildElement()
         IQ_id = iq['id']
@@ -518,17 +512,18 @@
                 factory = self.client_factory
                 self.server_factory.protocol.mode = "target"
                 factory.protocol.host = self.host #needed for progress CB
+                factory.protocol.xmlstream = client.xmlstream
                 factory.protocol.data = self.data
                 factory.protocol.transfert_id = self.transfert_id
                 factory.protocol.filesize = self.data["size"]
                 factory.protocol.sid = SI_elem['sid']
                 factory.protocol.initiator_jid = element['jid']
-                factory.protocol.target_jid = self.host.me.full()
+                factory.protocol.target_jid = client.jid.full()
                 factory.protocol.IQ_id = IQ_id
                 factory.protocol.activateCB = self.activateStream
                 reactor.connectTCP(element['host'], int(element['port']), factory)
                 
-    def activateStream(self, from_jid, to_jid, sid, IQ_id):
+    def activateStream(self, from_jid, to_jid, sid, IQ_id, xmlstream):
         debug("activating stream")
         result = domish.Element(('', 'iq'))
         result['type'] = 'result'
@@ -539,5 +534,21 @@
         query['sid'] = sid
         streamhost = query.addElement('streamhost-used')
         streamhost['jid'] = to_jid  #FIXME: use real streamhost
-        self.host.xmlstream.send(result)
+        xmlstream.send(result)
 
+class XEP_0065_handler(XMPPHandler):
+    implements(iwokkel.IDisco)
+    
+    def __init__(self, plugin_parent):
+        self.plugin_parent = plugin_parent
+        self.host = plugin_parent.host
+    
+    def connectionInitialized(self):
+        self.xmlstream.addObserver(BS_REQUEST, self.plugin_parent.getFile)
+
+
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
+        return [disco.DiscoFeature(NS_BS)]
+
+    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+        return []
--- a/plugins/plugin_xep_0077.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/plugins/plugin_xep_0077.py	Sat Jan 30 16:17:33 2010 +1100
@@ -20,7 +20,7 @@
 """
 
 from logging import debug, info, error
-from twisted.words.protocols.jabber import client, jid, xmlstream
+from twisted.words.protocols.jabber import client, jid
 from twisted.words.protocols.jabber import error as jab_error
 from twisted.words.protocols.jabber.xmlstream import IQ
 from twisted.internet import reactor
@@ -47,7 +47,7 @@
         info("Plugin XEP_0077 initialization")
         self.host = host
         self.triggers = {}  #used by other protocol (e.g. XEP-0100) to finish registration. key = target_jid
-        host.bridge.addMethod("in_band_register", ".communication", in_sign='s', out_sign='s', method=self.in_band_register)
+        host.bridge.addMethod("in_band_register", ".communication", in_sign='ss', out_sign='s', method=self.in_band_register)
         host.bridge.addMethod("in_band_submit", ".request", in_sign='sa(ss)', out_sign='s', method=self.in_band_submit)
    
     def addTrigger(self, target, cb):
@@ -127,12 +127,16 @@
             deferred.addCallbacks(self.registrationAnswer, self.registrationFailure)
         return id
     
-    def in_band_register(self, target):
+    def in_band_register(self, target, profile_key='@DEFAULT@'):
         """register to a target JID"""
+        current_jid, xmlstream = self.host.getJidNStream(profile_key)
+        if not xmlstream:
+            error ('Asking profile for an non-existant or not connected profile')
+            return ""
         to_jid = jid.JID(target)
         debug("Asking registration for [%s]" % to_jid.full())
-        reg_request=IQ(self.host.xmlstream,'get')
-        reg_request["from"]=self.host.me.full()
+        reg_request=IQ(xmlstream,'get')
+        reg_request["from"]=current_jid.full()
         reg_request["to"] = to_jid.full()
         query=reg_request.addElement('query', NS_REG)
         reg_request.send(to_jid.full()).addCallbacks(self.reg_ok, self.reg_err)
--- a/plugins/plugin_xep_0096.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/plugins/plugin_xep_0096.py	Sat Jan 30 16:17:33 2010 +1100
@@ -22,7 +22,7 @@
 from logging import debug, info, error
 from twisted.words.xish import domish
 from twisted.internet import protocol
-from twisted.words.protocols.jabber import client, jid, xmlstream
+from twisted.words.protocols.jabber import client, jid
 from twisted.words.protocols.jabber import error as jab_error
 import os.path
 from twisted.internet import reactor #FIXME best way ???
@@ -48,29 +48,22 @@
 "protocols": ["XEP-0096"],
 "dependencies": ["XEP_0065"],
 "main": "XEP_0096",
+"handler": "yes",
 "description": """Implementation of SI File Transfert"""
 }
 
-class XEP_0096(XMPPHandler):
-    implements(iwokkel.IDisco)
+class XEP_0096():
 
     def __init__(self, host):
         info("Plugin XEP_0096 initialization")
         self.host = host
         self._waiting_for_approval = {}
-        host.bridge.addMethod("sendFile", ".communication", in_sign='ss', out_sign='s', method=self.sendFile)
+        host.bridge.addMethod("sendFile", ".communication", in_sign='sss', out_sign='s', method=self.sendFile)
     
-    def connectionInitialized(self):
-        self.xmlstream.addObserver(SI_REQUEST, self.xep_96)
-
+    def getHandler(self):
+        return XEP_0096_handler(self)  
 
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
-        return [disco.DiscoFeature(NS_SI)]
-
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
-        return []
-
-    def xep_96(self, IQ):
+    def xep_96(self, IQ, profile):
         info ("XEP-0096 management")
         IQ.handled=True
         SI_elem = IQ.firstChildElement()
@@ -84,7 +77,7 @@
                 file_size = element["size"]
             elif element.name == "feature":
                 from_jid = IQ["from"]
-                self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size)
+                self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size, profile)
                 data={ "filename":filename, "from":from_jid, "size":file_size }
                 self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB)
 
@@ -106,13 +99,15 @@
             error ("Approved unknow id !")
             #TODO: manage this (maybe approved by several frontends)
         else:
-            element, from_id, size = self._waiting_for_approval[id]
+            element, from_id, size, profile = self._waiting_for_approval[id]
             del(self._waiting_for_approval[id])
-            self.negociate(element, id, from_id)
+            self.negociate(element, id, from_id, profile)
 
-    def negociate(self, feat_elem, id, to_jid):
+    def negociate(self, feat_elem, id, to_jid, profile):
         #TODO: put this in a plugin
         #FIXME: over ultra mega ugly, need to be generic
+        client = self.host.getClient(profile)
+        assert(client)
         info ("Feature negociation")
         data = feat_elem.firstChildElement()
         field = data.firstChildElement()
@@ -134,40 +129,41 @@
             field['var'] = 'stream-method'
             value = field.addElement('value')
             value.addContent('http://jabber.org/protocol/bytestreams')
-            self.host.xmlstream.send(result)
+            client.xmlstream.send(result)
     
-    def fileCB(self, answer):
+    def fileCB(self, answer, xmlstream, current_jid):
         if answer['type']=="result":  #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME
             info("SENDING UGLY ANSWER")
-            offer=client.IQ(self.host.xmlstream,'set')
-            offer["from"]=self.host.me.full()
+            offer=client.IQ(xmlstream,'set')
+            offer["from"]=current_jid.full()
             offer["to"]=answer['from']
             query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams')
             query['mode']='tcp'
             streamhost=query.addElement('streamhost')
             streamhost['host']=self.host.memory.getParamA("IP", "File Transfert")
             streamhost['port']=self.host.memory.getParamA("Port", "File Transfert")
-            streamhost['jid']=self.host.me.full()
+            streamhost['jid']=current_jid.full()
             offer.send()
 
-
-
-
-    def sendFile(self, to, filepath):
+    def sendFile(self, to, filepath, profile_key='@DEFAULT@'):
         """send a file using XEP-0096
         Return an unique id to identify the transfert
         """
+        current_jid, xmlstream = self.host.getJidNStream(profile_key)
+        if not xmlstream:
+            error ('Asking profile for an non-existant or not connected profile')
+            return ""
         debug ("sendfile (%s) to %s", filepath, to )
         print type(filepath), type(to)
         
         statinfo = os.stat(filepath)
 
-        offer=client.IQ(self.host.xmlstream,'set')
+        offer=client.IQ(xmlstream,'set')
         debug ("Transfert ID: %s", offer["id"])
 
         self.host.plugins["XEP_0065"].sendFile(offer["id"], filepath, str(statinfo.st_size))
 
-        offer["from"]=self.host.me.full()
+        offer["from"]=current_jid.full()
         offer["to"]=jid.JID(to).full()
         si=offer.addElement('si','http://jabber.org/protocol/si')
         si["mime-type"]='text/plain'
@@ -190,7 +186,23 @@
         option = field.addElement('option')
         value = option.addElement('value', content='http://jabber.org/protocol/bytestreams')
 
-        offer.addCallback(self.fileCB)
+        offer.addCallback(self.fileCB, current_jid = current_jid, xmlstream = xmlstream)
         offer.send()
         return offer["id"]  #XXX: using IQ id as file transfert id seems OK as IQ id are required
 
+class XEP_0096_handler(XMPPHandler):
+    implements(iwokkel.IDisco)
+    
+    def __init__(self, plugin_parent):
+        self.plugin_parent = plugin_parent
+        self.host = plugin_parent.host
+
+    def connectionInitialized(self):
+        self.xmlstream.addObserver(SI_REQUEST, self.plugin_parent.xep_96, profile = self.parent.profile)
+
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
+        return [disco.DiscoFeature(NS_SI)]
+
+    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+        return []
+
--- a/plugins/plugin_xep_0100.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/plugins/plugin_xep_0100.py	Sat Jan 30 16:17:33 2010 +1100
@@ -21,7 +21,7 @@
 
 from logging import debug, info, error
 from twisted.internet import protocol
-from twisted.words.protocols.jabber import client, jid, xmlstream
+from twisted.words.protocols.jabber import client, jid
 from twisted.words.protocols.jabber import error as jab_error
 import pdb
 
@@ -76,7 +76,7 @@
         self.__inc_handled_items(request_id)
         
     
-    def discoItems(self, disco, request_id, target):
+    def discoItems(self, disco, request_id, target, client):
         """Look for items with disco protocol, and ask infos for each one"""
         #FIXME: target is used as we can't find the original iq node (parent is None)
         #       an other way would avoid this useless parameter (is there a way with wokkel ?)
@@ -88,8 +88,8 @@
         self.__gateways[request_id] = {'__total_items':len(disco._items), '__handled_items':0, '__private__':{'target':target.full()}}
         for item in disco._items:
             debug ("item found: %s", item.name)
-            self.host.disco.requestInfo(item.entity).addCallback(self.discoInfo, entity=item.entity, request_id=request_id)
-            self.host.disco.requestInfo(item.entity).addErrback(self.discoInfoErr, entity=item.entity, request_id=request_id)
+            client.disco.requestInfo(item.entity).addCallback(self.discoInfo, entity=item.entity, request_id=request_id)
+            client.disco.requestInfo(item.entity).addErrback(self.discoInfoErr, entity=item.entity, request_id=request_id)
 
     def registrationSuccessful(self, target):
         """Called when in_band registration is ok, we must now follow the rest of procedure"""
@@ -103,12 +103,14 @@
             self.host.plugins["XEP_0077"].addTrigger(target, self.registrationSuccessful)
         return self.host.plugins["XEP_0077"].in_band_submit(action, target, fields)
 
-    def findGateways(self, target):
+    def findGateways(self, target, profile_key='@DEFAULT@'):
         """Find gateways in the target JID, using discovery protocol
         Return an id used for retrieving the list of gateways
         """
+        client = self.host.getClient(profile_key)
+        assert(client)
         to_jid = jid.JID(target)
         debug ("find gateways (target = %s)" % to_jid.full())
         request_id = self.host.get_next_id()
-        self.host.disco.requestItems(to_jid).addCallback(self.discoItems, request_id=request_id, target = to_jid)
+        client.disco.requestItems(to_jid).addCallback(self.discoItems, request_id=request_id, target = to_jid, client = client)
         return request_id
--- a/sat.tac	Fri Jan 29 14:17:15 2010 +1100
+++ b/sat.tac	Sat Jan 30 16:17:33 2010 +1100
@@ -70,12 +70,12 @@
 
 class SatXMPPClient(client.XMPPClient):
     
-    def __init__(self, bridge, profile, user_jid, password, host=None, port=5222):
+    def __init__(self, host_app, profile, user_jid, password, host=None, port=5222):
         client.XMPPClient.__init__(self, user_jid, password, host, port)
         self.factory.clientConnectionLost = self.connectionLost
         self.__connected=False
-        self.bridge = bridge
         self.profile = profile
+        self.host_app = host_app
 
     def _authd(self, xmlstream):
         print "SatXMPPClient"
@@ -83,7 +83,7 @@
         self.__connected=True
         print "********** CONNECTED **********"
         self.streamInitialized()
-        self.bridge.connected() #we send the signal to the clients
+        self.host_app.bridge.connected() #we send the signal to the clients
 
     def streamInitialized(self):
         """Called after _authd"""
@@ -99,9 +99,8 @@
         self.roster.requestRoster()
         
         self.presence.available()
-        
-        #self.disco.requestInfo(jid.JID(self.memory.getParamA("Server", "Connection"))).addCallback(self.host.serverDisco)  #gof: FIXME
- 
+       
+        self.disco.requestInfo(jid.JID(self.host_app.memory.getParamA("Server", "Connection", profile_key=self.profile))).addCallback(self.host_app.serverDisco)  #FIXME: use these informations
 
     def isConnected(self):
         return self.__connected
@@ -113,7 +112,7 @@
             self.keep_alife.stop()
         except AttributeError:
             debug("No keep_alife")
-        self.bridge.disconnected() #we send the signal to the clients
+        self.host_app.bridge.disconnected() #we send the signal to the clients
 
 
 class SatMessageProtocol(xmppim.MessageProtocol):
@@ -351,14 +350,14 @@
         self.bridge.register("confirmationAnswer", self.confirmationAnswer)
         self.bridge.register("getProgress", self.getProgress)
 
-        #self._import_plugins() #gof:
+        self._import_plugins()
 
 
     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)
@@ -366,6 +365,10 @@
             plug_info = mod.PLUGIN_INFO
             info ("importing plugin: %s", plug_info['name'])
             self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self)
+            if plug_info.has_key('handler') and plug_info['handler'] == 'yes':
+                self.plugins[plug_info['import_name']].is_handler = True
+            else:
+                self.plugins[plug_info['import_name']].is_handler = False
             #TODO: test xmppclient presence and register handler parent
 
     def connect(self, profile_key = '@DEFAULT@'):
@@ -381,12 +384,10 @@
             info("already connected !")
             return
         print "connecting..."
-        current = self.profiles[profile] = SatXMPPClient(self.bridge, profile,
-            jid.JID(self.memory.getParamA("JabberID", "Connection"), profile),
-            self.memory.getParamA("Password", "Connection"),
-            self.memory.getParamA("Server", "Connection"), 5222)
-
-        #current.client.streamInitialized = self.streamInitialized   #gof:
+        current = self.profiles[profile] = SatXMPPClient(self, profile,
+            jid.JID(self.memory.getParamA("JabberID", "Connection", profile_key = profile_key), profile),
+            self.memory.getParamA("Password", "Connection", profile_key = profile_key),
+            self.memory.getParamA("Server", "Connection", profile_key = profile_key), 5222)
 
         current.messageProt = SatMessageProtocol(self)
         current.messageProt.setHandlerParent(current)
@@ -408,8 +409,8 @@
         
         #FIXME: gof
         for plugin in self.plugins.iteritems():
-            if isinstance(plugin[1], XMPPHandler):
-                plugin[1].setHandlerParent(current)
+            if plugin[1].is_handler:
+                plugin[1].getHandler().setHandlerParent(current)
 
         current.startService()
     
@@ -440,6 +441,22 @@
         
     ## Misc methods ##
    
+    def getJidNStream(self, profile_key):
+        """Convenient method to get jid and stream from profile key
+        @return: tuple (jid, xmlstream) from profile, can be None"""
+        profile = self.memory.getProfileName(profile_key)
+        if not profile or not self.profiles[profile].isConnected():
+            return (None, None)
+        return (self.profiles[profile].jid, self.profiles[profile].xmlstream)
+
+    def getClient(self, profile_key):
+        """Convenient method to get client from profile key
+        @return: client or None if it doesn't exist"""
+        profile = self.memory.getProfileName(profile_key)
+        if not profile:
+            return None
+        return self.profiles[profile]
+
     def registerNewAccount(self, login, password, server, port = 5222, id = None):
         """Connect to a server and create a new account using in-band registration"""
 
@@ -451,6 +468,7 @@
         return next_id 
 
     def registerNewAccountCB(self, id, data):
+        #FIXME: gof: profile not managed here !
         user = jid.parse(self.memory.getParamA("JabberID", "Connection"))[0]
         password = self.memory.getParamA("Password", "Connection")
         server = self.memory.getParamA("Server", "Connection")
@@ -472,6 +490,7 @@
         print "data=",data
 
     def regisConfirmCB(self, id, accepted, data):
+        #FIXME: gof: profile not managed here !
         print "register Confirmation CB ! (%s)" % str(accepted)
         action_id = self.__private_data[id]
         del self.__private_data[id]
--- a/tools/memory.py	Fri Jan 29 14:17:15 2010 +1100
+++ b/tools/memory.py	Sat Jan 30 16:17:33 2010 +1100
@@ -224,7 +224,10 @@
             error('Requesting a param for an non-existant profile')
             return None
 
-        return self.__getParam(profile, category, name) or node[1].getAttribute(attr)
+        if attr == "value": 
+            return self.__getParam(profile, category, name) or node[1].getAttribute(attr)
+        else:
+            return node[1].getAttribute(attr)
 
 
     def __getParam(self, profile, category, name, type='individual'):
@@ -550,8 +553,8 @@
         debug ("Memory getPresenceStatus (%s)", self.presenceStatus)
         return self.presenceStatus
 
-    def getParamA(self, name, category, attr="value"):
-        return self.params.getParamA(name, category, attr)
+    def getParamA(self, name, category, attr="value", profile_key="@DEFAULT@"):
+        return self.params.getParamA(name, category, attr, profile_key)
     
     def getParams(self):
         return self.params.getParams()