Python 3 update https://patch-diff.githubusercontent.com/raw/google/gmail-oauth2-tools/pull/25.patch Index: python/oauth2.py --- python/oauth2.py.orig +++ python/oauth2.py @@ -60,15 +60,27 @@ IMAPFE and pass it as the second argument to the AUTHE a AUTHENTICATE XOAUTH2 a9sha9sfs[...]9dfja929dk== """ +from __future__ import print_function + import base64 import imaplib import json from optparse import OptionParser import smtplib import sys -import urllib +try: + from urllib.request import urlopen + from urllib.parse import quote, unquote, urlencode +except ImportError: + from urllib import urlopen, quote, unquote, urlencode +if hasattr(__builtins__, 'raw_input'): + def input_str(prompt): + return raw_input(prompt).decode(sys.stdin.encoding) +else: + input_str = input + def SetupOptionParser(): # Usage message is the module's docstring. parser = OptionParser(usage=__doc__) @@ -144,12 +156,12 @@ def AccountsUrl(command): def UrlEscape(text): # See OAUTH 5.1 for a definition of which characters need to be escaped. - return urllib.quote(text, safe='~-._') + return quote(text, safe='~-._') def UrlUnescape(text): # See OAUTH 5.1 for a definition of which characters need to be escaped. - return urllib.unquote(text) + return unquote(text) def FormatUrlParams(params): @@ -162,7 +174,7 @@ def FormatUrlParams(params): A URL query string version of the given parameters. """ param_fragments = [] - for param in sorted(params.iteritems(), key=lambda x: x[0]): + for param in sorted(iter(params.items()), key=lambda x: x[0]): param_fragments.append('%s=%s' % (param[0], UrlEscape(param[1]))) return '&'.join(param_fragments) @@ -211,10 +223,12 @@ def AuthorizeTokens(client_id, client_secret, authoriz params['grant_type'] = 'authorization_code' request_url = AccountsUrl('o/oauth2/token') - response = urllib.urlopen(request_url, urllib.urlencode(params)).read() - return json.loads(response) + data = urlencode(params).encode('utf-8') + response = urlopen(request_url, data).read() + return json.loads(response.decode('utf-8')) + def RefreshToken(client_id, client_secret, refresh_token): """Obtains a new token given a refresh token. @@ -235,10 +249,12 @@ def RefreshToken(client_id, client_secret, refresh_tok params['grant_type'] = 'refresh_token' request_url = AccountsUrl('o/oauth2/token') - response = urllib.urlopen(request_url, urllib.urlencode(params)).read() - return json.loads(response) + data = urlencode(params).encode('utf-8') + response = urlopen(request_url, data).read() + return json.loads(response.decode('utf-8')) + def GenerateOAuth2String(username, access_token, base64_encode=True): """Generates an IMAP OAuth2 authentication string. @@ -254,7 +270,8 @@ def GenerateOAuth2String(username, access_token, base6 """ auth_string = 'user=%s\1auth=Bearer %s\1\1' % (username, access_token) if base64_encode: - auth_string = base64.b64encode(auth_string) + auth_bytes = auth_string.encode('utf-8') + auth_string = base64.b64encode(auth_bytes).decode() return auth_string @@ -268,10 +285,11 @@ def TestImapAuthentication(user, auth_string): auth_string: A valid OAuth2 string, as returned by GenerateOAuth2String. Must not be base64-encoded, since imaplib does its own base64-encoding. """ - print + print() + auth_bytes = auth_string.encode('utf-8') imap_conn = imaplib.IMAP4_SSL('imap.gmail.com') imap_conn.debug = 4 - imap_conn.authenticate('XOAUTH2', lambda x: auth_string) + imap_conn.authenticate('XOAUTH2', lambda x: auth_bytes) imap_conn.select('INBOX') @@ -283,18 +301,19 @@ def TestSmtpAuthentication(user, auth_string): auth_string: A valid OAuth2 string, not base64-encoded, as returned by GenerateOAuth2String. """ - print + print() + auth_bytes = auth_string.encode('utf-8') smtp_conn = smtplib.SMTP('smtp.gmail.com', 587) smtp_conn.set_debuglevel(True) smtp_conn.ehlo('test') smtp_conn.starttls() - smtp_conn.docmd('AUTH', 'XOAUTH2 ' + base64.b64encode(auth_string)) + smtp_conn.docmd('AUTH', 'XOAUTH2 ' + base64.b64encode(auth_bytes).decode()) def RequireOptions(options, *args): missing = [arg for arg in args if getattr(options, arg) is None] if missing: - print 'Missing options: %s' % ' '.join(missing) + print('Missing options: %s' % ' '.join(missing)) sys.exit(-1) @@ -306,27 +325,27 @@ def main(argv): response = RefreshToken(options.client_id, options.client_secret, options.refresh_token) if options.quiet: - print response['access_token'] + print(response['access_token']) else: - print 'Access Token: %s' % response['access_token'] - print 'Access Token Expiration Seconds: %s' % response['expires_in'] + print('Access Token: %s' % response['access_token']) + print('Access Token Expiration Seconds: %s' % response['expires_in']) elif options.generate_oauth2_string: RequireOptions(options, 'user', 'access_token') oauth2_string = GenerateOAuth2String(options.user, options.access_token) if options.quiet: - print oauth2_string + print(oauth2_string) else: - print 'OAuth2 argument:\n' + oauth2_string + print('OAuth2 argument:\n' + oauth2_string) elif options.generate_oauth2_token: RequireOptions(options, 'client_id', 'client_secret') - print 'To authorize token, visit this url and follow the directions:' - print ' %s' % GeneratePermissionUrl(options.client_id, options.scope) - authorization_code = raw_input('Enter verification code: ') + print('To authorize token, visit this url and follow the directions:') + print(' %s' % GeneratePermissionUrl(options.client_id, options.scope)) + authorization_code = input_str('Enter verification code: ') response = AuthorizeTokens(options.client_id, options.client_secret, authorization_code) - print 'Refresh Token: %s' % response['refresh_token'] - print 'Access Token: %s' % response['access_token'] - print 'Access Token Expiration Seconds: %s' % response['expires_in'] + print('Refresh Token: %s' % response['refresh_token']) + print('Access Token: %s' % response['access_token']) + print('Access Token Expiration Seconds: %s' % response['expires_in']) elif options.test_imap_authentication: RequireOptions(options, 'user', 'access_token') TestImapAuthentication(options.user, @@ -339,7 +358,7 @@ def main(argv): base64_encode=False)) else: options_parser.print_help() - print 'Nothing to do, exiting.' + print('Nothing to do, exiting.') return