Rename to slixmpp

This commit is contained in:
Florent Le Coz
2014-07-17 14:19:04 +02:00
parent e5582694c0
commit 5ab77c7452
514 changed files with 3473 additions and 3530 deletions

View File

@@ -0,0 +1,16 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
__all__ = [
'feature_starttls',
'feature_mechanisms',
'feature_bind',
'feature_session',
'feature_rosterver',
'feature_preapproval'
]

View File

@@ -0,0 +1,19 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_bind.bind import FeatureBind
from slixmpp.features.feature_bind.stanza import Bind
register_plugin(FeatureBind)
# Retain some backwards compatibility
feature_bind = FeatureBind

View File

@@ -0,0 +1,65 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.jid import JID
from slixmpp.stanza import Iq, StreamFeatures
from slixmpp.features.feature_bind import stanza
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins import BasePlugin
log = logging.getLogger(__name__)
class FeatureBind(BasePlugin):
name = 'feature_bind'
description = 'RFC 6120: Stream Feature: Resource Binding'
dependencies = set()
stanza = stanza
def plugin_init(self):
self.xmpp.register_feature('bind',
self._handle_bind_resource,
restart=False,
order=10000)
register_stanza_plugin(Iq, stanza.Bind)
register_stanza_plugin(StreamFeatures, stanza.Bind)
def _handle_bind_resource(self, features):
"""
Handle requesting a specific resource.
Arguments:
features -- The stream features stanza.
"""
log.debug("Requesting resource: %s", self.xmpp.requested_jid.resource)
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq.enable('bind')
if self.xmpp.requested_jid.resource:
iq['bind']['resource'] = self.xmpp.requested_jid.resource
response = iq.send(now=True)
self.xmpp.boundjid = JID(response['bind']['jid'], cache_lock=True)
self.xmpp.bound = True
self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
self.xmpp.session_bind_event.set()
self.xmpp.features.add('bind')
log.info("JID set to: %s", self.xmpp.boundjid.full)
if 'session' not in features['features']:
log.debug("Established Session")
self.xmpp.sessionstarted = True
self.xmpp.session_started_event.set()
self.xmpp.event('session_start')

View File

@@ -0,0 +1,21 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class Bind(ElementBase):
"""
"""
name = 'bind'
namespace = 'urn:ietf:params:xml:ns:xmpp-bind'
interfaces = set(('resource', 'jid'))
sub_interfaces = interfaces
plugin_attrib = 'bind'

View File

@@ -0,0 +1,22 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_mechanisms.mechanisms import FeatureMechanisms
from slixmpp.features.feature_mechanisms.stanza import Mechanisms
from slixmpp.features.feature_mechanisms.stanza import Auth
from slixmpp.features.feature_mechanisms.stanza import Success
from slixmpp.features.feature_mechanisms.stanza import Failure
register_plugin(FeatureMechanisms)
# Retain some backwards compatibility
feature_mechanisms = FeatureMechanisms

View File

@@ -0,0 +1,244 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import ssl
import logging
from slixmpp.util import sasl
from slixmpp.util.stringprep_profiles import StringPrepError
from slixmpp.stanza import StreamFeatures
from slixmpp.xmlstream import RestartStream, register_stanza_plugin
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream.handler import Callback
from slixmpp.features.feature_mechanisms import stanza
log = logging.getLogger(__name__)
class FeatureMechanisms(BasePlugin):
name = 'feature_mechanisms'
description = 'RFC 6120: Stream Feature: SASL'
dependencies = set()
stanza = stanza
default_config = {
'use_mech': None,
'use_mechs': None,
'min_mech': None,
'sasl_callback': None,
'security_callback': None,
'encrypted_plain': True,
'unencrypted_plain': False,
'unencrypted_digest': False,
'unencrypted_cram': False,
'unencrypted_scram': True,
'order': 100
}
def plugin_init(self):
if self.sasl_callback is None:
self.sasl_callback = self._default_credentials
if self.security_callback is None:
self.security_callback = self._default_security
creds = self.sasl_callback(set(['username']), set())
if not self.use_mech and not creds['username']:
self.use_mech = 'ANONYMOUS'
self.mech = None
self.mech_list = set()
self.attempted_mechs = set()
register_stanza_plugin(StreamFeatures, stanza.Mechanisms)
self.xmpp.register_stanza(stanza.Success)
self.xmpp.register_stanza(stanza.Failure)
self.xmpp.register_stanza(stanza.Auth)
self.xmpp.register_stanza(stanza.Challenge)
self.xmpp.register_stanza(stanza.Response)
self.xmpp.register_stanza(stanza.Abort)
self.xmpp.register_handler(
Callback('SASL Success',
MatchXPath(stanza.Success.tag_name()),
self._handle_success,
instream=True))
self.xmpp.register_handler(
Callback('SASL Failure',
MatchXPath(stanza.Failure.tag_name()),
self._handle_fail,
instream=True))
self.xmpp.register_handler(
Callback('SASL Challenge',
MatchXPath(stanza.Challenge.tag_name()),
self._handle_challenge))
self.xmpp.register_feature('mechanisms',
self._handle_sasl_auth,
restart=True,
order=self.order)
def _default_credentials(self, required_values, optional_values):
creds = self.xmpp.credentials
result = {}
values = required_values.union(optional_values)
for value in values:
if value == 'username':
result[value] = creds.get('username', self.xmpp.requested_jid.user)
elif value == 'email':
jid = self.xmpp.requested_jid.bare
result[value] = creds.get('email', jid)
elif value == 'channel_binding':
if hasattr(self.xmpp.socket, 'get_channel_binding'):
result[value] = self.xmpp.socket.get_channel_binding()
else:
log.debug("Channel binding not supported.")
log.debug("Use Python 3.3+ for channel binding and " + \
"SCRAM-SHA-1-PLUS support")
result[value] = None
elif value == 'host':
result[value] = creds.get('host', self.xmpp.requested_jid.domain)
elif value == 'realm':
result[value] = creds.get('realm', self.xmpp.requested_jid.domain)
elif value == 'service-name':
result[value] = creds.get('service-name', self.xmpp._service_name)
elif value == 'service':
result[value] = creds.get('service', 'xmpp')
elif value in creds:
result[value] = creds[value]
return result
def _default_security(self, values):
result = {}
for value in values:
if value == 'encrypted':
if 'starttls' in self.xmpp.features:
result[value] = True
elif isinstance(self.xmpp.socket, ssl.SSLSocket):
result[value] = True
else:
result[value] = False
else:
result[value] = self.config.get(value, False)
return result
def _handle_sasl_auth(self, features):
"""
Handle authenticating using SASL.
Arguments:
features -- The stream features stanza.
"""
if 'mechanisms' in self.xmpp.features:
# SASL authentication has already succeeded, but the
# server has incorrectly offered it again.
return False
enforce_limit = False
limited_mechs = self.use_mechs
if limited_mechs is None:
limited_mechs = set()
elif limited_mechs and not isinstance(limited_mechs, set):
limited_mechs = set(limited_mechs)
enforce_limit = True
if self.use_mech:
limited_mechs.add(self.use_mech)
enforce_limit = True
if enforce_limit:
self.use_mechs = limited_mechs
self.mech_list = set(features['mechanisms'])
return self._send_auth()
def _send_auth(self):
mech_list = self.mech_list - self.attempted_mechs
try:
self.mech = sasl.choose(mech_list,
self.sasl_callback,
self.security_callback,
limit=self.use_mechs,
min_mech=self.min_mech)
except sasl.SASLNoAppropriateMechanism:
log.error("No appropriate login method.")
self.xmpp.event("no_auth", direct=True)
self.xmpp.event("failed_auth", direct=True)
self.attempted_mechs = set()
return self.xmpp.disconnect()
except StringPrepError:
log.exception("A credential value did not pass SASLprep.")
self.xmpp.disconnect()
resp = stanza.Auth(self.xmpp)
resp['mechanism'] = self.mech.name
try:
resp['value'] = self.mech.process()
except sasl.SASLCancelled:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
except sasl.SASLFailed:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \
"A security breach is possible.")
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
else:
resp.send(now=True)
return True
def _handle_challenge(self, stanza):
"""SASL challenge received. Process and send response."""
resp = self.stanza.Response(self.xmpp)
try:
resp['value'] = self.mech.process(stanza['value'])
except sasl.SASLCancelled:
self.stanza.Abort(self.xmpp).send()
except sasl.SASLFailed:
self.stanza.Abort(self.xmpp).send()
except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \
"A security breach is possible.")
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
else:
if resp.get_value() == '':
resp.del_value()
resp.send(now=True)
def _handle_success(self, stanza):
"""SASL authentication succeeded. Restart the stream."""
try:
final = self.mech.process(stanza['value'])
except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \
"A security breach is possible.")
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
else:
self.attempted_mechs = set()
self.xmpp.authenticated = True
self.xmpp.features.add('mechanisms')
self.xmpp.event('auth_success', stanza, direct=True)
raise RestartStream()
def _handle_fail(self, stanza):
"""SASL authentication failed. Disconnect and shutdown."""
self.attempted_mechs.add(self.mech.name)
log.info("Authentication failed: %s", stanza['condition'])
self.xmpp.event("failed_auth", stanza, direct=True)
self._send_auth()
return True

View File

@@ -0,0 +1,16 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.features.feature_mechanisms.stanza.mechanisms import Mechanisms
from slixmpp.features.feature_mechanisms.stanza.auth import Auth
from slixmpp.features.feature_mechanisms.stanza.success import Success
from slixmpp.features.feature_mechanisms.stanza.failure import Failure
from slixmpp.features.feature_mechanisms.stanza.challenge import Challenge
from slixmpp.features.feature_mechanisms.stanza.response import Response
from slixmpp.features.feature_mechanisms.stanza.abort import Abort

View File

@@ -0,0 +1,24 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import StanzaBase
class Abort(StanzaBase):
"""
"""
name = 'abort'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set()
plugin_attrib = name
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()

View File

@@ -0,0 +1,49 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import base64
from slixmpp.util import bytes
from slixmpp.xmlstream import StanzaBase
class Auth(StanzaBase):
"""
"""
name = 'auth'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('mechanism', 'value'))
plugin_attrib = name
#: Some SASL mechs require sending values as is,
#: without converting base64.
plain_mechs = set(['X-MESSENGER-OAUTH2'])
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()
def get_value(self):
if not self['mechanism'] in self.plain_mechs:
return base64.b64decode(bytes(self.xml.text))
else:
return self.xml.text
def set_value(self, values):
if not self['mechanism'] in self.plain_mechs:
if values:
self.xml.text = bytes(base64.b64encode(values)).decode('utf-8')
elif values == b'':
self.xml.text = '='
else:
self.xml.text = bytes(values).decode('utf-8')
def del_value(self):
self.xml.text = ''

View File

@@ -0,0 +1,39 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import base64
from slixmpp.util import bytes
from slixmpp.xmlstream import StanzaBase
class Challenge(StanzaBase):
"""
"""
name = 'challenge'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('value',))
plugin_attrib = name
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()
def get_value(self):
return base64.b64decode(bytes(self.xml.text))
def set_value(self, values):
if values:
self.xml.text = bytes(base64.b64encode(values)).decode('utf-8')
else:
self.xml.text = '='
def del_value(self):
self.xml.text = ''

View File

@@ -0,0 +1,76 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import StanzaBase, ET
class Failure(StanzaBase):
"""
"""
name = 'failure'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('condition', 'text'))
plugin_attrib = name
sub_interfaces = set(('text',))
conditions = set(('aborted', 'account-disabled', 'credentials-expired',
'encryption-required', 'incorrect-encoding', 'invalid-authzid',
'invalid-mechanism', 'malformed-request', 'mechansism-too-weak',
'not-authorized', 'temporary-auth-failure'))
def setup(self, xml=None):
"""
Populate the stanza object using an optional XML object.
Overrides ElementBase.setup.
Sets a default error type and condition, and changes the
parent stanza's type to 'error'.
Arguments:
xml -- Use an existing XML object for the stanza's values.
"""
# StanzaBase overrides self.namespace
self.namespace = Failure.namespace
if StanzaBase.setup(self, xml):
#If we had to generate XML then set default values.
self['condition'] = 'not-authorized'
self.xml.tag = self.tag_name()
def get_condition(self):
"""Return the condition element's name."""
for child in self.xml:
if "{%s}" % self.namespace in child.tag:
cond = child.tag.split('}', 1)[-1]
if cond in self.conditions:
return cond
return 'not-authorized'
def set_condition(self, value):
"""
Set the tag name of the condition element.
Arguments:
value -- The tag name of the condition element.
"""
if value in self.conditions:
del self['condition']
self.xml.append(ET.Element("{%s}%s" % (self.namespace, value)))
return self
def del_condition(self):
"""Remove the condition element."""
for child in self.xml:
if "{%s}" % self.condition_ns in child.tag:
tag = child.tag.split('}', 1)[-1]
if tag in self.conditions:
self.xml.remove(child)
return self

View File

@@ -0,0 +1,53 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase, ET
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)

View File

@@ -0,0 +1,39 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import base64
from slixmpp.util import bytes
from slixmpp.xmlstream import StanzaBase
class Response(StanzaBase):
"""
"""
name = 'response'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('value',))
plugin_attrib = name
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()
def get_value(self):
return base64.b64decode(bytes(self.xml.text))
def set_value(self, values):
if values:
self.xml.text = bytes(base64.b64encode(values)).decode('utf-8')
else:
self.xml.text = '='
def del_value(self):
self.xml.text = ''

View File

@@ -0,0 +1,38 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import base64
from slixmpp.util import bytes
from slixmpp.xmlstream import StanzaBase
class Success(StanzaBase):
"""
"""
name = 'success'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(['value'])
plugin_attrib = name
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()
def get_value(self):
return base64.b64decode(bytes(self.xml.text))
def set_value(self, values):
if values:
self.xml.text = bytes(base64.b64encode(values)).decode('utf-8')
else:
self.xml.text = '='
def del_value(self):
self.xml.text = ''

View File

@@ -0,0 +1,15 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_preapproval.preapproval import FeaturePreApproval
from slixmpp.features.feature_preapproval.stanza import PreApproval
register_plugin(FeaturePreApproval)

View File

@@ -0,0 +1,42 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.stanza import StreamFeatures
from slixmpp.features.feature_preapproval import stanza
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.base import BasePlugin
log = logging.getLogger(__name__)
class FeaturePreApproval(BasePlugin):
name = 'feature_preapproval'
description = 'RFC 6121: Stream Feature: Subscription Pre-Approval'
dependences = set()
stanza = stanza
def plugin_init(self):
self.xmpp.register_feature('preapproval',
self._handle_preapproval,
restart=False,
order=9001)
register_stanza_plugin(StreamFeatures, stanza.PreApproval)
def _handle_preapproval(self, features):
"""Save notice that the server support subscription pre-approvals.
Arguments:
features -- The stream features stanza.
"""
log.debug("Server supports subscription pre-approvals.")
self.xmpp.features.add('preapproval')

View File

@@ -0,0 +1,17 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class PreApproval(ElementBase):
name = 'sub'
namespace = 'urn:xmpp:features:pre-approval'
interfaces = set()
plugin_attrib = 'preapproval'

View File

@@ -0,0 +1,19 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_rosterver.rosterver import FeatureRosterVer
from slixmpp.features.feature_rosterver.stanza import RosterVer
register_plugin(FeatureRosterVer)
# Retain some backwards compatibility
feature_rosterver = FeatureRosterVer

View File

@@ -0,0 +1,42 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.stanza import StreamFeatures
from slixmpp.features.feature_rosterver import stanza
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.base import BasePlugin
log = logging.getLogger(__name__)
class FeatureRosterVer(BasePlugin):
name = 'feature_rosterver'
description = 'RFC 6121: Stream Feature: Roster Versioning'
dependences = set()
stanza = stanza
def plugin_init(self):
self.xmpp.register_feature('rosterver',
self._handle_rosterver,
restart=False,
order=9000)
register_stanza_plugin(StreamFeatures, stanza.RosterVer)
def _handle_rosterver(self, features):
"""Enable using roster versioning.
Arguments:
features -- The stream features stanza.
"""
log.debug("Enabling roster versioning.")
self.xmpp.features.add('rosterver')

View File

@@ -0,0 +1,17 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class RosterVer(ElementBase):
name = 'ver'
namespace = 'urn:xmpp:features:rosterver'
interfaces = set()
plugin_attrib = 'rosterver'

View File

@@ -0,0 +1,19 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_session.session import FeatureSession
from slixmpp.features.feature_session.stanza import Session
register_plugin(FeatureSession)
# Retain some backwards compatibility
feature_session = FeatureSession

View File

@@ -0,0 +1,54 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.stanza import Iq, StreamFeatures
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins import BasePlugin
from slixmpp.features.feature_session import stanza
log = logging.getLogger(__name__)
class FeatureSession(BasePlugin):
name = 'feature_session'
description = 'RFC 3920: Stream Feature: Start Session'
dependencies = set()
stanza = stanza
def plugin_init(self):
self.xmpp.register_feature('session',
self._handle_start_session,
restart=False,
order=10001)
register_stanza_plugin(Iq, stanza.Session)
register_stanza_plugin(StreamFeatures, stanza.Session)
def _handle_start_session(self, features):
"""
Handle the start of the session.
Arguments:
feature -- The stream features element.
"""
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq.enable('session')
iq.send(now=True)
self.xmpp.features.add('session')
log.debug("Established Session")
self.xmpp.sessionstarted = True
self.xmpp.session_started_event.set()
self.xmpp.event('session_start')

View File

@@ -0,0 +1,20 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class Session(ElementBase):
"""
"""
name = 'session'
namespace = 'urn:ietf:params:xml:ns:xmpp-session'
interfaces = set()
plugin_attrib = 'session'

View File

@@ -0,0 +1,19 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.features.feature_starttls.starttls import FeatureSTARTTLS
from slixmpp.features.feature_starttls.stanza import *
register_plugin(FeatureSTARTTLS)
# Retain some backwards compatibility
feature_starttls = FeatureSTARTTLS

View File

@@ -0,0 +1,45 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import StanzaBase, ElementBase
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()

View File

@@ -0,0 +1,66 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.stanza import StreamFeatures
from slixmpp.xmlstream import RestartStream, register_stanza_plugin
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream.handler import Callback
from slixmpp.features.feature_starttls import stanza
log = logging.getLogger(__name__)
class FeatureSTARTTLS(BasePlugin):
name = 'feature_starttls'
description = 'RFC 6120: Stream Feature: STARTTLS'
dependencies = set()
stanza = stanza
def plugin_init(self):
self.xmpp.register_handler(
Callback('STARTTLS Proceed',
MatchXPath(stanza.Proceed.tag_name()),
self._handle_starttls_proceed,
instream=True))
self.xmpp.register_feature('starttls',
self._handle_starttls,
restart=True,
order=self.config.get('order', 0))
self.xmpp.register_stanza(stanza.Proceed)
self.xmpp.register_stanza(stanza.Failure)
register_stanza_plugin(StreamFeatures, stanza.STARTTLS)
def _handle_starttls(self, features):
"""
Handle notification that the server supports TLS.
Arguments:
features -- The stream:features element.
"""
if 'starttls' in self.xmpp.features:
# We have already negotiated TLS, but the server is
# offering it again, against spec.
return False
elif not self.xmpp.use_tls:
return False
else:
self.xmpp.send(features['starttls'], now=True)
return True
def _handle_starttls_proceed(self, proceed):
"""Restart the XML stream when TLS is accepted."""
log.debug("Starting TLS")
if self.xmpp.start_tls():
self.xmpp.features.add('starttls')
raise RestartStream()