Up

googlelogin2.py

 1 from httplib import HTTPConnection, HTTPSConnection
 2 import re
 3 
 4 class GoogleLoginAuthError(Exception): pass
 5 class GoogleLoginAuthUnknownError(Exception): pass
 6 
 7 # In regex below:
 8 #    [^\0-\x1f\x7f-\xff()<>@,;:\\\"/[\]?={} \t]+             matches a "token" as defined by HTTP
 9 #    "(?:[^\0-\x08\x0A-\x1f\x7f-\xff\\\"]|\\[\0-\x7f])*?"    matches a "quoted-string" as defined by HTTP, when LWS have already been replaced by a single space
10 # Actually, as an auth-param value can be either a token or a quoted-string, they are combined in a single pattern which matches both:
11 #    \"?((?<=\")(?:[^\0-\x1f\x7f-\xff\\\"]|\\[\0-\x7f])*?(?=\")|(?<!\")[^\0-\x08\x0A-\x1f\x7f-\xff()<>@,;:\\\"/[\]?={} \t]+(?!\"))\"?
12 WWW_AUTH = re.compile(r"^(?:\s*(?:,\s*)?([^ \t\r\n=]+)\s*=\s*\"?((?<=\")(?:[^\\\"]|\\.)*?(?=\")|(?<!\")[^ \t\r\n,]+(?!\"))\"?)(.*)$")
13 UNQUOTE_PAIRS = re.compile(r'\\(.)')
14 def _parse_www_authenticate(authenticate):
15     """Returns a dictionary of dictionaries, one dict
16     per auth-scheme. The dictionary for each auth-scheme
17     contains all the auth-params.
18     """
19     retval = {}
20     authenticate = authenticate.strip()
21     while authenticate:
22         # Break off the scheme at the beginning of the line
23         (auth_scheme, the_rest) = authenticate.split(" ", 1)
24         # Now loop over all the key value pairs that come after the scheme,
25         # being careful not to roll into the next scheme
26         match = WWW_AUTH.search(the_rest)
27         auth_params = {}
28         while match:
29             if match and len(match.groups()) == 3:
30                 (key, value, the_rest) = match.groups()
31                 auth_params[key.lower()] = UNQUOTE_PAIRS.sub(r'\1', value) # '\\'.join([x.replace('\\', '') for x in value.split('\\\\')])
32             match = WWW_AUTH.search(the_rest)
33         retval[auth_scheme.lower()] = auth_params
34         authenticate = the_rest.strip()
35     return retval
36 
37 GOOGLE_ERROR_MESSAGES = {
38 "BadAuthentication":  "The login request used a username or password that is not recognized.",
39 "NotVerified":        "The account email address has not been verified. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application.",
40 "TermsNotAgreed":     "The user has not agreed to terms. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application.",
41 "CaptchaRequired":    "Please visit https://www.google.com/accounts/DisplayUnlockCaptcha to enable access.",
42 "Unknown":            "The error is unknown or unspecified; the request contained invalid input or was malformed.",
43 "AccountDeleted":     "The user account has been deleted.",
44 "AccountDisabled":    "The user account has been disabled.",
45 "ServiceDisabled":    "The user's access to the specified service has been disabled. (The user account may still be valid.)",
46 "ServiceUnavailable": "The service is not available; try again later.",
47 }
48 
49 def googlelogin(name, password, useragent, challenge):
50     from urllib import urlencode
51     service = challenge['googlelogin']['service']
52     auth = dict(Email=name, Passwd=password, service=service, source=useragent)
53     conn = HTTPSConnection('www.google.com')
54     conn.request('POST', '/accounts/ClientLogin', body=urlencode(auth), headers={'content-type': 'application/x-www-form-urlencoded'})
55     r = conn.getresponse()
56     content = r.read()
57     lines = content.split('\n')
58     d = dict([tuple(line.split("=", 1)) for line in lines if line])
59     if r.status == 403:
60         if 'Error' in d:
61             errorname = d['Error']
62             if errorname in GOOGLE_ERROR_MESSAGES:
63                 raise GoogleLoginAuthError(GOOGLE_ERROR_MESSAGES[errorname])
64             else:
65                 raise GoogleLoginAuthUnknownError(errorname)
66         auth = ""
67     else:
68         auth = d['Auth']
69     return auth
70 
71 # Try an operation
72 headers = {}
73 conn = HTTPConnection('www.blogger.com')
74 conn.request("GET", "/feeds/default/blogs", headers=headers)
75 r = conn.getresponse()
76 print r.status
77 content = r.read()
78 # If we get a 401 then respond to the challenge
79 if r.status == 401:
80     name, password = open("/home/jcgregorio/gmail", "r").read().split()
81     useragent = "My-App-01"
82     headers = {}
83     challenge = _parse_www_authenticate(r.getheader('www-authenticate'))
84     if 'googlelogin' in challenge:
85         auth = googlelogin(name, password, useragent, challenge)
86         headers['authorization'] = 'GoogleLogin Auth=' + auth
87     else:
88         # Obviously you could check for 'basic' in challenge and
89         # do Basic auth here. Similarly for Digest.
90         pass
91 
92 # Now try the request again, this time with the authorization header in place.
93 conn.request("GET", "/feeds/default/blogs", headers=headers)
94 r = conn.getresponse()
95 print r.status
96 print r.read()