chromevox_webstore_util.py revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#!/usr/bin/env python 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# Copyright 2014 The Chromium Authors. All rights reserved. 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# Use of this source code is governed by a BSD-style license that can be 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# found in the LICENSE file. 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com'''A set of utilities to interface with the Chrome Webstore API.''' 8bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com 9bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comimport SimpleHTTPServer 10bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comimport SocketServer 11bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comimport httplib 12bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comimport json 13f523e25da069e3e1af2cb73d37073a34f3bb9ea2reed@android.comimport os 14bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comimport re 15b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comimport sys 16b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comimport thread 17b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comimport urllib 18b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comimport webbrowser 19b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 20b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comPROJECT_ARGS = { 21b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 'client_id': ('937534751394-gbj5334v9144c57qjqghl7d283plj5r4' 22b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com '.apps.googleusercontent.com'), 23b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 'grant_type': 'authorization_code', 24b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 'redirect_uri': 'http://localhost:8000' 25b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com} 26b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 27b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.comPORT = 8000 28b8b92ea089a380e17984e4df7b49b379cb6bd929reed@google.com 29bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comAPP_ID = 'kgejglhpjiefppelpmljglcjbhoiplfn' 304bc1983e01d756ae9c91fd380758457f579d26eareed@android.comOAUTH_DOMAIN = 'accounts.google.com' 31bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comOAUTH_AUTH_COMMAND = '/o/oauth2/auth' 324e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comOAUTH_TOKEN_COMMAND = '/o/oauth2/token' 334e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comWEBSTORE_API_SCOPE = 'https://www.googleapis.com/auth/chromewebstore' 344e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com 354e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comAPI_ENDPOINT_DOMAIN = 'www.googleapis.com' 364e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comCOMMAND_GET_UPLOAD_STATUS = ( 374e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com '/chromewebstore/v1.1/items/%s?projection=draft' % APP_ID) 384e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comCOMMAND_POST_PUBLISH = '/chromewebstore/v1.1/items/%s/publish' % APP_ID 39c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.comCOMMAND_POST_UPLOAD = '/upload/chromewebstore/v1.1/items/%s' % APP_ID 404e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com 414e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comclass CodeRequestHandler(SocketServer.StreamRequestHandler): 42bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com def handle(self): 43bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com content = self.rfile.readline() 4415e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com self.server.code = re.search('code=(.*) ', content).groups()[0] 4515e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com self.rfile.close() 46e9d0060f4d7b5a07a220182d83aae3a140784c4breed@android.com 474bc1983e01d756ae9c91fd380758457f579d26eareed@android.comdef GetAuthCode(): 48bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com Handler = CodeRequestHandler 49bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com httpd = SocketServer.TCPServer(("", PORT), Handler) 5030e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com query = '&'.join(['response_type=code', 5130e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com 'scope=%s' % WEBSTORE_API_SCOPE, 5230e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com 'client_id=%(client_id)s' % PROJECT_ARGS, 5330e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com 'redirect_uri=%(redirect_uri)s' % PROJECT_ARGS]) 5430e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com auth_url = ' https://%s%s?%s' % (OAUTH_DOMAIN, OAUTH_AUTH_COMMAND, query) 5530e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com print 'Navigating to %s' % auth_url 56bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com webbrowser.open(auth_url) 5730e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com httpd.handle_request() 5830e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com httpd.server_close() 5930e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com return httpd.code 6030e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com 6130e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.comdef GetOauthToken(code, client_secret): 6230e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com PROJECT_ARGS['code'] = code 634bc1983e01d756ae9c91fd380758457f579d26eareed@android.com PROJECT_ARGS['client_secret'] = client_secret 644bc1983e01d756ae9c91fd380758457f579d26eareed@android.com body = urllib.urlencode(PROJECT_ARGS) 654bc1983e01d756ae9c91fd380758457f579d26eareed@android.com conn = httplib.HTTPSConnection(OAUTH_DOMAIN) 66fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com conn.putrequest('POST', OAUTH_TOKEN_COMMAND) 674bc1983e01d756ae9c91fd380758457f579d26eareed@android.com conn.putheader('content-type', 'application/x-www-form-urlencoded') 684bc1983e01d756ae9c91fd380758457f579d26eareed@android.com conn.putheader('content-length', len(body)) 694bc1983e01d756ae9c91fd380758457f579d26eareed@android.com conn.endheaders() 70fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com conn.send(body) 7129348cb0612e19030d979156860946241e2ff4bdreed@android.com content = conn.getresponse().read() 7229348cb0612e19030d979156860946241e2ff4bdreed@android.com return json.loads(content) 7329348cb0612e19030d979156860946241e2ff4bdreed@android.com 74fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.comdef GetPopulatedHeader(client_secret): 754e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com code = GetAuthCode() 764e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com access_token = GetOauthToken(code, client_secret) 774e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.com url = 'www.googleapis.com' 78e9d0060f4d7b5a07a220182d83aae3a140784c4breed@android.com 799dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com return {'Authorization': 'Bearer %(access_token)s' % access_token, 809dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com 'x-goog-api-version': 2, 819dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com 'Content-Length': 0 829dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com } 839dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com 849dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.comdef SendGetCommand(command, client_secret): 859dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com headers = GetPopulatedHeader(client_secret) 86ef77ec210416733e38072175cc948dcbdb831685reed@google.com conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN) 87ef77ec210416733e38072175cc948dcbdb831685reed@google.com conn.request('GET', command, '', headers) 88ef77ec210416733e38072175cc948dcbdb831685reed@google.com return conn.getresponse() 89ef77ec210416733e38072175cc948dcbdb831685reed@google.com 90ef77ec210416733e38072175cc948dcbdb831685reed@google.comdef SendPostCommand(command, client_secret, header_additions = {}, body=None): 91ef77ec210416733e38072175cc948dcbdb831685reed@google.com headers = GetPopulatedHeader(client_secret) 92ef77ec210416733e38072175cc948dcbdb831685reed@google.com headers = dict(headers.items() + header_additions.items()) 93ef77ec210416733e38072175cc948dcbdb831685reed@google.com conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN) 94ef77ec210416733e38072175cc948dcbdb831685reed@google.com conn.request('POST', command, body, headers) 95ef77ec210416733e38072175cc948dcbdb831685reed@google.com return conn.getresponse() 96ef77ec210416733e38072175cc948dcbdb831685reed@google.com 97ef77ec210416733e38072175cc948dcbdb831685reed@google.comdef GetUploadStatus(client_secret): 98ef77ec210416733e38072175cc948dcbdb831685reed@google.com '''Gets the status of a previous upload. 99ef77ec210416733e38072175cc948dcbdb831685reed@google.com Args: 100c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com client_secret ChromeVox's client secret creds. 101c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com ''' 102c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com return SendGetCommand(COMMAND_GET_UPLOAD_STATUS, client_secret) 103c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com 104c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com# httplib fails to persist the connection during upload; use curl instead. 105c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.comdef PostUpload(file, client_secret): 106c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com '''Posts an uploaded version of ChromeVox. 107c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com Args: 108ef77ec210416733e38072175cc948dcbdb831685reed@google.com file A string path to the ChromeVox extension zip. 109bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com client_secret ChromeVox's client secret creds. 1100561a3ccd24e46259d825adf5507c7ee9531b9c5reed@google.com ''' 1114bc1983e01d756ae9c91fd380758457f579d26eareed@android.com header = GetPopulatedHeader(client_secret) 112bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com curl_command = ' '.join(['curl', 11330e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com '-H "Authorization: %(Authorization)s"' % header, 114bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com '-H "x-goog-api-version: 2"', 11530e6d2c2054c15f1cb6c0637bee6756261291751bsalomon@google.com '-X PUT', 116f523e25da069e3e1af2cb73d37073a34f3bb9ea2reed@android.com '-T %s' % file, 117f523e25da069e3e1af2cb73d37073a34f3bb9ea2reed@android.com '-v', 1189dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com 'https://%s%s' % (API_ENDPOINT_DOMAIN, 1199dc2713fc4a75e7fbee2f985057fde680a07c7f0tomhudson@google.com COMMAND_POST_UPLOAD)]) 120f523e25da069e3e1af2cb73d37073a34f3bb9ea2reed@android.com 1214bc1983e01d756ae9c91fd380758457f579d26eareed@android.com print 'Running %s' % curl_command 1224bc1983e01d756ae9c91fd380758457f579d26eareed@android.com if os.system(curl_command) != 0: 1234bc1983e01d756ae9c91fd380758457f579d26eareed@android.com sys.exit(-1) 12429348cb0612e19030d979156860946241e2ff4bdreed@android.com 1254e635f9eb218b15f656a02e71c60d4f787fe9306reed@android.comdef PostPublishTrustedTesters(client_secret): 126ef77ec210416733e38072175cc948dcbdb831685reed@google.com '''Publishes a previously uploaded ChromeVox extension to trusted testers. 127c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com Args: 12815e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com client_secret ChromeVox's client secret creds. 12915e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com ''' 130bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com return SendPostCommand(COMMAND_POST_PUBLISH, 131bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.com client_secret, 132bd6343b1d60d2a85e930f33f4b06b4502b3e8caamtklein@google.com { 'publishTarget': 'trustedTesters'}) 133f523e25da069e3e1af2cb73d37073a34f3bb9ea2reed@android.com 134bd700c391dd45acd8ea1a40321789c9d92a14bb8reed@android.comdef 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