Home | History | Annotate | Download | only in cros
      1 #!/usr/bin/python
      2 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 import httplib2
      7 import json
      8 import logging
      9 import os
     10 import re
     11 import shutil
     12 import urllib2
     13 
     14 import common
     15 
     16 from autotest_lib.client.common_lib import autotemp
     17 from autotest_lib.client.common_lib import utils
     18 
     19 
     20 TEST_EXTENSION_ID = 'hfaagokkkhdbgiakmmlclaapfelnkoah'
     21 UPDATE_CHECK_URL = ('https://clients2.google.com/service/update2/')
     22 UPDATE_CHECK_PARAMETER = ('crx?x=id%%3D%s%%26v%%3D0%%26uc')
     23 MANIFEST_KEY = ('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+hlN5FB+tjCsBszmBIvI'
     24                 'cD/djLLQm2zZfFygP4U4/o++ZM91EWtgII10LisoS47qT2TIOg4Un4+G57e'
     25                 'lZ9PjEIhcJfANqkYrD3t9dpEzMNr936TLB2u683B5qmbB68Nq1Eel7KVc+F'
     26                 '0BqhBondDqhvDvGPEV0vBsbErJFlNH7SQIDAQAB')
     27 
     28 
     29 class SonicDownloaderException(Exception):
     30     """Generic sonic dowloader exception."""
     31     pass
     32 
     33 
     34 def get_download_url_from_omaha(extension_id):
     35     """Retrieves an update url from omaha for the specified extension id.
     36 
     37     @param extension_id: The extension id of the chromecast extension.
     38 
     39     @return: A url to download the extension from.
     40 
     41     @raises IOError: If the response returned by the omaha server is invalid.
     42     """
     43     update_check_link = '%s%s' % (UPDATE_CHECK_URL,
     44                                   UPDATE_CHECK_PARAMETER % extension_id)
     45     response_xml = httplib2.Http().request(update_check_link, 'GET')[1]
     46     codebase_match = re.compile(r'codebase="(.*crx)"').search(response_xml)
     47     if codebase_match is not None:
     48         logging.info('Omaha response while downloading extension: %s',
     49                      response_xml)
     50         return codebase_match.groups()[0]
     51     raise IOError('Omaha response is invalid %s.' % response_xml)
     52 
     53 
     54 def download_extension(dest_file):
     55     """Retrieve the extension into a destination crx file.
     56 
     57     @param dest_file: Path to a destination file for the extension.
     58     """
     59     download_url = get_download_url_from_omaha(TEST_EXTENSION_ID)
     60     logging.info('Downloading extension from %s', download_url)
     61     response = urllib2.urlopen(download_url)
     62     with open(dest_file, 'w') as f:
     63         f.write(response.read())
     64 
     65 
     66 def fix_public_key(extracted_extension_folder):
     67     """Modifies the manifest.json to include a public key.
     68 
     69     This function will erase the content in the original manifest
     70     and replace it with a new manifest that contains the key.
     71 
     72     @param extracted_extension_folder: The folder containing
     73         the extracted extension.
     74     """
     75     manifest_json_file = os.path.join(extracted_extension_folder,
     76                                       'manifest.json')
     77     with open(manifest_json_file, 'r') as f:
     78         manifest_json = json.loads(f.read())
     79 
     80     manifest_json['key'] = MANIFEST_KEY
     81 
     82     with open(manifest_json_file, 'w') as f:
     83         f.write(json.dumps(manifest_json))
     84 
     85 
     86 def setup_extension(unzipped_crx_dir):
     87     """Setup for tests that need a chromecast extension.
     88 
     89     Download the extension from an omaha server, unzip it and modify its
     90     manifest.json to include a public key.
     91 
     92     @param unzipped_crx_dir: Destination directory for the unzipped extension.
     93 
     94     @raises CmdTimeoutError: If we timeout unzipping the extension.
     95     """
     96     output_crx_dir = autotemp.tempdir()
     97     output_crx = os.path.join(output_crx_dir.name, 'sonic_extension.crx')
     98     try:
     99         download_extension(output_crx)
    100         unzip_cmd = 'unzip -o "%s" -d "%s"' % (output_crx, unzipped_crx_dir)
    101 
    102         # The unzip command will return a non-zero exit status if there are
    103         # extra bytes at the start/end of the zipfile. This is not a critical
    104         # failure and the extension will still work.
    105         cmd_output = utils.run(unzip_cmd, ignore_status=True, timeout=1)
    106     except Exception as e:
    107         if os.path.exists(unzipped_crx_dir):
    108             shutil.rmtree()
    109         raise SonicDownloaderException(e)
    110     finally:
    111         if os.path.exists(output_crx):
    112             os.remove(output_crx)
    113         output_crx_dir.clean()
    114 
    115     if not os.path.exists(unzipped_crx_dir):
    116         raise SonicDownloaderException('Unable to download sonic extension.')
    117     logging.info('Sonic extension successfully downloaded into %s.',
    118                  unzipped_crx_dir)
    119 
    120     # TODO(beeps): crbug.com/325869, investigate the limits of component
    121     # extensions. For now this is ok because even sonic testing inlines a
    122     # public key for their test extension.
    123     try:
    124         fix_public_key(unzipped_crx_dir)
    125     except Exception as e:
    126         shutil.rmtree(unzipped_crx_dir)
    127         raise SonicDownloaderException(e)
    128