summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml10
-rwxr-xr-xexamples/set_avatar.py2
-rw-r--r--sleekxmpp/plugins/google/gmail/notifications.py4
-rw-r--r--sleekxmpp/plugins/xep_0009/remote.py54
-rw-r--r--sleekxmpp/plugins/xep_0065/proxy.py8
-rw-r--r--sleekxmpp/plugins/xep_0084/stanza.py9
-rw-r--r--sleekxmpp/plugins/xep_0257/stanza.py2
-rw-r--r--sleekxmpp/stanza/rootstanza.py4
-rw-r--r--sleekxmpp/thirdparty/socks.py13
-rw-r--r--sleekxmpp/util/__init__.py9
-rw-r--r--sleekxmpp/util/sasl/mechanisms.py19
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py14
12 files changed, 109 insertions, 39 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..22e3abf1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+install:
+ - "pip install ."
+script: testall.py
diff --git a/examples/set_avatar.py b/examples/set_avatar.py
index cae93c99..08e0b664 100755
--- a/examples/set_avatar.py
+++ b/examples/set_avatar.py
@@ -63,7 +63,7 @@ class AvatarSetter(sleekxmpp.ClientXMPP):
avatar_file = None
try:
- avatar_file = open(os.path.expanduser(self.filepath))
+ avatar_file = open(os.path.expanduser(self.filepath), 'rb')
except IOError:
print('Could not find file: %s' % self.filepath)
return self.disconnect()
diff --git a/sleekxmpp/plugins/google/gmail/notifications.py b/sleekxmpp/plugins/google/gmail/notifications.py
index 509a95fd..e65b2ca7 100644
--- a/sleekxmpp/plugins/google/gmail/notifications.py
+++ b/sleekxmpp/plugins/google/gmail/notifications.py
@@ -74,8 +74,8 @@ class Gmail(BasePlugin):
return resp
def _update_last_results(self, iq, callback=None):
- self._last_result_time = data['gmail_messages']['result_time']
- threads = data['gmail_messages']['threads']
+ self._last_result_time = iq['gmail_messages']['result_time']
+ threads = iq['gmail_messages']['threads']
if threads:
self._last_result_tid = threads[0]['tid']
if callback:
diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py
index e85c3fa9..b02f587e 100644
--- a/sleekxmpp/plugins/xep_0009/remote.py
+++ b/sleekxmpp/plugins/xep_0009/remote.py
@@ -6,7 +6,7 @@
See the file LICENSE for copying permission.
"""
-from binding import py2xml, xml2py, xml2fault, fault2xml
+from sleekxmpp.plugins.xep_0009.binding import py2xml, xml2py, xml2fault, fault2xml
from threading import RLock
import abc
import inspect
@@ -18,6 +18,45 @@ import traceback
log = logging.getLogger(__name__)
+# Define a function _isstr() to check if an object is a string in a way
+# compatible with Python 2 and Python 3 (basestring does not exists in Python 3).
+try:
+ basestring # This evaluation will throw an exception if basestring does not exists (Python 3).
+ def _isstr(obj):
+ return isinstance(obj, basestring)
+except NameError:
+ def _isstr(obj):
+ return isinstance(obj, str)
+
+
+# Class decorator to declare a metaclass to a class in a way compatible with Python 2 and 3.
+# This decorator is copied from 'six' (https://bitbucket.org/gutworth/six):
+#
+# Copyright (c) 2010-2015 Benjamin Peterson
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+def _add_metaclass(metaclass):
+ def wrapper(cls):
+ orig_vars = cls.__dict__.copy()
+ slots = orig_vars.get('__slots__')
+ if slots is not None:
+ if isinstance(slots, str):
+ slots = [slots]
+ for slots_var in slots:
+ orig_vars.pop(slots_var)
+ orig_vars.pop('__dict__', None)
+ orig_vars.pop('__weakref__', None)
+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
+ return wrapper
+
def _intercept(method, name, public):
def _resolver(instance, *args, **kwargs):
log.debug("Locally calling %s.%s with arguments %s.", instance.FQN(), method.__name__, args)
@@ -68,7 +107,7 @@ def remote(function_argument, public = True):
if hasattr(function_argument, '__call__'):
return _intercept(function_argument, None, public)
else:
- if not isinstance(function_argument, basestring):
+ if not _isstr(function_argument):
if not isinstance(function_argument, bool):
raise Exception('Expected an RPC method name or visibility modifier!')
else:
@@ -222,12 +261,11 @@ class TimeoutException(Exception):
pass
+@_add_metaclass(abc.ABCMeta)
class Callback(object):
'''
A base class for callback handlers.
'''
- __metaclass__ = abc.ABCMeta
-
@abc.abstractproperty
def set_value(self, value):
@@ -291,7 +329,7 @@ class Future(Callback):
self._event.set()
-
+@_add_metaclass(abc.ABCMeta)
class Endpoint(object):
'''
The Endpoint class is an abstract base class for all objects
@@ -303,8 +341,6 @@ class Endpoint(object):
which specifies which object an RPC call refers to. It is the
first part in a RPC method name '<fqn>.<method>'.
'''
- __metaclass__ = abc.ABCMeta
-
def __init__(self, session, target_jid):
'''
@@ -491,7 +527,7 @@ class RemoteSession(object):
def _find_key(self, dict, value):
"""return the key of dictionary dic given the value"""
- search = [k for k, v in dict.iteritems() if v == value]
+ search = [k for k, v in dict.items() if v == value]
if len(search) == 0:
return None
else:
@@ -547,7 +583,7 @@ class RemoteSession(object):
result = handler_cls(*args, **kwargs)
Endpoint.__init__(result, self, self._client.boundjid.full)
method_dict = result.get_methods()
- for method_name, method in method_dict.iteritems():
+ for method_name, method in method_dict.items():
#!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name)
self._register_call(result.FQN(), method, method_name)
self._register_acl(result.FQN(), acl)
diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py
index fdd9f97e..d890b57a 100644
--- a/sleekxmpp/plugins/xep_0065/proxy.py
+++ b/sleekxmpp/plugins/xep_0065/proxy.py
@@ -206,7 +206,7 @@ class XEP_0065(base_plugin):
# Though this should not be neccessary remove the closed session anyway
with self._sessions_lock:
if sid in self._sessions:
- log.warn(('SOCKS5 session with sid = "%s" was not ' +
+ log.warn(('SOCKS5 session with sid = "%s" was not ' +
'removed from _sessions by sock.close()') % sid)
del self._sessions[sid]
@@ -240,9 +240,9 @@ class XEP_0065(base_plugin):
# The hostname MUST be SHA1(SID + Requester JID + Target JID)
# where the output is hexadecimal-encoded (not binary).
digest = sha1()
- digest.update(sid)
- digest.update(str(requester))
- digest.update(str(target))
+ digest.update(sid.encode('utf-8'))
+ digest.update(str(requester).encode('utf-8'))
+ digest.update(str(target).encode('utf-8'))
dest = digest.hexdigest()
diff --git a/sleekxmpp/plugins/xep_0084/stanza.py b/sleekxmpp/plugins/xep_0084/stanza.py
index 22f11b72..fd21e6f1 100644
--- a/sleekxmpp/plugins/xep_0084/stanza.py
+++ b/sleekxmpp/plugins/xep_0084/stanza.py
@@ -8,7 +8,7 @@
from base64 import b64encode, b64decode
-from sleekxmpp.util import bytes
+from sleekxmpp.util import bytes as sbytes
from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
@@ -20,12 +20,15 @@ class Data(ElementBase):
def get_value(self):
if self.xml.text:
- return b64decode(bytes(self.xml.text))
+ return b64decode(sbytes(self.xml.text))
return ''
def set_value(self, value):
if value:
- self.xml.text = b64encode(bytes(value))
+ self.xml.text = b64encode(sbytes(value))
+ # Python3 base64 encoded is bytes and needs to be decoded to string
+ if isinstance(self.xml.text, bytes):
+ self.xml.text = self.xml.text.decode()
else:
self.xml.text = ''
diff --git a/sleekxmpp/plugins/xep_0257/stanza.py b/sleekxmpp/plugins/xep_0257/stanza.py
index 17e20136..c3c41db2 100644
--- a/sleekxmpp/plugins/xep_0257/stanza.py
+++ b/sleekxmpp/plugins/xep_0257/stanza.py
@@ -10,7 +10,7 @@ from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
class Certs(ElementBase):
- name = 'query'
+ name = 'items'
namespace = 'urn:xmpp:saslcert:1'
plugin_attrib = 'sasl_certs'
interfaces = set()
diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py
index a7c2b218..52b807e5 100644
--- a/sleekxmpp/stanza/rootstanza.py
+++ b/sleekxmpp/stanza/rootstanza.py
@@ -60,7 +60,9 @@ class RootStanza(StanzaBase):
self.send()
elif isinstance(e, XMPPError):
# We raised this deliberately
+ keep_id = self['id']
self.reply(clear=e.clear)
+ self['id'] = keep_id
self['error']['condition'] = e.condition
self['error']['text'] = e.text
self['error']['type'] = e.etype
@@ -72,7 +74,9 @@ class RootStanza(StanzaBase):
self.send()
else:
# We probably didn't raise this on purpose, so send an error stanza
+ keep_id = self['id']
self.reply()
+ self['id'] = keep_id
self['error']['condition'] = 'undefined-condition'
self['error']['text'] = "SleekXMPP got into trouble."
self['error']['type'] = 'cancel'
diff --git a/sleekxmpp/thirdparty/socks.py b/sleekxmpp/thirdparty/socks.py
index 0c1f6eba..34090d51 100644
--- a/sleekxmpp/thirdparty/socks.py
+++ b/sleekxmpp/thirdparty/socks.py
@@ -28,6 +28,9 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
+"""
+
+"""
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/)
@@ -35,10 +38,13 @@ for use in PyLoris (http://pyloris.sourceforge.net/)
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
mainly to merge bug fixes found in Sourceforge
+Minor modifications made by Eugene Dementiev (http://www.dementiev.eu/)
+
"""
import socket
import struct
+import sys
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
@@ -208,7 +214,7 @@ class socksocket(socket.socket):
if self.__proxy[3]:
# Resolve remotely
ipaddr = None
- req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
+ req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr.encode()
else:
# Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
@@ -323,7 +329,10 @@ class socksocket(socket.socket):
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
- resp = resp + self.recv(1)
+ recv = self.recv(1)
+ if not recv:
+ raise GeneralProxyError((1, _generalerrors[1]))
+ resp = resp + recv
# We just need the first line to check if the connection
# was successful
statusline = resp.splitlines()[0].split(" ".encode(), 2)
diff --git a/sleekxmpp/util/__init__.py b/sleekxmpp/util/__init__.py
index 05286d33..47a935af 100644
--- a/sleekxmpp/util/__init__.py
+++ b/sleekxmpp/util/__init__.py
@@ -32,12 +32,17 @@ def _gevent_threads_enabled():
if _gevent_threads_enabled():
import gevent.queue as queue
- Queue = queue.JoinableQueue
+ _queue = queue.JoinableQueue
else:
try:
import queue
except ImportError:
import Queue as queue
- Queue = queue.Queue
+ _queue = queue.Queue
+class Queue(_queue):
+ def put(self, item, block=True, timeout=None):
+ if _queue.full(self):
+ _queue.get(self)
+ return _queue.put(self, item, block, timeout)
QueueEmpty = queue.Empty
diff --git a/sleekxmpp/util/sasl/mechanisms.py b/sleekxmpp/util/sasl/mechanisms.py
index d341ed3e..7a7ebf7b 100644
--- a/sleekxmpp/util/sasl/mechanisms.py
+++ b/sleekxmpp/util/sasl/mechanisms.py
@@ -223,17 +223,16 @@ class SCRAM(Mech):
return self.hash(text).digest()
def saslname(self, value):
- escaped = b''
- for char in bytes(value):
- if char == b',':
- escaped += b'=2C'
- elif char == b'=':
- escaped += b'=3D'
+ value = value.decode("utf-8")
+ escaped = []
+ for char in value:
+ if char == ',':
+ escaped += '=2C'
+ elif char == '=':
+ escaped += '=3D'
else:
- if isinstance(char, int):
- char = chr(char)
- escaped += bytes(char)
- return escaped
+ escaped += char
+ return "".join(escaped).encode("utf-8")
def parse(self, challenge):
items = {}
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index 01787b5e..f9ec4947 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -291,7 +291,7 @@ class XMLStream(object):
self.event_queue = Queue()
#: A queue of string data to be sent over the stream.
- self.send_queue = Queue()
+ self.send_queue = Queue(maxsize=256)
self.send_queue_lock = threading.Lock()
self.send_lock = threading.RLock()
@@ -460,9 +460,11 @@ class XMLStream(object):
def _connect(self, reattempt=True):
self.scheduler.remove('Session timeout check')
- if self.reconnect_delay is None or not reattempt:
+ if self.reconnect_delay is None:
delay = 1.0
- else:
+ self.reconnect_delay = delay
+
+ if reattempt:
delay = min(self.reconnect_delay * 2, self.reconnect_max_delay)
delay = random.normalvariate(delay, delay * 0.1)
log.debug('Waiting %s seconds before connecting.', delay)
@@ -523,7 +525,8 @@ class XMLStream(object):
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
- 'do_handshake_on_connect': False
+ 'do_handshake_on_connect': False,
+ "ssl_version": self.ssl_version
})
if sys.version_info >= (2, 7):
@@ -847,7 +850,8 @@ class XMLStream(object):
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
- 'do_handshake_on_connect': False
+ 'do_handshake_on_connect': False,
+ "ssl_version": self.ssl_version
})
if sys.version_info >= (2, 7):