From a88b9737ffb1b0fc0c1962a501595ff2375e6619 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 4 Sep 2012 19:42:49 -0700 Subject: Add support for XEP-0235: OAuth over XMPP --- sleekxmpp/plugins/__init__.py | 1 + sleekxmpp/plugins/xep_0235/__init__.py | 16 +++++++ sleekxmpp/plugins/xep_0235/oauth.py | 32 ++++++++++++++ sleekxmpp/plugins/xep_0235/stanza.py | 80 ++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0235/__init__.py create mode 100644 sleekxmpp/plugins/xep_0235/oauth.py create mode 100644 sleekxmpp/plugins/xep_0235/stanza.py (limited to 'sleekxmpp') diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index e76a4318..d0afc950 100644 --- a/sleekxmpp/plugins/__init__.py +++ b/sleekxmpp/plugins/__init__.py @@ -60,6 +60,7 @@ __all__ = [ 'xep_0223', # Persistent Storage of Private Data via Pubsub 'xep_0224', # Attention 'xep_0231', # Bits of Binary + 'xep_0235', # OAuth Over XMPP 'xep_0242', # XMPP Client Compliance 2009 'xep_0249', # Direct MUC Invitations 'xep_0256', # Last Activity in Presence diff --git a/sleekxmpp/plugins/xep_0235/__init__.py b/sleekxmpp/plugins/xep_0235/__init__.py new file mode 100644 index 00000000..29d4408a --- /dev/null +++ b/sleekxmpp/plugins/xep_0235/__init__.py @@ -0,0 +1,16 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.base import register_plugin + +from sleekxmpp.plugins.xep_0235 import stanza +from sleekxmpp.plugins.xep_0235.stanza import OAuth +from sleekxmpp.plugins.xep_0235.oauth import XEP_0235 + + +register_plugin(XEP_0235) diff --git a/sleekxmpp/plugins/xep_0235/oauth.py b/sleekxmpp/plugins/xep_0235/oauth.py new file mode 100644 index 00000000..df0e2ebf --- /dev/null +++ b/sleekxmpp/plugins/xep_0235/oauth.py @@ -0,0 +1,32 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + + +import logging + +from sleekxmpp import Message +from sleekxmpp.plugins import BasePlugin +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.plugins.xep_0235 import stanza, OAuth + + +class XEP_0235(BasePlugin): + + name = 'xep_0235' + description = 'XEP-0235: OAuth Over XMPP' + dependencies = set(['xep_0030']) + stanza = stanza + + def plugin_init(self): + register_stanza_plugin(Message, OAuth) + + def session_bind(self, jid): + self.xmpp['xep_0030'].add_feature('urn:xmpp:oauth:0') + + def plugin_end(self): + self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:oauth:0') diff --git a/sleekxmpp/plugins/xep_0235/stanza.py b/sleekxmpp/plugins/xep_0235/stanza.py new file mode 100644 index 00000000..0050d583 --- /dev/null +++ b/sleekxmpp/plugins/xep_0235/stanza.py @@ -0,0 +1,80 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import hmac +import hashlib +import urllib +import base64 + +from sleekxmpp.xmlstream import ET, ElementBase, JID + + +class OAuth(ElementBase): + + name = 'oauth' + namespace = 'urn:xmpp:oauth:0' + plugin_attrib = 'oauth' + interfaces = set(['oauth_consumer_key', 'oauth_nonce', 'oauth_signature', + 'oauth_signature_method', 'oauth_timestamp', + 'oauth_token', 'oauth_version']) + sub_interfaces = interfaces + + def generate_signature(self, stanza, sfrom, sto, consumer_secret, + token_secret, method='HMAC-SHA1'): + self['oauth_signature_method'] = method + + request = urllib.quote('%s&%s' % (sfrom, sto), '') + parameters = urllib.quote('&'.join([ + 'oauth_consumer_key=%s' % self['oauth_consumer_key'], + 'oauth_nonce=%s' % self['oauth_nonce'], + 'oauth_signature_method=%s' % self['oauth_signature_method'], + 'oauth_timestamp=%s' % self['oauth_timestamp'], + 'oauth_token=%s' % self['oauth_token'], + 'oauth_version=%s' % self['oauth_version'] + ]), '') + + sigbase = '%s&%s&%s' % (stanza, request, parameters) + + consumer_secret = urllib.quote(consumer_secret, '') + token_secret = urllib.quote(token_secret, '') + key = '%s&%s' % (consumer_secret, token_secret) + + if method == 'HMAC-SHA1': + sig = base64.b64encode(hmac.new(key, sigbase, hashlib.sha1).digest()) + elif method == 'PLAINTEXT': + sig = key + + self['oauth_signature'] = sig + return sig + + def verify_signature(self, stanza, sfrom, sto, consumer_secret, + token_secret): + method = self['oauth_signature_method'] + + request = urllib.quote('%s&%s' % (sfrom, sto), '') + parameters = urllib.quote('&'.join([ + 'oauth_consumer_key=%s' % self['oauth_consumer_key'], + 'oauth_nonce=%s' % self['oauth_nonce'], + 'oauth_signature_method=%s' % self['oauth_signature_method'], + 'oauth_timestamp=%s' % self['oauth_timestamp'], + 'oauth_token=%s' % self['oauth_token'], + 'oauth_version=%s' % self['oauth_version'] + ]), '') + + sigbase = '%s&%s&%s' % (stanza, request, parameters) + + consumer_secret = urllib.quote(consumer_secret, '') + token_secret = urllib.quote(token_secret, '') + key = '%s&%s' % (consumer_secret, token_secret) + + if method == 'HMAC-SHA1': + sig = base64.b64encode(hmac.new(key, sigbase, hashlib.sha1).digest()) + elif method == 'PLAINTEXT': + sig = key + + return self['oauth_signature'] == sig -- cgit v1.2.3 From 0016d9a638dfe3aca7cc526ad78f7c3f9971a591 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 4 Sep 2012 20:39:43 -0700 Subject: Add support for XEP-0279: Server IP Check --- sleekxmpp/plugins/__init__.py | 1 + sleekxmpp/plugins/xep_0279/__init__.py | 16 ++++++++++++++ sleekxmpp/plugins/xep_0279/ipcheck.py | 39 ++++++++++++++++++++++++++++++++++ sleekxmpp/plugins/xep_0279/stanza.py | 30 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0279/__init__.py create mode 100644 sleekxmpp/plugins/xep_0279/ipcheck.py create mode 100644 sleekxmpp/plugins/xep_0279/stanza.py (limited to 'sleekxmpp') diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index d0afc950..ad15eacb 100644 --- a/sleekxmpp/plugins/__init__.py +++ b/sleekxmpp/plugins/__init__.py @@ -66,5 +66,6 @@ __all__ = [ 'xep_0256', # Last Activity in Presence 'xep_0258', # Security Labels in XMPP 'xep_0270', # XMPP Compliance Suites 2010 + 'xep_0279', # Server IP Check 'xep_0302', # XMPP Compliance Suites 2012 ] diff --git a/sleekxmpp/plugins/xep_0279/__init__.py b/sleekxmpp/plugins/xep_0279/__init__.py new file mode 100644 index 00000000..93db9e7c --- /dev/null +++ b/sleekxmpp/plugins/xep_0279/__init__.py @@ -0,0 +1,16 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.base import register_plugin + +from sleekxmpp.plugins.xep_0279 import stanza +from sleekxmpp.plugins.xep_0279.stanza import IPCheck +from sleekxmpp.plugins.xep_0279.ipcheck import XEP_0279 + + +register_plugin(XEP_0279) diff --git a/sleekxmpp/plugins/xep_0279/ipcheck.py b/sleekxmpp/plugins/xep_0279/ipcheck.py new file mode 100644 index 00000000..f8c167c7 --- /dev/null +++ b/sleekxmpp/plugins/xep_0279/ipcheck.py @@ -0,0 +1,39 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + + +import logging + +from sleekxmpp import Iq +from sleekxmpp.plugins import BasePlugin +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.plugins.xep_0279 import stanza, IPCheck + + +class XEP_0279(BasePlugin): + + name = 'xep_0279' + description = 'XEP-0279: Server IP Check' + dependencies = set(['xep_0030']) + stanza = stanza + + def plugin_init(self): + register_stanza_plugin(Iq, IPCheck) + + def session_bind(self, jid): + self.xmpp['xep_0030'].add_feature('urn:xmpp:sic:0') + + def plugin_end(self): + self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:sic:0') + + def check_ip(self, ifrom=None, block=True, timeout=None, callback=None): + iq = self.xmpp.Iq() + iq['type'] = 'get' + iq['from'] = ifrom + iq.enable('ip_check') + return iq.send(block=block, timeout=timeout, callback=callback) diff --git a/sleekxmpp/plugins/xep_0279/stanza.py b/sleekxmpp/plugins/xep_0279/stanza.py new file mode 100644 index 00000000..181b5957 --- /dev/null +++ b/sleekxmpp/plugins/xep_0279/stanza.py @@ -0,0 +1,30 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.xmlstream import ElementBase + + +class IPCheck(ElementBase): + + name = 'ip' + namespace = 'urn:xmpp:sic:0' + plugin_attrib = 'ip_check' + interfaces = set(['ip_check']) + is_extension = True + + def get_ip_check(self): + return self.xml.text + + def set_ip_check(self, value): + if value: + self.xml.text = value + else: + self.xml.text = '' + + def del_ip_check(self): + self.xml.text = '' -- cgit v1.2.3 From 26fa9bd87e1c722c442bb793c77a4eeb649998c7 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 11 Sep 2012 20:28:28 -0700 Subject: Don't perform caps lookup if the disco info is already known. --- sleekxmpp/plugins/xep_0115/caps.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sleekxmpp') diff --git a/sleekxmpp/plugins/xep_0115/caps.py b/sleekxmpp/plugins/xep_0115/caps.py index 5130cc98..b7a346c0 100644 --- a/sleekxmpp/plugins/xep_0115/caps.py +++ b/sleekxmpp/plugins/xep_0115/caps.py @@ -143,6 +143,11 @@ class XEP_0115(BasePlugin): if str(existing_verstring) == str(pres['caps']['ver']): return + existing_caps = self.get_caps(verstring=pres['caps']['ver']) + if existing_caps is not None: + self.assign_verstring(pres['from'], pres['caps']['ver']) + return + if pres['caps']['hash'] not in self.hashes: try: log.debug("Unknown caps hash: %s", pres['caps']['hash']) -- cgit v1.2.3 From f65eb5eeea80adca1cadd3dde421fde030ad2dac Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 11 Sep 2012 20:29:22 -0700 Subject: Add support for Google's X-OAUTH2 SASL mechanism --- sleekxmpp/util/sasl/mechanisms.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sleekxmpp') diff --git a/sleekxmpp/util/sasl/mechanisms.py b/sleekxmpp/util/sasl/mechanisms.py index 5822a6e4..55ae44dd 100644 --- a/sleekxmpp/util/sasl/mechanisms.py +++ b/sleekxmpp/util/sasl/mechanisms.py @@ -123,6 +123,17 @@ class X_MESSENGER_OAUTH2(Mech): return self.credentials['access_token'] +@sasl_mech(10) +class X_OAUTH2(Mech): + + name = 'X-OAUTH2' + required_credentials = set(['username', 'access_token']) + + def process(self, challenge=b''): + return b'\x00' + self.credentials['username'] + \ + b'\x00' + self.credentials['access_token'] + + @sasl_mech(3) class X_GOOGLE_TOKEN(Mech): -- cgit v1.2.3 From cf28d4586d32082f10578770a14eb74a68b902de Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 11 Sep 2012 20:39:32 -0700 Subject: Add support for XEP-0049: Private XML Storage --- sleekxmpp/plugins/__init__.py | 1 + sleekxmpp/plugins/xep_0049/__init__.py | 15 ++++++++ sleekxmpp/plugins/xep_0049/private_storage.py | 53 +++++++++++++++++++++++++++ sleekxmpp/plugins/xep_0049/stanza.py | 17 +++++++++ 4 files changed, 86 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0049/__init__.py create mode 100644 sleekxmpp/plugins/xep_0049/private_storage.py create mode 100644 sleekxmpp/plugins/xep_0049/stanza.py (limited to 'sleekxmpp') diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index ad15eacb..ed82c185 100644 --- a/sleekxmpp/plugins/__init__.py +++ b/sleekxmpp/plugins/__init__.py @@ -24,6 +24,7 @@ __all__ = [ 'xep_0033', # Extended Stanza Addresses 'xep_0045', # Multi-User Chat (Client) 'xep_0047', # In-Band Bytestreams + 'xep_0049', # Private XML Storage 'xep_0050', # Ad-hoc Commands 'xep_0054', # vcard-temp 'xep_0059', # Result Set Management diff --git a/sleekxmpp/plugins/xep_0049/__init__.py b/sleekxmpp/plugins/xep_0049/__init__.py new file mode 100644 index 00000000..b0c4f904 --- /dev/null +++ b/sleekxmpp/plugins/xep_0049/__init__.py @@ -0,0 +1,15 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.base import register_plugin + +from sleekxmpp.plugins.xep_0049.stanza import PrivateXML +from sleekxmpp.plugins.xep_0049.private_storage import XEP_0049 + + +register_plugin(XEP_0049) diff --git a/sleekxmpp/plugins/xep_0049/private_storage.py b/sleekxmpp/plugins/xep_0049/private_storage.py new file mode 100644 index 00000000..ef6cbdde --- /dev/null +++ b/sleekxmpp/plugins/xep_0049/private_storage.py @@ -0,0 +1,53 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp import Iq +from sleekxmpp.plugins import BasePlugin +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.xmlstream.matcher import StanzaPath +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.plugins.xep_0049 import stanza, PrivateXML + + +log = logging.getLogger(__name__) + + +class XEP_0049(BasePlugin): + + name = 'xep_0049' + description = 'XEP-0049: Private XML Storage' + dependencies = set([]) + stanza = stanza + + def plugin_init(self): + register_stanza_plugin(Iq, PrivateXML) + + def register(self, stanza): + register_stanza_plugin(PrivateXML, stanza, iterable=True) + + def store(self, data, ifrom=None, block=True, timeout=None, callback=None): + iq = self.xmpp.Iq() + iq['type'] = 'set' + iq['from'] = ifrom + + if not isinstance(data, list): + data = [data] + + for elem in data: + iq['private'].append(elem) + + return iq.send(block=block, timeout=timeout, callback=callback) + + def retrieve(self, name, ifrom=None, block=True, timeout=None, callback=None): + iq = self.xmpp.Iq() + iq['type'] = 'get' + iq['from'] = ifrom + iq['private'].enable(name) + return iq.send(block=block, timeout=timeout, callback=callback) diff --git a/sleekxmpp/plugins/xep_0049/stanza.py b/sleekxmpp/plugins/xep_0049/stanza.py new file mode 100644 index 00000000..d424e2f0 --- /dev/null +++ b/sleekxmpp/plugins/xep_0049/stanza.py @@ -0,0 +1,17 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.xmlstream import ET, ElementBase + + +class PrivateXML(ElementBase): + + name = 'query' + namespace = 'jabber:iq:private' + plugin_attrib = 'private' + interfaces = set() -- cgit v1.2.3 From fb3e6b7e35bb949f73a756ae5be683e2fec12454 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 13 Sep 2012 11:00:29 -0700 Subject: Don't break checking certs for localhost. --- sleekxmpp/xmlstream/cert.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sleekxmpp') diff --git a/sleekxmpp/xmlstream/cert.py b/sleekxmpp/xmlstream/cert.py index 339f872d..6e877efc 100644 --- a/sleekxmpp/xmlstream/cert.py +++ b/sleekxmpp/xmlstream/cert.py @@ -147,7 +147,10 @@ def verify(expected, raw_cert): raise CertificateError( 'Certificate has expired.') - expected_wild = expected[expected.index('.'):] + if '.' in expected: + expected_wild = expected[expected.index('.'):] + else: + expected_wild = expected expected_srv = '_xmpp-client.%s' % expected for name in cert_names['XMPPAddr']: @@ -160,7 +163,10 @@ def verify(expected, raw_cert): if name == expected: return True if name.startswith('*'): - name_wild = name[name.index('.'):] + if '.' in name: + name_wild = name[name.index('.'):] + else: + name_wild = name if expected_wild == name_wild: return True for name in cert_names['URI']: -- cgit v1.2.3