1 #!/usr/bin/env python 2 3 # Copyright 2014 The Chromium Authors. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 '''A set of utilities to interface with the Chrome Webstore API.''' 8 9 import SimpleHTTPServer 10 import SocketServer 11 import httplib 12 import json 13 import os 14 import re 15 import sys 16 import thread 17 import urllib 18 import webbrowser 19 20 PROJECT_ARGS = { 21 'client_id': ('937534751394-gbj5334v9144c57qjqghl7d283plj5r4' 22 '.apps.googleusercontent.com'), 23 'grant_type': 'authorization_code', 24 'redirect_uri': 'http://localhost:8000' 25 } 26 27 PORT = 8000 28 29 APP_ID = 'kgejglhpjiefppelpmljglcjbhoiplfn' 30 OAUTH_DOMAIN = 'accounts.google.com' 31 OAUTH_AUTH_COMMAND = '/o/oauth2/auth' 32 OAUTH_TOKEN_COMMAND = '/o/oauth2/token' 33 WEBSTORE_API_SCOPE = 'https://www.googleapis.com/auth/chromewebstore' 34 35 API_ENDPOINT_DOMAIN = 'www.googleapis.com' 36 COMMAND_GET_UPLOAD_STATUS = ( 37 '/chromewebstore/v1.1/items/%s?projection=draft' % APP_ID) 38 COMMAND_POST_PUBLISH = '/chromewebstore/v1.1/items/%s/publish' % APP_ID 39 COMMAND_POST_UPLOAD = '/upload/chromewebstore/v1.1/items/%s' % APP_ID 40 41 class CodeRequestHandler(SocketServer.StreamRequestHandler): 42 def handle(self): 43 content = self.rfile.readline() 44 self.server.code = re.search('code=(.*) ', content).groups()[0] 45 self.rfile.close() 46 47 def GetAuthCode(): 48 Handler = CodeRequestHandler 49 httpd = SocketServer.TCPServer(("", PORT), Handler) 50 query = '&'.join(['response_type=code', 51 'scope=%s' % WEBSTORE_API_SCOPE, 52 'client_id=%(client_id)s' % PROJECT_ARGS, 53 'redirect_uri=%(redirect_uri)s' % PROJECT_ARGS]) 54 auth_url = ' https://%s%s?%s' % (OAUTH_DOMAIN, OAUTH_AUTH_COMMAND, query) 55 print 'Navigating to %s' % auth_url 56 webbrowser.open(auth_url) 57 httpd.handle_request() 58 httpd.server_close() 59 return httpd.code 60 61 def GetOauthToken(code, client_secret): 62 PROJECT_ARGS['code'] = code 63 PROJECT_ARGS['client_secret'] = client_secret 64 body = urllib.urlencode(PROJECT_ARGS) 65 conn = httplib.HTTPSConnection(OAUTH_DOMAIN) 66 conn.putrequest('POST', OAUTH_TOKEN_COMMAND) 67 conn.putheader('content-type', 'application/x-www-form-urlencoded') 68 conn.putheader('content-length', len(body)) 69 conn.endheaders() 70 conn.send(body) 71 content = conn.getresponse().read() 72 return json.loads(content) 73 74 def GetPopulatedHeader(client_secret): 75 code = GetAuthCode() 76 access_token = GetOauthToken(code, client_secret) 77 url = 'www.googleapis.com' 78 79 return {'Authorization': 'Bearer %(access_token)s' % access_token, 80 'x-goog-api-version': 2, 81 'Content-Length': 0 82 } 83 84 def SendGetCommand(command, client_secret): 85 headers = GetPopulatedHeader(client_secret) 86 conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN) 87 conn.request('GET', command, '', headers) 88 return conn.getresponse() 89 90 def SendPostCommand(command, client_secret, header_additions = {}, body=None): 91 headers = GetPopulatedHeader(client_secret) 92 headers = dict(headers.items() + header_additions.items()) 93 conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN) 94 conn.request('POST', command, body, headers) 95 return conn.getresponse() 96 97 def GetUploadStatus(client_secret): 98 '''Gets the status of a previous upload. 99 Args: 100 client_secret ChromeVox's client secret creds. 101 ''' 102 return SendGetCommand(COMMAND_GET_UPLOAD_STATUS, client_secret) 103 104 # httplib fails to persist the connection during upload; use curl instead. 105 def PostUpload(file, client_secret): 106 '''Posts an uploaded version of ChromeVox. 107 Args: 108 file A string path to the ChromeVox extension zip. 109 client_secret ChromeVox's client secret creds. 110 ''' 111 header = GetPopulatedHeader(client_secret) 112 curl_command = ' '.join(['curl', 113 '-H "Authorization: %(Authorization)s"' % header, 114 '-H "x-goog-api-version: 2"', 115 '-X PUT', 116 '-T %s' % file, 117 '-v', 118 'https://%s%s' % (API_ENDPOINT_DOMAIN, 119 COMMAND_POST_UPLOAD)]) 120 121 print 'Running %s' % curl_command 122 if os.system(curl_command) != 0: 123 sys.exit(-1) 124 125 def PostPublishTrustedTesters(client_secret): 126 '''Publishes a previously uploaded ChromeVox extension to trusted testers. 127 Args: 128 client_secret ChromeVox's client secret creds. 129 ''' 130 return SendPostCommand(COMMAND_POST_PUBLISH, 131 client_secret, 132 { 'publishTarget': 'trustedTesters'}) 133 134 def PostPublish(client_secret): 135 '''Publishes a previously uploaded ChromeVox extension publically. 136 Args: 137 client_secret ChromeVox's client secret creds. 138 ''' 139 return SendPostCommand(COMMAND_POST_PUBLISH, client_secret) 140