| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2012 The Chromium 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 """Provides an interface for installing Chrome.""" |
| 7 |
| 8 import _winreg |
| 9 import ctypes |
| 10 import httplib |
| 11 import logging |
| 12 import os |
| 13 import platform |
| 14 import shutil |
| 15 import socket |
| 16 import subprocess |
| 17 import tempfile |
| 18 import urllib |
| 19 |
| 20 |
| 21 _CSIDL_COMMON_APPDATA = 0x1C |
| 22 _CSIDL_PROGRAM_FILESX86 = 0x2A |
| 23 _PLATFORM = platform.system().lower() |
| 24 |
| 25 # Import only on Windows. On other platforms it will invoke a ValueError. |
| 26 if _PLATFORM == 'windows': |
| 27 from ctypes import wintypes, windll |
| 28 |
| 29 |
| 30 class InstallationType: |
| 31 """Defines the Chrome installation types.""" |
| 32 SYSTEM = 0 |
| 33 USER = 1 |
| 34 |
| 35 |
| 36 def _RemoveDuplicates(args): |
| 37 """Removes duplicates options from a list. |
| 38 |
| 39 Args: |
| 40 args: A list containing the options. |
| 41 |
| 42 Returns: |
| 43 A string without any duplicate options. |
| 44 """ |
| 45 return ((lambda arg: arg if arg else '') |
| 46 (' '.join(map(lambda opt: opt, list(frozenset(args.split(','))))))) |
| 47 |
| 48 |
| 49 def Install(installer_path, chrome_type, build, options='', clean=True): |
| 50 """Installs the specified Chrome build.""" |
| 51 def DoPreliminaryChecks(chrome_install): |
| 52 """Validates the test parameters and Chrome version.""" |
| 53 assert(_PLATFORM == 'windows' and os.path.isfile(installer_path)) |
| 54 # Chrome installation type specified by the user. |
| 55 install_type = ('system-level' in options and InstallationType.SYSTEM |
| 56 or InstallationType.USER) |
| 57 cur_build = chrome_install.GetChromeVersion() |
| 58 # Chrome already installed, make sure new build can be installed over it. |
| 59 if cur_build: |
| 60 current_type = chrome_install.GetChromeInstallationType() |
| 61 if(current_type == InstallationType.SYSTEM and install_type == |
| 62 InstallationType.USER): |
| 63 raise RuntimeError('System level Chrome exists, aborting user level ' |
| 64 'installation.') |
| 65 # Installing a build that's older than the currently installed build. |
| 66 elif current_type == install_type: |
| 67 if cur_build >= build: |
| 68 raise RuntimeError('Please specify a newer version of Chrome.') |
| 69 |
| 70 chrome_install = Installation(chrome_type) |
| 71 DoPreliminaryChecks(chrome_install) |
| 72 options += ' --install --do-not-launch-chrome' |
| 73 options = _RemoveDuplicates(options) |
| 74 logging.log(logging.INFO, 'Launching Chrome installer...') |
| 75 cmd = '%s %s' % (installer_path, options) |
| 76 ret = subprocess.Popen(cmd, shell=True).wait() |
| 77 if ret == 0: |
| 78 logging.log(logging.INFO, 'Installation complete.') |
| 79 return chrome_install |
| 80 logging.log(logging.ERROR, 'Installation failed.') |
| 81 return None |
| 82 |
| 83 |
| 84 class Installation(object): |
| 85 """Provides pertinent information about the installed Chrome version. |
| 86 |
| 87 The type of Chrome version must be passed as an argument to the constructor, |
| 88 (i.e. - user or system level). |
| 89 """ |
| 90 |
| 91 HKEY_LOCAL = (r'SOFTWARE\Wow6432Node\Google\Update\ClientState' |
| 92 '\{8A69D345-D564-463C-AFF1-A69D9E530F96}') |
| 93 HKEY_USER = HKEY_LOCAL.replace('\\Wow6432Node', '') |
| 94 |
| 95 def __init__(self, install_type): |
| 96 assert(install_type == InstallationType.SYSTEM or |
| 97 install_type == InstallationType.USER) |
| 98 self._type = install_type |
| 99 self._key_type = self._GetKeyType(self._type) |
| 100 |
| 101 def _OpenKey(self, key_type, key_name, key_access=_winreg.KEY_READ): |
| 102 """Opens a registry key and returns the key handle. |
| 103 |
| 104 Args: |
| 105 key_type: The type of key HKLM or HKCU. |
| 106 key_name: Name of the key. |
| 107 key_access: Type of access required. Be default its read only. |
| 108 |
| 109 Returns: |
| 110 Key handle if successful, otherwise None. |
| 111 """ |
| 112 try: |
| 113 key = _winreg.OpenKey(key_type, key_name, 0, key_access) |
| 114 return key |
| 115 except _winreg.error: |
| 116 return None |
| 117 |
| 118 def _GetWinLocalFolder(self, ftype=_CSIDL_COMMON_APPDATA): |
| 119 """Returns full path of the 'Local' folder on Windows. |
| 120 |
| 121 Args: |
| 122 ftype: Location to look up, which could vary based on installation type. |
| 123 |
| 124 Returns: |
| 125 A String representing the folder path if successful, otherwise an empty |
| 126 string. |
| 127 """ |
| 128 if _PLATFORM != 'windows': |
| 129 return '' |
| 130 SHGetFolderPathW = windll.shell32.SHGetFolderPathW |
| 131 SHGetFolderPathW.argtypes = [wintypes.HWND, |
| 132 ctypes.c_int, |
| 133 wintypes.HANDLE, |
| 134 wintypes.DWORD, |
| 135 wintypes.LPCWSTR] |
| 136 path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH) |
| 137 result = SHGetFolderPathW(0, ftype, 0, 0, path_buf) |
| 138 return str(path_buf.value) |
| 139 |
| 140 def _GetKeyType(self, install_type): |
| 141 """Determines the registry key to use based on installation type.""" |
| 142 if install_type == InstallationType.SYSTEM: |
| 143 return _winreg.HKEY_LOCAL_MACHINE |
| 144 elif install_type == InstallationType.USER: |
| 145 return _winreg.HKEY_CURRENT_USER |
| 146 |
| 147 def _GetKeyName(self, install_type, replace=True): |
| 148 """Returns Chrome's registry key name based on installation type.""" |
| 149 name = (install_type == InstallationType.SYSTEM and self.HKEY_LOCAL or |
| 150 self.HKEY_USER) |
| 151 return (replace and name.replace('ClientState', 'Clients') or name) |
| 152 |
| 153 def _DeleteChromeRegEntries(self): |
| 154 """Deletes chrome registry settings.""" |
| 155 if self._type == InstallationType.USER: |
| 156 p_key = self.HKEY_USER[: self.HKEY_USER.rfind('\\')] |
| 157 key_name = self.HKEY_USER[self.HKEY_USER.rfind('\\') + 1 :] |
| 158 _type = _winreg.HKEY_CURRENT_USER |
| 159 elif self._type == InstallationType.SYSTEM: |
| 160 p_key = self.HKEY_LOCAL[: self.HKEY_LOCAL.rfind('\\')] |
| 161 key_name = self.HKEY_LOCAL[self.HKEY_LOCAL.rfind('\\') + 1 :] |
| 162 _type = _winreg.HKEY_LOCAL_MACHINE |
| 163 try: |
| 164 key = self._OpenKey(_type, p_key, _winreg.KEY_ALL_ACCESS) |
| 165 _winreg.DeleteKey(key, key_name) |
| 166 key.Close() |
| 167 return 0 |
| 168 except _winreg.error, err: |
| 169 logging.log(logging.ERROR, 'Could not delete registry entries: %s' % err)
|
| 170 return -1 |
| 171 |
| 172 def _GetValueFromRegistry(self, reg_type, key, subkey): |
| 173 """Gets value of the specified subkey from the registry. |
| 174 |
| 175 Args: |
| 176 reg_type: Type of key to use HKLM or HKCU. |
| 177 key: Name of the key. |
| 178 subkey: Name of the subkey whose value will be returned. |
| 179 |
| 180 Returns: |
| 181 A string representing the subkey value if successful, otherwise None.
|
| 182 """ |
| 183 hkey = self._OpenKey(reg_type, key) |
| 184 if hkey: |
| 185 try: |
| 186 value = str(_winreg.QueryValueEx(hkey, subkey)[0]) |
| 187 except _winreg.error: |
| 188 value = None |
| 189 _winreg.CloseKey(hkey) |
| 190 return value |
| 191 return None |
| 192 |
| 193 def _GetUninstallString(self): |
| 194 """Returns Chrome uninstall string from the registry.""" |
| 195 key_name = self._GetKeyName(self._type, False) |
| 196 return self._GetValueFromRegistry(self._key_type, key_name, |
| 197 'UninstallString') |
| 198 |
| 199 def _GetUninstallArguments(self): |
| 200 """Returns Chrome uninstall arguments from the registry.""" |
| 201 key_name = self._GetKeyName(self._type, False) |
| 202 return self._GetValueFromRegistry(self._key_type, key_name, |
| 203 'UninstallArguments') |
| 204 |
| 205 def GetChromeVersion(self): |
| 206 """Returns the installed version of Chrome.""" |
| 207 key_name = self._GetKeyName(self._type, True) |
| 208 return self._GetValueFromRegistry(self._key_type, key_name, 'pv') |
| 209 |
| 210 def _LaunchInstaller(self, installer_path, options): |
| 211 """Launches the Chrome installer. |
| 212 |
| 213 Args: |
| 214 installer_path: Path where the installer is located. |
| 215 options: Any additional options to be used for installation. |
| 216 """ |
| 217 cmd = '%s %s' % (installer_path, options) |
| 218 try: |
| 219 subprocess.Popen(cmd, shell=True).wait() |
| 220 except OSError, err: |
| 221 raise err |
| 222 |
| 223 def _Delete(self, _path): |
| 224 """Deletes a file or folder.""" |
| 225 try: |
| 226 (lambda p: shutil.rmtree(p) if os.path.isdir(p) else os.remove(p))(_path) |
| 227 return 0 |
| 228 except(OSError, IOError, TypeError), err: |
| 229 return -1 |
| 230 |
| 231 def GetChromeExePath(self): |
| 232 """Returns Chrome binary location based on installation type.""" |
| 233 chrome_path = '' |
| 234 if self._type == InstallationType.USER: |
| 235 folder_id = _CSIDL_COMMON_APPDATA |
| 236 elif self._type == InstallationType.SYSTEM: |
| 237 folder_id = _CSIDL_PROGRAM_FILESX86 |
| 238 if _PLATFORM == 'windows': |
| 239 chrome_path = os.path.join(self._GetWinLocalFolder(folder_id), 'Google', |
| 240 'Chrome', 'Application', 'chrome.exe') |
| 241 return chrome_path |
| 242 |
| 243 def GetChromeInstallationType(self): |
| 244 """Determines Chrome installation type.""" |
| 245 reg_type = _winreg.HKEY_CURRENT_USER |
| 246 key_name = self.HKEY_USER.replace('ClientState', 'Clients') |
| 247 key = self._OpenKey(reg_type, key_name) |
| 248 if not key: |
| 249 reg_type = _winreg.HKEY_LOCAL_MACHINE |
| 250 key_name = self.HKEY_LOCAL.replace('ClientState', 'Clients') |
| 251 key = self._OpenKey(reg_type, key_name) |
| 252 if not key: |
| 253 return None |
| 254 version = self._GetValueFromRegistry(reg_type, key_name, 'pv')
|
| 255 key.Close() |
| 256 if version: |
| 257 return (reg_type == _winreg.HKEY_CURRENT_USER and |
| 258 InstallationType.USER or InstallationType.SYSTEM) |
| 259 return None |
| 260 |
| 261 def UninstallChrome(self): |
| 262 """Uninstalls Chrome.""" |
| 263 if not self.GetChromeVersion(): |
| 264 raise RuntimeError('No Chrome version found on this system.') |
| 265 chrome_path = self.GetChromeExePath() |
| 266 install_type = self.GetChromeInstallationType() |
| 267 reg_opts = self._GetUninstallArguments() |
| 268 uninstall_str = self._GetUninstallString() |
| 269 options = '%s --force-uninstall' % (reg_opts) |
| 270 if self._type == InstallationType.SYSTEM: |
| 271 options += ' --system-level' |
| 272 if not os.path.exists(chrome_path): |
| 273 logging.log(logging.ERROR, 'Could not find chrome, aborting uninstall.') |
| 274 return False |
| 275 logging.log(logging.INFO, 'Launching Chrome installer...') |
| 276 ret = self._LaunchInstaller(uninstall_str, options) |
| 277 if not os.path.exists(chrome_path): |
| 278 logging.log(logging.INFO, 'Chrome was uninstalled successfully...') |
| 279 # TODO: add clean option like ChromeInstaller |
| 280 logging.log(logging.INFO, 'Deleting registry entries...') |
| 281 self._DeleteChromeRegEntries() |
| 282 logging.log(logging.INFO, 'Uninstall complete.') |
| 283 return True |
| 284 else: |
| 285 logging.log(logging.ERROR, 'Uninstall failed.') |
| 286 return False |
| OLD | NEW |