changeset 50:daa1f01a5332

SàT improvement: - subscription & presence refactoring - multilingual statuses are now correctly sent - registration: error condition now specified in error message
author Goffi <goffi@goffi.org>
date Thu, 07 Jan 2010 00:05:15 +1100
parents 9c79eb49d51f
children 8c67ea98ab91
files sat.tac
diffstat 1 files changed, 77 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/sat.tac	Thu Jan 07 00:00:25 2010 +1100
+++ b/sat.tac	Thu Jan 07 00:05:15 2010 +1100
@@ -70,8 +70,8 @@
 
 class SatXMPPClient(client.XMPPClient):
     
-    def __init__(self, jid, password, host=None, port=5222):
-        client.XMPPClient.__init__(self, jid, password, host, port)
+    def __init__(self, user_jid, password, host=None, port=5222):
+        client.XMPPClient.__init__(self, user_jid, password, host, port)
         self.factory.clientConnectionLost = self.connectionLost
         self.__connected=False
 
@@ -120,11 +120,8 @@
         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)
+        for raw_jid, item in roster.iteritems():
+            self.onRosterSet(item)
 
     def requestRoster(self):
         """ ask the server for Roster list """
@@ -133,16 +130,33 @@
 
     def removeItem(self, to):
         """Remove a contact from roster list"""
-        to_jid=jid.JID(to)
-        xmppim.RosterClientProtocol.removeItem(self, to_jid)
+        xmppim.RosterClientProtocol.removeItem(self, to)
         #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)
+        xmppim.RosterClientProtocol.addItem(self, to)
         #TODO: check IQ result
 
+    def onRosterSet(self, item):
+        """Called when a new/update roster item is received"""
+        #TODO: send a signal to frontends
+        item_attr = {'to': str(item.subscriptionTo),
+                     'from': str(item.subscriptionFrom),
+                     'ask': str(item.ask)
+                     }
+        if item.name:
+            item_attr['name'] = item.name
+        info ("new contact in roster list: %s", item.jid.full())
+        self.host.memory.addContact(item.jid, item_attr, item.groups)
+        self.host.bridge.newContact(item.jid.full(), item_attr, item.groups)
+    
+    def onRosterRemove(self, entity):
+        """Called when a roster removal event is received"""
+        #TODO: send a signal to frontends
+        print "removing %s from roster list" % entity.full()
+        self.host.memory.delContact(entity)
+
 class SatPresenceProtocol(xmppim.PresenceClientProtocol):
 
     def __init__(self, host):
@@ -151,42 +165,53 @@
     
     def availableReceived(self, entity, show=None, statuses=None, priority=0):
         info ("presence update for [%s]", entity)
+        
+        if statuses.has_key(None):   #we only want string keys
+            statuses["default"] = statuses[None]
+            del statuses[None]
 
-        ### 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))
+        self.host.memory.addPresenceStatus(entity, show or "",
+                int(priority), statuses)
 
         #now it's time to notify frontends
-        self.host.bridge.presenceUpdate(entity.full(), "", show or "",
-                status or "", int(priority))
+        self.host.bridge.presenceUpdate(entity.full(),  show or "",
+                int(priority), statuses)
     
     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)
+        if statuses and statuses.has_key(None):   #we only want string keys
+            statuses["default"] = statuses[None]
+            del statuses[None]
+        self.host.memory.addPresenceStatus(entity, "unavailable", 0, statuses)
 
         #now it's time to notify frontends
-        self.host.bridge.presenceUpdate(entity.full(), "unavailable", "",
-                status or "", 0)
+        self.host.bridge.presenceUpdate(entity.full(), "unavailable", 0, statuses)
         
-
+    
+    def available(self, entity=None, show=None, statuses=None, priority=0):
+        if statuses and statuses.has_key('default'):
+            statuses[None] = statuses['default']
+            del statuses['default']
+        xmppim.PresenceClientProtocol.available(self, entity, show, statuses, priority)
+    
     def subscribedReceived(self, entity):
-        debug ("subscription approved for [%s]" % entity)
+        debug ("subscription approved for [%s]" % entity.userhost())
+        self.host.memory.delWaitingSub(entity.userhost())
+        self.host.bridge.subscribe('subscribed', entity.userhost())
 
     def unsubscribedReceived(self, entity):
-        debug ("unsubscription confirmed for [%s]" % entity)
+        debug ("unsubscription confirmed for [%s]" % entity.userhost())
+        self.host.memory.delWaitingSub(entity.userhost())
+        self.host.bridge.subscribe('unsubscribed', entity.userhost())
 
     def subscribeReceived(self, entity):
-        #FIXME: auto answer for subscribe request, must be checked !
-        debug ("subscription request for [%s]" % entity)
-        self.subscribed(entity)
+        debug ("subscription request for [%s]" % entity.userhost())
+        self.host.memory.addWaitingSub('subscribe', entity.userhost())
+        self.host.bridge.subscribe('subscribe', entity.userhost())
 
     def unsubscribeReceived(self, entity):
-        debug ("unsubscription asked for [%s]" % entity)
+        debug ("unsubscription asked for [%s]" % entity.userhost())
+        self.host.memory.addWaitingSub('unsubscribe', entity.userhost())
+        self.host.bridge.subscribe('unsubscribe', entity.userhost())
 
 class SatDiscoProtocol(disco.DiscoClientProtocol):
     def __init__(self, host):
@@ -243,7 +268,7 @@
             answer_data={"message":"Username already exists, please choose an other one"}
         else:
             answer_data['reason'] = 'unknown'
-            answer_data={"message":"Registration failed"}
+            answer_data={"message":"Registration failed (%s)" % str(failure.value.condition)}
         self.host.bridge.actionResult(answer_type, self.answer_id, answer_data)
         self.xmlstream.sendFooter()
         
@@ -289,6 +314,7 @@
         self.bridge.register("disconnect", self.disconnect)
         self.bridge.register("getContacts", self.memory.getContacts)
         self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus)
+        self.bridge.register("getWaitingSub", self.memory.getWaitingSub)
         self.bridge.register("sendMessage", self.sendMessage)
         self.bridge.register("setParam", self.setParam)
         self.bridge.register("getParamA", self.memory.getParamA)
@@ -297,6 +323,7 @@
         self.bridge.register("getParamsCategories", self.memory.getParamsCategories)
         self.bridge.register("getHistory", self.memory.getHistory)
         self.bridge.register("setPresence", self.setPresence)
+        self.bridge.register("subscription", self.subscription)
         self.bridge.register("addContact", self.addContact)
         self.bridge.register("delContact", self.delContact)
         self.bridge.register("isConnected", self.isConnected)
@@ -523,22 +550,23 @@
         self.bridge.newMessage(message['from'], unicode(msg), to=message['to']) #We send back the message, so all clients are aware of it
 
 
-    def setPresence(self, to="", type="", show="", status="", priority=0):
+    def setPresence(self, to="", show="", priority = 0, statuses={}):
         """Send our presence information"""
-        if not type in ["", "unavailable", "subscribed", "subscribe",
-                        "unsubscribe", "unsubscribed", "prob", "error"]:
-            error("Type error !")
-            #TODO: throw an error
-            return
-        to_jid=jid.JID(to)
-        status = None if not status else {None:status}  #FIXME: use the proper way here (change signature to use dict)
-        #TODO: refactor subscription bridge API
-        if type=="":
-            self.presence.available(to_jid, show, status, priority)
-        elif type=="subscribe":
+        to_jid = jid.JID(to) if to else None
+        self.presence.available(to_jid, show, statuses, priority)
+    
+    def subscription(self, type, raw_jid):
+        """Called to manage subscription"""
+        to_jid = jid.JID(raw_jid)
+        debug ('subsciption request [%s] for %s', type, to_jid.full())
+        if type=="subscribe":
             self.presence.subscribe(to_jid)
         elif type=="subscribed":
             self.presence.subscribed(to_jid)
+            contact = self.memory.getContact(to_jid) 
+            if not contact or not bool(contact['to']): #we automatically subscribe to 'to' presence
+                debug('sending automatic "to" subscription request')
+                self.subscription('subscribe', to_jid.userhost())
         elif type=="unsubscribe":
             self.presence.unsubscribe(to_jid)
         elif type=="unsubscribed":
@@ -548,13 +576,14 @@
     def addContact(self, to):
         """Add a contact in roster list"""
         to_jid=jid.JID(to)
-        self.roster.addItem(to_jid.userhost())
-        self.setPresence(to_jid.userhost(), "subscribe")
+        self.roster.addItem(to_jid)
+        self.presence.subscribe(to_jid)
 
     def delContact(self, to):
         """Remove contact from roster list"""
         to_jid=jid.JID(to)
-        self.roster.removeItem(to_jid.userhost())
+        self.roster.removeItem(to_jid)
+        self.presence.unsubscribe(to_jid)
         self.bridge.contactDeleted(to)