|
22 | 22 | THE SOFTWARE.
|
23 | 23 | """
|
24 | 24 |
|
25 |
| -import cgi |
26 | 25 | import urllib
|
27 | 26 | import time
|
28 | 27 | import random
|
29 | 28 | import urlparse
|
30 | 29 | import hmac
|
31 | 30 | import binascii
|
| 31 | +import httplib2 |
32 | 32 |
|
33 | 33 |
|
34 | 34 | VERSION = '1.0' # Hi Blaine!
|
@@ -108,6 +108,14 @@ def __init__(self, key, secret):
|
108 | 108 | if self.key is None or self.secret is None:
|
109 | 109 | raise ValueError("Key and secret must be set.")
|
110 | 110 |
|
| 111 | + def __str__(self): |
| 112 | + data = { |
| 113 | + 'oauth_consumer_key': self.key, |
| 114 | + 'oauth_consumer_secret': self.secret |
| 115 | + } |
| 116 | + |
| 117 | + return urllib.urlencode(data) |
| 118 | + |
111 | 119 |
|
112 | 120 | class Token(object):
|
113 | 121 | """An OAuth credential used to request authorization or a protected
|
@@ -184,7 +192,7 @@ def from_string(s):
|
184 | 192 | if not len(s):
|
185 | 193 | raise ValueError("Invalid parameter string.")
|
186 | 194 |
|
187 |
| - params = cgi.parse_qs(s, keep_blank_values=False) |
| 195 | + params = urlparse.parse_qs(s, keep_blank_values=False) |
188 | 196 | if not len(params):
|
189 | 197 | raise ValueError("Invalid parameter string.")
|
190 | 198 |
|
@@ -255,11 +263,16 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None):
|
255 | 263 | def url(self, value):
|
256 | 264 | parts = urlparse.urlparse(value)
|
257 | 265 | scheme, netloc, path = parts[:3]
|
| 266 | + |
258 | 267 | # Exclude default port numbers.
|
259 | 268 | if scheme == 'http' and netloc[-3:] == ':80':
|
260 | 269 | netloc = netloc[:-3]
|
261 | 270 | elif scheme == 'https' and netloc[-4:] == ':443':
|
262 | 271 | netloc = netloc[:-4]
|
| 272 | + |
| 273 | + if scheme != 'http' and scheme != 'https': |
| 274 | + raise ValueError("Unsupported URL %s (%s)." % (value, scheme)) |
| 275 | + |
263 | 276 | value = '%s://%s%s' % (scheme, netloc, path)
|
264 | 277 | self.__dict__['url'] = value
|
265 | 278 |
|
@@ -311,6 +324,13 @@ def get_normalized_parameters(self):
|
311 | 324 |
|
312 | 325 | def sign_request(self, signature_method, consumer, token):
|
313 | 326 | """Set the signature parameter to the result of sign."""
|
| 327 | + |
| 328 | + if 'oauth_consumer_key' not in self: |
| 329 | + self['oauth_consumer_key'] = consumer.key |
| 330 | + |
| 331 | + if token and 'oauth_token' not in self: |
| 332 | + self['oauth_token'] = token.key |
| 333 | + |
314 | 334 | self['oauth_signature_method'] = signature_method.name
|
315 | 335 | self['oauth_signature'] = signature_method.sign(self, consumer, token)
|
316 | 336 |
|
@@ -415,7 +435,7 @@ def _split_header(header):
|
415 | 435 | @staticmethod
|
416 | 436 | def _split_url_string(param_str):
|
417 | 437 | """Turn URL string into parameters."""
|
418 |
| - parameters = cgi.parse_qs(param_str, keep_blank_values=False) |
| 438 | + parameters = urlparse.parse_qs(param_str, keep_blank_values=False) |
419 | 439 | for k, v in parameters.iteritems():
|
420 | 440 | parameters[k] = urllib.unquote(v[0])
|
421 | 441 | return parameters
|
@@ -515,32 +535,60 @@ def _check_timestamp(self, timestamp):
|
515 | 535 | 'greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold))
|
516 | 536 |
|
517 | 537 |
|
518 |
| -class Client(object): |
| 538 | +class Client(httplib2.Http): |
519 | 539 | """OAuthClient is a worker to attempt to execute a request."""
|
520 |
| - consumer = None |
521 |
| - token = None |
522 | 540 |
|
523 |
| - def __init__(self, oauth_consumer, oauth_token): |
524 |
| - self.consumer = oauth_consumer |
525 |
| - self.token = oauth_token |
| 541 | + def __init__(self, consumer, token=None, cache=None, timeout=None, |
| 542 | + proxy_info=None): |
526 | 543 |
|
527 |
| - def get_consumer(self): |
528 |
| - return self.consumer |
| 544 | + if consumer is not None and not isinstance(consumer, Consumer): |
| 545 | + raise ValueError("Invalid consumer.") |
529 | 546 |
|
530 |
| - def get_token(self): |
531 |
| - return self.token |
| 547 | + if token is not None and not isinstance(token, Token): |
| 548 | + raise ValueError("Invalid token.") |
532 | 549 |
|
533 |
| - def fetch_request_token(self, request): |
534 |
| - """-> OAuthToken.""" |
535 |
| - raise NotImplementedError |
| 550 | + self.consumer = consumer |
| 551 | + self.token = token |
| 552 | + self.method = SignatureMethod_HMAC_SHA1() |
536 | 553 |
|
537 |
| - def fetch_access_token(self, request): |
538 |
| - """-> OAuthToken.""" |
539 |
| - raise NotImplementedError |
| 554 | + httplib2.Http.__init__(self, cache=cache, timeout=timeout, |
| 555 | + proxy_info=proxy_info) |
540 | 556 |
|
541 |
| - def access_resource(self, request): |
542 |
| - """-> Some protected resource.""" |
543 |
| - raise NotImplementedError |
| 557 | + def set_signature_method(method): |
| 558 | + if not isinstance(method, SignatureMethod): |
| 559 | + raise ValueError("Invalid signature method.") |
| 560 | + |
| 561 | + self.method = method |
| 562 | + |
| 563 | + def request(self, uri, method="GET", body=None, headers=None, |
| 564 | + redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None): |
| 565 | + |
| 566 | + if body and method == "POST": |
| 567 | + parameters = urlparse.parse_qs(body) |
| 568 | + elif method == "GET": |
| 569 | + parsed = urlparse.urlparse(uri) |
| 570 | + parameters = urlparse.parse_qs(parsed.query) |
| 571 | + else: |
| 572 | + parameters = None |
| 573 | + |
| 574 | + req = Request.from_consumer_and_token(self.consumer, token=self.token, |
| 575 | + http_method=method, http_url=uri, parameters=parameters) |
| 576 | + |
| 577 | + req.sign_request(self.method, self.consumer, self.token) |
| 578 | + |
| 579 | + if method == "POST": |
| 580 | + body = req.to_postdata() |
| 581 | + elif method == "GET": |
| 582 | + uri = req.to_url() |
| 583 | + else: |
| 584 | + if headers is None: |
| 585 | + headers = {} |
| 586 | + |
| 587 | + headers.update(req.to_header()) |
| 588 | + |
| 589 | + return httplib2.Http.request(self, uri, method=method, body=body, |
| 590 | + headers=headers, redirections=redirections, |
| 591 | + connection_type=connection_type) |
544 | 592 |
|
545 | 593 |
|
546 | 594 | class SignatureMethod(object):
|
@@ -626,13 +674,3 @@ def sign(self, request, consumer, token):
|
626 | 674 | key, raw = self.signing_base(request, consumer, token)
|
627 | 675 | return raw
|
628 | 676 |
|
629 |
| -# Backwards compatibility |
630 |
| -OAuthError = Error |
631 |
| -OAuthToken = Token |
632 |
| -OAuthConsumer = Consumer |
633 |
| -OAuthRequest = Request |
634 |
| -OAuthServer = Server |
635 |
| -OAuthClient = Client |
636 |
| -OAuthSignatureMethod = SignatureMethod |
637 |
| -OAuthSignatureMethod_HMAC_SHA1 = SignatureMethod_HMAC_SHA1 |
638 |
| -OAuthSignatureMethod_PLAINTEXT = SignatureMethod_PLAINTEXT |
|
0 commit comments