# HG changeset patch # User Goffi # Date 1489003171 -3600 # Node ID 545a1261ac3bfb89fbcee2a71a2359e81ca19d0d # Parent 97801153389259723fad5b13692f1ae3d8cf1c4b core, plugin XEP-0077: in-band registration fix and move: in-band was partially in core for historical reason, it has been moved to XEP-0077, and fixed. It is still incomplete, but should work for basic accounts creation. diff -r 978011533892 -r 545a1261ac3b src/core/xmpp.py --- a/src/core/xmpp.py Wed Mar 08 20:44:28 2017 +0100 +++ b/src/core/xmpp.py Wed Mar 08 20:59:31 2017 +0100 @@ -1090,47 +1090,6 @@ generic.FallbackHandler.iqFallback(self, iq) -class RegisteringAuthenticator(xmlstream.ConnectAuthenticator): - - def __init__(self, host, jabber_host, user_login, user_pass, email, deferred, profile): - xmlstream.ConnectAuthenticator.__init__(self, jabber_host) - self.host = host - self.jabber_host = jabber_host - self.user_login = user_login - self.user_pass = user_pass - self.user_email = email - self.deferred = deferred - self.profile = profile - log.debug(_(u"Registration asked for %(user)s@%(host)s") % {'user': user_login, 'host': jabber_host}) - - def connectionMade(self): - log.debug(_(u"Connection made with %s" % self.jabber_host)) - self.xmlstream.namespace = C.NS_CLIENT - self.xmlstream.sendHeader() - - iq = xmlstream.IQ(self.xmlstream, 'set') - iq["to"] = self.jabber_host - query = iq.addElement(('jabber:iq:register', 'query')) - _user = query.addElement('username') - _user.addContent(self.user_login) - _pass = query.addElement('password') - _pass.addContent(self.user_pass) - if self.user_email: - _email = query.addElement('email') - _email.addContent(self.user_email) - d = iq.send(self.jabber_host).addCallbacks(self.registrationAnswer, self.registrationFailure) - d.chainDeferred(self.deferred) - - def registrationAnswer(self, answer): - log.debug(_(u"Registration answer: %s") % answer.toXml()) - self.xmlstream.sendFooter() - - def registrationFailure(self, failure_): - log.info(_("Registration failure: %s") % unicode(failure_.value)) - self.xmlstream.sendFooter() - raise failure_.value - - class SatVersionHandler(generic.VersionHandler): def getDiscoInfo(self, requestor, target, node): diff -r 978011533892 -r 545a1261ac3b src/plugins/plugin_misc_register_account.py --- a/src/plugins/plugin_misc_register_account.py Wed Mar 08 20:44:28 2017 +0100 +++ b/src/plugins/plugin_misc_register_account.py Wed Mar 08 20:59:31 2017 +0100 @@ -22,10 +22,8 @@ from sat.core.log import getLogger log = getLogger(__name__) from sat.core.constants import Const as C -from twisted.words.protocols.jabber import jid, xmlstream -from sat.core import xmpp +from twisted.words.protocols.jabber import jid from sat.memory.memory import Sessions -from twisted.internet import reactor, defer from sat.tools import xml_tools from sat.tools.xml_tools import SAT_FORM_PREFIX, SAT_PARAM_SEPARATOR @@ -35,7 +33,7 @@ C.PI_IMPORT_NAME: "REGISTER-ACCOUNT", C.PI_TYPE: "MISC", C.PI_PROTOCOLS: [], - C.PI_DEPENDENCIES: [], + C.PI_DEPENDENCIES: ["XEP-0077"], C.PI_RECOMMENDATIONS: [], C.PI_MAIN: "RegisterAccount", C.PI_HANDLER: "no", @@ -44,6 +42,7 @@ class RegisterAccount(object): + # FIXME: this plugin is messy and difficult to read, it needs to be cleaned up and documented def __init__(self, host): log.info(_(u"Plugin Register Account initialization")) @@ -53,30 +52,35 @@ self.__register_account_id = host.registerCallback(self._registerConfirmation, with_data=True) def registerNewAccountCB(self, data, profile): - """Called when the use click on the "New account" button.""" + """Called when the user click on the "New account" button.""" session_data = {} - for param in ('JabberID', 'Password', C.FORCE_PORT_PARAM, C.FORCE_SERVER_PARAM): + + # FIXME: following loop is overcomplicated, hard to read + # FIXME: while used with parameters, hashed password is used and overwrite clear one + for param in (u'JabberID', u'Password', C.FORCE_PORT_PARAM, C.FORCE_SERVER_PARAM): try: - session_data[param] = data["%s%s%s%s" % (SAT_FORM_PREFIX, "Connection", SAT_PARAM_SEPARATOR, param)] + session_data[param] = data[SAT_FORM_PREFIX + u"Connection" + SAT_PARAM_SEPARATOR + param] except KeyError: if param in (C.FORCE_PORT_PARAM, C.FORCE_SERVER_PARAM): session_data[param] = '' - for param in ('JabberID', 'Password'): + for param in (u'JabberID', u'Password'): if not session_data[param]: - form_ui = xml_tools.XMLUI("popup", title=D_("Missing values")) - form_ui.addText(D_("No user JID or password given: can't register new account.")) - return {'xmlui': form_ui.toXml()} + form_ui = xml_tools.XMLUI(u"popup", title=D_(u"Missing values")) + form_ui.addText(D_(u"No user JID or password given: can't register new account.")) + return {u'xmlui': form_ui.toXml()} session_data['user'], host, resource = jid.parse(session_data['JabberID']) session_data['server'] = session_data[C.FORCE_SERVER_PARAM] or host session_id, dummy = self._sessions.newSession(session_data, profile=profile) form_ui = xml_tools.XMLUI("form", title=D_("Register new account"), submit_id=self.__register_account_id, session_id=session_id) - form_ui.addText(D_("Do you want to register a new XMPP account [%(user)s] on server %(server)s ?") % {'user': session_data['user'], 'server': session_data['server']}) + form_ui.addText(D_(u"Do you want to register a new XMPP account {jid}?").format( + jid = session_data['JabberID'])) return {'xmlui': form_ui.toXml()} def _registerConfirmation(self, data, profile): """Save the related parameters and proceed the registration.""" + client = self.host.getClient(profile) session_data = self._sessions.profileGet(data['session_id'], profile) self.host.memory.setParam("JabberID", session_data["JabberID"], "Connection", profile_key=profile) @@ -84,32 +88,18 @@ self.host.memory.setParam(C.FORCE_SERVER_PARAM, session_data[C.FORCE_SERVER_PARAM], "Connection", profile_key=profile) self.host.memory.setParam(C.FORCE_PORT_PARAM, session_data[C.FORCE_PORT_PARAM], "Connection", profile_key=profile) - d = self._registerNewAccount(session_data['user'], session_data["Password"], None, session_data['server'], profile_key=profile) + d = self._registerNewAccount(client, jid.JID(session_data['JabberID']), session_data["Password"], None, session_data['server']) del self._sessions[data['session_id']] return d - def _registerNewAccount(self, user, password, email, host, port=C.XMPP_C2S_PORT, profile_key=C.PROF_KEY_NONE): - """Connect to a server and create a new account using in-band registration. - @param user: login of the account - @param password: password of the account - @param email: email of the account - @param host: host of the server to register to - @param port: port of the server to register to - @param profile_key: %(doc_profile_key)s - """ - profile = self.host.memory.getProfileName(profile_key) - - d = defer.Deferred() - serverRegistrer = xmlstream.XmlStreamFactory(xmpp.RegisteringAuthenticator(self, host, user, password, email, d, profile)) - connector = reactor.connectTCP(host, port or C.XMPP_C2S_PORT, serverRegistrer) - serverRegistrer.clientConnectionLost = lambda conn, reason: connector.disconnect() - - def cb(dummy): - xmlui = xml_tools.XMLUI("popup", title=D_("Confirmation")) + def _registerNewAccount(self, client, jid_, password, email, server): + # FIXME: port is not set here + def registeredCb(dummy): + xmlui = xml_tools.XMLUI(u"popup", title=D_(u"Confirmation")) xmlui.addText(D_("Registration successful.")) return ({'xmlui': xmlui.toXml()}) - def eb(failure): + def registeredEb(failure): xmlui = xml_tools.XMLUI("popup", title=D_("Failure")) xmlui.addText(D_("Registration failed: %s") % failure.getErrorMessage()) try: @@ -119,5 +109,6 @@ pass return ({'xmlui': xmlui.toXml()}) - d.addCallbacks(cb, eb) - return d + registered_d = self.host.plugins['XEP-0077'].registerNewAccount(client, jid_, password, email=email, host=server, port=C.XMPP_C2S_PORT) + registered_d.addCallbacks(registeredCb, registeredEb) + return registered_d diff -r 978011533892 -r 545a1261ac3b src/plugins/plugin_xep_0077.py --- a/src/plugins/plugin_xep_0077.py Wed Mar 08 20:44:28 2017 +0100 +++ b/src/plugins/plugin_xep_0077.py Wed Mar 08 20:59:31 2017 +0100 @@ -23,10 +23,11 @@ from sat.core.log import getLogger log = getLogger(__name__) from twisted.words.protocols.jabber import jid -from twisted.words.protocols.jabber.xmlstream import IQ +from twisted.words.protocols.jabber import xmlstream +from twisted.internet import defer, reactor from sat.tools import xml_tools -from wokkel import data_form, compat +from wokkel import data_form NS_REG = 'jabber:iq:register' @@ -40,18 +41,60 @@ C.PI_DESCRIPTION: _("""Implementation of in-band registration""") } +# FIXME: this implementation is incomplete + +class RegisteringAuthenticator(xmlstream.ConnectAuthenticator): + # FIXME: request IQ is not send to check available fields, while XEP recommand to use it + # FIXME: doesn't handle data form or oob + + def __init__(self, jid_, password, email=None): + xmlstream.ConnectAuthenticator.__init__(self, jid_.host) + self.jid = jid_ + self.password = password + self.email = email + self.registered = defer.Deferred() + log.debug(_(u"Registration asked for {jid}").format( + jid = jid_)) + + def connectionMade(self): + log.debug(_(u"Connection made with {server}".format(server=self.jid.host))) + self.xmlstream.otherEntity = jid.JID(self.jid.host) + self.xmlstream.namespace = C.NS_CLIENT + self.xmlstream.sendHeader() + + iq = xmlstream.IQ(self.xmlstream, 'set') + iq["to"] = self.jid.host + query_elt = iq.addElement(('jabber:iq:register', 'query')) + username_elt = query_elt.addElement('username') + username_elt.addContent(self.jid.user) + password_elt = query_elt.addElement('password') + password_elt.addContent(self.password) + if self.email is not None: + email_elt = query_elt.addElement('email') + email_elt.addContent(self.email) + d = iq.send(self.jid.host).addCallbacks(self.registrationCb, self.registrationEb) + d.chainDeferred(self.registered) + + def registrationCb(self, answer): + log.debug(_(u"Registration answer: {}").format(answer.toXml())) + self.xmlstream.sendFooter() + + def registrationEb(self, failure_): + log.info(_("Registration failure: {}").format(unicode(failure_.value))) + self.xmlstream.sendFooter() + raise failure_.value + class XEP_0077(object): def __init__(self, host): log.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("inBandRegister", ".plugin", in_sign='ss', out_sign='s', method=self._inBandRegister, async=True) - def _regOk(self, answer, client, post_treat_cb): + def _regCb(self, answer, client, post_treat_cb): """Called after the first get IQ""" try: query_elt = answer.elements(NS_REG, 'query').next() @@ -68,7 +111,7 @@ def submitForm(data, profile): form_elt = xml_tools.XMLUIResultToElt(data) - iq_elt = compat.IQ(client.xmlstream, 'set') + iq_elt = client.IQ() iq_elt['id'] = answer['id'] iq_elt['to'] = answer['from'] query_elt = iq_elt.addElement("query", NS_REG) @@ -82,7 +125,7 @@ submit_reg_id = self.host.registerCallback(submitForm, with_data=True, one_shot=True) return xml_tools.dataForm2XMLUI(form, submit_reg_id) - def _regErr(self, failure, client): + def _regEb(self, failure, client): """Called when something is wrong with registration""" log.info(_("Registration failure: %s") % unicode(failure.value)) raise failure @@ -105,10 +148,26 @@ def inBandRegister(self, to_jid, post_treat_cb=None, profile_key=C.PROF_KEY_NONE): """register to a target JID""" client = self.host.getClient(profile_key) - log.debug(_(u"Asking registration for [%s]") % to_jid.full()) - reg_request = IQ(client.xmlstream, 'get') + log.debug(_(u"Asking registration for {}").format(to_jid.full())) + reg_request = client.IQ(u'get') reg_request["from"] = client.jid.full() reg_request["to"] = to_jid.full() reg_request.addElement('query', NS_REG) - d = reg_request.send(to_jid.full()).addCallbacks(self._regOk, self._regErr, callbackArgs=[client, post_treat_cb], errbackArgs=[client]) + d = reg_request.send(to_jid.full()).addCallbacks(self._regCb, self._regEb, callbackArgs=[client, post_treat_cb], errbackArgs=[client]) return d + + def registerNewAccount(self, client, jid_, password, email=None, host="127.0.0.1", port=C.XMPP_C2S_PORT): + """register a new account on a XMPP server + + @param jid_(jid.JID): request jid to register + @param password(unicode): password of the account + @param email(unicode): email of the account + @param host(unicode): host of the server to register to + @param port(int): port of the server to register to + """ + authenticator = RegisteringAuthenticator(jid_, password, email) + registered_d = authenticator.registered + serverRegistrer = xmlstream.XmlStreamFactory(authenticator) + connector = reactor.connectTCP(host, port, serverRegistrer) + serverRegistrer.clientConnectionLost = lambda conn, reason: connector.disconnect() + return registered_d