First pass at re-worked stream features.
Stream features now use stanza objects! Features are given a ranking that expresses the dependency relationships (since only one feature is negotiated at a time, the dependency graph can be replaced by a line). >>> xmpp.register_feature('my_feature', _my_handler, >>> restart=True, # Requires stream restart >>> order=600) # Ranking (out of ~ 10,000, >>> # lower #'s executed first) SASL mechanisms may now be added or disabled as needed. Each mechanism is given a priority value indicating the order in which the client wishes for mechanisms to be tried. Higher priority numbers are executed first. >>> xmpp.register_sasl_mechanism('SASL-MECH', _mech_handler, >>> priority=0) Disabling a SASL mechanism: >>> xmpp.remove_sasl_mechanism('ANONYMOUS')
This commit is contained in:
parent
bd9bf3f1c7
commit
1a270dc05c
7 changed files with 470 additions and 145 deletions
|
@ -18,9 +18,11 @@ import threading
|
|||
from sleekxmpp import plugins
|
||||
from sleekxmpp import stanza
|
||||
from sleekxmpp.basexmpp import BaseXMPP
|
||||
from sleekxmpp.stanza import Message, Presence, Iq
|
||||
from sleekxmpp.stanza import *
|
||||
from sleekxmpp.stanza import tls
|
||||
from sleekxmpp.stanza import sasl
|
||||
from sleekxmpp.xmlstream import XMLStream, RestartStream
|
||||
from sleekxmpp.xmlstream import StanzaBase, ET
|
||||
from sleekxmpp.xmlstream import StanzaBase, ET, register_stanza_plugin
|
||||
from sleekxmpp.xmlstream.matcher import *
|
||||
from sleekxmpp.xmlstream.handler import *
|
||||
|
||||
|
@ -92,14 +94,24 @@ class ClientXMPP(BaseXMPP):
|
|||
self.stream_footer = "</stream:stream>"
|
||||
|
||||
self.features = []
|
||||
self.registered_features = []
|
||||
self._stream_feature_handlers = {}
|
||||
self._stream_feature_order = []
|
||||
self._sasl_mechanism_handlers = {}
|
||||
self._sasl_mechanism_priorities = []
|
||||
|
||||
#TODO: Use stream state here
|
||||
self.authenticated = False
|
||||
self.sessionstarted = False
|
||||
self.bound = False
|
||||
self.bindfail = False
|
||||
self.add_event_handler('connected', self.handle_connected)
|
||||
|
||||
self.add_event_handler('connected', self._handle_connected)
|
||||
|
||||
self.register_stanza(StreamFeatures)
|
||||
self.register_stanza(tls.Proceed)
|
||||
self.register_stanza(sasl.Success)
|
||||
self.register_stanza(sasl.Failure)
|
||||
self.register_stanza(sasl.Auth)
|
||||
|
||||
self.register_handler(
|
||||
Callback('Stream Features',
|
||||
|
@ -112,32 +124,25 @@ class ClientXMPP(BaseXMPP):
|
|||
'jabber:iq:roster')),
|
||||
self._handle_roster))
|
||||
|
||||
self.register_feature(
|
||||
"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />",
|
||||
self._handle_starttls, True)
|
||||
self.register_feature(
|
||||
"<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />",
|
||||
self._handle_sasl_auth, True)
|
||||
self.register_feature(
|
||||
"<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' />",
|
||||
self._handle_bind_resource)
|
||||
self.register_feature(
|
||||
"<session xmlns='urn:ietf:params:xml:ns:xmpp-session' />",
|
||||
self._handle_start_session)
|
||||
self.register_feature('starttls', self._handle_starttls,
|
||||
restart=True,
|
||||
order=0)
|
||||
self.register_feature('mechanisms', self._handle_sasl_auth,
|
||||
restart=True,
|
||||
order=100)
|
||||
self.register_feature('bind', self._handle_bind_resource,
|
||||
restart=False,
|
||||
order=10000)
|
||||
self.register_feature('session', self._handle_start_session,
|
||||
restart=False,
|
||||
order=10001)
|
||||
|
||||
def handle_connected(self, event=None):
|
||||
#TODO: Use stream state here
|
||||
self.authenticated = False
|
||||
self.sessionstarted = False
|
||||
self.bound = False
|
||||
self.bindfail = False
|
||||
self.schedule("session timeout checker", 15,
|
||||
self._session_timeout_check)
|
||||
|
||||
def _session_timeout_check(self):
|
||||
if not self.session_started_event.isSet():
|
||||
log.debug("Session start has taken more than 15 seconds")
|
||||
self.disconnect(reconnect=self.auto_reconnect)
|
||||
self.register_sasl_mechanism('PLAIN',
|
||||
self._handle_sasl_plain,
|
||||
priority=1)
|
||||
self.register_sasl_mechanism('ANONYMOUS',
|
||||
self._handle_sasl_plain,
|
||||
priority=0)
|
||||
|
||||
def connect(self, address=tuple(), reattempt=True):
|
||||
"""
|
||||
|
@ -197,19 +202,54 @@ class ClientXMPP(BaseXMPP):
|
|||
return XMLStream.connect(self, address[0], address[1],
|
||||
use_tls=True, reattempt=reattempt)
|
||||
|
||||
def register_feature(self, mask, pointer, breaker=False):
|
||||
def register_feature(self, name, handler, restart=False, order=5000):
|
||||
"""
|
||||
Register a stream feature.
|
||||
|
||||
Arguments:
|
||||
mask -- An XML string matching the feature's element.
|
||||
pointer -- The function to execute if the feature is received.
|
||||
breaker -- Indicates if feature processing should halt with
|
||||
name -- The name of the stream feature.
|
||||
handler -- The function to execute if the feature is received.
|
||||
restart -- Indicates if feature processing should halt with
|
||||
this feature. Defaults to False.
|
||||
order -- The relative ordering in which the feature should
|
||||
be negotiated. Lower values will be attempted
|
||||
earlier when available.
|
||||
"""
|
||||
self.registered_features.append((MatchXMLMask(mask),
|
||||
pointer,
|
||||
breaker))
|
||||
self._stream_feature_handlers[name] = (handler, restart)
|
||||
self._stream_feature_order.append((order, name))
|
||||
self._stream_feature_order.sort()
|
||||
|
||||
def register_sasl_mechanism(self, name, handler, priority=0):
|
||||
"""
|
||||
Register a handler for a SASL authentication mechanism.
|
||||
|
||||
Arguments:
|
||||
name -- The name of the mechanism (all caps)
|
||||
handler -- The function that will perform the
|
||||
authentication. The function must
|
||||
return True if it is able to carry
|
||||
out the authentication, False if
|
||||
a required condition is not met.
|
||||
priority -- An integer value indicating the
|
||||
preferred ordering for the mechanism.
|
||||
High values will be attempted first.
|
||||
"""
|
||||
self._sasl_mechanism_handlers[name] = handler
|
||||
self._sasl_mechanism_priorities.append((priority, name))
|
||||
self._sasl_mechanism_priorities.sort(reverse=True)
|
||||
|
||||
def remove_sasl_mechanism(self, name):
|
||||
"""
|
||||
Remove support for a given SASL authentication mechanism.
|
||||
|
||||
Arguments:
|
||||
name -- The name of the mechanism to remove (all caps)
|
||||
"""
|
||||
if name in self._sasl_mechanism_handlers:
|
||||
del self._sasl_mechanism_handlers[name]
|
||||
|
||||
p = self._sasl_mechanism_priorities
|
||||
self._sasl_mechanism_priorities = [i for i in p if i[1] != name]
|
||||
|
||||
def update_roster(self, jid, name=None, subscription=None, groups=[]):
|
||||
"""
|
||||
|
@ -223,7 +263,8 @@ class ClientXMPP(BaseXMPP):
|
|||
to 'remove', the entry will be deleted.
|
||||
groups -- The roster groups that contain this item.
|
||||
"""
|
||||
iq = self.Iq()._set_stanza_values({'type': 'set'})
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['roster']['items'] = {jid: {'name': name,
|
||||
'subscription': subscription,
|
||||
'groups': groups}}
|
||||
|
@ -242,10 +283,27 @@ class ClientXMPP(BaseXMPP):
|
|||
|
||||
def get_roster(self):
|
||||
"""Request the roster from the server."""
|
||||
iq = self.Iq()._set_stanza_values({'type': 'get'}).enable('roster')
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq.enable('roster')
|
||||
response = iq.send()
|
||||
self._handle_roster(response, request=True)
|
||||
|
||||
def _handle_connected(self, event=None):
|
||||
#TODO: Use stream state here
|
||||
self.authenticated = False
|
||||
self.sessionstarted = False
|
||||
self.bound = False
|
||||
self.bindfail = False
|
||||
self.features = []
|
||||
|
||||
def session_timeout():
|
||||
if not self.session_started_event.isSet():
|
||||
log.debug("Session start has taken more than 15 seconds")
|
||||
self.disconnect(reconnect=self.auto_reconnect)
|
||||
|
||||
self.schedule("session timeout checker", 15, session_timeout)
|
||||
|
||||
def _handle_stream_features(self, features):
|
||||
"""
|
||||
Process the received stream features.
|
||||
|
@ -253,167 +311,174 @@ class ClientXMPP(BaseXMPP):
|
|||
Arguments:
|
||||
features -- The features stanza.
|
||||
"""
|
||||
# Record all of the features.
|
||||
self.features = []
|
||||
for sub in features.xml:
|
||||
self.features.append(sub.tag)
|
||||
for order, name in self._stream_feature_order:
|
||||
if name in features['features']:
|
||||
handler, restart = self._stream_feature_handlers[name]
|
||||
if handler(features) and restart:
|
||||
# Don't continue if the feature requires
|
||||
# restarting the XML stream.
|
||||
return True
|
||||
|
||||
# Process the features.
|
||||
for sub in features.xml:
|
||||
for feature in self.registered_features:
|
||||
mask, handler, halt = feature
|
||||
if mask.match(sub):
|
||||
if handler(sub) and halt:
|
||||
# Don't continue if the feature was
|
||||
# marked as a breaker.
|
||||
return True
|
||||
|
||||
def _handle_starttls(self, xml):
|
||||
def _handle_starttls(self, features):
|
||||
"""
|
||||
Handle notification that the server supports TLS.
|
||||
|
||||
Arguments:
|
||||
xml -- The STARTLS proceed element.
|
||||
features -- The stream:features element.
|
||||
"""
|
||||
if not self.authenticated and self.ssl_support:
|
||||
tls_ns = 'urn:ietf:params:xml:ns:xmpp-tls'
|
||||
self.add_handler("<proceed xmlns='%s' />" % tls_ns,
|
||||
self._handle_tls_start,
|
||||
name='TLS Proceed',
|
||||
instream=True)
|
||||
self.send_xml(xml)
|
||||
|
||||
def tls_proceed(proceed):
|
||||
"""Restart the XML stream when TLS is accepted."""
|
||||
log.debug("Starting TLS")
|
||||
if self.start_tls():
|
||||
self.features.append('starttls')
|
||||
raise RestartStream()
|
||||
|
||||
if self.ssl_support:
|
||||
self.register_handler(
|
||||
Callback('STARTTLS Proceed',
|
||||
MatchXPath(tls.Proceed.tag_name()),
|
||||
tls_proceed,
|
||||
instream=True))
|
||||
self.send(features['starttls'])
|
||||
return True
|
||||
else:
|
||||
log.warning("The module tlslite is required to log in" +\
|
||||
" to some servers, and has not been found.")
|
||||
return False
|
||||
|
||||
def _handle_tls_start(self, xml):
|
||||
"""
|
||||
Handle encrypting the stream using TLS.
|
||||
|
||||
Restarts the stream.
|
||||
"""
|
||||
log.debug("Starting TLS")
|
||||
if self.start_tls():
|
||||
raise RestartStream()
|
||||
|
||||
def _handle_sasl_auth(self, xml):
|
||||
def _handle_sasl_auth(self, features):
|
||||
"""
|
||||
Handle authenticating using SASL.
|
||||
|
||||
Arguments:
|
||||
xml -- The SASL mechanisms stanza.
|
||||
features -- The stream features stanza.
|
||||
"""
|
||||
if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features:
|
||||
return False
|
||||
|
||||
log.debug("Starting SASL Auth")
|
||||
sasl_ns = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
||||
self.add_handler("<success xmlns='%s' />" % sasl_ns,
|
||||
self._handle_auth_success,
|
||||
name='SASL Sucess',
|
||||
instream=True)
|
||||
self.add_handler("<failure xmlns='%s' />" % sasl_ns,
|
||||
self._handle_auth_fail,
|
||||
name='SASL Failure',
|
||||
instream=True)
|
||||
def sasl_success(stanza):
|
||||
"""SASL authentication succeeded. Restart the stream."""
|
||||
self.authenticated = True
|
||||
self.features.append('mechanisms')
|
||||
raise RestartStream()
|
||||
|
||||
sasl_mechs = xml.findall('{%s}mechanism' % sasl_ns)
|
||||
if sasl_mechs:
|
||||
for sasl_mech in sasl_mechs:
|
||||
self.features.append("sasl:%s" % sasl_mech.text)
|
||||
if 'sasl:PLAIN' in self.features and self.boundjid.user:
|
||||
if sys.version_info < (3, 0):
|
||||
user = bytes(self.boundjid.user)
|
||||
password = bytes(self.password)
|
||||
else:
|
||||
user = bytes(self.boundjid.user, 'utf-8')
|
||||
password = bytes(self.password, 'utf-8')
|
||||
def sasl_fail(stanza):
|
||||
"""SASL authentication failed. Disconnect and shutdown."""
|
||||
log.info("Authentication failed.")
|
||||
self.event("failed_auth", direct=True)
|
||||
self.disconnect()
|
||||
log.debug("Starting SASL Auth")
|
||||
return True
|
||||
|
||||
auth = base64.b64encode(b'\x00' + user + \
|
||||
b'\x00' + password).decode('utf-8')
|
||||
self.register_handler(
|
||||
Callback('SASL Success',
|
||||
MatchXPath(sasl.Success.tag_name()),
|
||||
sasl_success,
|
||||
instream=True,
|
||||
once=True))
|
||||
|
||||
self.register_handler(
|
||||
Callback('SASL Failure',
|
||||
MatchXPath(sasl.Failure.tag_name()),
|
||||
sasl_fail,
|
||||
instream=True,
|
||||
once=True))
|
||||
|
||||
for priority, mech in self._sasl_mechanism_priorities:
|
||||
if mech in self._sasl_mechanism_handlers:
|
||||
handler = self._sasl_mechanism_handlers[mech]
|
||||
if handler(self):
|
||||
break
|
||||
else:
|
||||
log.error("No appropriate login method.")
|
||||
self.disconnect()
|
||||
|
||||
self.send("<auth xmlns='%s' mechanism='PLAIN'>%s</auth>" % (
|
||||
sasl_ns,
|
||||
auth))
|
||||
elif 'sasl:ANONYMOUS' in self.features and not self.boundjid.user:
|
||||
self.send("<auth xmlns='%s' mechanism='%s' />" % (
|
||||
sasl_ns,
|
||||
'ANONYMOUS'))
|
||||
else:
|
||||
log.error("No appropriate login method.")
|
||||
self.disconnect()
|
||||
return True
|
||||
|
||||
def _handle_auth_success(self, xml):
|
||||
def _handle_sasl_plain(self, xmpp):
|
||||
"""
|
||||
SASL authentication succeeded. Restart the stream.
|
||||
Attempt to authenticate using SASL PLAIN.
|
||||
|
||||
Arguments:
|
||||
xml -- The SASL authentication success element.
|
||||
xmpp -- The SleekXMPP connection instance.
|
||||
"""
|
||||
self.authenticated = True
|
||||
self.features = []
|
||||
raise RestartStream()
|
||||
if not xmpp.boundjid.user:
|
||||
return False
|
||||
|
||||
def _handle_auth_fail(self, xml):
|
||||
if sys.version_info < (3, 0):
|
||||
user = bytes(self.boundjid.user)
|
||||
password = bytes(self.password)
|
||||
else:
|
||||
user = bytes(self.boundjid.user, 'utf-8')
|
||||
password = bytes(self.password, 'utf-8')
|
||||
|
||||
auth = base64.b64encode(b'\x00' + user + \
|
||||
b'\x00' + password).decode('utf-8')
|
||||
|
||||
resp = sasl.Auth(xmpp)
|
||||
resp['mechanism'] = 'PLAIN'
|
||||
resp['value'] = auth
|
||||
resp.send()
|
||||
|
||||
return True
|
||||
|
||||
def _handle_sasl_anonymous(self, xmpp):
|
||||
"""
|
||||
SASL authentication failed. Disconnect and shutdown.
|
||||
Attempt to authenticate using SASL ANONYMOUS.
|
||||
|
||||
Arguments:
|
||||
xml -- The SASL authentication failure element.
|
||||
xmpp -- The SleekXMPP connection instance.
|
||||
"""
|
||||
log.info("Authentication failed.")
|
||||
self.event("failed_auth", direct=True)
|
||||
self.disconnect()
|
||||
if xmpp.boundjid.user:
|
||||
return False
|
||||
|
||||
def _handle_bind_resource(self, xml):
|
||||
resp = sasl.Auth(xmpp)
|
||||
resp['mechanism'] = 'ANONYMOUS'
|
||||
resp.send()
|
||||
|
||||
return True
|
||||
|
||||
def _handle_bind_resource(self, features):
|
||||
"""
|
||||
Handle requesting a specific resource.
|
||||
|
||||
Arguments:
|
||||
xml -- The bind feature element.
|
||||
features -- The stream features stanza.
|
||||
"""
|
||||
log.debug("Requesting resource: %s" % self.boundjid.resource)
|
||||
xml.clear()
|
||||
iq = self.Iq(stype='set')
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq.enable('bind')
|
||||
if self.boundjid.resource:
|
||||
res = ET.Element('resource')
|
||||
res.text = self.boundjid.resource
|
||||
xml.append(res)
|
||||
iq.append(xml)
|
||||
iq['bind']['resource'] = self.boundjid.resource
|
||||
response = iq.send()
|
||||
|
||||
bind_ns = 'urn:ietf:params:xml:ns:xmpp-bind'
|
||||
self.set_jid(response.xml.find('{%s}bind/{%s}jid' % (bind_ns,
|
||||
bind_ns)).text)
|
||||
self.set_jid(response['bind']['jid'])
|
||||
self.bound = True
|
||||
|
||||
log.info("Node set to: %s" % self.boundjid.full)
|
||||
session_ns = 'urn:ietf:params:xml:ns:xmpp-session'
|
||||
if "{%s}session" % session_ns not in self.features or self.bindfail:
|
||||
|
||||
if 'session' not in features['features']:
|
||||
log.debug("Established Session")
|
||||
self.sessionstarted = True
|
||||
self.session_started_event.set()
|
||||
self.event("session_start")
|
||||
|
||||
def _handle_start_session(self, xml):
|
||||
def _handle_start_session(self, features):
|
||||
"""
|
||||
Handle the start of the session.
|
||||
|
||||
Arguments:
|
||||
xml -- The session feature element.
|
||||
feature -- The stream features element.
|
||||
"""
|
||||
if self.authenticated and self.bound:
|
||||
iq = self.makeIqSet(xml)
|
||||
response = iq.send()
|
||||
log.debug("Established Session")
|
||||
self.sessionstarted = True
|
||||
self.session_started_event.set()
|
||||
self.event("session_start")
|
||||
else:
|
||||
# Bind probably hasn't happened yet.
|
||||
self.bindfail = True
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq.enable('session')
|
||||
response = iq.send()
|
||||
|
||||
log.debug("Established Session")
|
||||
self.sessionstarted = True
|
||||
self.session_started_event.set()
|
||||
self.event("session_start")
|
||||
|
||||
def _handle_roster(self, iq, request=False):
|
||||
"""
|
||||
|
|
|
@ -12,3 +12,6 @@ from sleekxmpp.stanza.stream_error import StreamError
|
|||
from sleekxmpp.stanza.iq import Iq
|
||||
from sleekxmpp.stanza.message import Message
|
||||
from sleekxmpp.stanza.presence import Presence
|
||||
from sleekxmpp.stanza.stream_features import StreamFeatures
|
||||
from sleekxmpp.stanza.bind import Bind
|
||||
|
||||
|
|
26
sleekxmpp/stanza/bind.py
Normal file
26
sleekxmpp/stanza/bind.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza import Iq, StreamFeatures
|
||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
|
||||
|
||||
|
||||
class Bind(ElementBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'bind'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-bind'
|
||||
interfaces = set(('resource', 'jid'))
|
||||
sub_interfaces = interfaces
|
||||
plugin_attrib = 'bind'
|
||||
|
||||
|
||||
register_stanza_plugin(Iq, Bind)
|
||||
register_stanza_plugin(StreamFeatures, Bind)
|
104
sleekxmpp/stanza/sasl.py
Normal file
104
sleekxmpp/stanza/sasl.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza import StreamFeatures
|
||||
from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
|
||||
class Mechanisms(ElementBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'mechanisms'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
||||
interfaces = set(('mechanisms', 'required'))
|
||||
plugin_attrib = name
|
||||
is_extension = True
|
||||
|
||||
def get_required(self):
|
||||
"""
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_mechanisms(self):
|
||||
"""
|
||||
"""
|
||||
results = []
|
||||
mechs = self.findall('{%s}mechanism' % self.namespace)
|
||||
if mechs:
|
||||
for mech in mechs:
|
||||
results.append(mech.text)
|
||||
return results
|
||||
|
||||
def set_mechanisms(self, values):
|
||||
"""
|
||||
"""
|
||||
self.del_mechanisms()
|
||||
for val in values:
|
||||
mech = ET.Element('{%s}mechanism' % self.namespace)
|
||||
mech.text = val
|
||||
self.append(mech)
|
||||
|
||||
def del_mechanisms(self):
|
||||
"""
|
||||
"""
|
||||
mechs = self.findall('{%s}mechanism' % self.namespace)
|
||||
if mechs:
|
||||
for mech in mechs:
|
||||
self.xml.remove(mech)
|
||||
|
||||
|
||||
class Success(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'success'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
||||
interfaces = set()
|
||||
plugin_attrib = name
|
||||
|
||||
|
||||
class Failure(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'failure'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
||||
interfaces = set()
|
||||
plugin_attrib = name
|
||||
|
||||
|
||||
class Auth(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'auth'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
||||
interfaces = set(('mechanism', 'value'))
|
||||
plugin_attrib = name
|
||||
|
||||
def setup(self, xml):
|
||||
StanzaBase.setup(self, xml)
|
||||
self.xml.tag = self.tag_name()
|
||||
|
||||
def set_value(self, value):
|
||||
self.xml.text = value
|
||||
|
||||
def get_value(self):
|
||||
return self.xml.text
|
||||
|
||||
def del_value(self):
|
||||
self.xml.text = ''
|
||||
|
||||
|
||||
register_stanza_plugin(StreamFeatures, Mechanisms)
|
25
sleekxmpp/stanza/session.py
Normal file
25
sleekxmpp/stanza/session.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza import Iq, StreamFeatures
|
||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
|
||||
|
||||
|
||||
class Session(ElementBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'bind'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-session'
|
||||
interfaces = set()
|
||||
plugin_attrib = 'session'
|
||||
|
||||
|
||||
register_stanza_plugin(Iq, Session)
|
||||
register_stanza_plugin(StreamFeatures, Session)
|
52
sleekxmpp/stanza/stream_features.py
Normal file
52
sleekxmpp/stanza/stream_features.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
|
||||
class StreamFeatures(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'features'
|
||||
namespace = 'http://etherx.jabber.org/streams'
|
||||
interfaces = set(('features', 'required', 'optional'))
|
||||
sub_interfaces = interfaces
|
||||
|
||||
def setup(self, xml):
|
||||
StanzaBase.setup(self, xml)
|
||||
self.values = self.values
|
||||
|
||||
def get_features(self):
|
||||
"""
|
||||
"""
|
||||
return self.plugins
|
||||
|
||||
def set_features(self, value):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def del_features(self):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_required(self):
|
||||
"""
|
||||
"""
|
||||
features = self['features']
|
||||
return [f for n, f in features.items() if f['required']]
|
||||
|
||||
def get_optional(self):
|
||||
"""
|
||||
"""
|
||||
features = self['features']
|
||||
return [f for n, f in features.items() if not f['required']]
|
50
sleekxmpp/stanza/tls.py
Normal file
50
sleekxmpp/stanza/tls.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza import StreamFeatures
|
||||
from sleekxmpp.xmlstream import StanzaBase, ElementBase
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
|
||||
class STARTTLS(ElementBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'starttls'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-tls'
|
||||
interfaces = set(('required',))
|
||||
plugin_attrib = name
|
||||
|
||||
def get_required(self):
|
||||
"""
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
class Proceed(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'proceed'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-tls'
|
||||
interfaces = set()
|
||||
|
||||
|
||||
class Failure(StanzaBase):
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
name = 'failure'
|
||||
namespace = 'urn:ietf:params:xml:ns:xmpp-tls'
|
||||
interfaces = set()
|
||||
|
||||
|
||||
register_stanza_plugin(StreamFeatures, STARTTLS)
|
Loading…
Reference in a new issue