• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
6import httplib2
7import json
8import logging
9import os
10import re
11import shutil
12import urllib2
13
14import common
15
16from autotest_lib.client.common_lib import autotemp
17from autotest_lib.client.common_lib import utils
18
19
20TEST_EXTENSION_ID = 'hfaagokkkhdbgiakmmlclaapfelnkoah'
21UPDATE_CHECK_URL = ('https://clients2.google.com/service/update2/')
22UPDATE_CHECK_PARAMETER = ('crx?x=id%%3D%s%%26v%%3D0%%26uc')
23MANIFEST_KEY = ('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+hlN5FB+tjCsBszmBIvI'
24                'cD/djLLQm2zZfFygP4U4/o++ZM91EWtgII10LisoS47qT2TIOg4Un4+G57e'
25                'lZ9PjEIhcJfANqkYrD3t9dpEzMNr936TLB2u683B5qmbB68Nq1Eel7KVc+F'
26                '0BqhBondDqhvDvGPEV0vBsbErJFlNH7SQIDAQAB')
27
28
29class SonicDownloaderException(Exception):
30    """Generic sonic dowloader exception."""
31    pass
32
33
34def 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
54def 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
66def 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
86def 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