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 9import SimpleHTTPServer 10import SocketServer 11import httplib 12import json 13import os 14import re 15import sys 16import thread 17import urllib 18import webbrowser 19 20PROJECT_ARGS = { 21 'client_id': ('937534751394-gbj5334v9144c57qjqghl7d283plj5r4' 22 '.apps.googleusercontent.com'), 23 'grant_type': 'authorization_code', 24 'redirect_uri': 'http://localhost:8000' 25} 26 27PORT = 8000 28 29APP_ID = 'kgejglhpjiefppelpmljglcjbhoiplfn' 30OAUTH_DOMAIN = 'accounts.google.com' 31OAUTH_AUTH_COMMAND = '/o/oauth2/auth' 32OAUTH_TOKEN_COMMAND = '/o/oauth2/token' 33WEBSTORE_API_SCOPE = 'https://www.googleapis.com/auth/chromewebstore' 34 35API_ENDPOINT_DOMAIN = 'www.googleapis.com' 36COMMAND_GET_UPLOAD_STATUS = ( 37 '/chromewebstore/v1.1/items/%s?projection=draft' % APP_ID) 38COMMAND_POST_PUBLISH = '/chromewebstore/v1.1/items/%s/publish' % APP_ID 39COMMAND_POST_UPLOAD = '/upload/chromewebstore/v1.1/items/%s' % APP_ID 40 41class 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 47def 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 61def 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 74def 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 84def 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 90def 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 97def 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. 105def 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 125def 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 134def 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