except ImportError:
from django.utils import simplejson
-from lib import oauth
+from lib import oauth2
import settings
class TwitterAuthConsumer(OAuthAbstractAuthConsumer):
from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
from django.utils.translation import ugettext as _
-from lib import oauth
+from lib import oauth2
class OAuthAbstractAuthConsumer(AuthenticationConsumer):
self.consumer_secret = consumer_secret
self.consumer_key = consumer_key
- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
- self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+ self.consumer = oauth2.Consumer(consumer_key, consumer_secret)
+ self.signature_method = oauth2.SignatureMethod_HMAC_SHA1()
self.server_url = server_url
self.request_token_url = request_token_url
if not unauthed_token:
raise InvalidAuthentication(_('Error, the oauth token is not on the server'))
- token = oauth.OAuthToken.from_string(unauthed_token)
+ token = oauth2.Token.from_string(unauthed_token)
if token.key != request.GET.get('oauth_token', 'no-token'):
raise InvalidAuthentication(_("Something went wrong! Auth tokens do not match"))
return {}
def fetch_request_token(self):
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
+ oauth_request = oauth2.Request.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
oauth_request.sign_request(self.signature_method, self.consumer, None)
- params = oauth_request.parameters
+ params = oauth_request
data = urllib.urlencode(params)
full_url='%s?%s'%(self.request_token_url, data)
response = urllib2.urlopen(full_url)
- return oauth.OAuthToken.from_string(response.read())
+ return oauth2.Token.from_string(response.read())
def authorize_token_url(self, token, callback_url=None):
- oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token,\
+ oauth_request = oauth2.Request.from_token_and_callback(token=token,\
callback=callback_url, http_url=self.authorization_url)
- params = oauth_request.parameters
+ params = oauth_request
data = urllib.urlencode(params)
full_url='%s?%s'%(self.authorization_url, data)
return full_url
def fetch_access_token(self, token):
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url)
+ oauth_request = oauth2.Request.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url)
oauth_request.sign_request(self.signature_method, self.consumer, token)
- params = oauth_request.parameters
+ params = oauth_request
data = urllib.urlencode(params)
full_url='%s?%s'%(self.access_token_url, data)
response = urllib2.urlopen(full_url)
- return oauth.OAuthToken.from_string(response.read())
+ return oauth2.Token.from_string(response.read())
def fetch_data(self, token, http_url, parameters=None):
- access_token = oauth.OAuthToken.from_string(token)
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+ access_token = oauth2.Token.from_string(token)
+ oauth_request = oauth2.Request.from_consumer_and_token(
self.consumer, token=access_token, http_method="GET",
http_url=http_url, parameters=parameters,
)
+++ /dev/null
-"""
-The MIT License
-
-Copyright (c) 2007 Leah Culver
-
-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.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-"""
-
-import cgi
-import urllib
-import time
-import random
-import urlparse
-import hmac
-import binascii
-
-
-VERSION = '1.0' # Hi Blaine!
-HTTP_METHOD = 'GET'
-SIGNATURE_METHOD = 'PLAINTEXT'
-
-
-class OAuthError(RuntimeError):
- """Generic exception class."""
- def __init__(self, message='OAuth error occured.'):
- self.message = message
-
-def build_authenticate_header(realm=''):
- """Optional WWW-Authenticate header (401 error)"""
- return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
-def escape(s):
- """Escape a URL including any /."""
- return urllib.quote(s, safe='~')
-
-def _utf8_str(s):
- """Convert unicode to utf-8."""
- if isinstance(s, unicode):
- return s.encode("utf-8")
- else:
- return str(s)
-
-def generate_timestamp():
- """Get seconds since epoch (UTC)."""
- return int(time.time())
-
-def generate_nonce(length=8):
- """Generate pseudorandom number."""
- return ''.join([str(random.randint(0, 9)) for i in range(length)])
-
-
-class OAuthConsumer(object):
- """Consumer of OAuth authentication.
-
- OAuthConsumer is a data type that represents the identity of the Consumer
- via its shared secret with the Service Provider.
-
- """
- key = None
- secret = None
-
- def __init__(self, key, secret):
- self.key = key
- self.secret = secret
-
-
-class OAuthToken(object):
- """OAuthToken is a data type that represents an End User via either an access
- or request token.
-
- key -- the token
- secret -- the token secret
-
- """
- key = None
- secret = None
-
- def __init__(self, key, secret):
- self.key = key
- self.secret = secret
-
- def to_string(self):
- return urllib.urlencode({'oauth_token': self.key,
- 'oauth_token_secret': self.secret})
-
- def from_string(s):
- """ Returns a token from something like:
- oauth_token_secret=xxx&oauth_token=xxx
- """
- params = cgi.parse_qs(s, keep_blank_values=False)
- key = params['oauth_token'][0]
- secret = params['oauth_token_secret'][0]
- return OAuthToken(key, secret)
- from_string = staticmethod(from_string)
-
- def __str__(self):
- return self.to_string()
-
-
-class OAuthRequest(object):
- """OAuthRequest represents the request and can be serialized.
-
- OAuth parameters:
- - oauth_consumer_key
- - oauth_token
- - oauth_signature_method
- - oauth_signature
- - oauth_timestamp
- - oauth_nonce
- - oauth_version
- ... any additional parameters, as defined by the Service Provider.
- """
- parameters = None # OAuth parameters.
- http_method = HTTP_METHOD
- http_url = None
- version = VERSION
-
- def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
- self.http_method = http_method
- self.http_url = http_url
- self.parameters = parameters or {}
-
- def set_parameter(self, parameter, value):
- self.parameters[parameter] = value
-
- def get_parameter(self, parameter):
- try:
- return self.parameters[parameter]
- except:
- raise OAuthError('Parameter not found: %s' % parameter)
-
- def _get_timestamp_nonce(self):
- return self.get_parameter('oauth_timestamp'), self.get_parameter(
- 'oauth_nonce')
-
- def get_nonoauth_parameters(self):
- """Get any non-OAuth parameters."""
- parameters = {}
- for k, v in self.parameters.iteritems():
- # Ignore oauth parameters.
- if k.find('oauth_') < 0:
- parameters[k] = v
- return parameters
-
- def to_header(self, realm=''):
- """Serialize as a header for an HTTPAuth request."""
- auth_header = 'OAuth realm="%s"' % realm
- # Add the oauth parameters.
- if self.parameters:
- for k, v in self.parameters.iteritems():
- if k[:6] == 'oauth_':
- auth_header += ', %s="%s"' % (k, escape(str(v)))
- return {'Authorization': auth_header}
-
- def to_postdata(self):
- """Serialize as post data for a POST request."""
- return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
- for k, v in self.parameters.iteritems()])
-
- def to_url(self):
- """Serialize as a URL for a GET request."""
- return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
-
- def get_normalized_parameters(self):
- """Return a string that contains the parameters that must be signed."""
- params = self.parameters
- try:
- # Exclude the signature if it exists.
- del params['oauth_signature']
- except:
- pass
- # Escape key values before sorting.
- key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
- for k,v in params.items()]
- # Sort lexicographically, first after key, then after value.
- key_values.sort()
- # Combine key value pairs into a string.
- return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
-
- def get_normalized_http_method(self):
- """Uppercases the http method."""
- return self.http_method.upper()
-
- def get_normalized_http_url(self):
- """Parses the URL and rebuilds it to be scheme://host/path."""
- parts = urlparse.urlparse(self.http_url)
- scheme, netloc, path = parts[:3]
- # Exclude default port numbers.
- if scheme == 'http' and netloc[-3:] == ':80':
- netloc = netloc[:-3]
- elif scheme == 'https' and netloc[-4:] == ':443':
- netloc = netloc[:-4]
- return '%s://%s%s' % (scheme, netloc, path)
-
- def sign_request(self, signature_method, consumer, token):
- """Set the signature parameter to the result of build_signature."""
- # Set the signature method.
- self.set_parameter('oauth_signature_method',
- signature_method.get_name())
- # Set the signature.
- self.set_parameter('oauth_signature',
- self.build_signature(signature_method, consumer, token))
-
- def build_signature(self, signature_method, consumer, token):
- """Calls the build signature method within the signature method."""
- return signature_method.build_signature(self, consumer, token)
-
- def from_request(http_method, http_url, headers=None, parameters=None,
- query_string=None):
- """Combines multiple parameter sources."""
- if parameters is None:
- parameters = {}
-
- # Headers
- if headers and 'Authorization' in headers:
- auth_header = headers['Authorization']
- # Check that the authorization header is OAuth.
- if auth_header.index('OAuth') > -1:
- auth_header = auth_header.lstrip('OAuth ')
- try:
- # Get the parameters from the header.
- header_params = OAuthRequest._split_header(auth_header)
- parameters.update(header_params)
- except:
- raise OAuthError('Unable to parse OAuth parameters from '
- 'Authorization header.')
-
- # GET or POST query string.
- if query_string:
- query_params = OAuthRequest._split_url_string(query_string)
- parameters.update(query_params)
-
- # URL parameters.
- param_str = urlparse.urlparse(http_url)[4] # query
- url_params = OAuthRequest._split_url_string(param_str)
- parameters.update(url_params)
-
- if parameters:
- return OAuthRequest(http_method, http_url, parameters)
-
- return None
- from_request = staticmethod(from_request)
-
- def from_consumer_and_token(oauth_consumer, token=None,
- http_method=HTTP_METHOD, http_url=None, parameters=None):
- if not parameters:
- parameters = {}
-
- defaults = {
- 'oauth_consumer_key': oauth_consumer.key,
- 'oauth_timestamp': generate_timestamp(),
- 'oauth_nonce': generate_nonce(),
- 'oauth_version': OAuthRequest.version,
- }
-
- defaults.update(parameters)
- parameters = defaults
-
- if token:
- parameters['oauth_token'] = token.key
-
- return OAuthRequest(http_method, http_url, parameters)
- from_consumer_and_token = staticmethod(from_consumer_and_token)
-
- def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
- http_url=None, parameters=None):
- if not parameters:
- parameters = {}
-
- parameters['oauth_token'] = token.key
-
- if callback:
- parameters['oauth_callback'] = callback
-
- return OAuthRequest(http_method, http_url, parameters)
- from_token_and_callback = staticmethod(from_token_and_callback)
-
- def _split_header(header):
- """Turn Authorization: header into parameters."""
- params = {}
- parts = header.split(',')
- for param in parts:
- # Ignore realm parameter.
- if param.find('realm') > -1:
- continue
- # Remove whitespace.
- param = param.strip()
- # Split key-value.
- param_parts = param.split('=', 1)
- # Remove quotes and unescape the value.
- params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
- return params
- _split_header = staticmethod(_split_header)
-
- def _split_url_string(param_str):
- """Turn URL string into parameters."""
- parameters = cgi.parse_qs(param_str, keep_blank_values=False)
- for k, v in parameters.iteritems():
- parameters[k] = urllib.unquote(v[0])
- return parameters
- _split_url_string = staticmethod(_split_url_string)
-
-class OAuthServer(object):
- """A worker to check the validity of a request against a data store."""
- timestamp_threshold = 300 # In seconds, five minutes.
- version = VERSION
- signature_methods = None
- data_store = None
-
- def __init__(self, data_store=None, signature_methods=None):
- self.data_store = data_store
- self.signature_methods = signature_methods or {}
-
- def set_data_store(self, data_store):
- self.data_store = data_store
-
- def get_data_store(self):
- return self.data_store
-
- def add_signature_method(self, signature_method):
- self.signature_methods[signature_method.get_name()] = signature_method
- return self.signature_methods
-
- def fetch_request_token(self, oauth_request):
- """Processes a request_token request and returns the
- request token on success.
- """
- try:
- # Get the request token for authorization.
- token = self._get_token(oauth_request, 'request')
- except OAuthError:
- # No token required for the initial token request.
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- self._check_signature(oauth_request, consumer, None)
- # Fetch a new token.
- token = self.data_store.fetch_request_token(consumer)
- return token
-
- def fetch_access_token(self, oauth_request):
- """Processes an access_token request and returns the
- access token on success.
- """
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- # Get the request token.
- token = self._get_token(oauth_request, 'request')
- self._check_signature(oauth_request, consumer, token)
- new_token = self.data_store.fetch_access_token(consumer, token)
- return new_token
-
- def verify_request(self, oauth_request):
- """Verifies an api call and checks all the parameters."""
- # -> consumer and token
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- # Get the access token.
- token = self._get_token(oauth_request, 'access')
- self._check_signature(oauth_request, consumer, token)
- parameters = oauth_request.get_nonoauth_parameters()
- return consumer, token, parameters
-
- def authorize_token(self, token, user):
- """Authorize a request token."""
- return self.data_store.authorize_request_token(token, user)
-
- def get_callback(self, oauth_request):
- """Get the callback URL."""
- return oauth_request.get_parameter('oauth_callback')
-
- def build_authenticate_header(self, realm=''):
- """Optional support for the authenticate header."""
- return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
- def _get_version(self, oauth_request):
- """Verify the correct version request for this server."""
- try:
- version = oauth_request.get_parameter('oauth_version')
- except:
- version = VERSION
- if version and version != self.version:
- raise OAuthError('OAuth version %s not supported.' % str(version))
- return version
-
- def _get_signature_method(self, oauth_request):
- """Figure out the signature with some defaults."""
- try:
- signature_method = oauth_request.get_parameter(
- 'oauth_signature_method')
- except:
- signature_method = SIGNATURE_METHOD
- try:
- # Get the signature method object.
- signature_method = self.signature_methods[signature_method]
- except:
- signature_method_names = ', '.join(self.signature_methods.keys())
- raise OAuthError('Signature method %s not supported try one of the '
- 'following: %s' % (signature_method, signature_method_names))
-
- return signature_method
-
- def _get_consumer(self, oauth_request):
- consumer_key = oauth_request.get_parameter('oauth_consumer_key')
- consumer = self.data_store.lookup_consumer(consumer_key)
- if not consumer:
- raise OAuthError('Invalid consumer.')
- return consumer
-
- def _get_token(self, oauth_request, token_type='access'):
- """Try to find the token for the provided request token key."""
- token_field = oauth_request.get_parameter('oauth_token')
- token = self.data_store.lookup_token(token_type, token_field)
- if not token:
- raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
- return token
-
- def _check_signature(self, oauth_request, consumer, token):
- timestamp, nonce = oauth_request._get_timestamp_nonce()
- self._check_timestamp(timestamp)
- self._check_nonce(consumer, token, nonce)
- signature_method = self._get_signature_method(oauth_request)
- try:
- signature = oauth_request.get_parameter('oauth_signature')
- except:
- raise OAuthError('Missing signature.')
- # Validate the signature.
- valid_sig = signature_method.check_signature(oauth_request, consumer,
- token, signature)
- if not valid_sig:
- key, base = signature_method.build_signature_base_string(
- oauth_request, consumer, token)
- raise OAuthError('Invalid signature. Expected signature base '
- 'string: %s' % base)
- built = signature_method.build_signature(oauth_request, consumer, token)
-
- def _check_timestamp(self, timestamp):
- """Verify that timestamp is recentish."""
- timestamp = int(timestamp)
- now = int(time.time())
- lapsed = now - timestamp
- if lapsed > self.timestamp_threshold:
- raise OAuthError('Expired timestamp: given %d and now %s has a '
- 'greater difference than threshold %d' %
- (timestamp, now, self.timestamp_threshold))
-
- def _check_nonce(self, consumer, token, nonce):
- """Verify that the nonce is uniqueish."""
- nonce = self.data_store.lookup_nonce(consumer, token, nonce)
- if nonce:
- raise OAuthError('Nonce already used: %s' % str(nonce))
-
-
-class OAuthClient(object):
- """OAuthClient is a worker to attempt to execute a request."""
- consumer = None
- token = None
-
- def __init__(self, oauth_consumer, oauth_token):
- self.consumer = oauth_consumer
- self.token = oauth_token
-
- def get_consumer(self):
- return self.consumer
-
- def get_token(self):
- return self.token
-
- def fetch_request_token(self, oauth_request):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_access_token(self, oauth_request):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def access_resource(self, oauth_request):
- """-> Some protected resource."""
- raise NotImplementedError
-
-
-class OAuthDataStore(object):
- """A database abstraction used to lookup consumers and tokens."""
-
- def lookup_consumer(self, key):
- """-> OAuthConsumer."""
- raise NotImplementedError
-
- def lookup_token(self, oauth_consumer, token_type, token_token):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_request_token(self, oauth_consumer):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_access_token(self, oauth_consumer, oauth_token):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def authorize_request_token(self, oauth_token, user):
- """-> OAuthToken."""
- raise NotImplementedError
-
-
-class OAuthSignatureMethod(object):
- """A strategy class that implements a signature method."""
- def get_name(self):
- """-> str."""
- raise NotImplementedError
-
- def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
- """-> str key, str raw."""
- raise NotImplementedError
-
- def build_signature(self, oauth_request, oauth_consumer, oauth_token):
- """-> str."""
- raise NotImplementedError
-
- def check_signature(self, oauth_request, consumer, token, signature):
- built = self.build_signature(oauth_request, consumer, token)
- return built == signature
-
-
-class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
-
- def get_name(self):
- return 'HMAC-SHA1'
-
- def build_signature_base_string(self, oauth_request, consumer, token):
- sig = (
- escape(oauth_request.get_normalized_http_method()),
- escape(oauth_request.get_normalized_http_url()),
- escape(oauth_request.get_normalized_parameters()),
- )
-
- key = '%s&' % escape(consumer.secret)
- if token:
- key += escape(token.secret)
- raw = '&'.join(sig)
- return key, raw
-
- def build_signature(self, oauth_request, consumer, token):
- """Builds the base signature string."""
- key, raw = self.build_signature_base_string(oauth_request, consumer,
- token)
-
- # HMAC object.
- try:
- import hashlib # 2.5
- hashed = hmac.new(key, raw, hashlib.sha1)
- except:
- import sha # Deprecated
- hashed = hmac.new(key, raw, sha)
-
- # Calculate the digest base 64.
- return binascii.b2a_base64(hashed.digest())[:-1]
-
-
-class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
-
- def get_name(self):
- return 'PLAINTEXT'
-
- def build_signature_base_string(self, oauth_request, consumer, token):
- """Concatenates the consumer key and secret."""
- sig = '%s&' % escape(consumer.secret)
- if token:
- sig = sig + escape(token.secret)
- return sig, sig
-
- def build_signature(self, oauth_request, consumer, token):
- key, raw = self.build_signature_base_string(oauth_request, consumer,
- token)
- return key
\ No newline at end of file
--- /dev/null
+"""
+The MIT License
+
+Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
+
+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.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import base64
+import urllib
+import time
+import random
+import urlparse
+import hmac
+import binascii
+import httplib2
+
+try:
+ from urlparse import parse_qs
+ parse_qs # placate pyflakes
+except ImportError:
+ # fall back for Python 2.5
+ from cgi import parse_qs
+
+try:
+ from hashlib import sha1
+ sha = sha1
+except ImportError:
+ # hashlib was added in Python 2.5
+ import sha
+
+import _version
+
+__version__ = _version.__version__
+
+OAUTH_VERSION = '1.0' # Hi Blaine!
+HTTP_METHOD = 'GET'
+SIGNATURE_METHOD = 'PLAINTEXT'
+
+
+class Error(RuntimeError):
+ """Generic exception class."""
+
+ def __init__(self, message='OAuth error occurred.'):
+ self._message = message
+
+ @property
+ def message(self):
+ """A hack to get around the deprecation errors in 2.6."""
+ return self._message
+
+ def __str__(self):
+ return self._message
+
+
+class MissingSignature(Error):
+ pass
+
+
+def build_authenticate_header(realm=''):
+ """Optional WWW-Authenticate header (401 error)"""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+
+def build_xoauth_string(url, consumer, token=None):
+ """Build an XOAUTH string for use in SMTP/IMPA authentication."""
+ request = Request.from_consumer_and_token(consumer, token,
+ "GET", url)
+
+ signing_method = SignatureMethod_HMAC_SHA1()
+ request.sign_request(signing_method, consumer, token)
+
+ params = []
+ for k, v in sorted(request.iteritems()):
+ if v is not None:
+ params.append('%s="%s"' % (k, escape(v)))
+
+ return "%s %s %s" % ("GET", url, ','.join(params))
+
+
+def to_unicode(s):
+ """ Convert to unicode, raise exception with instructive error
+ message if s is not unicode, ascii, or utf-8. """
+ if not isinstance(s, unicode):
+ if not isinstance(s, str):
+ raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s))
+ try:
+ s = s.decode('utf-8')
+ except UnicodeDecodeError, le:
+ raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,))
+ return s
+
+def to_utf8(s):
+ return to_unicode(s).encode('utf-8')
+
+def to_unicode_if_string(s):
+ if isinstance(s, basestring):
+ return to_unicode(s)
+ else:
+ return s
+
+def to_utf8_if_string(s):
+ if isinstance(s, basestring):
+ return to_utf8(s)
+ else:
+ return s
+
+def to_unicode_optional_iterator(x):
+ """
+ Raise TypeError if x is a str containing non-utf8 bytes or if x is
+ an iterable which contains such a str.
+ """
+ if isinstance(x, basestring):
+ return to_unicode(x)
+
+ try:
+ l = list(x)
+ except TypeError, e:
+ assert 'is not iterable' in str(e)
+ return x
+ else:
+ return [ to_unicode(e) for e in l ]
+
+def to_utf8_optional_iterator(x):
+ """
+ Raise TypeError if x is a str or if x is an iterable which
+ contains a str.
+ """
+ if isinstance(x, basestring):
+ return to_utf8(x)
+
+ try:
+ l = list(x)
+ except TypeError, e:
+ assert 'is not iterable' in str(e)
+ return x
+ else:
+ return [ to_utf8_if_string(e) for e in l ]
+
+def escape(s):
+ """Escape a URL including any /."""
+ return urllib.quote(s.encode('utf-8'), safe='~')
+
+def generate_timestamp():
+ """Get seconds since epoch (UTC)."""
+ return int(time.time())
+
+
+def generate_nonce(length=8):
+ """Generate pseudorandom number."""
+ return ''.join([str(random.randint(0, 9)) for i in range(length)])
+
+
+def generate_verifier(length=8):
+ """Generate pseudorandom number."""
+ return ''.join([str(random.randint(0, 9)) for i in range(length)])
+
+
+class Consumer(object):
+ """A consumer of OAuth-protected services.
+
+ The OAuth consumer is a "third-party" service that wants to access
+ protected resources from an OAuth service provider on behalf of an end
+ user. It's kind of the OAuth client.
+
+ Usually a consumer must be registered with the service provider by the
+ developer of the consumer software. As part of that process, the service
+ provider gives the consumer a *key* and a *secret* with which the consumer
+ software can identify itself to the service. The consumer will include its
+ key in each request to identify itself, but will use its secret only when
+ signing requests, to prove that the request is from that particular
+ registered consumer.
+
+ Once registered, the consumer can then use its consumer credentials to ask
+ the service provider for a request token, kicking off the OAuth
+ authorization process.
+ """
+
+ key = None
+ secret = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+ if self.key is None or self.secret is None:
+ raise ValueError("Key and secret must be set.")
+
+ def __str__(self):
+ data = {'oauth_consumer_key': self.key,
+ 'oauth_consumer_secret': self.secret}
+
+ return urllib.urlencode(data)
+
+
+class Token(object):
+ """An OAuth credential used to request authorization or a protected
+ resource.
+
+ Tokens in OAuth comprise a *key* and a *secret*. The key is included in
+ requests to identify the token being used, but the secret is used only in
+ the signature, to prove that the requester is who the server gave the
+ token to.
+
+ When first negotiating the authorization, the consumer asks for a *request
+ token* that the live user authorizes with the service provider. The
+ consumer then exchanges the request token for an *access token* that can
+ be used to access protected resources.
+ """
+
+ key = None
+ secret = None
+ callback = None
+ callback_confirmed = None
+ verifier = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+ if self.key is None or self.secret is None:
+ raise ValueError("Key and secret must be set.")
+
+ def set_callback(self, callback):
+ self.callback = callback
+ self.callback_confirmed = 'true'
+
+ def set_verifier(self, verifier=None):
+ if verifier is not None:
+ self.verifier = verifier
+ else:
+ self.verifier = generate_verifier()
+
+ def get_callback_url(self):
+ if self.callback and self.verifier:
+ # Append the oauth_verifier.
+ parts = urlparse.urlparse(self.callback)
+ scheme, netloc, path, params, query, fragment = parts[:6]
+ if query:
+ query = '%s&oauth_verifier=%s' % (query, self.verifier)
+ else:
+ query = 'oauth_verifier=%s' % self.verifier
+ return urlparse.urlunparse((scheme, netloc, path, params,
+ query, fragment))
+ return self.callback
+
+ def to_string(self):
+ """Returns this token as a plain string, suitable for storage.
+
+ The resulting string includes the token's secret, so you should never
+ send or store this string where a third party can read it.
+ """
+
+ data = {
+ 'oauth_token': self.key,
+ 'oauth_token_secret': self.secret,
+ }
+
+ if self.callback_confirmed is not None:
+ data['oauth_callback_confirmed'] = self.callback_confirmed
+ return urllib.urlencode(data)
+
+ @staticmethod
+ def from_string(s):
+ """Deserializes a token from a string like one returned by
+ `to_string()`."""
+
+ if not len(s):
+ raise ValueError("Invalid parameter string.")
+
+ params = parse_qs(s, keep_blank_values=False)
+ if not len(params):
+ raise ValueError("Invalid parameter string.")
+
+ try:
+ key = params['oauth_token'][0]
+ except Exception:
+ raise ValueError("'oauth_token' not found in OAuth request.")
+
+ try:
+ secret = params['oauth_token_secret'][0]
+ except Exception:
+ raise ValueError("'oauth_token_secret' not found in "
+ "OAuth request.")
+
+ token = Token(key, secret)
+ try:
+ token.callback_confirmed = params['oauth_callback_confirmed'][0]
+ except KeyError:
+ pass # 1.0, no callback confirmed.
+ return token
+
+ def __str__(self):
+ return self.to_string()
+
+
+def setter(attr):
+ name = attr.__name__
+
+ def getter(self):
+ try:
+ return self.__dict__[name]
+ except KeyError:
+ raise AttributeError(name)
+
+ def deleter(self):
+ del self.__dict__[name]
+
+ return property(getter, attr, deleter)
+
+
+class Request(dict):
+
+ """The parameters and information for an HTTP request, suitable for
+ authorizing with OAuth credentials.
+
+ When a consumer wants to access a service's protected resources, it does
+ so using a signed HTTP request identifying itself (the consumer) with its
+ key, and providing an access token authorized by the end user to access
+ those resources.
+
+ """
+
+ version = OAUTH_VERSION
+
+ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
+ body='', is_form_encoded=False):
+ if url is not None:
+ self.url = to_unicode(url)
+ self.method = method
+ if parameters is not None:
+ for k, v in parameters.iteritems():
+ k = to_unicode(k)
+ v = to_unicode_optional_iterator(v)
+ self[k] = v
+ self.body = body
+ self.is_form_encoded = is_form_encoded
+
+
+ @setter
+ def url(self, value):
+ self.__dict__['url'] = value
+ if value is not None:
+ scheme, netloc, path, params, query, fragment = urlparse.urlparse(value)
+
+ # Exclude default port numbers.
+ if scheme == 'http' and netloc[-3:] == ':80':
+ netloc = netloc[:-3]
+ elif scheme == 'https' and netloc[-4:] == ':443':
+ netloc = netloc[:-4]
+ if scheme not in ('http', 'https'):
+ raise ValueError("Unsupported URL %s (%s)." % (value, scheme))
+
+ # Normalized URL excludes params, query, and fragment.
+ self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None))
+ else:
+ self.normalized_url = None
+ self.__dict__['url'] = None
+
+ @setter
+ def method(self, value):
+ self.__dict__['method'] = value.upper()
+
+ def _get_timestamp_nonce(self):
+ return self['oauth_timestamp'], self['oauth_nonce']
+
+ def get_nonoauth_parameters(self):
+ """Get any non-OAuth parameters."""
+ return dict([(k, v) for k, v in self.iteritems()
+ if not k.startswith('oauth_')])
+
+ def to_header(self, realm=''):
+ """Serialize as a header for an HTTPAuth request."""
+ oauth_params = ((k, v) for k, v in self.items()
+ if k.startswith('oauth_'))
+ stringy_params = ((k, escape(str(v))) for k, v in oauth_params)
+ header_params = ('%s="%s"' % (k, v) for k, v in stringy_params)
+ params_header = ', '.join(header_params)
+
+ auth_header = 'OAuth realm="%s"' % realm
+ if params_header:
+ auth_header = "%s, %s" % (auth_header, params_header)
+
+ return {'Authorization': auth_header}
+
+ def to_postdata(self):
+ """Serialize as post data for a POST request."""
+ d = {}
+ for k, v in self.iteritems():
+ d[k.encode('utf-8')] = to_utf8_optional_iterator(v)
+
+ # tell urlencode to deal with sequence values and map them correctly
+ # to resulting querystring. for example self["k"] = ["v1", "v2"] will
+ # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D
+ return urllib.urlencode(d, True).replace('+', '%20')
+
+ def to_url(self):
+ """Serialize as a URL for a GET request."""
+ base_url = urlparse.urlparse(self.url)
+ try:
+ query = base_url.query
+ except AttributeError:
+ # must be python <2.5
+ query = base_url[4]
+ query = parse_qs(query)
+ for k, v in self.items():
+ query.setdefault(k, []).append(v)
+
+ try:
+ scheme = base_url.scheme
+ netloc = base_url.netloc
+ path = base_url.path
+ params = base_url.params
+ fragment = base_url.fragment
+ except AttributeError:
+ # must be python <2.5
+ scheme = base_url[0]
+ netloc = base_url[1]
+ path = base_url[2]
+ params = base_url[3]
+ fragment = base_url[5]
+
+ url = (scheme, netloc, path, params,
+ urllib.urlencode(query, True), fragment)
+ return urlparse.urlunparse(url)
+
+ def get_parameter(self, parameter):
+ ret = self.get(parameter)
+ if ret is None:
+ raise Error('Parameter not found: %s' % parameter)
+
+ return ret
+
+ def get_normalized_parameters(self):
+ """Return a string that contains the parameters that must be signed."""
+ items = []
+ for key, value in self.iteritems():
+ if key == 'oauth_signature':
+ continue
+ # 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
+ # so we unpack sequence values into multiple items for sorting.
+ if isinstance(value, basestring):
+ items.append((to_utf8_if_string(key), to_utf8(value)))
+ else:
+ try:
+ value = list(value)
+ except TypeError, e:
+ assert 'is not iterable' in str(e)
+ items.append((to_utf8_if_string(key), to_utf8_if_string(value)))
+ else:
+ items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value)
+
+ # Include any query string parameters from the provided URL
+ query = urlparse.urlparse(self.url)[4]
+
+ url_items = self._split_url_string(query).items()
+ url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ]
+ items.extend(url_items)
+
+ items.sort()
+ encoded_str = urllib.urlencode(items)
+ # Encode signature parameters per Oauth Core 1.0 protocol
+ # spec draft 7, section 3.6
+ # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
+ # Spaces must be encoded with "%20" instead of "+"
+ return encoded_str.replace('+', '%20').replace('%7E', '~')
+
+ def sign_request(self, signature_method, consumer, token):
+ """Set the signature parameter to the result of sign."""
+
+ if not self.is_form_encoded:
+ # according to
+ # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
+ # section 4.1.1 "OAuth Consumers MUST NOT include an
+ # oauth_body_hash parameter on requests with form-encoded
+ # request bodies."
+ self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest())
+
+ if 'oauth_consumer_key' not in self:
+ self['oauth_consumer_key'] = consumer.key
+
+ if token and 'oauth_token' not in self:
+ self['oauth_token'] = token.key
+
+ self['oauth_signature_method'] = signature_method.name
+ self['oauth_signature'] = signature_method.sign(self, consumer, token)
+
+ @classmethod
+ def make_timestamp(cls):
+ """Get seconds since epoch (UTC)."""
+ return str(int(time.time()))
+
+ @classmethod
+ def make_nonce(cls):
+ """Generate pseudorandom number."""
+ return str(random.randint(0, 100000000))
+
+ @classmethod
+ def from_request(cls, http_method, http_url, headers=None, parameters=None,
+ query_string=None):
+ """Combines multiple parameter sources."""
+ if parameters is None:
+ parameters = {}
+
+ # Headers
+ if headers and 'Authorization' in headers:
+ auth_header = headers['Authorization']
+ # Check that the authorization header is OAuth.
+ if auth_header[:6] == 'OAuth ':
+ auth_header = auth_header[6:]
+ try:
+ # Get the parameters from the header.
+ header_params = cls._split_header(auth_header)
+ parameters.update(header_params)
+ except:
+ raise Error('Unable to parse OAuth parameters from '
+ 'Authorization header.')
+
+ # GET or POST query string.
+ if query_string:
+ query_params = cls._split_url_string(query_string)
+ parameters.update(query_params)
+
+ # URL parameters.
+ param_str = urlparse.urlparse(http_url)[4] # query
+ url_params = cls._split_url_string(param_str)
+ parameters.update(url_params)
+
+ if parameters:
+ return cls(http_method, http_url, parameters)
+
+ return None
+
+ @classmethod
+ def from_consumer_and_token(cls, consumer, token=None,
+ http_method=HTTP_METHOD, http_url=None, parameters=None,
+ body='', is_form_encoded=False):
+ if not parameters:
+ parameters = {}
+
+ defaults = {
+ 'oauth_consumer_key': consumer.key,
+ 'oauth_timestamp': cls.make_timestamp(),
+ 'oauth_nonce': cls.make_nonce(),
+ 'oauth_version': cls.version,
+ }
+
+ defaults.update(parameters)
+ parameters = defaults
+
+ if token:
+ parameters['oauth_token'] = token.key
+ if token.verifier:
+ parameters['oauth_verifier'] = token.verifier
+
+ return Request(http_method, http_url, parameters, body=body,
+ is_form_encoded=is_form_encoded)
+
+ @classmethod
+ def from_token_and_callback(cls, token, callback=None,
+ http_method=HTTP_METHOD, http_url=None, parameters=None):
+
+ if not parameters:
+ parameters = {}
+
+ parameters['oauth_token'] = token.key
+
+ if callback:
+ parameters['oauth_callback'] = callback
+
+ return cls(http_method, http_url, parameters)
+
+ @staticmethod
+ def _split_header(header):
+ """Turn Authorization: header into parameters."""
+ params = {}
+ parts = header.split(',')
+ for param in parts:
+ # Ignore realm parameter.
+ if param.find('realm') > -1:
+ continue
+ # Remove whitespace.
+ param = param.strip()
+ # Split key-value.
+ param_parts = param.split('=', 1)
+ # Remove quotes and unescape the value.
+ params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
+ return params
+
+ @staticmethod
+ def _split_url_string(param_str):
+ """Turn URL string into parameters."""
+ parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True)
+ for k, v in parameters.iteritems():
+ parameters[k] = urllib.unquote(v[0])
+ return parameters
+
+
+class Client(httplib2.Http):
+ """OAuthClient is a worker to attempt to execute a request."""
+
+ def __init__(self, consumer, token=None, cache=None, timeout=None,
+ proxy_info=None):
+
+ if consumer is not None and not isinstance(consumer, Consumer):
+ raise ValueError("Invalid consumer.")
+
+ if token is not None and not isinstance(token, Token):
+ raise ValueError("Invalid token.")
+
+ self.consumer = consumer
+ self.token = token
+ self.method = SignatureMethod_HMAC_SHA1()
+
+ httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info)
+
+ def set_signature_method(self, method):
+ if not isinstance(method, SignatureMethod):
+ raise ValueError("Invalid signature method.")
+
+ self.method = method
+
+ def request(self, uri, method="GET", body='', headers=None,
+ redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None):
+ DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded'
+
+ if not isinstance(headers, dict):
+ headers = {}
+
+ if method == "POST":
+ headers['Content-Type'] = headers.get('Content-Type',
+ DEFAULT_POST_CONTENT_TYPE)
+
+ is_form_encoded = \
+ headers.get('Content-Type') == 'application/x-www-form-urlencoded'
+
+ if is_form_encoded and body:
+ parameters = parse_qs(body)
+ else:
+ parameters = None
+
+ req = Request.from_consumer_and_token(self.consumer,
+ token=self.token, http_method=method, http_url=uri,
+ parameters=parameters, body=body, is_form_encoded=is_form_encoded)
+
+ req.sign_request(self.method, self.consumer, self.token)
+
+ schema, rest = urllib.splittype(uri)
+ if rest.startswith('//'):
+ hierpart = '//'
+ else:
+ hierpart = ''
+ host, rest = urllib.splithost(rest)
+
+ realm = schema + ':' + hierpart + host
+
+ if is_form_encoded:
+ body = req.to_postdata()
+ elif method == "GET":
+ uri = req.to_url()
+ else:
+ headers.update(req.to_header(realm=realm))
+
+ return httplib2.Http.request(self, uri, method=method, body=body,
+ headers=headers, redirections=redirections,
+ connection_type=connection_type)
+
+
+class Server(object):
+ """A skeletal implementation of a service provider, providing protected
+ resources to requests from authorized consumers.
+
+ This class implements the logic to check requests for authorization. You
+ can use it with your web server or web framework to protect certain
+ resources with OAuth.
+ """
+
+ timestamp_threshold = 300 # In seconds, five minutes.
+ version = OAUTH_VERSION
+ signature_methods = None
+
+ def __init__(self, signature_methods=None):
+ self.signature_methods = signature_methods or {}
+
+ def add_signature_method(self, signature_method):
+ self.signature_methods[signature_method.name] = signature_method
+ return self.signature_methods
+
+ def verify_request(self, request, consumer, token):
+ """Verifies an api call and checks all the parameters."""
+
+ self._check_version(request)
+ self._check_signature(request, consumer, token)
+ parameters = request.get_nonoauth_parameters()
+ return parameters
+
+ def build_authenticate_header(self, realm=''):
+ """Optional support for the authenticate header."""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+ def _check_version(self, request):
+ """Verify the correct version of the request for this server."""
+ version = self._get_version(request)
+ if version and version != self.version:
+ raise Error('OAuth version %s not supported.' % str(version))
+
+ def _get_version(self, request):
+ """Return the version of the request for this server."""
+ try:
+ version = request.get_parameter('oauth_version')
+ except:
+ version = OAUTH_VERSION
+
+ return version
+
+ def _get_signature_method(self, request):
+ """Figure out the signature with some defaults."""
+ try:
+ signature_method = request.get_parameter('oauth_signature_method')
+ except:
+ signature_method = SIGNATURE_METHOD
+
+ try:
+ # Get the signature method object.
+ signature_method = self.signature_methods[signature_method]
+ except:
+ signature_method_names = ', '.join(self.signature_methods.keys())
+ raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names))
+
+ return signature_method
+
+ def _get_verifier(self, request):
+ return request.get_parameter('oauth_verifier')
+
+ def _check_signature(self, request, consumer, token):
+ timestamp, nonce = request._get_timestamp_nonce()
+ self._check_timestamp(timestamp)
+ signature_method = self._get_signature_method(request)
+
+ try:
+ signature = request.get_parameter('oauth_signature')
+ except:
+ raise MissingSignature('Missing oauth_signature.')
+
+ # Validate the signature.
+ valid = signature_method.check(request, consumer, token, signature)
+
+ if not valid:
+ key, base = signature_method.signing_base(request, consumer, token)
+
+ raise Error('Invalid signature. Expected signature base '
+ 'string: %s' % base)
+
+ def _check_timestamp(self, timestamp):
+ """Verify that timestamp is recentish."""
+ timestamp = int(timestamp)
+ now = int(time.time())
+ lapsed = now - timestamp
+ if lapsed > self.timestamp_threshold:
+ raise Error('Expired timestamp: given %d and now %s has a '
+ 'greater difference than threshold %d' % (timestamp, now,
+ self.timestamp_threshold))
+
+
+class SignatureMethod(object):
+ """A way of signing requests.
+
+ The OAuth protocol lets consumers and service providers pick a way to sign
+ requests. This interface shows the methods expected by the other `oauth`
+ modules for signing requests. Subclass it and implement its methods to
+ provide a new way to sign requests.
+ """
+
+ def signing_base(self, request, consumer, token):
+ """Calculates the string that needs to be signed.
+
+ This method returns a 2-tuple containing the starting key for the
+ signing and the message to be signed. The latter may be used in error
+ messages to help clients debug their software.
+
+ """
+ raise NotImplementedError
+
+ def sign(self, request, consumer, token):
+ """Returns the signature for the given request, based on the consumer
+ and token also provided.
+
+ You should use your implementation of `signing_base()` to build the
+ message to sign. Otherwise it may be less useful for debugging.
+
+ """
+ raise NotImplementedError
+
+ def check(self, request, consumer, token, signature):
+ """Returns whether the given signature is the correct signature for
+ the given consumer and token signing the given request."""
+ built = self.sign(request, consumer, token)
+ return built == signature
+
+
+class SignatureMethod_HMAC_SHA1(SignatureMethod):
+ name = 'HMAC-SHA1'
+
+ def signing_base(self, request, consumer, token):
+ if not hasattr(request, 'normalized_url') or request.normalized_url is None:
+ raise ValueError("Base URL for request is not set.")
+
+ sig = (
+ escape(request.method),
+ escape(request.normalized_url),
+ escape(request.get_normalized_parameters()),
+ )
+
+ key = '%s&' % escape(consumer.secret)
+ if token:
+ key += escape(token.secret)
+ raw = '&'.join(sig)
+ return key, raw
+
+ def sign(self, request, consumer, token):
+ """Builds the base signature string."""
+ key, raw = self.signing_base(request, consumer, token)
+
+ hashed = hmac.new(key, raw, sha)
+
+ # Calculate the digest base 64.
+ return binascii.b2a_base64(hashed.digest())[:-1]
+
+
+class SignatureMethod_PLAINTEXT(SignatureMethod):
+
+ name = 'PLAINTEXT'
+
+ def signing_base(self, request, consumer, token):
+ """Concatenates the consumer key and secret with the token's
+ secret."""
+ sig = '%s&' % escape(consumer.secret)
+ if token:
+ sig = sig + escape(token.secret)
+ return sig, sig
+
+ def sign(self, request, consumer, token):
+ key, raw = self.signing_base(request, consumer, token)
+ return raw
--- /dev/null
+# This is the version of this source code.
+
+manual_verstr = "1.5"
+
+
+
+auto_build_num = "170"
+
+
+
+verstr = manual_verstr + "." + auto_build_num
+try:
+ from pyutil.version_class import Version as pyutil_Version
+ __version__ = pyutil_Version(verstr)
+except (ImportError, ValueError):
+ # Maybe there is no pyutil installed.
+ from distutils.version import LooseVersion as distutils_Version
+ __version__ = distutils_Version(verstr)
--- /dev/null
+"""
+The MIT License
+
+Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
+
+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.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import oauth2
+import imaplib
+
+
+class IMAP4_SSL(imaplib.IMAP4_SSL):
+ """IMAP wrapper for imaplib.IMAP4_SSL that implements XOAUTH."""
+
+ def authenticate(self, url, consumer, token):
+ if consumer is not None and not isinstance(consumer, oauth2.Consumer):
+ raise ValueError("Invalid consumer.")
+
+ if token is not None and not isinstance(token, oauth2.Token):
+ raise ValueError("Invalid token.")
+
+ imaplib.IMAP4_SSL.authenticate(self, 'XOAUTH',
+ lambda x: oauth2.build_xoauth_string(url, consumer, token))
--- /dev/null
+"""
+The MIT License
+
+Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
+
+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.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import oauth2
+import smtplib
+import base64
+
+
+class SMTP(smtplib.SMTP):
+ """SMTP wrapper for smtplib.SMTP that implements XOAUTH."""
+
+ def authenticate(self, url, consumer, token):
+ if consumer is not None and not isinstance(consumer, oauth2.Consumer):
+ raise ValueError("Invalid consumer.")
+
+ if token is not None and not isinstance(token, oauth2.Token):
+ raise ValueError("Invalid token.")
+
+ self.docmd('AUTH', 'XOAUTH %s' % \
+ base64.b64encode(oauth2.build_xoauth_string(url, consumer, token)))