mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-12-18 03:00:18 +00:00
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',
|
||||||
'sleekxmpp/plugins/xep_0060/stanza',
|
'sleekxmpp/plugins/xep_0060/stanza',
|
||||||
'sleekxmpp/plugins/xep_0066',
|
'sleekxmpp/plugins/xep_0066',
|
||||||
|
'sleekxmpp/plugins/xep_0077',
|
||||||
'sleekxmpp/plugins/xep_0078',
|
'sleekxmpp/plugins/xep_0078',
|
||||||
'sleekxmpp/plugins/xep_0085',
|
'sleekxmpp/plugins/xep_0085',
|
||||||
'sleekxmpp/plugins/xep_0086',
|
'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