diff sat.tac @ 13:bd9e9997d540

wokkel integration (not finished yet)
author Goffi <goffi@goffi.org>
date Fri, 30 Oct 2009 17:38:27 +0100
parents ef8060d365cb
children a62d7d453f22
line wrap: on
line diff
--- a/sat.tac	Wed Oct 28 00:39:29 2009 +0100
+++ b/sat.tac	Fri Oct 30 17:38:27 2009 +0100
@@ -24,12 +24,14 @@
 from twisted.internet import glib2reactor, protocol, task
 glib2reactor.install()
 
-from twisted.words.protocols.jabber import client, jid, xmlstream, error
+from twisted.words.protocols.jabber import jid, xmlstream, error
 from twisted.words.xish import domish
 
 from twisted.internet import reactor
 import pdb
 
+from wokkel import client, disco, xmppim
+
 from sat_bridge.DBus import DBusBridge
 import logging
 from logging import debug, info, error
@@ -47,15 +49,132 @@
 ###
 
 
+class SatXMPPClient(client.XMPPClient):
+    
+    def __init__(self, jid, password, host=None, port=5222):
+        client.XMPPClient.__init__(self, jid, password, host, port)
+        self.factory.clientConnectionLost = self.connectionLost
+        self.__connected=False
+
+    def _authd(self, xmlstream):
+        print "SatXMPPClient"
+        client.XMPPClient._authd(self, xmlstream)
+        self.__connected=True
+        print "********** CONNECTED **********"
+        self.streamInitialized()
+
+    def streamInitialized(self):
+        """Called after _authd"""
+        self.keep_alife = task.LoopingCall(self.xmlstream.send, " ")  #Needed to avoid disconnection (specially with openfire)
+        self.keep_alife.start(180)
+
+    def isConnected(self):
+        return self.__connected
+    
+    def connectionLost(self, connector, unused_reason):
+        print "********** DISCONNECTED **********"
+        try:
+            self.keep_alife.stop()
+        except AttributeError:
+            debug("No keep_alife")
 
 
-class SAT:
+class SatMessageProtocol(xmppim.MessageProtocol):
+    
+    def __init__(self, host):
+        xmppim.MessageProtocol.__init__(self)
+        self.host = host
+
+    def onMessage(self, message):
+      debug (u"got_message from: %s", message["from"])
+      for e in message.elements():
+        if e.name == "body":
+          self.host.bridge.newMessage(message["from"], e.children[0])
+          self.host.memory.addToHistory(self.host.me, jid.JID(message["from"]), self.host.me, "chat", e.children[0])
+          break
+    
+class SatRosterProtocol(xmppim.RosterClientProtocol):
+
+    def __init__(self, host):
+        xmppim.RosterClientProtocol.__init__(self)
+        self.host = host
+    
+    def rosterCb(self, roster):
+        for jid, item in roster.iteritems():
+            info ("new contact in roster list: %s", jid)
+            #FIXME: fill attributes
+            self.host.memory.addContact(jid, {}, item.groups)
+            self.host.bridge.newContact(jid, {}, item.groups)
+
+    def requestRoster(self):
+        """ ask the server for Roster list """
+        debug("requestRoster")
+        self.getRoster().addCallback(self.rosterCb)
+
+    def removeItem(self, to):
+        """Remove a contact from roster list"""
+        to_jid=jid.JID(to)
+        xmppim.RosterClientProtocol.removeItem(self, to_jid)
+        #TODO: check IQ result
+    
+    def addItem(self, to):
+        """Add a contact to roster list"""
+        to_jid=jid.JID(to)
+        xmppim.RosterClientProtocol.addItem(self, to_jid)
+        #TODO: check IQ result
+
+class SatPresenceProtocol(xmppim.PresenceClientProtocol):
+
+    def __init__(self, host):
+        xmppim.PresenceClientProtocol.__init__(self)
+        self.host = host
+    
+    def availableReceived(self, entity, show=None, statuses=None, priority=0):
+        info ("presence update for [%s]", entity)
+
+        ### we check if the status is not about subscription ###
+        #FIXME: type is not needed anymore
+        #TODO: management of differents statuses (differents languages)
+        status = statuses.values()[0] if len(statuses) else ""
+        self.host.memory.addPresenceStatus(entity.full(), "", show or "",
+                status or "", int(priority))
+
+        #now it's time to notify frontends
+        pdb.set_trace()
+        self.host.bridge.presenceUpdate(entity.full(), "", show or "",
+                status or "", int(priority))
+    
+    def unavailableReceived(self, entity, statuses=None):
+        #TODO: management of differents statuses (differents languages)
+        status = statuses.values()[0] if len(statuses) else ""
+        self.host.memory.addPresenceStatus(entity.full(), "unavailable", "",
+                status or "", 0)
+
+        #now it's time to notify frontends
+        self.host.bridge.presenceUpdate(entity.full(), "unavailable", "",
+                status or "", 0)
+        
+
+    def subscribedReceived(self, entity):
+        debug ("subscription approved for [%s]" % entity)
+
+    def unsubscribedReceived(self, entity):
+        debug ("unsubscription confirmed for [%s]" % entity)
+
+    def subscribeReceived(self, entity):
+        #FIXME: auto answer for subscribe request, must be checked !
+        debug ("subscription request for [%s]" % entity)
+        self.subscribed(entity)
+
+    def unsubscribeReceived(self, entity):
+        debug ("unsubscription asked for [%s]" % entity)
+
+class SAT(service.Service):
     
     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
@@ -98,33 +217,39 @@
             self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self)
 
     def connect(self):
-        if (self.connected):
+        if (self.isConnected()):
             info("already connected !")
             return
         print "connecting..."
-        reactor.connectTCP(self.memory.getParamV("Server", "Connection"), 5222, self.factory)
+        self.me = jid.JID(self.memory.getParamV("JabberID", "Connection"))
+        self.xmppclient = SatXMPPClient(self.me, self.memory.getParamV("Password", "Connection"),
+            self.memory.getParamV("Server", "Connection"), 5222)
+        self.xmppclient.streamInitialized = self.streamInitialized
+
+        self.messageProt = SatMessageProtocol(self)
+        self.messageProt.setHandlerParent(self.xmppclient)
+        
+        self.roster = SatRosterProtocol(self)
+        self.roster.setHandlerParent(self.xmppclient)
+
+        self.presence = SatPresenceProtocol(self)
+        self.presence.setHandlerParent(self.xmppclient)
+
+        self.xmppclient.startService()
     
     def disconnect(self):
-        if (not self.connected):
+        if (not self.isConnected()):
             info("not connected !")
             return
         info("Disconnecting...")
-        self.factory.stopTrying()
-        if self.xmlstream:
-            self.xmlstream.sendFooter()
+        self.xmppclient.stopService()
 
-    def getService(self):
-        print "GetService !"
-        """if (self.connected):
-            info("already connected !")
-            return"""
-        info("Getting service...")
-        self.me = jid.JID(self.memory.getParamV("JabberID", "Connection"))
-        self.factory = client.XMPPClientFactory(self.me, self.memory.getParamV("Password", "Connection"))
-        self.factory.clientConnectionLost = self.connectionLost
-        self.factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,self.authd)
-        self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT,self.failed)
-        return internet.TCPClient(self.memory.getParamV("Server", "Connection"), 5222, self.factory)
+    def startService(self):
+        info("Salut à toi ô mon frère !")
+        self.connect()
+    
+    def stopService(self):
+        info("Salut aussi à Rantanplan")
 
     def run(self):
         debug("running app")
@@ -134,38 +259,30 @@
         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"))
-
+    def streamInitialized(self):
+        """Called when xmlstream is OK"""
+        SatXMPPClient.streamInitialized(self.xmppclient)
+        debug ("XML stream is initialized")
+        self.xmlstream = self.xmppclient.xmlstream
+        self.me = self.xmppclient.jid #in case of the ressource has changed
+        
+        self.roster.requestRoster()
+        
+        self.presence.available()
+        
+        #FIXME:tmp
+        self.xmlstream.addObserver("/iq[@type='set' or @type='get']", self.iqCb)
+        """
         ###FIXME: tmp disco ###
+        #self.discoHandler = disco.discoHandler()
         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)
-        print "********** CONNECTED **********"
-        self.connected=True
-        self.keep_alife = task.LoopingCall(self.xmlstream.send, " ")  #Needed to avoid disconnection (specially with openfire)
-        self.keep_alife.start(180)
 
         #reactor.callLater(2,self.sendFile,"goffi2@jabber.goffi.int/Psi", "/tmp/fakefile")
-
-    def connectionLost(self, connector, unused_reason):
-        print "********** DISCONNECTED **********"
-        if self.keep_alife:
-            self.keep_alife.stop()
-        self.connected=False      
+        """
         
 
     def sendMessage(self,to,msg,type='chat'):
@@ -186,86 +303,64 @@
         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
+        try:
+            if self.xmppclient.isConnected():
+                return True
+        except AttributeError:
+            #xmppclient not available
+            pass
+        return False
 
     ## jabber methods ##
 
     def disco (self, item, callback, node=None):
         """XEP-0030 Service discovery Feature."""
-        disco=client.IQ(self.xmlstream,'get')
+        """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()
+        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
+        to_jid=jid.JID(to)
+        #TODO: refactor subscription bridge API
+        if type=="":
+            self.presence.available(to_jid, show, status, priority)
+        elif type=="subscribe":
+            self.presence.subscribe(to_jid)
+        elif type=="subscribed":
+            self.presence.subscribed(to_jid)
+        elif type=="unsubscribe":
+            self.presence.unsubscribe(to_jid)
+        elif type=="unsubscribed":
+            self.presence.unsubscribed(to_jid)
 
-        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.roster.addItem(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.roster.removeItem(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 ##
 
@@ -286,52 +381,7 @@
             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."""
@@ -390,8 +440,7 @@
 
 
 application = service.Application('SàT')
-sat = SAT()
-service = sat.getService()
+service = SAT()
 service.setServiceParent(application)