Add basic start for a client side XEP-0077 plugin.
This commit is contained in:
parent
b25668b5b7
commit
02f4006153
5 changed files with 354 additions and 0 deletions
175
examples/register_account.py
Normal file
175
examples/register_account.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import getpass
|
||||
from optparse import OptionParser
|
||||
|
||||
import sleekxmpp
|
||||
from sleekxmpp.exceptions import IqError, IqTimeout
|
||||
|
||||
# Python versions before 3.0 do not use UTF-8 encoding
|
||||
# by default. To ensure that Unicode is handled properly
|
||||
# throughout SleekXMPP, we will set the default encoding
|
||||
# ourselves to UTF-8.
|
||||
if sys.version_info < (3, 0):
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf8')
|
||||
else:
|
||||
raw_input = input
|
||||
|
||||
|
||||
class RegisterBot(sleekxmpp.ClientXMPP):
|
||||
|
||||
"""
|
||||
A basic bot that will attempt to register an account
|
||||
with an XMPP server.
|
||||
|
||||
NOTE: This follows the very basic registration workflow
|
||||
from XEP-0077. More advanced server registration
|
||||
workflows will need to check for data forms, etc.
|
||||
"""
|
||||
|
||||
def __init__(self, jid, password):
|
||||
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||
|
||||
# The session_start event will be triggered when
|
||||
# the bot establishes its connection with the server
|
||||
# and the XML streams are ready for use. We want to
|
||||
# listen for this event so that we we can initialize
|
||||
# our roster.
|
||||
self.add_event_handler("session_start", self.start)
|
||||
|
||||
# The register event provides an Iq result stanza with
|
||||
# a registration form from the server. This may include
|
||||
# the basic registration fields, a data form, an
|
||||
# out-of-band URL, or any combination. For more advanced
|
||||
# cases, you will need to examine the fields provided
|
||||
# and respond accordingly. SleekXMPP provides plugins
|
||||
# for data forms and OOB links that will make that easier.
|
||||
self.add_event_handler("register", self.register)
|
||||
|
||||
def start(self, event):
|
||||
"""
|
||||
Process the session_start event.
|
||||
|
||||
Typical actions for the session_start event are
|
||||
requesting the roster and broadcasting an initial
|
||||
presence stanza.
|
||||
|
||||
Arguments:
|
||||
event -- An empty dictionary. The session_start
|
||||
event does not provide any additional
|
||||
data.
|
||||
"""
|
||||
self.send_presence()
|
||||
self.get_roster()
|
||||
|
||||
# We're only concerned about registering, so nothing more to do here.
|
||||
self.disconnect()
|
||||
|
||||
def register(self, iq):
|
||||
"""
|
||||
Fill out and submit a registration form.
|
||||
|
||||
The form may be composed of basic registration fields, a data form,
|
||||
an out-of-band link, or any combination thereof. Data forms and OOB
|
||||
links can be checked for as so:
|
||||
|
||||
if iq.match('iq/register/form'):
|
||||
# do stuff with data form
|
||||
# iq['register']['form']['fields']
|
||||
if iq.match('iq/register/oob'):
|
||||
# do stuff with OOB URL
|
||||
# iq['register']['oob']['url']
|
||||
|
||||
To get the list of basic registration fields, you can use:
|
||||
iq['register']['fields']
|
||||
"""
|
||||
resp = self.Iq()
|
||||
resp['type'] = 'set'
|
||||
resp['register']['username'] = self.boundjid.user
|
||||
resp['register']['password'] = self.password
|
||||
|
||||
try:
|
||||
resp.send()
|
||||
logging.info("Account created for %s!" % self.boundjid)
|
||||
except IqError as e:
|
||||
logging.error("Could not register account: %s" %
|
||||
e.iq['error']['text'])
|
||||
self.disconnect()
|
||||
except IqTimeout:
|
||||
logging.error("No response from server.")
|
||||
self.disconnect()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Setup the command line arguments.
|
||||
optp = OptionParser()
|
||||
|
||||
# Output verbosity options.
|
||||
optp.add_option('-q', '--quiet', help='set logging to ERROR',
|
||||
action='store_const', dest='loglevel',
|
||||
const=logging.ERROR, default=logging.INFO)
|
||||
optp.add_option('-d', '--debug', help='set logging to DEBUG',
|
||||
action='store_const', dest='loglevel',
|
||||
const=logging.DEBUG, default=logging.INFO)
|
||||
optp.add_option('-v', '--verbose', help='set logging to COMM',
|
||||
action='store_const', dest='loglevel',
|
||||
const=5, default=logging.INFO)
|
||||
|
||||
# JID and password options.
|
||||
optp.add_option("-j", "--jid", dest="jid",
|
||||
help="JID to use")
|
||||
optp.add_option("-p", "--password", dest="password",
|
||||
help="password to use")
|
||||
|
||||
opts, args = optp.parse_args()
|
||||
|
||||
# Setup logging.
|
||||
logging.basicConfig(level=opts.loglevel,
|
||||
format='%(levelname)-8s %(message)s')
|
||||
|
||||
if opts.jid is None:
|
||||
opts.jid = raw_input("Username: ")
|
||||
if opts.password is None:
|
||||
opts.password = getpass.getpass("Password: ")
|
||||
|
||||
# Setup the RegisterBot and register plugins. Note that while plugins may
|
||||
# have interdependencies, the order in which you register them does
|
||||
# not matter.
|
||||
xmpp = RegisterBot(opts.jid, opts.password)
|
||||
xmpp.register_plugin('xep_0030') # Service Discovery
|
||||
xmpp.register_plugin('xep_0004') # Data forms
|
||||
xmpp.register_plugin('xep_0066') # Out-of-band Data
|
||||
xmpp.register_plugin('xep_0077') # In-band Registration
|
||||
|
||||
# If you are working with an OpenFire server, you may need
|
||||
# to adjust the SSL version used:
|
||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
||||
|
||||
# If you want to verify the SSL certificates offered by a server:
|
||||
# xmpp.ca_certs = "path/to/ca/cert"
|
||||
|
||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||
if xmpp.connect():
|
||||
# If you do not have the dnspython library installed, you will need
|
||||
# to manually specify the name of the server if it does not match
|
||||
# the one in the JID. For example, to use Google Talk you would
|
||||
# need to use:
|
||||
#
|
||||
# if xmpp.connect(('talk.google.com', 5222)):
|
||||
# ...
|
||||
xmpp.process(block=True)
|
||||
print("Done")
|
||||
else:
|
||||
print("Unable to connect.")
|
1
setup.py
1
setup.py
|
@ -64,6 +64,7 @@ packages = [ 'sleekxmpp',
|
|||
'sleekxmpp/plugins/xep_0060',
|
||||
'sleekxmpp/plugins/xep_0060/stanza',
|
||||
'sleekxmpp/plugins/xep_0066',
|
||||
'sleekxmpp/plugins/xep_0077',
|
||||
'sleekxmpp/plugins/xep_0078',
|
||||
'sleekxmpp/plugins/xep_0085',
|
||||
'sleekxmpp/plugins/xep_0086',
|
||||
|
|
10
sleekxmpp/plugins/xep_0077/__init__.py
Normal file
10
sleekxmpp/plugins/xep_0077/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.plugins.xep_0077.stanza import Register, RegisterFeature
|
||||
from sleekxmpp.plugins.xep_0077.register import xep_0077
|
95
sleekxmpp/plugins/xep_0077/register.py
Normal file
95
sleekxmpp/plugins/xep_0077/register.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import sleekxmpp
|
||||
from sleekxmpp.stanza import StreamFeatures, Iq
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin, JID
|
||||
from sleekxmpp.plugins.base import base_plugin
|
||||
from sleekxmpp.plugins.xep_0077 import stanza, Register, RegisterFeature
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class xep_0077(base_plugin):
|
||||
|
||||
"""
|
||||
XEP-0077: In-Band Registration
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
self.xep = '0077'
|
||||
self.description = 'In-Band Registration'
|
||||
self.stanza = stanza
|
||||
|
||||
self.create_account = self.config.get('create_account', True)
|
||||
|
||||
register_stanza_plugin(StreamFeatures, RegisterFeature)
|
||||
register_stanza_plugin(Iq, Register)
|
||||
|
||||
|
||||
if self.xmpp.is_component:
|
||||
pass
|
||||
else:
|
||||
self.xmpp.register_feature('register',
|
||||
self._handle_register_feature,
|
||||
restart=False,
|
||||
order=self.config.get('order', 50))
|
||||
|
||||
def post_init(self):
|
||||
base_plugin.post_init(self)
|
||||
if 'xep_0004' in self.xmpp.plugin:
|
||||
register_stanza_plugin(Register, self.xmpp['xep_0004'].stanza.Form)
|
||||
|
||||
if 'xep_0066' in self.xmpp.plugin:
|
||||
register_stanza_plugin(Register, self.xmpp['xep_0066'].stanza.OOB)
|
||||
|
||||
def _handle_register_feature(self, features):
|
||||
if 'mechanisms' in self.xmpp.features:
|
||||
# We have already logged in with an account
|
||||
return False
|
||||
|
||||
if self.create_account:
|
||||
form = self.get_registration()
|
||||
self.xmpp.event('register', form, direct=True)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_registration(self, jid=None, ifrom=None, block=True,
|
||||
timeout=None, callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = jid
|
||||
iq['from'] = ifrom
|
||||
iq.enable('register')
|
||||
return iq.send(block=block, timeout=timeout, callback=callback, now=True)
|
||||
|
||||
def cancel_registration(self, jid=None, ifrom=None, block=True,
|
||||
timeout=None, callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['to'] = jid
|
||||
iq['from'] = ifrom
|
||||
iq['register']['remove'] = True
|
||||
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||
|
||||
def change_password(self, password, jid=None, ifrom=None, block=True,
|
||||
timeout=None, callback=None):
|
||||
iq= self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['to'] = jid
|
||||
iq['from'] = ifrom
|
||||
if self.xmpp.is_component:
|
||||
ifrom = JID(ifrom)
|
||||
iq['register']['username'] = ifrom.user
|
||||
else:
|
||||
iq['register']['username'] = self.xmpp.boundjid.user
|
||||
iq['register']['password'] = password
|
||||
return iq.send(block=block, timeout=timeout, callback=callback)
|
73
sleekxmpp/plugins/xep_0077/stanza.py
Normal file
73
sleekxmpp/plugins/xep_0077/stanza.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from sleekxmpp.xmlstream import ElementBase, ET
|
||||
|
||||
|
||||
class Register(ElementBase):
|
||||
|
||||
namespace = 'jabber:iq:register'
|
||||
name = 'query'
|
||||
plugin_attrib = 'register'
|
||||
interfaces = set(('username', 'password', 'email', 'nick', 'name',
|
||||
'first', 'last', 'address', 'city', 'state', 'zip',
|
||||
'phone', 'url', 'date', 'misc', 'text', 'key',
|
||||
'registered', 'remove', 'instructions', 'fields'))
|
||||
sub_interfaces = interfaces
|
||||
form_fields = set(('username', 'password', 'email', 'nick', 'name',
|
||||
'first', 'last', 'address', 'city', 'state', 'zip',
|
||||
'phone', 'url', 'date', 'misc', 'text', 'key'))
|
||||
|
||||
def get_registered(self):
|
||||
present = self.xml.find('{%s}registered' % self.namespace)
|
||||
return present is not None
|
||||
|
||||
def get_remove(self):
|
||||
present = self.xml.find('{%s}remove' % self.namespace)
|
||||
return present is not None
|
||||
|
||||
def set_registered(self, value):
|
||||
if value:
|
||||
self.add_field('registered')
|
||||
else:
|
||||
del self['registered']
|
||||
|
||||
def set_remove(self, value):
|
||||
if value:
|
||||
self.add_field('remove')
|
||||
else:
|
||||
del self['remove']
|
||||
|
||||
def add_field(self, value):
|
||||
self._set_sub_text(value, '', keep=True)
|
||||
|
||||
def get_fields(self):
|
||||
fields = set()
|
||||
for field in self.form_fields:
|
||||
if self.xml.find('{%s}%s' % (self.namespace, field)) is not None:
|
||||
fields.add(field)
|
||||
return fields
|
||||
|
||||
def set_fields(self, fields):
|
||||
del self['fields']
|
||||
for field in fields:
|
||||
self._set_sub_text(field, '', keep=True)
|
||||
|
||||
def del_fields(self):
|
||||
for field in self.form_fields:
|
||||
self._del_sub(field)
|
||||
|
||||
|
||||
class RegisterFeature(ElementBase):
|
||||
|
||||
name = 'register'
|
||||
namespace = 'http://jabber.org/features/iq-register'
|
||||
plugin_attrib = name
|
||||
interfaces = set()
|
Loading…
Reference in a new issue