diff src/plugins/plugin_xep_0077.py @ 2172:545a1261ac3b

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.
author Goffi <goffi@goffi.org>
date Wed, 08 Mar 2017 20:59:31 +0100
parents 33c8c4973743
children 75002ac33801
line wrap: on
line diff
--- 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