comparison 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
comparison
equal deleted inserted replaced
2171:978011533892 2172:545a1261ac3b
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core import exceptions 22 from sat.core import exceptions
23 from sat.core.log import getLogger 23 from sat.core.log import getLogger
24 log = getLogger(__name__) 24 log = getLogger(__name__)
25 from twisted.words.protocols.jabber import jid 25 from twisted.words.protocols.jabber import jid
26 from twisted.words.protocols.jabber.xmlstream import IQ 26 from twisted.words.protocols.jabber import xmlstream
27 from twisted.internet import defer, reactor
27 from sat.tools import xml_tools 28 from sat.tools import xml_tools
28 29
29 from wokkel import data_form, compat 30 from wokkel import data_form
30 31
31 NS_REG = 'jabber:iq:register' 32 NS_REG = 'jabber:iq:register'
32 33
33 PLUGIN_INFO = { 34 PLUGIN_INFO = {
34 C.PI_NAME: "XEP 0077 Plugin", 35 C.PI_NAME: "XEP 0077 Plugin",
38 C.PI_DEPENDENCIES: [], 39 C.PI_DEPENDENCIES: [],
39 C.PI_MAIN: "XEP_0077", 40 C.PI_MAIN: "XEP_0077",
40 C.PI_DESCRIPTION: _("""Implementation of in-band registration""") 41 C.PI_DESCRIPTION: _("""Implementation of in-band registration""")
41 } 42 }
42 43
44 # FIXME: this implementation is incomplete
45
46 class RegisteringAuthenticator(xmlstream.ConnectAuthenticator):
47 # FIXME: request IQ is not send to check available fields, while XEP recommand to use it
48 # FIXME: doesn't handle data form or oob
49
50 def __init__(self, jid_, password, email=None):
51 xmlstream.ConnectAuthenticator.__init__(self, jid_.host)
52 self.jid = jid_
53 self.password = password
54 self.email = email
55 self.registered = defer.Deferred()
56 log.debug(_(u"Registration asked for {jid}").format(
57 jid = jid_))
58
59 def connectionMade(self):
60 log.debug(_(u"Connection made with {server}".format(server=self.jid.host)))
61 self.xmlstream.otherEntity = jid.JID(self.jid.host)
62 self.xmlstream.namespace = C.NS_CLIENT
63 self.xmlstream.sendHeader()
64
65 iq = xmlstream.IQ(self.xmlstream, 'set')
66 iq["to"] = self.jid.host
67 query_elt = iq.addElement(('jabber:iq:register', 'query'))
68 username_elt = query_elt.addElement('username')
69 username_elt.addContent(self.jid.user)
70 password_elt = query_elt.addElement('password')
71 password_elt.addContent(self.password)
72 if self.email is not None:
73 email_elt = query_elt.addElement('email')
74 email_elt.addContent(self.email)
75 d = iq.send(self.jid.host).addCallbacks(self.registrationCb, self.registrationEb)
76 d.chainDeferred(self.registered)
77
78 def registrationCb(self, answer):
79 log.debug(_(u"Registration answer: {}").format(answer.toXml()))
80 self.xmlstream.sendFooter()
81
82 def registrationEb(self, failure_):
83 log.info(_("Registration failure: {}").format(unicode(failure_.value)))
84 self.xmlstream.sendFooter()
85 raise failure_.value
86
43 87
44 class XEP_0077(object): 88 class XEP_0077(object):
45 89
46 def __init__(self, host): 90 def __init__(self, host):
47 log.info(_("Plugin XEP_0077 initialization")) 91 log.info(_("Plugin XEP_0077 initialization"))
48 self.host = host 92 self.host = host
49 self.triggers = {} # used by other protocol (e.g. XEP-0100) to finish registration. key = target_jid
50 host.bridge.addMethod("inBandRegister", ".plugin", in_sign='ss', out_sign='s', 93 host.bridge.addMethod("inBandRegister", ".plugin", in_sign='ss', out_sign='s',
51 method=self._inBandRegister, 94 method=self._inBandRegister,
52 async=True) 95 async=True)
53 96
54 def _regOk(self, answer, client, post_treat_cb): 97 def _regCb(self, answer, client, post_treat_cb):
55 """Called after the first get IQ""" 98 """Called after the first get IQ"""
56 try: 99 try:
57 query_elt = answer.elements(NS_REG, 'query').next() 100 query_elt = answer.elements(NS_REG, 'query').next()
58 except StopIteration: 101 except StopIteration:
59 raise exceptions.DataError("Can't find expected query element") 102 raise exceptions.DataError("Can't find expected query element")
66 raise exceptions.DataError(_("This gateway can't be managed by SàT, sorry :(")) 109 raise exceptions.DataError(_("This gateway can't be managed by SàT, sorry :("))
67 110
68 def submitForm(data, profile): 111 def submitForm(data, profile):
69 form_elt = xml_tools.XMLUIResultToElt(data) 112 form_elt = xml_tools.XMLUIResultToElt(data)
70 113
71 iq_elt = compat.IQ(client.xmlstream, 'set') 114 iq_elt = client.IQ()
72 iq_elt['id'] = answer['id'] 115 iq_elt['id'] = answer['id']
73 iq_elt['to'] = answer['from'] 116 iq_elt['to'] = answer['from']
74 query_elt = iq_elt.addElement("query", NS_REG) 117 query_elt = iq_elt.addElement("query", NS_REG)
75 query_elt.addChild(form_elt) 118 query_elt.addChild(form_elt)
76 d = iq_elt.send() 119 d = iq_elt.send()
80 123
81 form = data_form.Form.fromElement(x_elem) 124 form = data_form.Form.fromElement(x_elem)
82 submit_reg_id = self.host.registerCallback(submitForm, with_data=True, one_shot=True) 125 submit_reg_id = self.host.registerCallback(submitForm, with_data=True, one_shot=True)
83 return xml_tools.dataForm2XMLUI(form, submit_reg_id) 126 return xml_tools.dataForm2XMLUI(form, submit_reg_id)
84 127
85 def _regErr(self, failure, client): 128 def _regEb(self, failure, client):
86 """Called when something is wrong with registration""" 129 """Called when something is wrong with registration"""
87 log.info(_("Registration failure: %s") % unicode(failure.value)) 130 log.info(_("Registration failure: %s") % unicode(failure.value))
88 raise failure 131 raise failure
89 132
90 def _regSuccess(self, answer, client, post_treat_cb): 133 def _regSuccess(self, answer, client, post_treat_cb):
103 return self.inBandRegister, jid.JID(to_jid_s, profile_key) 146 return self.inBandRegister, jid.JID(to_jid_s, profile_key)
104 147
105 def inBandRegister(self, to_jid, post_treat_cb=None, profile_key=C.PROF_KEY_NONE): 148 def inBandRegister(self, to_jid, post_treat_cb=None, profile_key=C.PROF_KEY_NONE):
106 """register to a target JID""" 149 """register to a target JID"""
107 client = self.host.getClient(profile_key) 150 client = self.host.getClient(profile_key)
108 log.debug(_(u"Asking registration for [%s]") % to_jid.full()) 151 log.debug(_(u"Asking registration for {}").format(to_jid.full()))
109 reg_request = IQ(client.xmlstream, 'get') 152 reg_request = client.IQ(u'get')
110 reg_request["from"] = client.jid.full() 153 reg_request["from"] = client.jid.full()
111 reg_request["to"] = to_jid.full() 154 reg_request["to"] = to_jid.full()
112 reg_request.addElement('query', NS_REG) 155 reg_request.addElement('query', NS_REG)
113 d = reg_request.send(to_jid.full()).addCallbacks(self._regOk, self._regErr, callbackArgs=[client, post_treat_cb], errbackArgs=[client]) 156 d = reg_request.send(to_jid.full()).addCallbacks(self._regCb, self._regEb, callbackArgs=[client, post_treat_cb], errbackArgs=[client])
114 return d 157 return d
158
159 def registerNewAccount(self, client, jid_, password, email=None, host="127.0.0.1", port=C.XMPP_C2S_PORT):
160 """register a new account on a XMPP server
161
162 @param jid_(jid.JID): request jid to register
163 @param password(unicode): password of the account
164 @param email(unicode): email of the account
165 @param host(unicode): host of the server to register to
166 @param port(int): port of the server to register to
167 """
168 authenticator = RegisteringAuthenticator(jid_, password, email)
169 registered_d = authenticator.registered
170 serverRegistrer = xmlstream.XmlStreamFactory(authenticator)
171 connector = reactor.connectTCP(host, port, serverRegistrer)
172 serverRegistrer.clientConnectionLost = lambda conn, reason: connector.disconnect()
173 return registered_d