# HG changeset patch # User Goffi # Date 1260800852 -39600 # Node ID a61beb21d16dd86f0cf540ebb3706856405c0555 # Parent 6491b7956c807b11af4ded0b0f0674315e04b3e1 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 diff -r 6491b7956c80 -r a61beb21d16d frontends/sat_bridge_frontend/DBus.py --- 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) diff -r 6491b7956c80 -r a61beb21d16d frontends/wix/form.py --- 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() diff -r 6491b7956c80 -r a61beb21d16d frontends/wix/gateways.py --- 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() diff -r 6491b7956c80 -r a61beb21d16d plugins/plugin_xep_0077.py --- 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) diff -r 6491b7956c80 -r a61beb21d16d plugins/plugin_xep_0100.py --- 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 diff -r 6491b7956c80 -r a61beb21d16d sat.tac --- 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") diff -r 6491b7956c80 -r a61beb21d16d sat_bridge/DBus.py --- 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 diff -r 6491b7956c80 -r a61beb21d16d tools/xml_tools.py --- 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 + + +