Compare commits

..

1 Commits

Author SHA1 Message Date
mathieui
4512248901 Merge branch 'develop' of https://github.com/fritzy/SleekXMPP into sleek-merge
Conflicts:
	README.rst
	examples/IoT_TestDevice.py
	examples/disco_browser.py
	setup.py
	sleekxmpp/jid.py
	sleekxmpp/plugins/google/auth/stanza.py
	sleekxmpp/plugins/google/gmail/notifications.py
	sleekxmpp/plugins/google/nosave/stanza.py
	sleekxmpp/plugins/google/settings/settings.py
	sleekxmpp/thirdparty/__init__.py
	sleekxmpp/thirdparty/socks.py
	sleekxmpp/thirdparty/statemachine.py
	sleekxmpp/util/__init__.py
	sleekxmpp/xmlstream/xmlstream.py
	slixmpp/basexmpp.py
	slixmpp/plugins/xep_0004/stanza/form.py
	slixmpp/plugins/xep_0009/rpc.py
	slixmpp/plugins/xep_0050/adhoc.py
	slixmpp/plugins/xep_0065/proxy.py
	slixmpp/plugins/xep_0084/stanza.py
	slixmpp/plugins/xep_0202/time.py
	slixmpp/plugins/xep_0323/sensordata.py
	slixmpp/plugins/xep_0325/control.py
	slixmpp/plugins/xep_0325/stanza/control.py
	slixmpp/roster/single.py
	slixmpp/stanza/atom.py
	slixmpp/stanza/rootstanza.py
	slixmpp/test/slixtest.py
	slixmpp/util/sasl/mechanisms.py
	slixmpp/version.py
	slixmpp/xmlstream/stanzabase.py
	tests/test_stanza_xep_0323.py
	tests/test_stanza_xep_0325.py
	tests/test_stream_xep_0323.py
	tests/test_stream_xep_0325.py
2015-09-23 23:15:09 +02:00
94 changed files with 589 additions and 1077 deletions

View File

@@ -1,14 +0,0 @@
Contributing to the Slixmpp project
===================================
To contribute, the preferred way is to commit your changes on some
publicly-available git repository (on a fork `on github
<https://github.com/poezio/slixmpp>`_ or on your own repository) and to
notify the developers with either:
- a ticket `on the bug tracker <https://dev.poez.io/new>`_
- a pull request on github
- a simple message on `the XMPP MUC <xmpp:slixmpp@muc.poez.io>`_
Even though Slixmpps github repository is just a read-only mirror, we can
still be notified of the pull requests and fetch your mirror manually to
integrate your changes.

View File

@@ -8,15 +8,6 @@ Slixmpp's goals is to only rewrite the core of the library (the low level
socket handling, the timers, the events dispatching) in order to remove all
threads.
Building
--------
Slixmpp can make use of cython to improve performance on critical modules.
To do that, cython3 is necessary along with libidn headers. Otherwise,
no compilation is needed. Building is done by running setup.py::
python3 setup.py build_ext --inplace
Documentation and Testing
-------------------------
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
@@ -102,12 +93,8 @@ Slixmpp projects::
Slixmpp Credits
---------------
**Maintainers:**
- Florent Le Coz (`louiz@louiz.org <xmpp:louiz@louiz.org?message>`_),
- Mathieu Pasquet (`mathieui@mathieui.net <xmpp:mathieui@mathieui.net?message>`_),
**Contributors:**
- Emmanuel Gil Peyrot (`Link mauve <xmpp:linkmauve@linkmauve.fr?message>`_)
**Maintainer of the slixmpp fork:** Florent Le Coz
`louiz@louiz.org <xmpp:louiz@louiz.org?message>`_,
Credits (SleekXMPP)
-------------------

View File

@@ -48,9 +48,9 @@ copyright = u'2011, Nathan Fritz, Lance Stout'
# built documents.
#
# The short X.Y version.
version = '1.1'
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.1'
release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -259,8 +259,8 @@ Event Index
Signal that a connection to the XMPP server has been lost and the current
stream session has ended. Currently equivalent to :term:`disconnected`, but
implementations of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
distinguish between the two events.
future implementation of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
will distinguish the two events.
Plugins that maintain session-based state should clear themselves when
this event is fired.

View File

@@ -329,7 +329,7 @@ The Final Product
-----------------
Here then is what the final result should look like after working through the guide above. The code
can also be found in the Slixmpp `examples directory <http://git.poez.io/slixmpp/tree/examples>`_.
can also be found in the Slixmpp `examples directory <http://github.com/fritzy/Slixmpp/tree/master/examples>`_.
.. compound::

View File

@@ -61,7 +61,7 @@ operation using these stanzas without doing any complex operations such as
checking an ACL, etc.
You may find it necessary at some point to revert a particular node or JID to
using the default, static handlers. To do so, use the method ``restore_defaults()``.
using the default, static handlers. To do so, use the method ``make_static()``.
You may also elect to only convert a given set of actions instead.
Creating a Node Handler
@@ -162,7 +162,7 @@ item itself, and the JID and node that will own the item.
parameters ``ijid`` and ``node``.
Performing Disco Queries
------------------------
-----------------------
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
and their nodes for disco information. Since these methods are wrappers for
sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()``

View File

@@ -68,7 +68,7 @@ class CommandBot(slixmpp.ClientXMPP):
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
form = self['xep_0004'].make_form('form', 'Greeting')
form = self['xep_0004'].makeForm('form', 'Greeting')
form['instructions'] = 'Send a custom greeting to a JID'
form.addField(var='greeting',
ftype='text-single',

View File

@@ -94,7 +94,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
# label="Your greeting" />
# </x>
form = self['xep_0004'].make_form(ftype='submit')
form = self['xep_0004'].makeForm(ftype='submit')
form.addField(var='greeting',
value=session['greeting'])

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
log = logging.getLogger(__name__)
class AnswerConfirm(slixmpp.ClientXMPP):
"""
A basic client demonstrating how to confirm or deny an HTTP request.
"""
def __init__(self, jid, password, trusted):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("http_confirm", self.confirm)
self.add_event_handler("session_start", self.start)
def start(self, *args):
self.make_presence().send()
def prompt(self, stanza):
confirm = stanza['confirm']
print('Received confirm request %s from %s to access %s using '
'method %s' % (
confirm['id'], stanza['from'], confirm['url'],
confirm['method'])
)
result = input("Do you accept (y/N)? ")
return 'y' == result.lower()
def confirm(self, stanza):
if self.prompt(stanza):
reply = stanza.reply()
else:
reply = stanza.reply()
reply.enable('error')
reply['error']['type'] = 'auth'
reply['error']['code'] = '401'
reply['error']['condition'] = 'not-authorized'
reply.append(stanza['confirm'])
reply.send()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options.
parser.add_argument("-t", "--trusted", nargs='*',
help="List of trusted JIDs")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = AnswerConfirm(args.jid, args.password, args.trusted)
xmpp.register_plugin('xep_0070')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process()

View File

@@ -1,125 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import sys
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError, IqError
from slixmpp import asyncio
log = logging.getLogger(__name__)
class AskConfirm(slixmpp.ClientXMPP):
"""
A basic client asking an entity if they confirm the access to an HTTP URL.
"""
def __init__(self, jid, password, recipient, id, url, method):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.recipient = recipient
self.id = id
self.url = url
self.method = method
# Will be used to set the proper exit code.
self.confirmed = asyncio.Future()
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.start)
self.add_event_handler("http_confirm_message", self.confirm)
def confirm(self, message):
print(message)
if message['confirm']['id'] == self.id:
if message['type'] == 'error':
self.confirmed.set_result(False)
else:
self.confirmed.set_result(True)
@asyncio.coroutine
def start(self, event):
log.info('Sending confirm request %s to %s who wants to access %s using '
'method %s...' % (self.id, self.recipient, self.url, self.method))
try:
confirmed = yield from self['xep_0070'].ask_confirm(self.recipient,
id=self.id,
url=self.url,
method=self.method,
message='Plz say yes or no for {method} {url} ({id}).')
if isinstance(confirmed, slixmpp.Message):
confirmed = yield from self.confirmed
else:
confirmed = True
except IqError:
confirmed = False
if confirmed:
print('Confirmed')
else:
print('Denied')
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options.
parser.add_argument("-r", "--recipient", required=True,
help="Recipient JID")
parser.add_argument("-i", "--id", required=True,
help="id TODO")
parser.add_argument("-u", "--url", required=True,
help="URL the user tried to access")
parser.add_argument("-m", "--method", required=True,
help="HTTP method used")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = AskConfirm(args.jid, args.password, args.recipient, args.id,
args.url, args.method)
xmpp.register_plugin('xep_0070')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)
sys.exit(0 if xmpp.confirmed else 1)

View File

@@ -7,8 +7,7 @@
"""
import asyncio
if hasattr(asyncio, 'sslproto'): # no ssl proto: very old asyncio = no need for this
asyncio.sslproto._is_sslproto_available=lambda: False
asyncio.sslproto._is_sslproto_available=lambda: False
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())

View File

@@ -12,8 +12,8 @@
:license: MIT, see LICENSE for more details
"""
import asyncio
import logging
import threading
from slixmpp import plugins, roster, stanza
from slixmpp.api import APIRegistry
@@ -21,6 +21,7 @@ from slixmpp.exceptions import IqError, IqTimeout
from slixmpp.stanza import Message, Presence, Iq, StreamError
from slixmpp.stanza.roster import Roster
from slixmpp.stanza.nick import Nick
from slixmpp.xmlstream import XMLStream, JID
from slixmpp.xmlstream import ET, register_stanza_plugin
@@ -69,7 +70,7 @@ class BaseXMPP(XMLStream):
#: redirections that will be followed before quitting.
self.max_redirects = 5
self.session_bind_event = asyncio.Event()
self.session_bind_event = threading.Event()
#: A dictionary mapping plugin names to plugins.
self.plugin = PluginManager(self)
@@ -193,6 +194,7 @@ class BaseXMPP(XMLStream):
# Initialize a few default stanza plugins.
register_stanza_plugin(Iq, Roster)
register_stanza_plugin(Message, Nick)
def start_stream_handler(self, xml):
"""Save the stream ID once the streams have been established.
@@ -685,6 +687,7 @@ class BaseXMPP(XMLStream):
self.address = (host, port)
self.default_domain = host
self.dns_records = None
self.reconnect_delay = None
self.reconnect()
def _handle_message(self, msg):
@@ -750,9 +753,6 @@ class BaseXMPP(XMLStream):
Update the roster with presence information.
"""
if self.roster[presence['from']].ignore_updates:
return
if not self.is_component and not presence['to'].bare:
presence['to'] = self.boundjid

View File

@@ -12,7 +12,6 @@
:license: MIT, see LICENSE for more details
"""
import asyncio
import logging
from slixmpp.stanza import StreamFeatures
@@ -20,7 +19,7 @@ from slixmpp.basexmpp import BaseXMPP
from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream import XMLStream
from slixmpp.xmlstream.matcher import StanzaPath, MatchXPath
from slixmpp.xmlstream.handler import Callback, CoroutineCallback
from slixmpp.xmlstream.handler import Callback
# Flag indicating if DNS SRV records are available for use.
try:
@@ -105,9 +104,9 @@ class ClientXMPP(BaseXMPP):
self.register_stanza(StreamFeatures)
self.register_handler(
CoroutineCallback('Stream Features',
MatchXPath('{%s}features' % self.stream_ns),
self._handle_stream_features))
Callback('Stream Features',
MatchXPath('{%s}features' % self.stream_ns),
self._handle_stream_features))
self.register_handler(
Callback('Roster Update',
StanzaPath('iq@type=set/roster'),
@@ -250,7 +249,6 @@ class ClientXMPP(BaseXMPP):
self.bindfail = False
self.features = set()
@asyncio.coroutine
def _handle_stream_features(self, features):
"""Process the received stream features.
@@ -259,11 +257,7 @@ class ClientXMPP(BaseXMPP):
for order, name in self._stream_feature_order:
if name in features['features']:
handler, restart = self._stream_feature_handlers[name]
if asyncio.iscoroutinefunction(handler):
result = yield from handler(features)
else:
result = handler(features)
if result and restart:
if handler(features) and restart:
# Don't continue if the feature requires
# restarting the XML stream.
return True

View File

@@ -13,3 +13,7 @@ from slixmpp.features.feature_bind.stanza import Bind
register_plugin(FeatureBind)
# Retain some backwards compatibility
feature_bind = FeatureBind

View File

@@ -6,7 +6,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from slixmpp.jid import JID
@@ -35,7 +34,6 @@ class FeatureBind(BasePlugin):
register_stanza_plugin(Iq, stanza.Bind)
register_stanza_plugin(StreamFeatures, stanza.Bind)
@asyncio.coroutine
def _handle_bind_resource(self, features):
"""
Handle requesting a specific resource.
@@ -51,7 +49,7 @@ class FeatureBind(BasePlugin):
if self.xmpp.requested_jid.resource:
iq['bind']['resource'] = self.xmpp.requested_jid.resource
yield from iq.send(callback=self._on_bind_response)
iq.send(callback=self._on_bind_response)
def _on_bind_response(self, response):
self.xmpp.boundjid = JID(response['bind']['jid'])

View File

@@ -16,3 +16,7 @@ from slixmpp.features.feature_mechanisms.stanza import Failure
register_plugin(FeatureMechanisms)
# Retain some backwards compatibility
feature_mechanisms = FeatureMechanisms

View File

@@ -29,7 +29,7 @@ class Mechanisms(ElementBase):
"""
"""
results = []
mechs = self.xml.findall('{%s}mechanism' % self.namespace)
mechs = self.findall('{%s}mechanism' % self.namespace)
if mechs:
for mech in mechs:
results.append(mech.text)
@@ -47,7 +47,7 @@ class Mechanisms(ElementBase):
def del_mechanisms(self):
"""
"""
mechs = self.xml.findall('{%s}mechanism' % self.namespace)
mechs = self.findall('{%s}mechanism' % self.namespace)
if mechs:
for mech in mechs:
self.xml.remove(mech)

View File

@@ -13,3 +13,7 @@ from slixmpp.features.feature_rosterver.stanza import RosterVer
register_plugin(FeatureRosterVer)
# Retain some backwards compatibility
feature_rosterver = FeatureRosterVer

View File

@@ -13,3 +13,7 @@ from slixmpp.features.feature_session.stanza import Session
register_plugin(FeatureSession)
# Retain some backwards compatibility
feature_session = FeatureSession

View File

@@ -6,7 +6,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from slixmpp.stanza import Iq, StreamFeatures
@@ -35,7 +34,6 @@ class FeatureSession(BasePlugin):
register_stanza_plugin(Iq, stanza.Session)
register_stanza_plugin(StreamFeatures, stanza.Session)
@asyncio.coroutine
def _handle_start_session(self, features):
"""
Handle the start of the session.
@@ -46,7 +44,7 @@ class FeatureSession(BasePlugin):
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq.enable('session')
yield from iq.send(callback=self._on_start_session_response)
iq.send(callback=self._on_start_session_response)
def _on_start_session_response(self, response):
self.xmpp.features.add('session')

View File

@@ -13,3 +13,7 @@ from slixmpp.features.feature_starttls.stanza import *
register_plugin(FeatureSTARTTLS)
# Retain some backwards compatibility
feature_starttls = FeatureSTARTTLS

View File

@@ -208,15 +208,16 @@ def _format_jid(local=None, domain=None, resource=None):
:return: A full or bare JID string.
"""
if domain is None:
return ''
result = []
if local is not None:
result = local + '@' + domain
else:
result = domain
result.append(local)
result.append('@')
if domain is not None:
result.append(domain)
if resource is not None:
result += '/' + resource
return result
result.append('/')
result.append(resource)
return ''.join(result)
class InvalidJID(ValueError):
@@ -299,23 +300,19 @@ class JID:
:raises InvalidJID:
"""
__slots__ = ('_node', '_domain', '_resource', '_bare', '_full')
__slots__ = ('_node', '_domain', '_resource')
def __init__(self, jid=None):
if not jid:
self._node = ''
self._domain = ''
self._resource = ''
self._bare = ''
self._full = ''
return
self._node = None
self._domain = None
self._resource = None
elif not isinstance(jid, JID):
self._node, self._domain, self._resource = _parse_jid(jid)
else:
self._node = jid._node
self._domain = jid._domain
self._resource = jid._resource
self._update_bare_full()
def unescape(self):
"""Return an unescaped JID object.
@@ -332,94 +329,77 @@ class JID:
self._domain,
self._resource)
def _update_bare_full(self):
"""Format the given JID into a bare and a full JID.
"""
self._bare = (self._node + '@' + self._domain
if self._node
else self._domain)
self._full = (self._bare + '/' + self._resource
if self._resource
else self._bare)
@property
def node(self):
return self._node
return self._node or ''
@property
def user(self):
return self._node
return self._node or ''
@property
def local(self):
return self._node
return self._node or ''
@property
def username(self):
return self._node
return self._node or ''
@property
def domain(self):
return self._domain
return self._domain or ''
@property
def server(self):
return self._domain
return self._domain or ''
@property
def host(self):
return self._domain
return self._domain or ''
@property
def resource(self):
return self._resource
return self._resource or ''
@property
def bare(self):
return self._bare
return _format_jid(self._node, self._domain)
@property
def full(self):
return self._full
return _format_jid(self._node, self._domain, self._resource)
@property
def jid(self):
return self._full
return _format_jid(self._node, self._domain, self._resource)
@node.setter
def node(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@user.setter
def user(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@local.setter
def local(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@username.setter
def username(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@domain.setter
def domain(self, value):
self._domain = _validate_domain(value)
self._update_bare_full()
@server.setter
def server(self, value):
self._domain = _validate_domain(value)
self._update_bare_full()
@host.setter
def host(self, value):
self._domain = _validate_domain(value)
self._update_bare_full()
@bare.setter
def bare(self, value):
@@ -427,30 +407,26 @@ class JID:
assert not resource
self._node = node
self._domain = domain
self._update_bare_full()
@resource.setter
def resource(self, value):
self._resource = _validate_resource(value)
self._update_bare_full()
@full.setter
def full(self, value):
self._node, self._domain, self._resource = _parse_jid(value)
self._update_bare_full()
@jid.setter
def jid(self, value):
self._node, self._domain, self._resource = _parse_jid(value)
self._update_bare_full()
def __str__(self):
"""Use the full JID as the string value."""
return self._full
return _format_jid(self._node, self._domain, self._resource)
def __repr__(self):
"""Use the full JID as the representation."""
return self._full
return _format_jid(self._node, self._domain, self._resource)
# pylint: disable=W0212
def __eq__(self, other):
@@ -470,4 +446,4 @@ class JID:
def __hash__(self):
"""Hash a JID based on the string version of its full JID."""
return hash(self._full)
return hash(_format_jid(self._node, self._domain, self._resource))

View File

@@ -14,3 +14,9 @@ from slixmpp.plugins.xep_0004.dataforms import XEP_0004
register_plugin(XEP_0004)
# Retain some backwards compatibility
xep_0004 = XEP_0004
xep_0004.makeForm = xep_0004.make_form
xep_0004.buildForm = xep_0004.build_form

View File

@@ -81,6 +81,18 @@ class Form(ElementBase):
self.append(field)
return field
def getXML(self, type='submit'):
self['type'] = type
log.warning("Form.getXML() is deprecated API compatibility " + \
"with plugins/old_0004.py")
return self.xml
def fromXML(self, xml):
log.warning("Form.fromXML() is deprecated API compatibility " + \
"with plugins/old_0004.py")
n = Form(xml=xml)
return n
def add_item(self, values):
itemXML = ET.Element('{%s}item' % self.namespace)
self.xml.append(itemXML)
@@ -184,13 +196,13 @@ class Form(ElementBase):
for var, field in fields:
field['var'] = var
self.add_field(
var=field.get('var'),
label=field.get('label'),
desc=field.get('desc'),
required=field.get('required'),
value=field.get('value'),
options=field.get('options'),
type=field.get('type'))
var = field.get('var'),
label = field.get('label'),
desc = field.get('desc'),
required = field.get('required'),
value = field.get('value'),
options = field.get('options'),
type = field.get('type'))
def set_instructions(self, instructions):
del self['instructions']
@@ -209,7 +221,7 @@ class Form(ElementBase):
def set_reported(self, reported):
"""
This either needs a dictionary of dictionaries or a dictionary of form fields.
This either needs a dictionary or dictionaries or a dictionary of form fields.
:param reported:
:return:
"""

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0009.stanza import RPCQuery, MethodCall, MethodResponse
register_plugin(XEP_0009)
# Retain some backwards compatibility
xep_0009 = XEP_0009

View File

@@ -25,7 +25,7 @@ def fault2xml(fault):
def xml2fault(params):
vals = []
for value in params.xml.findall('{%s}value' % _namespace):
for value in params.findall('{%s}value' % _namespace):
vals.append(_xml2py(value))
fault = dict()
fault['code'] = vals[0]['faultCode']
@@ -92,40 +92,39 @@ def _py2xml(*args):
def xml2py(params):
namespace = 'jabber:iq:rpc'
vals = []
for param in params.xml.findall('{%s}param' % namespace):
for param in params.findall('{%s}param' % namespace):
vals.append(_xml2py(param.find('{%s}value' % namespace)))
return vals
def _xml2py(value):
namespace = 'jabber:iq:rpc'
find_value = value.xml.find
if find_value('{%s}nil' % namespace) is not None:
if value.find('{%s}nil' % namespace) is not None:
return None
if find_value('{%s}i4' % namespace) is not None:
return int(find_value('{%s}i4' % namespace).text)
if find_value('{%s}int' % namespace) is not None:
return int(find_value('{%s}int' % namespace).text)
if find_value('{%s}boolean' % namespace) is not None:
return bool(int(find_value('{%s}boolean' % namespace).text))
if find_value('{%s}string' % namespace) is not None:
return find_value('{%s}string' % namespace).text
if find_value('{%s}double' % namespace) is not None:
return float(find_value('{%s}double' % namespace).text)
if find_value('{%s}base64' % namespace) is not None:
return rpcbase64(find_value('{%s}base64' % namespace).text.encode())
if find_value('{%s}Base64' % namespace) is not None:
if value.find('{%s}i4' % namespace) is not None:
return int(value.find('{%s}i4' % namespace).text)
if value.find('{%s}int' % namespace) is not None:
return int(value.find('{%s}int' % namespace).text)
if value.find('{%s}boolean' % namespace) is not None:
return bool(int(value.find('{%s}boolean' % namespace).text))
if value.find('{%s}string' % namespace) is not None:
return value.find('{%s}string' % namespace).text
if value.find('{%s}double' % namespace) is not None:
return float(value.find('{%s}double' % namespace).text)
if value.find('{%s}base64' % namespace) is not None:
return rpcbase64(value.find('{%s}base64' % namespace).text.encode())
if value.find('{%s}Base64' % namespace) is not None:
# Older versions of XEP-0009 used Base64
return rpcbase64(find_value('{%s}Base64' % namespace).text.encode())
if find_value('{%s}dateTime.iso8601' % namespace) is not None:
return rpctime(find_value('{%s}dateTime.iso8601' % namespace).text)
if find_value('{%s}struct' % namespace) is not None:
return rpcbase64(value.find('{%s}Base64' % namespace).text.encode())
if value.find('{%s}dateTime.iso8601' % namespace) is not None:
return rpctime(value.find('{%s}dateTime.iso8601' % namespace).text)
if value.find('{%s}struct' % namespace) is not None:
struct = {}
for member in find_value('{%s}struct' % namespace).findall('{%s}member' % namespace):
for member in value.find('{%s}struct' % namespace).findall('{%s}member' % namespace):
struct[member.find('{%s}name' % namespace).text] = _xml2py(member.find('{%s}value' % namespace))
return struct
if find_value('{%s}array' % namespace) is not None:
if value.find('{%s}array' % namespace) is not None:
array = []
for val in find_value('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace):
for val in value.find('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace):
array.append(_xml2py(val))
return array
raise ValueError()

View File

@@ -163,7 +163,7 @@ class ACL:
@classmethod
def _next_token(cls, expression, index):
new_index = expression.xml.find('*', index)
new_index = expression.find('*', index)
if new_index == 0:
return ''
else:
@@ -182,7 +182,7 @@ class ACL:
#! print "[TOKEN] '%s'" % token
size = len(token)
if size > 0:
token_index = value.xml.find(token, position)
token_index = value.find(token, position)
if token_index == -1:
return False
else:

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0012.last_activity import XEP_0012
register_plugin(XEP_0012)
# Retain some backwards compatibility
xep_0004 = XEP_0012

View File

@@ -66,7 +66,7 @@ class XEP_0012(BasePlugin):
self.del_last_activity(jid)
def start_uptime(self, status=None):
self.set_last_activity(None, 0, status)
self.set_last_activity(jid, 0, status)
def set_last_activity(self, jid=None, seconds=None, status=None):
self.api['set_last_activity'](jid, args={

View File

@@ -15,3 +15,8 @@ from slixmpp.plugins.xep_0030.disco import XEP_0030
register_plugin(XEP_0030)
# Retain some backwards compatibility
xep_0030 = XEP_0030
XEP_0030.getInfo = XEP_0030.get_info
XEP_0030.make_static = XEP_0030.restore_defaults

View File

@@ -137,7 +137,7 @@ class DiscoInfo(ElementBase):
identity = (category, itype, lang)
if identity in self._identities:
self._identities.remove(identity)
for id_xml in self.xml.findall('{%s}identity' % self.namespace):
for id_xml in self.findall('{%s}identity' % self.namespace):
id = (id_xml.attrib['category'],
id_xml.attrib['type'],
id_xml.attrib.get('{%s}lang' % self.xml_ns, None))
@@ -163,7 +163,7 @@ class DiscoInfo(ElementBase):
identities = set()
else:
identities = []
for id_xml in self.xml.findall('{%s}identity' % self.namespace):
for id_xml in self.findall('{%s}identity' % self.namespace):
xml_lang = id_xml.attrib.get('{%s}lang' % self.xml_ns, None)
if lang is None or xml_lang == lang:
id = (id_xml.attrib['category'],
@@ -205,7 +205,7 @@ class DiscoInfo(ElementBase):
Arguments:
lang -- Optional, standard xml:lang value.
"""
for id_xml in self.xml.findall('{%s}identity' % self.namespace):
for id_xml in self.findall('{%s}identity' % self.namespace):
if lang is None:
self.xml.remove(id_xml)
elif id_xml.attrib.get('{%s}lang' % self.xml_ns, None) == lang:
@@ -239,7 +239,7 @@ class DiscoInfo(ElementBase):
"""
if feature in self._features:
self._features.remove(feature)
for feature_xml in self.xml.findall('{%s}feature' % self.namespace):
for feature_xml in self.findall('{%s}feature' % self.namespace):
if feature_xml.attrib['var'] == feature:
self.xml.remove(feature_xml)
return True
@@ -251,7 +251,7 @@ class DiscoInfo(ElementBase):
features = set()
else:
features = []
for feature_xml in self.xml.findall('{%s}feature' % self.namespace):
for feature_xml in self.findall('{%s}feature' % self.namespace):
if dedupe:
features.add(feature_xml.attrib['var'])
else:
@@ -272,5 +272,5 @@ class DiscoInfo(ElementBase):
def del_features(self):
"""Remove all features."""
self._features = set()
for feature_xml in self.xml.findall('{%s}feature' % self.namespace):
for feature_xml in self.findall('{%s}feature' % self.namespace):
self.xml.remove(feature_xml)

View File

@@ -95,7 +95,7 @@ class DiscoItems(ElementBase):
node -- Optional extra identifying information.
"""
if (jid, node) in self._items:
for item_xml in self.xml.findall('{%s}item' % self.namespace):
for item_xml in self.findall('{%s}item' % self.namespace):
item = (item_xml.attrib['jid'],
item_xml.attrib.get('node', None))
if item == (jid, node):

View File

@@ -7,6 +7,7 @@
"""
import logging
import threading
from slixmpp import Iq
from slixmpp.exceptions import XMPPError, IqError, IqTimeout
@@ -47,6 +48,7 @@ class StaticDisco(object):
self.nodes = {}
self.xmpp = xmpp
self.disco = disco
self.lock = threading.RLock()
def add_node(self, jid=None, node=None, ifrom=None):
"""
@@ -57,45 +59,48 @@ class StaticDisco(object):
jid -- The JID that will own the new stanzas.
node -- The node that will own the new stanzas.
"""
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
self.nodes[(jid, node, ifrom)] = {'info': DiscoInfo(),
'items': DiscoItems()}
self.nodes[(jid, node, ifrom)]['info']['node'] = node
self.nodes[(jid, node, ifrom)]['items']['node'] = node
with self.lock:
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
self.nodes[(jid, node, ifrom)] = {'info': DiscoInfo(),
'items': DiscoItems()}
self.nodes[(jid, node, ifrom)]['info']['node'] = node
self.nodes[(jid, node, ifrom)]['items']['node'] = node
def get_node(self, jid=None, node=None, ifrom=None):
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
self.add_node(jid, node, ifrom)
return self.nodes[(jid, node, ifrom)]
with self.lock:
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
self.add_node(jid, node, ifrom)
return self.nodes[(jid, node, ifrom)]
def node_exists(self, jid=None, node=None, ifrom=None):
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
return False
return True
with self.lock:
if jid is None:
jid = self.xmpp.boundjid.full
if node is None:
node = ''
if ifrom is None:
ifrom = ''
if isinstance(ifrom, JID):
ifrom = ifrom.full
if (jid, node, ifrom) not in self.nodes:
return False
return True
# =================================================================
# Node Handlers
@@ -194,13 +199,14 @@ class StaticDisco(object):
The data parameter is not used.
"""
if not self.node_exists(jid, node):
if not node:
return DiscoInfo()
with self.lock:
if not self.node_exists(jid, node):
if not node:
return DiscoInfo()
else:
raise XMPPError(condition='item-not-found')
else:
raise XMPPError(condition='item-not-found')
else:
return self.get_node(jid, node)['info']
return self.get_node(jid, node)['info']
def set_info(self, jid, node, ifrom, data):
"""
@@ -208,8 +214,9 @@ class StaticDisco(object):
The data parameter is a disco#info substanza.
"""
self.add_node(jid, node)
self.get_node(jid, node)['info'] = data
with self.lock:
self.add_node(jid, node)
self.get_node(jid, node)['info'] = data
def del_info(self, jid, node, ifrom, data):
"""
@@ -217,8 +224,9 @@ class StaticDisco(object):
The data parameter is not used.
"""
if self.node_exists(jid, node):
self.get_node(jid, node)['info'] = DiscoInfo()
with self.lock:
if self.node_exists(jid, node):
self.get_node(jid, node)['info'] = DiscoInfo()
def get_items(self, jid, node, ifrom, data):
"""
@@ -226,13 +234,14 @@ class StaticDisco(object):
The data parameter is not used.
"""
if not self.node_exists(jid, node):
if not node:
return DiscoItems()
with self.lock:
if not self.node_exists(jid, node):
if not node:
return DiscoItems()
else:
raise XMPPError(condition='item-not-found')
else:
raise XMPPError(condition='item-not-found')
else:
return self.get_node(jid, node)['items']
return self.get_node(jid, node)['items']
def set_items(self, jid, node, ifrom, data):
"""
@@ -241,9 +250,10 @@ class StaticDisco(object):
The data parameter may provide:
items -- A set of items in tuple format.
"""
items = data.get('items', set())
self.add_node(jid, node)
self.get_node(jid, node)['items']['items'] = items
with self.lock:
items = data.get('items', set())
self.add_node(jid, node)
self.get_node(jid, node)['items']['items'] = items
def del_items(self, jid, node, ifrom, data):
"""
@@ -251,8 +261,9 @@ class StaticDisco(object):
The data parameter is not used.
"""
if self.node_exists(jid, node):
self.get_node(jid, node)['items'] = DiscoItems()
with self.lock:
if self.node_exists(jid, node):
self.get_node(jid, node)['items'] = DiscoItems()
def add_identity(self, jid, node, ifrom, data):
"""
@@ -264,12 +275,13 @@ class StaticDisco(object):
name -- Optional human readable name for this identity.
lang -- Optional standard xml:lang value.
"""
self.add_node(jid, node)
self.get_node(jid, node)['info'].add_identity(
data.get('category', ''),
data.get('itype', ''),
data.get('name', None),
data.get('lang', None))
with self.lock:
self.add_node(jid, node)
self.get_node(jid, node)['info'].add_identity(
data.get('category', ''),
data.get('itype', ''),
data.get('name', None),
data.get('lang', None))
def set_identities(self, jid, node, ifrom, data):
"""
@@ -279,9 +291,10 @@ class StaticDisco(object):
identities -- A list of identities in tuple form:
(category, type, name, lang)
"""
identities = data.get('identities', set())
self.add_node(jid, node)
self.get_node(jid, node)['info']['identities'] = identities
with self.lock:
identities = data.get('identities', set())
self.add_node(jid, node)
self.get_node(jid, node)['info']['identities'] = identities
def del_identity(self, jid, node, ifrom, data):
"""
@@ -293,12 +306,13 @@ class StaticDisco(object):
name -- Optional human readable name for this identity.
lang -- Optional, standard xml:lang value.
"""
if self.node_exists(jid, node):
self.get_node(jid, node)['info'].del_identity(
data.get('category', ''),
data.get('itype', ''),
data.get('name', None),
data.get('lang', None))
with self.lock:
if self.node_exists(jid, node):
self.get_node(jid, node)['info'].del_identity(
data.get('category', ''),
data.get('itype', ''),
data.get('name', None),
data.get('lang', None))
def del_identities(self, jid, node, ifrom, data):
"""
@@ -306,8 +320,9 @@ class StaticDisco(object):
The data parameter is not used.
"""
if self.node_exists(jid, node):
del self.get_node(jid, node)['info']['identities']
with self.lock:
if self.node_exists(jid, node):
del self.get_node(jid, node)['info']['identities']
def add_feature(self, jid, node, ifrom, data):
"""
@@ -316,9 +331,10 @@ class StaticDisco(object):
The data parameter should include:
feature -- The namespace of the supported feature.
"""
self.add_node(jid, node)
self.get_node(jid, node)['info'].add_feature(
data.get('feature', ''))
with self.lock:
self.add_node(jid, node)
self.get_node(jid, node)['info'].add_feature(
data.get('feature', ''))
def set_features(self, jid, node, ifrom, data):
"""
@@ -327,9 +343,10 @@ class StaticDisco(object):
The data parameter should include:
features -- The new set of supported features.
"""
features = data.get('features', set())
self.add_node(jid, node)
self.get_node(jid, node)['info']['features'] = features
with self.lock:
features = data.get('features', set())
self.add_node(jid, node)
self.get_node(jid, node)['info']['features'] = features
def del_feature(self, jid, node, ifrom, data):
"""
@@ -338,9 +355,10 @@ class StaticDisco(object):
The data parameter should include:
feature -- The namespace of the removed feature.
"""
if self.node_exists(jid, node):
self.get_node(jid, node)['info'].del_feature(
data.get('feature', ''))
with self.lock:
if self.node_exists(jid, node):
self.get_node(jid, node)['info'].del_feature(
data.get('feature', ''))
def del_features(self, jid, node, ifrom, data):
"""
@@ -348,9 +366,10 @@ class StaticDisco(object):
The data parameter is not used.
"""
if not self.node_exists(jid, node):
return
del self.get_node(jid, node)['info']['features']
with self.lock:
if not self.node_exists(jid, node):
return
del self.get_node(jid, node)['info']['features']
def add_item(self, jid, node, ifrom, data):
"""
@@ -362,11 +381,12 @@ class StaticDisco(object):
non-addressable items.
name -- Optional human readable name for the item.
"""
self.add_node(jid, node)
self.get_node(jid, node)['items'].add_item(
data.get('ijid', ''),
node=data.get('inode', ''),
name=data.get('name', ''))
with self.lock:
self.add_node(jid, node)
self.get_node(jid, node)['items'].add_item(
data.get('ijid', ''),
node=data.get('inode', ''),
name=data.get('name', ''))
def del_item(self, jid, node, ifrom, data):
"""
@@ -376,10 +396,11 @@ class StaticDisco(object):
ijid -- JID of the item to remove.
inode -- Optional extra identifying information.
"""
if self.node_exists(jid, node):
self.get_node(jid, node)['items'].del_item(
data.get('ijid', ''),
node=data.get('inode', None))
with self.lock:
if self.node_exists(jid, node):
self.get_node(jid, node)['items'].del_item(
data.get('ijid', ''),
node=data.get('inode', None))
def cache_info(self, jid, node, ifrom, data):
"""
@@ -389,11 +410,12 @@ class StaticDisco(object):
containing the disco info to cache, or
the disco#info substanza itself.
"""
if isinstance(data, Iq):
data = data['disco_info']
with self.lock:
if isinstance(data, Iq):
data = data['disco_info']
self.add_node(jid, node, ifrom)
self.get_node(jid, node, ifrom)['info'] = data
self.add_node(jid, node, ifrom)
self.get_node(jid, node, ifrom)['info'] = data
def get_cached_info(self, jid, node, ifrom, data):
"""
@@ -401,7 +423,8 @@ class StaticDisco(object):
The data parameter is not used.
"""
if not self.node_exists(jid, node, ifrom):
return None
else:
return self.get_node(jid, node, ifrom)['info']
with self.lock:
if not self.node_exists(jid, node, ifrom):
return None
else:
return self.get_node(jid, node, ifrom)['info']

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0033.addresses import XEP_0033
register_plugin(XEP_0033)
# Retain some backwards compatibility
xep_0033 = XEP_0033
Addresses.addAddress = Addresses.add_address

View File

@@ -118,8 +118,14 @@ for atype in ('all', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'):
setattr(Addresses, "del_%s" % atype, del_multi)
# To retain backwards compatibility:
setattr(Addresses, "get%s" % atype.title(), get_multi)
setattr(Addresses, "set%s" % atype.title(), set_multi)
setattr(Addresses, "del%s" % atype.title(), del_multi)
if atype == 'all':
Addresses.interfaces.add('addresses')
setattr(Addresses, "getAddresses", get_multi)
setattr(Addresses, "setAddresses", set_multi)
setattr(Addresses, "delAddresses", del_multi)
register_stanza_plugin(Addresses, Address, iterable=True)

View File

@@ -160,7 +160,6 @@ class XEP_0045(BasePlugin):
got_online = False
if pr['muc']['room'] not in self.rooms.keys():
return
self.xmpp.roster[pr['from']].ignore_updates = True
entry = pr['muc'].get_stanza_values()
entry['show'] = pr['show']
entry['status'] = pr['status']
@@ -222,7 +221,7 @@ class XEP_0045(BasePlugin):
if ifrom is not None:
iq['from'] = ifrom
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
form['type'] = 'submit'
form = form.getXML('submit')
query.append(form)
iq.append(query)
# For now, swallow errors to preserve existing API
@@ -360,7 +359,7 @@ class XEP_0045(BasePlugin):
form = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
if form is None:
raise ValueError
return self.xmpp.plugin['xep_0004'].build_form(form)
return self.xmpp.plugin['xep_0004'].buildForm(form)
def cancelConfig(self, room, ifrom=None):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
@@ -373,8 +372,8 @@ class XEP_0045(BasePlugin):
def setRoomConfig(self, room, config, ifrom=''):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
config['type'] = 'submit'
query.append(config)
x = config.getXML('submit')
query.append(x)
iq = self.xmpp.make_iq_set(query)
iq['to'] = room
iq['from'] = ifrom

View File

@@ -15,3 +15,7 @@ from slixmpp.plugins.xep_0047.ibb import XEP_0047
register_plugin(XEP_0047)
# Retain some backwards compatibility
xep_0047 = XEP_0047

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0050.adhoc import XEP_0050
register_plugin(XEP_0050)
# Retain some backwards compatibility
xep_0050 = XEP_0050

View File

@@ -100,7 +100,7 @@ class Command(ElementBase):
self.del_actions()
if values:
self._set_sub_text('{%s}actions' % self.namespace, '', True)
actions = self.xml.find('{%s}actions' % self.namespace)
actions = self.find('{%s}actions' % self.namespace)
for val in values:
if val in self.next_actions:
action = ET.Element('{%s}%s' % (self.namespace, val))
@@ -111,7 +111,7 @@ class Command(ElementBase):
Return the set of allowable next actions.
"""
actions = set()
actions_xml = self.xml.find('{%s}actions' % self.namespace)
actions_xml = self.find('{%s}actions' % self.namespace)
if actions_xml is not None:
for action in self.next_actions:
action_xml = actions_xml.find('{%s}%s' % (self.namespace,

View File

@@ -13,3 +13,6 @@ from slixmpp.plugins.xep_0059.rsm import ResultIterator, XEP_0059
register_plugin(XEP_0059)
# Retain some backwards compatibility
xep_0059 = XEP_0059

View File

@@ -70,7 +70,7 @@ class Set(ElementBase):
'count', 'index', 'last', 'max'))
def set_first_index(self, val):
fi = self.xml.find("{%s}first" % (self.namespace))
fi = self.find("{%s}first" % (self.namespace))
if fi is not None:
if val:
fi.attrib['index'] = val
@@ -82,7 +82,7 @@ class Set(ElementBase):
self.xml.append(fi)
def get_first_index(self):
fi = self.xml.find("{%s}first" % (self.namespace))
fi = self.find("{%s}first" % (self.namespace))
if fi is not None:
return fi.attrib.get('index', '')

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0060 import stanza
register_plugin(XEP_0060)
# Retain some backwards compatibility
xep_0060 = XEP_0060

View File

@@ -206,7 +206,7 @@ class Options(ElementBase):
return form
def set_options(self, value):
self.xml.append(value)
self.xml.append(value.getXML())
return self
def del_options(self):
@@ -238,7 +238,7 @@ class PublishOptions(ElementBase):
if value is None:
self.del_publish_options()
else:
self.xml.append(value)
self.xml.append(value.getXML())
return self
def del_publish_options(self):

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0066.oob import XEP_0066
register_plugin(XEP_0066)
# Retain some backwards compatibility
xep_0066 = XEP_0066

View File

@@ -1,15 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0070.stanza import Confirm
from slixmpp.plugins.xep_0070.confirm import XEP_0070
register_plugin(XEP_0070)

View File

@@ -1,86 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from uuid import uuid4
from slixmpp.plugins import BasePlugin, register_plugin
from slixmpp import future_wrapper, Iq, Message
from slixmpp.exceptions import XMPPError, IqError, IqTimeout
from slixmpp.jid import JID
from slixmpp.xmlstream import JID, register_stanza_plugin
from slixmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.matcher import StanzaPath
from slixmpp.plugins.xep_0070 import stanza, Confirm
log = logging.getLogger(__name__)
class XEP_0070(BasePlugin):
"""
XEP-0070 Verifying HTTP Requests via XMPP
"""
name = 'xep_0070'
description = 'XEP-0070: Verifying HTTP Requests via XMPP'
dependencies = {'xep_0030'}
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Iq, Confirm)
register_stanza_plugin(Message, Confirm)
self.xmpp.register_handler(
Callback('Confirm',
StanzaPath('iq@type=get/confirm'),
self._handle_iq_confirm))
self.xmpp.register_handler(
Callback('Confirm',
StanzaPath('message/confirm'),
self._handle_message_confirm))
def plugin_end(self):
self.xmpp.remove_handler('Confirm')
self.xmpp['xep_0030'].del_feature(feature='http://jabber.org/protocol/http-auth')
def session_bind(self, jid):
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/http-auth')
@future_wrapper
def ask_confirm(self, jid, id, url, method, *, ifrom=None, message=None):
jid = JID(jid)
if jid.resource:
stanza = self.xmpp.Iq()
stanza['type'] = 'get'
else:
stanza = self.xmpp.Message()
stanza['thread'] = uuid4().hex
stanza['from'] = ifrom
stanza['to'] = jid
stanza['confirm']['id'] = id
stanza['confirm']['url'] = url
stanza['confirm']['method'] = method
if not jid.resource:
if message is not None:
stanza['body'] = message.format(id=id, url=url, method=method)
stanza.send()
return stanza
else:
return stanza.send()
def _handle_iq_confirm(self, iq):
self.xmpp.event('http_confirm_iq', iq)
self.xmpp.event('http_confirm', iq)
def _handle_message_confirm(self, message):
self.xmpp.event('http_confirm_message', message)
self.xmpp.event('http_confirm', message)

View File

@@ -1,17 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class Confirm(ElementBase):
name = 'confirm'
namespace = 'http://jabber.org/protocol/http-auth'
plugin_attrib = 'confirm'
interfaces = {'id', 'url', 'method'}

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0077.register import XEP_0077
register_plugin(XEP_0077)
# Retain some backwards compatibility
xep_0077 = XEP_0077

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0078.legacyauth import XEP_0078
register_plugin(XEP_0078)
# Retain some backwards compatibility
xep_0078 = XEP_0078

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0085.chat_states import XEP_0085
register_plugin(XEP_0085)
# Retain some backwards compatibility
xep_0085 = XEP_0085

View File

@@ -50,7 +50,7 @@ class ChatState(ElementBase):
def get_chat_state(self):
parent = self.parent()
for state in self.states:
state_xml = parent.xml.find('{%s}%s' % (self.namespace, state))
state_xml = parent.find('{%s}%s' % (self.namespace, state))
if state_xml is not None:
self.xml = state_xml
return state
@@ -68,7 +68,7 @@ class ChatState(ElementBase):
def del_chat_state(self):
parent = self.parent()
for state in self.states:
state_xml = parent.xml.find('{%s}%s' % (self.namespace, state))
state_xml = parent.find('{%s}%s' % (self.namespace, state))
if state_xml is not None:
self.xml = ET.Element('')
parent.xml.remove(state_xml)

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0086.legacy_error import XEP_0086
register_plugin(XEP_0086)
# Retain some backwards compatibility
xep_0086 = XEP_0086

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0092.version import XEP_0092
register_plugin(XEP_0092)
# Retain some backwards compatibility
xep_0092 = XEP_0092

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0115.caps import XEP_0115
register_plugin(XEP_0115)
# Retain some backwards compatibility
xep_0115 = XEP_0115

View File

@@ -124,19 +124,23 @@ class StaticCaps(object):
return None
def cache_caps(self, jid, node, ifrom, data):
verstring = data.get('verstring', None)
info = data.get('info', None)
if not verstring or not info:
return
self.ver_cache[verstring] = info
with self.static.lock:
verstring = data.get('verstring', None)
info = data.get('info', None)
if not verstring or not info:
return
self.ver_cache[verstring] = info
def assign_verstring(self, jid, node, ifrom, data):
if isinstance(jid, JID):
jid = jid.full
self.jid_vers[jid] = data.get('verstring', None)
with self.static.lock:
if isinstance(jid, JID):
jid = jid.full
self.jid_vers[jid] = data.get('verstring', None)
def get_verstring(self, jid, node, ifrom, data):
return self.jid_vers.get(jid, None)
with self.static.lock:
return self.jid_vers.get(jid, None)
def get_caps(self, jid, node, ifrom, data):
return self.ver_cache.get(data.get('verstring', None), None)
with self.static.lock:
return self.ver_cache.get(data.get('verstring', None), None)

View File

@@ -5,3 +5,7 @@ from slixmpp.plugins.xep_0122.data_validation import XEP_0122
register_plugin(XEP_0122)
# Retain some backwards compatibility
xep_0122 = XEP_0122

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0128.extended_disco import XEP_0128
register_plugin(XEP_0128)
# Retain some backwards compatibility
xep_0128 = XEP_0128

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0184.receipt import XEP_0184
register_plugin(XEP_0184)
# Retain some backwards compatibility
xep_0184 = XEP_0184

View File

@@ -32,7 +32,7 @@ class Request(ElementBase):
def get_request_receipt(self):
parent = self.parent()
if parent.xml.find("{%s}request" % self.namespace) is not None:
if parent.find("{%s}request" % self.namespace) is not None:
return True
else:
return False
@@ -63,7 +63,7 @@ class Received(ElementBase):
def get_receipt(self):
parent = self.parent()
xml = parent.xml.find("{%s}received" % self.namespace)
xml = parent.find("{%s}received" % self.namespace)
if xml is not None:
return xml.attrib.get('id', '')
return ''

View File

@@ -33,7 +33,7 @@ class XEP_0186(BasePlugin):
iq['type'] = 'set'
iq['from'] = ifrom
iq.enable('invisible')
return iq.send(callback=callback, timeout=timeout)
iq.send(callback=callback, timeout=timeout)
def set_visible(self, ifrom=None, callback=None,
timeout=None):
@@ -41,4 +41,4 @@ class XEP_0186(BasePlugin):
iq['type'] = 'set'
iq['from'] = ifrom
iq.enable('visible')
return iq.send(callback=callback, timeout=timeout)
iq.send(callback=callback, timeout=timeout)

View File

@@ -99,7 +99,7 @@ class StreamManagement(ElementBase):
interfaces = set(['required', 'optional'])
def get_required(self):
return self.xml.find('{%s}required' % self.namespace) is not None
return self.find('{%s}required' % self.namespace) is not None
def set_required(self, val):
self.del_required()
@@ -110,7 +110,7 @@ class StreamManagement(ElementBase):
self._del_sub('required')
def get_optional(self):
return self.xml.find('{%s}optional' % self.namespace) is not None
return self.find('{%s}optional' % self.namespace) is not None
def set_optional(self, val):
self.del_optional()

View File

@@ -6,8 +6,8 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
import threading
import collections
from slixmpp.stanza import Message, Presence, Iq, StreamFeatures
@@ -70,10 +70,15 @@ class XEP_0198(BasePlugin):
return
self.window_counter = self.window
self.window_counter_lock = threading.Lock()
self.enabled = False
self.enabled = threading.Event()
self.unacked_queue = collections.deque()
self.seq_lock = threading.Lock()
self.handled_lock = threading.Lock()
self.ack_lock = threading.Lock()
register_stanza_plugin(StreamFeatures, stanza.StreamManagement)
self.xmpp.register_stanza(stanza.Enable)
self.xmpp.register_stanza(stanza.Enabled)
@@ -156,7 +161,7 @@ class XEP_0198(BasePlugin):
def session_end(self, event):
"""Reset stream management state."""
self.enabled = False
self.enabled.clear()
self.unacked_queue.clear()
self.sm_id = None
self.handled = 0
@@ -166,15 +171,15 @@ class XEP_0198(BasePlugin):
def send_ack(self):
"""Send the current ack count to the server."""
ack = stanza.Ack(self.xmpp)
ack['h'] = self.handled
with self.handled_lock:
ack['h'] = self.handled
self.xmpp.send_raw(str(ack))
def request_ack(self, e=None):
"""Request an ack from the server."""
req = stanza.RequestAck(self.xmpp)
self.xmpp.send_raw(str(req))
self.xmpp.send_queue.put(str(req))
@asyncio.coroutine
def _handle_sm_feature(self, features):
"""
Enable or resume stream management.
@@ -191,21 +196,13 @@ class XEP_0198(BasePlugin):
return False
if not self.sm_id:
if 'bind' in self.xmpp.features:
self.enabled.set()
enable = stanza.Enable(self.xmpp)
enable['resume'] = self.allow_resume
enable.send()
self.enabled = True
self.handled = 0
self.unacked_queue.clear()
waiter = Waiter('enabled_or_failed',
MatchMany([
MatchXPath(stanza.Enabled.tag_name()),
MatchXPath(stanza.Failed.tag_name())]))
self.xmpp.register_handler(waiter)
result = yield from waiter.wait()
elif self.sm_id and self.allow_resume and 'bind' not in self.xmpp.features:
self.enabled = True
elif self.sm_id and self.allow_resume:
self.enabled.set()
resume = stanza.Resume(self.xmpp)
resume['h'] = self.handled
resume['previd'] = self.sm_id
@@ -219,7 +216,7 @@ class XEP_0198(BasePlugin):
MatchXPath(stanza.Resumed.tag_name()),
MatchXPath(stanza.Failed.tag_name())]))
self.xmpp.register_handler(waiter)
result = yield from waiter.wait()
result = waiter.wait()
if result is not None and result.name == 'resumed':
return True
return False
@@ -253,7 +250,7 @@ class XEP_0198(BasePlugin):
Raises an :term:`sm_failed` event.
"""
self.enabled = False
self.enabled.clear()
self.unacked_queue.clear()
self.xmpp.event('sm_failed', stanza)
@@ -265,24 +262,21 @@ class XEP_0198(BasePlugin):
if ack['h'] == self.last_ack:
return
num_acked = (ack['h'] - self.last_ack) % MAX_SEQ
num_unacked = len(self.unacked_queue)
log.debug("Ack: %s, Last Ack: %s, " + \
"Unacked: %s, Num Acked: %s, " + \
"Remaining: %s",
ack['h'],
self.last_ack,
num_unacked,
num_acked,
num_unacked - num_acked)
if num_acked > len(self.unacked_queue) or num_acked < 0:
log.error('Inconsistent sequence numbers from the server,'
' ignoring and replacing ours with them.')
num_acked = len(self.unacked_queue)
for x in range(num_acked):
seq, stanza = self.unacked_queue.popleft()
self.xmpp.event('stanza_acked', stanza)
self.last_ack = ack['h']
with self.ack_lock:
num_acked = (ack['h'] - self.last_ack) % MAX_SEQ
num_unacked = len(self.unacked_queue)
log.debug("Ack: %s, Last Ack: %s, " + \
"Unacked: %s, Num Acked: %s, " + \
"Remaining: %s",
ack['h'],
self.last_ack,
num_unacked,
num_acked,
num_unacked - num_acked)
for x in range(num_acked):
seq, stanza = self.unacked_queue.popleft()
self.xmpp.event('stanza_acked', stanza)
self.last_ack = ack['h']
def _handle_request_ack(self, req):
"""Handle an ack request by sending an ack."""
@@ -290,27 +284,30 @@ class XEP_0198(BasePlugin):
def _handle_incoming(self, stanza):
"""Increment the handled counter for each inbound stanza."""
if not self.enabled:
if not self.enabled.is_set():
return stanza
if isinstance(stanza, (Message, Presence, Iq)):
# Sequence numbers are mod 2^32
self.handled = (self.handled + 1) % MAX_SEQ
with self.handled_lock:
# Sequence numbers are mod 2^32
self.handled = (self.handled + 1) % MAX_SEQ
return stanza
def _handle_outgoing(self, stanza):
"""Store outgoing stanzas in a queue to be acked."""
if not self.enabled:
if not self.enabled.is_set():
return stanza
if isinstance(stanza, (Message, Presence, Iq)):
seq = None
# Sequence numbers are mod 2^32
self.seq = (self.seq + 1) % MAX_SEQ
seq = self.seq
with self.seq_lock:
# Sequence numbers are mod 2^32
self.seq = (self.seq + 1) % MAX_SEQ
seq = self.seq
self.unacked_queue.append((seq, stanza))
self.window_counter -= 1
if self.window_counter == 0:
self.window_counter = self.window
self.request_ack()
with self.window_counter_lock:
self.window_counter -= 1
if self.window_counter == 0:
self.window_counter = self.window
self.request_ack()
return stanza

View File

@@ -13,3 +13,8 @@ from slixmpp.plugins.xep_0199.ping import XEP_0199
register_plugin(XEP_0199)
# Backwards compatibility for names
xep_0199 = XEP_0199
xep_0199.sendPing = xep_0199.send_ping

View File

@@ -95,20 +95,18 @@ class XEP_0199(BasePlugin):
self.timeout = timeout
self.keepalive = True
handler = lambda event=None: asyncio.ensure_future(self._keepalive(event))
self.xmpp.schedule('Ping keepalive',
self.interval,
handler,
self._keepalive,
repeat=True)
def disable_keepalive(self, event=None):
self.xmpp.cancel_schedule('Ping keepalive')
@asyncio.coroutine
def _keepalive(self, event=None):
log.debug("Keepalive ping...")
try:
rtt = yield from self.ping(self.xmpp.boundjid.host, timeout=self.timeout)
rtt = self.ping(self.xmpp.boundjid.host, timeout=self.timeout)
except IqTimeout:
log.debug("Did not recieve ping back in time." + \
"Requesting Reconnect.")
@@ -174,7 +172,8 @@ class XEP_0199(BasePlugin):
log.debug('Pinging %s' % jid)
try:
yield from self.send_ping(jid, ifrom=ifrom, timeout=timeout)
yield from self.send_ping(jid, ifrom=ifrom, timeout=timeout,
coroutine=True)
except IqError as e:
if own_host:
rtt = time.time() - start

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0202.time import XEP_0202
register_plugin(XEP_0202)
# Retain some backwards compatibility
xep_0202 = XEP_0202

View File

@@ -14,3 +14,6 @@ from slixmpp.plugins.xep_0203.delay import XEP_0203
register_plugin(XEP_0203)
# Retain some backwards compatibility
xep_0203 = XEP_0203

View File

@@ -14,3 +14,7 @@ from slixmpp.plugins.xep_0224.attention import XEP_0224
register_plugin(XEP_0224)
# Retain some backwards compatibility
xep_0224 = XEP_0224

View File

@@ -13,3 +13,7 @@ from slixmpp.plugins.xep_0249.invite import XEP_0249
register_plugin(XEP_0249)
# Retain some backwards compatibility
xep_0249 = XEP_0249

View File

@@ -1,13 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0256.last_activity_presence import XEP_0256, LastActivity
register_plugin(XEP_0256)

View File

@@ -1,27 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp import Presence
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.xep_0012 import stanza, LastActivity
log = logging.getLogger(__name__)
class XEP_0256(BasePlugin):
name = 'xep_0256'
description = 'XEP-0256: Last Activity in Presence'
dependencies = set(['xep_0030'])
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Presence, LastActivity)

View File

@@ -540,7 +540,7 @@ class XEP_0325(BasePlugin):
fields = [f['name'] for f in iq['setResponse']['datas']]
error_msg = None
if not iq['setResponse'].xml.find('error') is None and not iq['setResponse']['error']['text'] == "":
if not iq['setResponse'].find('error') is None and not iq['setResponse']['error']['text'] == "":
error_msg = iq['setResponse']['error']['text']
callback = self.sessions[seqnr]["callback"]

View File

@@ -1,14 +0,0 @@
"""
slixmpp: The Slick XMPP Library
Copyright (C) 2016 Emmanuel Gil Peyrot
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0333.stanza import Markable, Received, Displayed, Acknowledged
from slixmpp.plugins.xep_0333.hints import XEP_0333
register_plugin(XEP_0333)

View File

@@ -1,53 +0,0 @@
"""
slixmpp: The Slick XMPP Library
Copyright (C) 2016 Emmanuel Gil Peyrot
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp import Message
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.xep_0333 import stanza, Markable, Received, Displayed, Acknowledged
log = logging.getLogger(__name__)
class XEP_0333(BasePlugin):
name = 'xep_0333'
description = 'XEP-0333: Chat Markers'
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Message, Markable)
register_stanza_plugin(Message, Received)
register_stanza_plugin(Message, Displayed)
register_stanza_plugin(Message, Acknowledged)
self.xmpp.register_handler(
Callback('Received Chat Marker',
StanzaPath('message/received'),
self._handle_received))
self.xmpp.register_handler(
Callback('Displayed Chat Marker',
StanzaPath('message/displayed'),
self._handle_displayed))
self.xmpp.register_handler(
Callback('Acknowledged Chat Marker',
StanzaPath('message/acknowledged'),
self._handle_acknowledged))
def _handle_received(self, message):
self.xmpp.event('marker_received', message)
self.xmpp.event('marker', message)
def _handle_displayed(self, message):
self.xmpp.event('marker_displayed', message)
self.xmpp.event('marker', message)
def _handle_acknowledged(self, message):
self.xmpp.event('marker_acknowledged', message)
self.xmpp.event('marker', message)

View File

@@ -1,32 +0,0 @@
"""
slixmpp: The Slick XMPP Library
Copyright (C) 2016 Emmanuel Gil Peyrot
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class Markable(ElementBase):
name = 'markable'
plugin_attrib = 'markable'
namespace = 'urn:xmpp:chat-markers:0'
class Received(ElementBase):
name = 'received'
plugin_attrib = 'received'
namespace = 'urn:xmpp:chat-markers:0'
interfaces = {'id'}
class Displayed(ElementBase):
name = 'displayed'
plugin_attrib = 'displayed'
namespace = 'urn:xmpp:chat-markers:0'
interfaces = {'id'}
class Acknowledged(ElementBase):
name = 'acknowledged'
plugin_attrib = 'acknowledged'
namespace = 'urn:xmpp:chat-markers:0'
interfaces = {'id'}

View File

@@ -1,14 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0334.stanza import Store, NoStore, NoPermanentStore, NoCopy
from slixmpp.plugins.xep_0334.hints import XEP_0334
register_plugin(XEP_0334)

View File

@@ -1,28 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2016 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp import Message
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.xep_0334 import stanza, Store, NoStore, NoPermanentStore, NoCopy
log = logging.getLogger(__name__)
class XEP_0334(BasePlugin):
name = 'xep_0334'
description = 'XEP-0334: Message Processing Hints'
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Message, Store)
register_stanza_plugin(Message, NoStore)
register_stanza_plugin(Message, NoPermanentStore)
register_stanza_plugin(Message, NoCopy)

View File

@@ -1,30 +0,0 @@
"""
slixmpp: The Slick XMPP Library
Implementation of Message Processing Hints
http://xmpp.org/extensions/xep-0334.html
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.xmlstream import ElementBase
class Store(ElementBase):
name = 'store'
plugin_attrib = 'store'
namespace = 'urn:xmpp:hints'
class NoPermanentStore(ElementBase):
name = 'no-permanent-store'
plugin_attrib = 'no-permanent-store'
namespace = 'urn:xmpp:hints'
class NoStore(ElementBase):
name = 'no-store'
plugin_attrib = 'no-store'
namespace = 'urn:xmpp:hints'
class NoCopy(ElementBase):
name = 'no-copy'
plugin_attrib = 'no-copy'
namespace = 'urn:xmpp:hints'

View File

@@ -1,15 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0352.stanza import Active, Inactive, ClientStateIndication
from slixmpp.plugins.xep_0352.csi import XEP_0352
register_plugin(XEP_0352)

View File

@@ -1,76 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from slixmpp.stanza import StreamFeatures
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.base import BasePlugin
from slixmpp.plugins.xep_0352 import stanza, Active, Inactive, ClientStateIndication
log = logging.getLogger(__name__)
class XEP_0352(BasePlugin):
"""
XEP-0352: Client State Indication
"""
name = 'xep_0352'
description = 'XEP-0352: Client State Indication'
dependencies = set()
stanza = stanza
default_config = {
"order": 12000,
}
def plugin_init(self):
"""Start the XEP-0352 plugin."""
self.enabled = False
register_stanza_plugin(StreamFeatures, ClientStateIndication)
self.xmpp.register_stanza(stanza.Active)
self.xmpp.register_stanza(stanza.Inactive)
self.xmpp.register_feature('csi',
self._handle_csi_feature,
restart=False,
order=self.order)
def plugin_end(self):
if self.xmpp.is_component:
return
self.xmpp.unregister_feature('csi', self.order)
self.xmpp.remove_stanza(stanza.Active)
self.xmpp.remove_stanza(stanza.Inactive)
def send_active(self):
"""Send an 'active' state"""
if self.enabled:
self.xmpp.send_raw(str(stanza.Active(self.xmpp)))
def send_inactive(self):
"""Send an 'active' state"""
if self.enabled:
self.xmpp.send_raw(str(stanza.Inactive(self.xmpp)))
def _handle_csi_feature(self, features):
"""
Enable CSI
"""
if 'csi' in self.xmpp.features:
log.debug('CSI already enabled')
return False
self.enabled = True
self.xmpp.event('csi_enabled', features)
return False

View File

@@ -1,33 +0,0 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.stanza import Error
from slixmpp.xmlstream import ElementBase, StanzaBase
class ClientStateIndication(ElementBase):
name = 'csi'
namespace = 'urn:xmpp:csi:0'
plugin_attrib = name
class Active(StanzaBase):
name = 'active'
plugin_attrib = 'active'
namespace = 'urn:xmpp:csi:0'
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()
class Inactive (StanzaBase):
name = 'inactive'
plugin_attrib = 'inactive'
namespace = 'urn:xmpp:csi:0'
def setup(self, xml):
StanzaBase.setup(self, xml)
self.xml.tag = self.tag_name()

View File

@@ -56,7 +56,6 @@ class RosterNode(object):
self.xmpp = xmpp
self.jid = jid
self.db = db
self.ignore_updates = False
self.auto_authorize = True
self.auto_subscribe = True
self.last_status = None

17
slixmpp/stanza/nick.py Normal file
View File

@@ -0,0 +1,17 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
# The nickname stanza has been moved to its own plugin, but the existing
# references are kept for backwards compatibility.
from slixmpp.stanza import Message, Presence
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.xep_0172 import UserNick as Nick
register_stanza_plugin(Message, Nick)
register_stanza_plugin(Presence, Nick)

View File

@@ -76,6 +76,12 @@ class Presence(RootStanza):
if self.stream is not None and self.stream.use_presence_ids:
self['id'] = self.stream.new_id()
def exception(self, e):
"""
Override exception passback for presence.
"""
pass
def set_show(self, show):
"""
Set the value of the <show> element.

View File

@@ -340,6 +340,9 @@ class SlixTest(unittest.TestCase):
self.xmpp.default_lang = None
self.xmpp.peer_default_lang = None
# Simulate connecting for mock sockets.
self.xmpp.auto_reconnect = False
# Must have the stream header ready for xmpp.process() to work.
if not header:
header = self.xmpp.stream_header

View File

@@ -1,4 +1,3 @@
import builtins
import sys
import hashlib
@@ -23,6 +22,7 @@ def bytes(text):
if text is None:
return b''
import builtins
if isinstance(text, builtins.bytes):
# We already have bytes, so do nothing
return text
@@ -80,9 +80,10 @@ def XOR(x, y):
:param bytes y: A byte string
:rtype: bytes
"""
# This operation is faster with a list comprehension than with a
# generator, as of 2016 on python 3.5.
return builtins.bytes([a ^ b for a, b in zip(x, y)])
result = b''
for a, b in zip(x, y):
result += bytes([a ^ b])
return result
def hash(name):

View File

@@ -9,5 +9,5 @@
# We don't want to have to import the entire library
# just to get the version info for setup.py
__version__ = '1.1'
__version_info__ = (1, 1)
__version__ = '1.0'
__version_info__ = (1, 0)

View File

@@ -10,10 +10,8 @@
"""
import logging
import asyncio
from asyncio import Queue, wait_for, TimeoutError
from queue import Queue, Empty
import slixmpp
from slixmpp.xmlstream.handler.base import BaseHandler
@@ -44,13 +42,12 @@ class Waiter(BaseHandler):
:param payload: The matched
:class:`~slixmpp.xmlstream.stanzabase.ElementBase` object.
"""
self._payload.put_nowait(payload)
self._payload.put(payload)
def run(self, payload):
"""Do not process this handler during the main event loop."""
pass
@asyncio.coroutine
def wait(self, timeout=None):
"""Block an event handler while waiting for a stanza to arrive.
@@ -66,13 +63,18 @@ class Waiter(BaseHandler):
value.
"""
if timeout is None:
timeout = slixmpp.xmlstream.RESPONSE_TIMEOUT
timeout = self.stream().response_timeout
stanza = None
try:
stanza = yield from self._payload.get()
except TimeoutError:
log.warning("Timed out waiting for %s", self.name)
elapsed_time = 0
stanza = False
while elapsed_time < timeout and not self.stream().stop.is_set():
try:
stanza = self._payload.get(True, 1)
break
except Empty:
elapsed_time += 1
if elapsed_time >= timeout:
log.warning("Timed out waiting for %s", self.name)
self.stream().remove_handler(self.name)
return stanza

View File

@@ -240,7 +240,7 @@ def get_AAAA(host, resolver=None, use_aiodns=True, loop=None):
except Exception as e:
log.debug('DNS: Exception while querying for %s AAAA records: %s', host, e)
recs = []
return [rec.host for rec in recs]
return recs
@asyncio.coroutine
def get_SRV(host, port, service, proto='tcp', resolver=None, use_aiodns=True):

View File

@@ -379,6 +379,20 @@ class ElementBase(object):
#: .. versionadded:: 1.0-Beta5
plugin_iterables = set()
#: A deprecated version of :attr:`plugin_iterables` that remains
#: for backward compatibility. It required a parent stanza to
#: know beforehand what stanza classes would be iterable::
#:
#: class DiscoItem(ElementBase):
#: ...
#:
#: class DiscoInfo(ElementBase):
#: subitem = (DiscoItem, )
#: ...
#:
#: .. deprecated:: 1.0-Beta5
subitem = set()
#: The default XML namespace: ``http://www.w3.org/XML/1998/namespace``.
xml_ns = XML_NS
@@ -412,6 +426,10 @@ class ElementBase(object):
else:
self.parent = parent
if self.subitem is not None:
for sub in self.subitem:
self.plugin_iterables.add(sub)
if self.setup(xml):
# If we generated our own XML, then everything is ready.
return
@@ -619,7 +637,7 @@ class ElementBase(object):
plugin.values = value
return self
def __getitem__(self, full_attrib):
def __getitem__(self, attrib):
"""Return the value of a stanza interface using dict-like syntax.
Example::
@@ -646,16 +664,24 @@ class ElementBase(object):
8. The plugin named ``'foo'``
9. An empty string.
:param string full_attrib: The name of the requested stanza interface.
:param string attrib: The name of the requested stanza interface.
"""
attrib, lang, *_ = ('%s|' % full_attrib).split('|')
full_attrib = attrib
attrib_lang = ('%s|' % attrib).split('|')
attrib = attrib_lang[0]
lang = attrib_lang[1] or None
kwargs = {'lang': lang} if lang and attrib in self.lang_interfaces else {}
kwargs = {}
if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang
kwargs = OrderedDict(kwargs)
if attrib == 'substanzas':
return self.iterables
elif attrib in self.interfaces or attrib == 'lang':
get_method = "get_%s" % attrib.lower()
get_method2 = "get%s" % attrib.title()
if self.plugin_overrides:
name = self.plugin_overrides.get(get_method, None)
@@ -668,8 +694,7 @@ class ElementBase(object):
if hasattr(self, get_method):
return getattr(self, get_method)(**kwargs)
get_method2 = "get%s" % attrib.title()
if hasattr(self, get_method2):
elif hasattr(self, get_method2):
return getattr(self, get_method2)(**kwargs)
else:
if attrib in self.sub_interfaces:
@@ -1123,6 +1148,36 @@ class ElementBase(object):
# Everything matched.
return True
def find(self, xpath):
"""Find an XML object in this stanza given an XPath expression.
Exposes ElementTree interface for backwards compatibility.
.. note::
Matching on attribute values is not supported in Python 2.6
or Python 3.1
:param string xpath: An XPath expression matching a single
desired element.
"""
return self.xml.find(xpath)
def findall(self, xpath):
"""Find multiple XML objects in this stanza given an XPath expression.
Exposes ElementTree interface for backwards compatibility.
.. note::
Matching on attribute values is not supported in Python 2.6
or Python 3.1.
:param string xpath: An XPath expression matching multiple
desired elements.
"""
return self.xml.findall(xpath)
def get(self, key, default=None):
"""Return the value of a stanza interface.
@@ -1250,6 +1305,20 @@ class ElementBase(object):
if attr in self.xml.attrib:
del self.xml.attrib[attr]
@property
def attrib(self):
"""Return the stanza object itself.
Older implementations of stanza objects used XML objects directly,
requiring the use of ``.attrib`` to access attribute values.
Use of the dictionary syntax with the stanza object itself for
accessing stanza interfaces is preferred.
.. deprecated:: 1.0
"""
return self
def _fix_ns(self, xpath, split=False, propagate_ns=True):
return fix_ns(xpath, split=split,
propagate_ns=propagate_ns,

View File

@@ -302,8 +302,7 @@ class XMLStream(asyncio.BaseProtocol):
yield from self.loop.create_connection(lambda: self,
self.address[0],
self.address[1],
ssl=self.use_ssl,
server_hostname=self.default_domain if self.use_ssl else None)
ssl=self.use_ssl)
except Socket.gaierror as e:
self.event('connection_failed',
'No DNS record available for %s' % self.default_domain)
@@ -349,7 +348,6 @@ class XMLStream(asyncio.BaseProtocol):
self.socket = self.transport.get_extra_info("socket")
self.init_parser()
self.send_raw(self.stream_header)
self.dns_answers = None
def data_received(self, data):
"""Called when incoming data is received on the socket.

View File

@@ -13,7 +13,7 @@ class TestAddresses(SlixTest):
def testAddAddress(self):
"""Testing adding extended stanza address."""
msg = self.Message()
msg['addresses'].add_address(atype='to', jid='to@header1.org')
msg['addresses'].addAddress(atype='to', jid='to@header1.org')
self.check(msg, """
<message>
<addresses xmlns="http://jabber.org/protocol/address">
@@ -23,9 +23,9 @@ class TestAddresses(SlixTest):
""")
msg = self.Message()
msg['addresses'].add_address(atype='replyto',
jid='replyto@header1.org',
desc='Reply address')
msg['addresses'].addAddress(atype='replyto',
jid='replyto@header1.org',
desc='Reply address')
self.check(msg, """
<message>
<addresses xmlns="http://jabber.org/protocol/address">
@@ -48,7 +48,7 @@ class TestAddresses(SlixTest):
"""
msg = self.Message()
msg['addresses'].set_addresses([
msg['addresses'].setAddresses([
{'type':'replyto',
'jid':'replyto@header1.org',
'desc':'Reply address'},
@@ -69,9 +69,9 @@ class TestAddresses(SlixTest):
"""Testing adding URI attribute to extended stanza address."""
msg = self.Message()
addr = msg['addresses'].add_address(atype='to',
jid='to@header1.org',
node='foo')
addr = msg['addresses'].addAddress(atype='to',
jid='to@header1.org',
node='foo')
self.check(msg, """
<message>
<addresses xmlns="http://jabber.org/protocol/address">
@@ -101,7 +101,7 @@ class TestAddresses(SlixTest):
"""
msg = self.Message()
addr = msg['addresses'].add_address(jid='to@header1.org', atype='to')
addr = msg['addresses'].addAddress(jid='to@header1.org', atype='to')
self.check(msg, xmlstring % '')
addr['delivered'] = True

View File

@@ -205,8 +205,8 @@ class TestStreamDisco(SlixTest):
handler=dynamic_jid)
self.xmpp['xep_0030'].restore_defaults(jid='tester@localhost',
node='testing')
self.xmpp['xep_0030'].make_static(jid='tester@localhost',
node='testing')
self.xmpp['xep_0030'].add_identity(jid='tester@localhost',
node='testing',
@@ -245,8 +245,8 @@ class TestStreamDisco(SlixTest):
self.xmpp['xep_0030'].set_node_handler('get_info',
handler=dynamic_global)
self.xmpp['xep_0030'].restore_defaults(jid='user@tester.localhost',
node='testing')
self.xmpp['xep_0030'].make_static(jid='user@tester.localhost',
node='testing')
self.xmpp['xep_0030'].add_feature(jid='user@tester.localhost',
node='testing',
@@ -396,8 +396,8 @@ class TestStreamDisco(SlixTest):
handler=dynamic_jid)
self.xmpp['xep_0030'].restore_defaults(jid='tester@localhost',
node='testing')
self.xmpp['xep_0030'].make_static(jid='tester@localhost',
node='testing')
self.xmpp['xep_0030'].add_item(ijid='tester@localhost',
node='testing',
@@ -436,8 +436,8 @@ class TestStreamDisco(SlixTest):
self.xmpp['xep_0030'].set_node_handler('get_items',
handler=dynamic_global)
self.xmpp['xep_0030'].restore_defaults(jid='user@tester.localhost',
node='testing')
self.xmpp['xep_0030'].make_static(jid='user@tester.localhost',
node='testing')
self.xmpp['xep_0030'].add_item(ijid='user@tester.localhost',
node='testing',

View File

@@ -76,7 +76,7 @@ class TestAdHocCommands(SlixTest):
"""Test running a command with no steps."""
def handle_command(iq, session):
form = self.xmpp['xep_0004'].make_form(ftype='result')
form = self.xmpp['xep_0004'].makeForm(ftype='result')
form.addField(var='foo', ftype='text-single',
label='Foo', value='bar')
@@ -122,7 +122,7 @@ class TestAdHocCommands(SlixTest):
results.append(form.get_values()['foo'])
session['payload'] = None
form = self.xmpp['xep_0004'].make_form('form')
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='foo', ftype='text-single', label='Foo')
session['payload'] = form
@@ -198,7 +198,7 @@ class TestAdHocCommands(SlixTest):
def handle_step1(form, session):
results.append(form.get_values()['foo'])
form = self.xmpp['xep_0004'].make_form('form')
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='bar', ftype='text-single', label='Bar')
session['payload'] = form
@@ -207,7 +207,7 @@ class TestAdHocCommands(SlixTest):
return session
form = self.xmpp['xep_0004'].make_form('form')
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='foo', ftype='text-single', label='Foo')
session['payload'] = form
@@ -312,7 +312,7 @@ class TestAdHocCommands(SlixTest):
def handle_cancel(iq, session):
results.append('canceled')
form = self.xmpp['xep_0004'].make_form('form')
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='foo', ftype='text-single', label='Foo')
session['payload'] = form
@@ -380,7 +380,7 @@ class TestAdHocCommands(SlixTest):
"""Test adding notes to commands."""
def handle_command(iq, session):
form = self.xmpp['xep_0004'].make_form(ftype='result')
form = self.xmpp['xep_0004'].makeForm(ftype='result')
form.addField(var='foo', ftype='text-single',
label='Foo', value='bar')
@@ -431,11 +431,11 @@ class TestAdHocCommands(SlixTest):
results.append(form.get_values()['FORM_TYPE'])
session['payload'] = None
form1 = self.xmpp['xep_0004'].make_form('form')
form1 = self.xmpp['xep_0004'].makeForm('form')
form1.addField(var='FORM_TYPE', ftype='hidden', value='form_1')
form1.addField(var='foo', ftype='text-single', label='Foo')
form2 = self.xmpp['xep_0004'].make_form('form')
form2 = self.xmpp['xep_0004'].makeForm('form')
form2.addField(var='FORM_TYPE', ftype='hidden', value='form_2')
form2.addField(var='foo', ftype='text-single', label='Foo')
@@ -528,7 +528,7 @@ class TestAdHocCommands(SlixTest):
results.append(item)
def handle_step2(iq, session):
form = self.xmpp['xep_0004'].make_form(ftype='submit')
form = self.xmpp['xep_0004'].makeForm(ftype='submit')
form.addField(var='bar', value='123')
session['custom_data'].append('baz')
@@ -537,7 +537,7 @@ class TestAdHocCommands(SlixTest):
self.xmpp['xep_0050'].complete_command(session)
def handle_step1(iq, session):
form = self.xmpp['xep_0004'].make_form(ftype='submit')
form = self.xmpp['xep_0004'].makeForm(ftype='submit')
form.addField(var='foo', value='42')
session['custom_data'].append('bar')

View File

@@ -18,7 +18,7 @@ class TestStreamExtendedDisco(SlixTest):
'xep_0004',
'xep_0128'])
form = self.xmpp['xep_0004'].make_form(ftype='result')
form = self.xmpp['xep_0004'].makeForm(ftype='result')
form.addField(var='FORM_TYPE', ftype='hidden', value='testing')
info_ns = 'http://jabber.org/protocol/disco#info'
@@ -57,10 +57,10 @@ class TestStreamExtendedDisco(SlixTest):
'xep_0004',
'xep_0128'])
form1 = self.xmpp['xep_0004'].make_form(ftype='result')
form1 = self.xmpp['xep_0004'].makeForm(ftype='result')
form1.addField(var='FORM_TYPE', ftype='hidden', value='testing')
form2 = self.xmpp['xep_0004'].make_form(ftype='result')
form2 = self.xmpp['xep_0004'].makeForm(ftype='result')
form2.addField(var='FORM_TYPE', ftype='hidden', value='testing_2')
info_ns = 'http://jabber.org/protocol/disco#info'