Compare commits

...

26 Commits

Author SHA1 Message Date
Lance Stout
753cb3580e Bump to 1.3.0 2014-06-08 20:01:07 -07:00
Lance Stout
60b050b82a Make ssl args work in Python <=2.6.4 2014-06-08 19:59:40 -07:00
Lance Stout
ad91a8cd5e Bring back use of dnspython for A/AAAA resolution.
This is behind a use_dnspython flag, however, so it can be disabled as
desired.
2014-06-08 19:51:57 -07:00
Lance Stout
02f79fc94b Only request auto-receipts for messages with bodies 2014-06-07 20:20:42 -07:00
Lance Stout
230a73fad2 Fix own_host in ping plugin 2014-06-07 20:06:17 -07:00
Lance Stout
d94dd486fe Merge pull request #294 from mofrank/develop
Fixes log.debug message in _connect_proxy
2014-05-16 08:43:39 -07:00
Lance Stout
6ecc39b816 Merge pull request #292 from 4gra/develop
Fix support for jabberd2 with GSSAPI
2014-05-16 08:43:26 -07:00
mofrank
9c240df9db Fixes log.debug message in _connect_proxy 2014-05-16 08:49:01 -05:00
Graham
a918bf3a95 Support jabberd2 SASL with really empty response
Despite http://xmpp.org/rfcs/rfc3920.html#rfc.section.6.2, jabberd version 2.2.14 cannot accept the typical "<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">=</response>".  Instead it must be truly empty, so we force an empty response for this stanza only.
2014-05-14 22:32:51 +01:00
Graham
9434ae267f support 'success' phase correctly
When the GSSAPI mechanism's process() function is invoked for the third time (on success) it must not attempt further processing.  Instead it should clean the context and return an empty response.
2014-05-14 22:25:09 +01:00
Graham
94187d215a don't use the kerberos.GSSError.message attribute
Replaced the reference to kerberos.GSSError.message in any raised exception, because:

 DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

and its natural repr is probably the most desirable output.
2014-05-14 17:47:34 +01:00
Lance Stout
ef2f5d2978 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2014-04-20 18:10:22 -07:00
Lance Stout
62671e0f56 Fix using SCRAM with ejabberd 2014-04-20 18:09:20 -07:00
Lance Stout
93869f77a0 Merge pull request #285 from lovesnow/develop
Fix Don't process vCard avatars for MUC occupants caused TypeError
2014-04-20 18:06:04 -07:00
Lance Stout
8282d135cc Bump version 2014-04-20 18:05:27 -07:00
Lance Stout
9acc78c81d Merge pull request #288 from tpltnt/develop
doc typo fixed
2014-04-20 17:56:56 -07:00
tpltnt
3642469630 doc typo fixed 2014-04-19 19:12:09 +02:00
lovesnow
34cd20339c Fix Don't process vCard avatars for MUC occupants caused TypeError 2014-02-21 10:31:04 +08:00
Lance Stout
7548f44047 Bump version 2014-02-14 13:53:25 -08:00
Lance Stout
7cf55ef695 Allow IQ processing based on only id value before the session is bound.
See issue #278
2014-02-14 13:50:21 -08:00
Lance Stout
543250da13 Bump version 2014-02-09 14:39:50 -08:00
Lance Stout
69e55d7316 Merge pull request #280 from allan-simon/develop
fixed setRole function,
2014-02-09 14:39:08 -08:00
Lance Stout
158411e918 Include stanza dirs 2014-02-09 14:36:36 -08:00
Lance Stout
3f873002c4 Bump minor version 2014-02-09 14:33:36 -08:00
Lance Stout
818f4e5973 Fix setup.py to include xep_0323 and xep_0325 2014-02-09 14:33:02 -08:00
Allan Simon
c8d6e512d2 fixed setRole function, the check where made against 'affiliation' values, now we do that against actual role values 2014-02-07 12:11:28 +08:00
14 changed files with 144 additions and 60 deletions

View File

@@ -45,7 +45,7 @@ The latest source code for SleekXMPP may be found on `Github
``develop`` branch.
**Latest Release**
- `1.1.11 <http://github.com/fritzy/SleekXMPP/zipball/1.1.11>`_
- `1.3.0 <http://github.com/fritzy/SleekXMPP/zipball/1.3.0>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_

View File

@@ -87,8 +87,8 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0091',
'sleekxmpp/plugins/xep_0092',
'sleekxmpp/plugins/xep_0095',
'sleekxmpp/plugins/xep_0096',
'sleekxmpp/plugins/xep_0095',
'sleekxmpp/plugins/xep_0096',
'sleekxmpp/plugins/xep_0107',
'sleekxmpp/plugins/xep_0108',
'sleekxmpp/plugins/xep_0115',
@@ -119,6 +119,10 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0308',
'sleekxmpp/plugins/xep_0313',
'sleekxmpp/plugins/xep_0319',
'sleekxmpp/plugins/xep_0323',
'sleekxmpp/plugins/xep_0323/stanza',
'sleekxmpp/plugins/xep_0325',
'sleekxmpp/plugins/xep_0325/stanza',
'sleekxmpp/plugins/google',
'sleekxmpp/plugins/google/gmail',
'sleekxmpp/plugins/google/auth',

View File

@@ -136,7 +136,7 @@ class ClientXMPP(BaseXMPP):
be attempted. If that fails, the server user in the JID
will be used.
:param address -- A tuple containing the server's host and port.
:param address: A tuple containing the server's host and port.
:param reattempt: If ``True``, repeat attempting to connect if an
error occurs. Defaults to ``True``.
:param use_tls: Indicates if TLS should be used for the

View File

@@ -215,6 +215,8 @@ class FeatureMechanisms(BasePlugin):
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
else:
if resp.get_value() == '':
resp.del_value()
resp.send(now=True)
def _handle_success(self, stanza):

View File

@@ -304,7 +304,7 @@ class XEP_0045(BasePlugin):
room), whereas affiliations are permanent (they last across groupchat
sessions).
"""
if role not in ('outcast', 'member', 'admin', 'owner', 'none'):
if role not in ('moderator', 'participant', 'visitor', 'none'):
raise TypeError
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
item = ET.Element('item', {'role':role, 'nick':nick})

View File

@@ -124,10 +124,12 @@ class XEP_0153(BasePlugin):
log.debug('Could not retrieve vCard for %s' % jid)
def _recv_presence(self, pres):
if pres['muc']['affiliation']:
# Don't process vCard avatars for MUC occupants
# since they all share the same bare JID.
return
try:
if pres['muc']['affiliation']:
# Don't process vCard avatars for MUC occupants
# since they all share the same bare JID.
return
except: pass
if not pres.match('presence/vcard_temp_update'):
self.api['set_hash'](pres['from'], args=None)

View File

@@ -118,6 +118,9 @@ class XEP_0184(BasePlugin):
if stanza['receipt']:
return stanza
if not stanza['body']:
return stanza
if stanza['to'].resource:
if not self.xmpp['xep_0030'].supports(stanza['to'],
feature='urn:xmpp:receipts',

View File

@@ -154,6 +154,7 @@ class XEP_0199(BasePlugin):
timeout -- Time in seconds to wait for a response.
Defaults to self.timeout.
"""
own_host = False
if not jid:
if self.xmpp.is_component:
jid = self.xmpp.server

View File

@@ -9,7 +9,7 @@
from sleekxmpp.stanza.rootstanza import RootStanza
from sleekxmpp.xmlstream import StanzaBase, ET
from sleekxmpp.xmlstream.handler import Waiter, Callback
from sleekxmpp.xmlstream.matcher import MatchIDSender
from sleekxmpp.xmlstream.matcher import MatchIDSender, MatcherId
from sleekxmpp.exceptions import IqTimeout, IqError
@@ -194,11 +194,14 @@ class Iq(RootStanza):
if timeout is None:
timeout = self.stream.response_timeout
criteria = {
'id': self['id'],
'self': self.stream.boundjid,
'peer': self['to']
}
if self.stream.session_bind_event.is_set():
matcher = MatchIDSender({
'id': self['id'],
'self': self.stream.boundjid,
'peer': self['to']
})
else:
matcher = MatcherId(self['id'])
if callback is not None and self['type'] in ('get', 'set'):
handler_name = 'IqCallback_%s' % self['id']
@@ -210,19 +213,19 @@ class Iq(RootStanza):
self._fire_timeout,
repeat=False)
handler = Callback(handler_name,
MatchIDSender(criteria),
matcher,
self._handle_result,
once=True)
else:
handler = Callback(handler_name,
MatchIDSender(criteria),
matcher,
callback,
once=True)
self.stream.register_handler(handler)
StanzaBase.send(self, now=now)
return handler_name
elif block and self['type'] in ('get', 'set'):
waitfor = Waiter('IqWait_%s' % self['id'], MatchIDSender(criteria))
waitfor = Waiter('IqWait_%s' % self['id'], matcher)
self.stream.register_handler(waitfor)
StanzaBase.send(self, now=now)
result = waitfor.wait(timeout)

View File

@@ -376,6 +376,7 @@ class SleekTest(unittest.TestCase):
if skip:
if socket != 'live':
# Mark send queue as usable
self.xmpp.session_bind_event.set()
self.xmpp.session_started_event.set()
# Clear startup stanzas
self.xmpp.socket.next_sent(timeout=1)

View File

@@ -287,7 +287,9 @@ class SCRAM(Mech):
if nonce[:len(self.cnonce)] != self.cnonce:
raise SASLCancelled('Invalid nonce')
cbind_data = self.credentials['channel_binding']
cbind_data = b''
if self.use_channel_binding:
cbind_data = self.credentials['channel_binding']
cbind_input = self.gs2_header + cbind_data
channel_binding = b'c=' + b64encode(cbind_input).replace(b'\n', b'')
@@ -530,6 +532,9 @@ else:
result = kerberos.authGSSClientStep(self.gss, b64_challenge)
if result != kerberos.AUTH_GSS_CONTINUE:
self.step = 1
elif not challenge:
kerberos.authGSSClientClean(self.gss)
return b''
elif self.step == 1:
username = self.credentials['username']
@@ -539,7 +544,7 @@ else:
resp = kerberos.authGSSClientResponse(self.gss)
except kerberos.GSSError as e:
raise SASLCancelled('Kerberos error: %s' % e.message)
raise SASLCancelled('Kerberos error: %s' % e)
if not resp:
return b''
else:

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.2.0'
__version_info__ = (1, 2, 0, '', 0)
__version__ = '1.3.0'
__version_info__ = (1, 3, 0, '', 0)

View File

@@ -32,10 +32,10 @@ log = logging.getLogger(__name__)
#: cd dnspython
#: git checkout python3
#: python3 setup.py install
USE_DNSPYTHON = False
DNSPYTHON_AVAILABLE = False
try:
import dns.resolver
USE_DNSPYTHON = True
DNSPYTHON_AVAILABLE = True
except ImportError as e:
log.debug("Could not find dnspython package. " + \
"Not all features will be available")
@@ -47,13 +47,13 @@ def default_resolver():
:returns: A :class:`dns.resolver.Resolver` object if dnspython
is available. Otherwise, ``None``.
"""
if USE_DNSPYTHON:
if DNSPYTHON_AVAILABLE:
return dns.resolver.get_default_resolver()
return None
def resolve(host, port=None, service=None, proto='tcp',
resolver=None, use_ipv6=True):
resolver=None, use_ipv6=True, use_dnspython=True):
"""Peform DNS resolution for a given hostname.
Resolution may perform SRV record lookups if a service and protocol
@@ -77,6 +77,9 @@ def resolve(host, port=None, service=None, proto='tcp',
:param use_ipv6: Optionally control the use of IPv6 in situations
where it is either not available, or performance
is degraded. Defaults to ``True``.
:param use_dnspython: Optionally control if dnspython is used to make
the DNS queries instead of the built-in DNS
library.
:type host: string
:type port: int
@@ -84,14 +87,22 @@ def resolve(host, port=None, service=None, proto='tcp',
:type proto: string
:type resolver: :class:`dns.resolver.Resolver`
:type use_ipv6: bool
:type use_dnspython: bool
:return: An iterable of IP address, port pairs in the order
dictated by SRV priorities and weights, if applicable.
"""
if not use_dnspython:
if DNSPYTHON_AVAILABLE:
log.debug("DNS: Not using dnspython, but dnspython is installed.")
else:
log.debug("DNS: Not using dnspython.")
if not use_ipv6:
log.debug("DNS: Use of IPv6 has been disabled.")
if resolver is None and USE_DNSPYTHON:
if resolver is None and DNSPYTHON_AVAILABLE and use_dnspython:
resolver = dns.resolver.get_default_resolver()
# An IPv6 literal is allowed to be enclosed in square brackets, but
@@ -122,7 +133,9 @@ def resolve(host, port=None, service=None, proto='tcp',
if not service:
hosts = [(host, port)]
else:
hosts = get_SRV(host, port, service, proto, resolver=resolver)
hosts = get_SRV(host, port, service, proto,
resolver=resolver,
use_dnspython=use_dnspython)
for host, port in hosts:
results = []
@@ -131,16 +144,18 @@ def resolve(host, port=None, service=None, proto='tcp',
results.append((host, '::1', port))
results.append((host, '127.0.0.1', port))
if use_ipv6:
for address in get_AAAA(host):
for address in get_AAAA(host, resolver=resolver,
use_dnspython=use_dnspython):
results.append((host, address, port))
for address in get_A(host):
for address in get_A(host, resolver=resolver,
use_dnspython=use_dnspython):
results.append((host, address, port))
for host, address, port in results:
yield host, address, port
def get_A(host):
def get_A(host, resolver=None, use_dnspython=True):
"""Lookup DNS A records for a given host.
If ``resolver`` is not provided, or is ``None``, then resolution will
@@ -148,9 +163,13 @@ def get_A(host):
:param host: The hostname to resolve for A record IPv4 addresses.
:param resolver: Optional DNS resolver object to use for the query.
:param use_dnspython: Optionally control if dnspython is used to make
the DNS queries instead of the built-in DNS
library.
:type host: string
:type resolver: :class:`dns.resolver.Resolver` or ``None``
:type use_dnspython: bool
:return: A list of IPv4 literals.
"""
@@ -158,15 +177,32 @@ def get_A(host):
# If not using dnspython, attempt lookup using the OS level
# getaddrinfo() method.
if resolver is None or not use_dnspython:
try:
recs = socket.getaddrinfo(host, None, socket.AF_INET,
socket.SOCK_STREAM)
return [rec[4][0] for rec in recs]
except socket.gaierror:
log.debug("DNS: Error retreiving A address info for %s." % host)
return []
# Using dnspython:
try:
recs = socket.getaddrinfo(host, None, socket.AF_INET,
socket.SOCK_STREAM)
return [rec[4][0] for rec in recs]
except socket.gaierror:
log.debug("DNS: Error retreiving A address info for %s." % host)
recs = resolver.query(host, dns.rdatatype.A)
return [rec.to_text() for rec in recs]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
log.debug("DNS: No A records for %s" % host)
return []
except dns.exception.Timeout:
log.debug("DNS: A record resolution timed out for %s" % host)
return []
except dns.exception.DNSException as e:
log.debug("DNS: Error querying A records for %s" % host)
log.exception(e)
return []
def get_AAAA(host):
def get_AAAA(host, resolver=None, use_dnspython=True):
"""Lookup DNS AAAA records for a given host.
If ``resolver`` is not provided, or is ``None``, then resolution will
@@ -174,9 +210,13 @@ def get_AAAA(host):
:param host: The hostname to resolve for AAAA record IPv6 addresses.
:param resolver: Optional DNS resolver object to use for the query.
:param use_dnspython: Optionally control if dnspython is used to make
the DNS queries instead of the built-in DNS
library.
:type host: string
:type resolver: :class:`dns.resolver.Resolver` or ``None``
:type use_dnspython: bool
:return: A list of IPv6 literals.
"""
@@ -184,19 +224,36 @@ def get_AAAA(host):
# If not using dnspython, attempt lookup using the OS level
# getaddrinfo() method.
if not socket.has_ipv6:
log.debug("Unable to query %s for AAAA records: IPv6 is not supported", host)
return []
if resolver is None or not use_dnspython:
if not socket.has_ipv6:
log.debug("Unable to query %s for AAAA records: IPv6 is not supported", host)
return []
try:
recs = socket.getaddrinfo(host, None, socket.AF_INET6,
socket.SOCK_STREAM)
return [rec[4][0] for rec in recs]
except (OSError, socket.gaierror):
log.debug("DNS: Error retreiving AAAA address " + \
"info for %s." % host)
return []
# Using dnspython:
try:
recs = socket.getaddrinfo(host, None, socket.AF_INET6,
socket.SOCK_STREAM)
return [rec[4][0] for rec in recs]
except (OSError, socket.gaierror):
log.debug("DNS: Error retreiving AAAA address " + \
"info for %s." % host)
recs = resolver.query(host, dns.rdatatype.AAAA)
return [rec.to_text() for rec in recs]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
log.debug("DNS: No AAAA records for %s" % host)
return []
except dns.exception.Timeout:
log.debug("DNS: AAAA record resolution timed out for %s" % host)
return []
except dns.exception.DNSException as e:
log.debug("DNS: Error querying AAAA records for %s" % host)
log.exception(e)
return []
def get_SRV(host, port, service, proto='tcp', resolver=None):
def get_SRV(host, port, service, proto='tcp', resolver=None, use_dnspython=True):
"""Perform SRV record resolution for a given host.
.. note::
@@ -222,7 +279,7 @@ def get_SRV(host, port, service, proto='tcp', resolver=None):
:return: A list of hostname, port pairs in the order dictacted
by SRV priorities and weights.
"""
if resolver is None:
if resolver is None or not use_dnspython:
log.warning("DNS: dnspython not found. Can not use SRV lookup.")
return [(host, port)]

View File

@@ -224,6 +224,11 @@ class XMLStream(object):
#: If set to ``True``, attempt to use IPv6.
self.use_ipv6 = True
#: If set to ``True``, allow using the ``dnspython`` DNS library
#: if available. If set to ``False``, the builtin DNS resolver
#: will be used, even if ``dnspython`` is installed.
self.use_dnspython = True
#: Use CDATA for escaping instead of XML entities. Defaults
#: to ``False``.
self.use_cdata = False
@@ -514,11 +519,11 @@ class XMLStream(object):
cert_policy = ssl.CERT_REQUIRED
ssl_args = {
'certfile': self.certfile,
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
'do_handshake_on_connect': False,
b'certfile': self.certfile,
b'keyfile': self.keyfile,
b'ca_certs': self.ca_certs,
b'cert_reqs': cert_policy,
b'do_handshake_on_connect': False,
}
if sys.version_info >= (2, 7):
@@ -611,7 +616,7 @@ class XMLStream(object):
headers = '\r\n'.join(headers) + '\r\n\r\n'
try:
log.debug("Connecting to proxy: %s:%s", address)
log.debug("Connecting to proxy: %s:%s", *address)
self.socket.connect(address)
self.send_raw(headers, now=True)
resp = ''
@@ -838,11 +843,11 @@ class XMLStream(object):
cert_policy = ssl.CERT_REQUIRED
ssl_args = {
'certfile': self.certfile,
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
'do_handshake_on_connect': False,
b'certfile': self.certfile,
b'keyfile': self.keyfile,
b'ca_certs': self.ca_certs,
b'cert_reqs': cert_policy,
b'do_handshake_on_connect': False,
}
if sys.version_info >= (2, 7):
@@ -1081,7 +1086,8 @@ class XMLStream(object):
return resolve(domain, port, service=self.dns_service,
resolver=resolver,
use_ipv6=self.use_ipv6)
use_ipv6=self.use_ipv6,
use_dnspython=self.use_dnspython)
def pick_dns_answer(self, domain, port=None):
"""Pick a server and port from DNS answers.