Home | History | Annotate | Download | only in tools
      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