changeset 37:a61beb21d16d

Gateway registration, unregistration & edition - default values added in form - DBus bridge: fixed array of struct management when adding dynamically a method - fixed doc for some methods - ugly fix for presence status - added dependency for XEP-0077 in XEP-0100 plugin - Wix: added unregister button in gateways manager - Wix: added privacy warning in gateways manager
author Goffi <goffi@goffi.org>
date Tue, 15 Dec 2009 01:27:32 +1100
parents 6491b7956c80
children 3e24753b9e0b
files frontends/sat_bridge_frontend/DBus.py frontends/wix/form.py frontends/wix/gateways.py plugins/plugin_xep_0077.py plugins/plugin_xep_0100.py sat.tac sat_bridge/DBus.py tools/xml_tools.py
diffstat 8 files changed, 159 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/sat_bridge_frontend/DBus.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/frontends/sat_bridge_frontend/DBus.py	Tue Dec 15 01:27:32 2009 +1100
@@ -100,8 +100,10 @@
     def confirmationAnswer(self, id, accepted, data):
         return self.db_req_iface.confirmationAnswer(id, accepted, data)
 
-    def submitForm(self, target, data):
-        return self.db_req_iface.submitForm(target, data)
+    def gatewayRegister(self, action, target, data):
+        if data == None:
+            data = [('', '')] #XXX: we have to this awful hack because python dbus need to guess the signature
+        return self.db_req_iface.gatewayRegister(action, target, data)
 
     def getProgress(self, id):
         return self.db_req_iface.getProgress(id)
--- a/frontends/wix/form.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/frontends/wix/form.py	Tue Dec 15 01:27:32 2009 +1100
@@ -103,23 +103,21 @@
         for ctrl in self.ctl_list:
             data.append((ctrl["name"], ctrl["control"].GetValue()))
         print "submitting:",data
-        id = self.host.bridge.submitForm(self.target, data)
+        id = self.host.bridge.gatewayRegister("SUBMIT",self.target, data)
         self.host.current_action_ids.add(id)
         print "action id:",id
-        event.Skip()
+        self.MakeModal(False)
+        self.Destroy()
         
     def onFormCancelled(self, event):
         """Called when cancel button is clicked"""
         debug("Cancelling form")
-        #id = self.host.bridge.submitForm("button", data)
-        #self.host.current_action_ids.add(id)
-        #print "action id:",id
-        event.Skip()
+        self.MakeModal(False)
+        self.Close()
    
     def onClose(self, event):
         """Close event: we have to send the form."""
         debug("close")
-
         self.MakeModal(False)
         event.Skip()
 
--- a/frontends/wix/gateways.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/frontends/wix/gateways.py	Tue Dec 15 01:27:32 2009 +1100
@@ -27,6 +27,8 @@
 from logging import debug, info, error
 from tools.jid  import JID
 
+WARNING_MSG = u"""Be careful ! Gateways allow you to use an external IM (legacy IM), so you can see your contact as jabber contacts.
+But when you do this, all your messages go throught the external legacy IM server, it is a huge privacy issue (i.e.: all your messages throught the gateway can be monitored, recorded, analyzed by the external server, most of time a private company)."""
 
 class GatewaysManager(wx.Frame):
     def __init__(self, host, gateways, title="Gateways manager"):
@@ -44,8 +46,14 @@
         self.ctl_list = {}  # usefull to access ctrl, key = (name, category)
 
         self.sizer = wx.BoxSizer(wx.VERTICAL)
+        warning = wx.TextCtrl(self, -1, value=WARNING_MSG, style = wx.TE_MULTILINE |
+                                                                       wx.TE_READONLY |
+                                                                       wx.TE_LEFT)
+        warning.SetFont(self.bold_font)
+        self.sizer.Add(warning, 0, wx.EXPAND)
+        warning.ShowPosition(0)
         self.panel = wx.Panel(self)
-        self.panel.sizer = wx.FlexGridSizer(cols=4)
+        self.panel.sizer = wx.FlexGridSizer(cols=5)
         self.panel.SetSizer(self.panel.sizer)
         self.panel.SetAutoLayout(True)
         self.sizer.Add(self.panel, 1, flag=wx.EXPAND)
@@ -64,12 +72,14 @@
         self.panel.sizer.Add(title_name)
         self.panel.sizer.Add(title_type)
         self.panel.sizer.Add(wx.Window(self.panel, -1))
+        self.panel.sizer.Add(wx.Window(self.panel, -1))
     
         for gateway in gateways:
             self.addGateway(gateway, gateways[gateway])
 
         
-        self.panel.sizer.Fit(self)
+        #self.panel.sizer.Fit(self)
+        self.sizer.Fit(self)
         
         self.Show()
 
@@ -121,24 +131,40 @@
             id = self.host.bridge.in_band_register(gateway_jid)
             self.host.current_action_ids.add(id)
             print "register id:",id
+            self.MakeModal(False) 
+            self.Destroy()
             event.Skip()
 
+        def unregister_cb(event):
+            """Called when unregister button is clicked"""
+            gateway_jid = event.GetEventObject().gateway_jid
+            id = self.host.bridge.gatewayRegister("CANCEL",gateway_jid, None)
+            self.host.current_action_ids.add(id)
+            print "unregister id:",id
+            self.MakeModal(False) 
+            self.Destroy()
+            event.Skip()
+        
         button_font = wx.Font(6, wx.DEFAULT, wx.NORMAL, wx.BOLD)
         reg_button = wx.Button(self.panel, -1, "Register", size=wx.Size(-1, 8))
         reg_button.SetFont(button_font)
         reg_button.gateway_jid = JID(gateway)
         self.panel.Bind(wx.EVT_BUTTON, register_cb, reg_button)
-
+        unreg_button = wx.Button(self.panel, -1, "Unregister", size=wx.Size(-1, 8))
+        unreg_button.SetFont(button_font)
+        unreg_button.gateway_jid = JID(gateway)
+        self.panel.Bind(wx.EVT_BUTTON, unregister_cb, unreg_button)
+        
         self.panel.sizer.Add(im_icon)
         self.panel.sizer.Add(label)
         self.panel.sizer.Add(type_label)
         self.panel.sizer.Add(reg_button, 1, wx.EXPAND)
+        self.panel.sizer.Add(unreg_button, 1, wx.EXPAND)
 
 
     def onClose(self, event):
         """Close event"""
         debug("close")
-        #now we save the modifier params
         self.MakeModal(False)
         event.Skip()
 
--- a/plugins/plugin_xep_0077.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/plugins/plugin_xep_0077.py	Tue Dec 15 01:27:32 2009 +1100
@@ -40,16 +40,21 @@
 }
 
 class XEP_0077():
-
+ 
     def __init__(self, host):
         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_submit", ".request", in_sign='sa(ss)', out_sign='s', method=self.in_band_submit)
    
+    def addTrigger(self, target, cb):
+        """Add a callback which is called when registration to target is successful"""
+        self.triggers[target] = cb
+    
     def reg_ok(self, answer):
         """Called after the first get IQ"""
         form = data_form.Form.fromElement(answer.firstChildElement().firstChildElement())
-        pdb.set_trace()
         xml_data = XMLTools.dataForm2xml(form)
         self.host.bridge.actionResult("FORM", answer['id'], {"target":answer["from"], "type":"registration", "xml":xml_data})
 
@@ -62,6 +67,53 @@
         answer_type = "ERROR"
         self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data)
    
+    def unregistrationAnswer(self, answer):
+        debug ("registration answer: %s" % answer.toXml())
+        answer_type = "SUCCESS"
+        answer_data={"message":"Your are now unregistred"}
+        self.host.bridge.actionResult(answer_type, answer['id'], answer_data)
+        
+    def unregistrationFailure(self, failure):
+        info ("Unregistration failure: %s" % str(failure.value))
+        answer_type = "ERROR"
+        answer_data = {}
+        answer_data['reason'] = 'unknown'
+        answer_data={"message":"Unregistration failed: %s" % failure.value.condition}
+        self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data)
+    
+    def registrationAnswer(self, answer):
+        debug ("registration answer: %s" % answer.toXml())
+        answer_type = "SUCCESS"
+        answer_data={"message":"Registration successfull"}
+        self.host.bridge.actionResult(answer_type, answer['id'], answer_data)
+        if self.triggers.has_key(answer["from"]):
+            self.triggers[answer["from"]](answer["from"])
+            del self.triggers[answer["from"]]
+        
+    def registrationFailure(self, failure):
+        info ("Registration failure: %s" % str(failure.value))
+        print failure.value.stanza.toXml()
+        answer_type = "ERROR"
+        answer_data = {}
+        if failure.value.condition == 'conflict':
+            answer_data['reason'] = 'conflict'
+            answer_data={"message":"Username already exists, please choose an other one"}
+        else:
+            answer_data['reason'] = 'unknown'
+            answer_data={"message":"Registration failed"}
+        self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data)
+        if self.triggers.has_key(answer["from"]):
+            del self.triggers[answer["from"]]
+
+    def in_band_submit(self, action, target, fields):
+        """Submit a form for registration, using data_form"""
+        id, deferred = self.host.submitForm(action, target, fields)
+        if action == 'CANCEL':
+            deferred.addCallbacks(self.unregistrationAnswer, self.unregistrationFailure)
+        else:    
+            deferred.addCallbacks(self.registrationAnswer, self.registrationFailure)
+        return id
+    
     def in_band_register(self, target):
         """register to a target JID"""
         to_jid = jid.JID(target)
--- a/plugins/plugin_xep_0100.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/plugins/plugin_xep_0100.py	Tue Dec 15 01:27:32 2009 +1100
@@ -30,7 +30,7 @@
 "name": "Gateways Plugin",
 "import_name": "XEP_0100",
 "type": "XEP",
-"dependencies": [],
+"dependencies": ["XEP_0077"],
 "main": "XEP_0100",
 "description": """Implementation of Gateways protocol"""
 }
@@ -42,6 +42,7 @@
         self.host = host
         self.__gateways = {}  #dict used to construct the answer to findGateways. Key = target jid
         host.bridge.addMethod("findGateways", ".communication", in_sign='s', out_sign='s', method=self.findGateways)
+        host.bridge.addMethod("gatewayRegister", ".request", in_sign='ssa(ss)', out_sign='s', method=self.gatewayRegister)
     
     def discoInfo(self, disco, entity, request_id):
         """Find disco infos about entity, to check if it is a gateway"""
@@ -76,6 +77,18 @@
             debug ("item found: %s", item.name)
             self.host.disco.requestInfo(item.entity).addCallback(self.discoInfo, 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"""
+        print "Registration successful, doing the rest"
+        self.host.addContact(target)
+        self.host.setPresence(target)
+    
+    def gatewayRegister(self, action, target, fields):
+        """Register gateway using in-band registration, then log-in to gateway"""
+        if action == 'SUBMIT':
+            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):
         """Find gateways in the target JID, using discovery protocol
         Return an id used for retrieving the list of gateways
--- a/sat.tac	Mon Dec 14 02:11:05 2009 +1100
+++ b/sat.tac	Tue Dec 15 01:27:32 2009 +1100
@@ -42,6 +42,7 @@
 import os.path
 
 from tools.memory import Memory
+from tools.xml_tools import XMLTools 
 from glob import glob
 
 try:
@@ -278,7 +279,6 @@
         self.bridge.register("isConnected", self.isConnected)
         self.bridge.register("launchAction", self.launchAction)
         self.bridge.register("confirmationAnswer", self.confirmationAnswer)
-        self.bridge.register("submitForm", self.submitForm)
         self.bridge.register("getProgress", self.getProgress)
 
         self._import_plugins()
@@ -373,8 +373,8 @@
         self.presence.available()
         
         self.disco.requestInfo(jid.JID(self.memory.getParamA("Server", "Connection"))).addCallback(self.serverDisco)
-    
-   ## Misc methods ##
+ 
+    ## Misc methods ##
    
     def registerNewAccount(self, login, password, server, port = 5222, id = None):
         """Connect to a server and create a new account using in-band registration"""
@@ -419,11 +419,29 @@
         else:
             self.actionResult(action_id, "SUPPRESS", {})
 
-    def submitForm(self, target, fields):
-        """Called when a form is submited"""
+    def submitForm(self, action, target, fields):
+        """submit a form
+        @param target: target jid where we are submitting
+        @param fields: list of tuples (name, value)
+        @return: tuple: (id, deferred)
+        """
         to_jid = jid.JID(target)
-        print "Form submitted !", fields
-        return self.get_next_id()
+        
+        iq = compat.IQ(self.xmlstream, 'set')
+        iq["to"] = target
+        iq["from"] = self.me.full()
+        query = iq.addElement(('jabber:iq:register', 'query'))
+        if action=='SUBMIT':
+            form = XMLTools.tupleList2dataForm(fields)
+            query.addChild(form.toElement())
+        elif action=='CANCEL':
+            query.addElement('remove')
+        else:
+            error ("FIXME FIXME FIXME: Unmanaged action (%s) in submitForm" % action)
+            raise NotImplementedError
+
+        deferred = iq.send(target)
+        return (iq['id'], deferred)
 
     ## Client management ##
 
@@ -448,7 +466,7 @@
     def launchAction(self, type, data):
         """Launch a specific action asked by client
         @param type: action type (button)
-        @data: needed data to launch the action
+        @param data: needed data to launch the action
 
         @return: action id for result, or empty string in case or error
         """
@@ -489,6 +507,7 @@
             #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)
@@ -531,16 +550,16 @@
     def actionResult(self, id, type, data):
         """Send the result of an action
         @param id: same id used with action
-        @type: result type ("PARAM", "SUCCESS", "ERROR", "FORM")
-        @data: dictionary
+        @param type: result type ("PARAM", "SUCCESS", "ERROR", "FORM")
+        @param data: dictionary
         """
         self.bridge.actionResult(type, id, data)
 
     def actionResultExt(self, id, type, data):
         """Send the result of an action, extended version
         @param id: same id used with action
-        @type: result type /!\ only "DICT_DICT" for this method
-        @data: dictionary of dictionaries
+        @param type: result type /!\ only "DICT_DICT" for this method
+        @param data: dictionary of dictionaries
         """
         if type != "DICT_DICT":
             error("type for actionResultExt must be DICT_DICT, fixing it")
@@ -552,9 +571,9 @@
     def askConfirmation(self, id, type, data, cb):
         """Add a confirmation callback
         @param id: id used to get answer
-        @type: confirmation type ("YES/NO", "FILE_TRANSFERT")
-        @data: data (depend of confirmation type)
-        @cb: callback called with the answer
+        @param type: confirmation type ("YES/NO", "FILE_TRANSFERT")
+        @param data: data (depend of confirmation type)
+        @param cb: callback called with the answer
         """
         if self.__waiting_conf.has_key(id):
             error ("Attempt to register two callbacks for the same confirmation")
--- a/sat_bridge/DBus.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/sat_bridge/DBus.py	Tue Dec 15 01:27:32 2009 +1100
@@ -186,11 +186,6 @@
         debug("Answer for confirmation [%s]: %s", id, "Accepted" if accepted else "Refused")
         return self.cb["confirmationAnswer"](id, accepted, data)
     
-    @dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
-                         in_signature='sa(ss)', out_signature='s')
-    def submitForm(self, target, fields):
-        info ("Form submited")
-        return self.cb["submitForm"](target, fields)
 
     @dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
                          in_signature='s', out_signature='a{ss}')
@@ -214,7 +209,7 @@
                     i+=1
                     if i>=len(in_sign):
                         raise Exception  #FIXME: create an exception here (the '}' is not presend)
-                    if in_sign[i] == '}':
+                    if in_sign[i] == '}' or in_sign[i] == ')':
                         break
             i+=1
         return attr_string
--- a/tools/xml_tools.py	Mon Dec 14 02:11:05 2009 +1100
+++ b/tools/xml_tools.py	Tue Dec 15 01:27:32 2009 +1100
@@ -21,6 +21,7 @@
 
 from logging import debug, info, error
 from xml.dom import minidom
+from wokkel import data_form
 import pdb
 
 class XMLTools:
@@ -57,10 +58,24 @@
             elem.setAttribute('name', field.var)
             elem.setAttribute('type', __field_type)
             elem.setAttribute('label', field.label)
-            #text = doc.createTextNode(field.value)
-            #elem.appendChild(text)
+            if field.value:
+                text = doc.createTextNode(field.value)
+                elem.appendChild(text)
             top_element.appendChild(elem)
             
         result = doc.toxml()
         doc.unlink()
         return result
+
+    @staticmethod
+    def tupleList2dataForm(values):
+        """convert a list of tuples (name,value) to a wokkel submit data form"""
+        form = data_form.Form('submit')
+        for value in values:
+            field = data_form.Field(var=value[0], value=value[1])
+            form.addField(field)
+
+        return form
+            
+    
+