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