@@ -63,6 +63,22 @@ def build_authenticate_header(realm=''):
6363 return {'WWW-Authenticate' : 'OAuth realm="%s"' % realm }
6464
6565
66+ def build_xoauth_string (url , consumer , token = None ):
67+ """Build an XOAUTH string for use in SMTP/IMPA authentication."""
68+ request = oauth .Request .from_consumer_and_token (consumer , token ,
69+ "GET" , url )
70+
71+ signing_method = oauth .SignatureMethod_HMAC_SHA1 ()
72+ request .sign_request (signing_method , consumer , token )
73+
74+ params = []
75+ for k ,v in sorted (request .iteritems ()):
76+ if v is not None :
77+ params .append ('%s="%s"' % (k , oauth .escape (v )))
78+
79+ return "%s %s %s" % ("GET" , url , ',' .join (params ))
80+
81+
6682def escape (s ):
6783 """Escape a URL including any /."""
6884 return urllib .quote (s , safe = '~' )
@@ -473,6 +489,69 @@ def _split_url_string(param_str):
473489 return parameters
474490
475491
492+ class Client (httplib2 .Http ):
493+ """OAuthClient is a worker to attempt to execute a request."""
494+
495+ def __init__ (self , consumer , token = None , cache = None , timeout = None ,
496+ proxy_info = None ):
497+
498+ if consumer is not None and not isinstance (consumer , Consumer ):
499+ raise ValueError ("Invalid consumer." )
500+
501+ if token is not None and not isinstance (token , Token ):
502+ raise ValueError ("Invalid token." )
503+
504+ self .consumer = consumer
505+ self .token = token
506+ self .method = SignatureMethod_HMAC_SHA1 ()
507+
508+ httplib2 .Http .__init__ (self , cache = cache , timeout = timeout ,
509+ proxy_info = proxy_info )
510+
511+ def set_signature_method (self , method ):
512+ if not isinstance (method , SignatureMethod ):
513+ raise ValueError ("Invalid signature method." )
514+
515+ self .method = method
516+
517+ def request (self , uri , method = "GET" , body = None , headers = None ,
518+ redirections = httplib2 .DEFAULT_MAX_REDIRECTS , connection_type = None ):
519+ DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'
520+
521+ if not isinstance (headers , dict ):
522+ headers = {}
523+
524+ is_multipart = method == 'POST' and headers .get ('Content-Type' ,
525+ DEFAULT_CONTENT_TYPE ) != DEFAULT_CONTENT_TYPE
526+
527+ if body and method == "POST" and not is_multipart :
528+ parameters = dict (parse_qsl (body ))
529+ else :
530+ parameters = None
531+
532+ req = Request .from_consumer_and_token (self .consumer ,
533+ token = self .token , http_method = method , http_url = uri ,
534+ parameters = parameters )
535+
536+ req .sign_request (self .method , self .consumer , self .token )
537+
538+ if method == "POST" :
539+ headers ['Content-Type' ] = headers .get ('Content-Type' ,
540+ DEFAULT_CONTENT_TYPE )
541+ if is_multipart :
542+ headers .update (req .to_header ())
543+ else :
544+ body = req .to_postdata ()
545+ elif method == "GET" :
546+ uri = req .to_url ()
547+ else :
548+ headers .update (req .to_header ())
549+
550+ return httplib2 .Http .request (self , uri , method = method , body = body ,
551+ headers = headers , redirections = redirections ,
552+ connection_type = connection_type )
553+
554+
476555class Server (object ):
477556 """A skeletal implementation of a service provider, providing protected
478557 resources to requests from authorized consumers.
@@ -564,68 +643,8 @@ def _check_timestamp(self, timestamp):
564643 lapsed = now - timestamp
565644 if lapsed > self .timestamp_threshold :
566645 raise Error ('Expired timestamp: given %d and now %s has a '
567- 'greater difference than threshold %d' % (timestamp , now , self .timestamp_threshold ))
568-
569-
570- class Client (httplib2 .Http ):
571- """OAuthClient is a worker to attempt to execute a request."""
572-
573- def __init__ (self , consumer , token = None , cache = None , timeout = None ,
574- proxy_info = None ):
575-
576- if consumer is not None and not isinstance (consumer , Consumer ):
577- raise ValueError ("Invalid consumer." )
578-
579- if token is not None and not isinstance (token , Token ):
580- raise ValueError ("Invalid token." )
581-
582- self .consumer = consumer
583- self .token = token
584- self .method = SignatureMethod_HMAC_SHA1 ()
585-
586- httplib2 .Http .__init__ (self , cache = cache , timeout = timeout ,
587- proxy_info = proxy_info )
588-
589- def set_signature_method (self , method ):
590- if not isinstance (method , SignatureMethod ):
591- raise ValueError ("Invalid signature method." )
592-
593- self .method = method
594-
595- def request (self , uri , method = "GET" , body = None , headers = None ,
596- redirections = httplib2 .DEFAULT_MAX_REDIRECTS , connection_type = None ):
597- DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'
598-
599- if not isinstance (headers , dict ):
600- headers = {}
601-
602- is_multipart = method == 'POST' and headers .get ('Content-Type' , DEFAULT_CONTENT_TYPE ) != DEFAULT_CONTENT_TYPE
603-
604- if body and method == "POST" and not is_multipart :
605- parameters = dict (parse_qsl (body ))
606- else :
607- parameters = None
608-
609- req = Request .from_consumer_and_token (self .consumer , token = self .token ,
610- http_method = method , http_url = uri , parameters = parameters )
611-
612- req .sign_request (self .method , self .consumer , self .token )
613-
614-
615- if method == "POST" :
616- headers ['Content-Type' ] = headers .get ('Content-Type' , DEFAULT_CONTENT_TYPE )
617- if is_multipart :
618- headers .update (req .to_header ())
619- else :
620- body = req .to_postdata ()
621- elif method == "GET" :
622- uri = req .to_url ()
623- else :
624- headers .update (req .to_header ())
625-
626- return httplib2 .Http .request (self , uri , method = method , body = body ,
627- headers = headers , redirections = redirections ,
628- connection_type = connection_type )
646+ 'greater difference than threshold %d' % (timestamp , now ,
647+ self .timestamp_threshold ))
629648
630649
631650class SignatureMethod (object ):
0 commit comments