| 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 import os |
| 7 import shutil |
| 8 import ctypes |
| 9 import urllib |
| 10 import socket |
| 11 import httplib |
| 12 import _winreg |
| 13 import tempfile |
| 14 import platform |
| 15 import urlparse |
| 16 import subprocess |
| 17 |
| 18 _PLATFORM = platform.system().lower() |
| 19 _CSIDL_COMMON_APPDATA = 28 |
| 20 _CSIDL_PROGRAM_FILESX86 = 42 |
| 21 |
| 22 # Import only on Windows. On other platforms it will invoke a ValueError. |
| 23 if _PLATFORM == 'windows': |
| 24 from ctypes import wintypes, windll |
| 25 |
| 26 |
| 27 class ChromeInstaller: |
| 28 """This class is used to install or uninstall Chrome.""" |
| 29 |
| 30 def __init__(self, url, build, dest, opts='', clean=True): |
| 31 self._url = (lambda u: u if self._DoesUrlExist(u) else '')(url) |
| 32 self._dest = dest |
| 33 self._opts = opts |
| 34 self._clean = clean |
| 35 self._type = (lambda o: 'system' if o.find('system-level') != -1 else |
| 36 'user')(self._opts) |
| 37 self._plat = 'win' |
| 38 self._installer = 'mini_installer.exe' |
| 39 self.SetCurrentBuild(build) |
| 40 self.c_install = ChromeVersion(self._type) |
| 41 self._c_path = self.c_install.GetChromeExePath() |
| 42 |
| 43 def SetCurrentBuild(self, bld): |
| 44 """Sets the build number that is to be installed.""" |
| 45 if type(bld) == str: |
| 46 self.build = (lambda b: (all(map(lambda x: x.isdigit(), b.split('.'))) |
| 47 and len(b.split('.')) == 4) and b or '')(bld) |
| 48 else: |
| 49 self.build = '' |
| 50 |
| 51 def _DoesUrlExist(self, url): |
| 52 """Checks if a url exists. |
| 53 |
| 54 Args: |
| 55 url: URL to be verified. |
| 56 |
| 57 Returns: |
| 58 Boolean - True if the URL exists, otherwise False. |
| 59 """ |
| 60 parse = urlparse.urlparse(url) |
| 61 if parse[0] == '' or parse[1] == '': |
| 62 return False |
| 63 try: |
| 64 conn = httplib.HTTPConnection(parse.netloc) |
| 65 conn.request('HEAD', parse.path) |
| 66 res = conn.getresponse() |
| 67 except socket.gaierror: |
| 68 return False |
| 69 finally: |
| 70 conn.close() |
| 71 # Redirected - (301: permanently moved, 302: temporarily moved) |
| 72 if res.status == 302 or res.status == 301: |
| 73 return self._DoesUrlExist(res.getheader('location')) |
| 74 return res.status == 200 |
| 75 |
| 76 def _Download(self, _file, url, dest=None): |
| 77 """Downloads a file from the specified URL |
| 78 |
| 79 Args: |
| 80 _file: Name of the file to download. |
| 81 url: URL where the file is located. |
| 82 dest: Where file will be downloaded (optional). Default location is CWD. |
| 83 |
| 84 Returns: |
| 85 An integer - zero if successful, otherwise a non-zero value. |
| 86 """ |
| 87 f_name = (lambda d, f: os.path.join(d, f) if(d and os.path.exists(d)) else |
| 88 os.path.join(tempfile.gettempdir(), f))(dest, _file) |
| 89 file_url = '%s/%s' % (url, _file) |
| 90 # Check link before downloading, urlretrieve *WILL NOT* report |
| 91 # an error if url doesn't exist. |
| 92 if not self._DoesUrlExist(file_url): |
| 93 print "Could not locate the file specified." |
| 94 return -1 |
| 95 try: |
| 96 d = urllib.urlretrieve(file_url, f_name) |
| 97 except IOError, err: |
| 98 print 'CChromeInstaller._Download: ', err |
| 99 return -1 |
| 100 if os.path.isfile(d[0]): |
| 101 return 0 |
| 102 return 1 |
| 103 |
| 104 def _Delete(self, _path): |
| 105 """Deletes a file or folder""" |
| 106 try: |
| 107 (lambda p: shutil.rmtree(p) if os.path.isdir(p) else os.remove(p))(_path) |
| 108 return 0 |
| 109 except(OSError, IOError, TypeError), err: |
| 110 print 'CChromeInstaller._Delete: ', err |
| 111 return -1 |
| 112 |
| 113 def _LaunchInstaller(self, opts=''): |
| 114 """Launches the Chrome installer using specified options. |
| 115 |
| 116 Args: |
| 117 opts: Any options that are to be passed to the installer. |
| 118 |
| 119 Returns: |
| 120 An integer - zero if successful, otherwise a non-zero value. |
| 121 """ |
| 122 print 'Downloading installer for build %s ...' % (self.build) |
| 123 build_url = '%s/%s/%s' % (self._url, self.build, self._plat) |
| 124 if self._Download(self._installer, build_url, self._dest) != 0: |
| 125 print 'Failed to download installer, exiting test' |
| 126 return -1 |
| 127 print 'Launching installer...' |
| 128 cmd = '%s %s' % (os.path.join(self._dest, self._installer), opts) |
| 129 try: |
| 130 return subprocess.Popen.wait(subprocess.Popen(cmd)) |
| 131 except OSError, err: |
| 132 print 'CChromeInstaller._LaunchInstaller: ', err |
| 133 return -2 |
| 134 |
| 135 def _RemoveDuplicates(self, args): |
| 136 """Removes duplicates from list of options. |
| 137 |
| 138 Args: |
| 139 args: A list containing the options. |
| 140 |
| 141 Returns: |
| 142 A string without any duplicate options. |
| 143 """ |
| 144 return ((lambda arg: arg if arg else '') |
| 145 (' '.join(map(lambda opt: opt, list(frozenset(args.split(','))))))) |
| 146 |
| 147 def _DoPreliminaryChecks(self, op='install'): |
| 148 """Checks test parameters for validity and performs version checking.""" |
| 149 if _PLATFORM != 'windows': |
| 150 print 'Unsupported platform, aborting operation...' |
| 151 return False |
| 152 if self._url == '': |
| 153 print 'URL specified is not valid. Please check the URL and try again.' |
| 154 return False |
| 155 ver = self.c_install.GetChromeVersion() |
| 156 c_type = self.c_install.GetInstallTypeString() |
| 157 if op == 'install': |
| 158 self.pyauto_url = '%s/%s/%s/%s' % (self._url, self.build, 'win', |
| 159 'chrome-win32.test') |
| 160 if c_type == 'system' and self._type == 'user': |
| 161 print 'System level Chrome exists, aborting user level installation.' |
| 162 return False |
| 163 # Version installed is higher than or same as the one we're installing. |
| 164 elif self._type == c_type: |
| 165 if self.build <= ver: |
| 166 print 'Please specify a version higher than the one installed.' |
| 167 return False |
| 168 elif op == 'uninstall': |
| 169 if not ver: |
| 170 print 'No version of Chrome found, aborting uninstall...' |
| 171 return False |
| 172 if self._type != c_type: |
| 173 print ('Invalid args passed. Please specify --opts=--system-level if ' |
| 174 'uninstalling system version of Chrome, or omit it otherwise.') |
| 175 return False |
| 176 return True |
| 177 |
| 178 def InstallChrome(self): |
| 179 """Installs specified chrome build.""" |
| 180 if not self._DoPreliminaryChecks('install'): |
| 181 return None |
| 182 opts = '--install --do-not-launch-chrome %s' % (self._opts) |
| 183 opts = self._RemoveDuplicates(opts) |
| 184 ret = self._LaunchInstaller(opts) |
| 185 self._Delete(os.path.join(self._dest, self._installer)) |
| 186 if ret == 0 and os.path.exists(self._c_path): |
| 187 print 'Installation complete...' |
| 188 return self.c_install |
| 189 else: |
| 190 print 'Installation failed.' |
| 191 return None |
| 192 |
| 193 def UninstallChrome(self): |
| 194 """Uninstalls Chrome.""" |
| 195 if not self._DoPreliminaryChecks('uninstall'): |
| 196 return False |
| 197 install_type = self.c_install.GetChromeInstallType() |
| 198 reg_opts = self.c_install.GetUninstallArgs(install_type) |
| 199 opts = '%s --force-uninstall %s' % (reg_opts, self._opts) |
| 200 opts = self._RemoveDuplicates(opts) |
| 201 if not os.path.exists(self._c_path): |
| 202 print 'Couldn\'t find Chrome. Verify Chrome is installed and try again.' |
| 203 return False |
| 204 ret = self._LaunchInstaller(opts) |
| 205 if os.path.exists(os.path.join(self._dest, self._installer)): |
| 206 self._Delete(os.path.join(self._dest, self._installer)) |
| 207 if not os.path.exists(self._c_path): |
| 208 print 'Chrome was successfully uninstalled...' |
| 209 if self._clean: |
| 210 print 'Removing registry settings...' |
| 211 self.c_install.DeleteChromeRegEntries() |
| 212 print 'Uninstall complete.' |
| 213 return True |
| 214 else: |
| 215 print 'Uninstall failed.' |
| 216 return False |
| 217 |
| 218 |
| 219 class ChromeVersion(): |
| 220 """Contains utility functions that provide information about installed |
| 221 |
| 222 version of Chrome. The type of Chrome version is passed as an argument |
| 223 to the constructor (i.e. - user or system level version). |
| 224 """ |
| 225 |
| 226 def __init__(self, install_type): |
| 227 self._type = install_type |
| 228 self.HKEY_LOCAL = (r'SOFTWARE\Wow6432Node\Google\Update\ClientState' |
| 229 '\{8A69D345-D564-463C-AFF1-A69D9E530F96}') |
| 230 self.HKEY_USER = self.HKEY_LOCAL.replace('\\Wow6432Node', '') |
| 231 |
| 232 def _OpenKey(self, key_type, key_name, key_access=_winreg.KEY_READ): |
| 233 """Opens a registry key. |
| 234 |
| 235 Args: |
| 236 key_type: HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE. |
| 237 key_name: Name of the key that is to be opened. |
| 238 key_access: Type of access required (i.e. - KEY_READ or KEY_ALL_ACCESS). |
| 239 |
| 240 Returns: |
| 241 Handle to the key if successful, otherwise zero. |
| 242 """ |
| 243 try: |
| 244 key = _winreg.OpenKey(key_type, key_name, 0, key_access) |
| 245 return key |
| 246 except(OSError, TypeError), err: |
| 247 return 0 |
| 248 |
| 249 def _GetKeyValue(self, key, name): |
| 250 """Gets the value of the specified key. |
| 251 |
| 252 Args: |
| 253 key: Handle to the key. |
| 254 name: Name of the key. |
| 255 |
| 256 Returns: |
| 257 A string representing key value if successful, otherwise empty string. |
| 258 """ |
| 259 try: |
| 260 val = str(_winreg.QueryValueEx(key, name)[0]) |
| 261 return val |
| 262 except OSError, err: |
| 263 print 'ChromeVersion._GetKeyValue: %s' % (err) |
| 264 return '' |
| 265 |
| 266 def _GetKeyName(self, install_type, b_replace=True): |
| 267 """Returns Chrome registry key name. """ |
| 268 if install_type == _winreg.HKEY_CURRENT_USER: |
| 269 if b_replace: |
| 270 return self.HKEY_USER.replace('ClientState', 'Clients') |
| 271 else: |
| 272 return self.HKEY_USER |
| 273 elif install_type == _winreg.HKEY_LOCAL_MACHINE: |
| 274 if b_replace: |
| 275 return self.HKEY_LOCAL.replace('ClientState', 'Clients') |
| 276 else: |
| 277 return self.HKEY_LOCAL |
| 278 |
| 279 def _GetWinLocalFolder(self, ftype=_CSIDL_COMMON_APPDATA): |
| 280 """Returns the full path of the 'Local' folder on Windows. |
| 281 |
| 282 Args: |
| 283 ftype: Location to look up, which could vary based on installation type. |
| 284 |
| 285 Returns: |
| 286 A string containing the path if successful, otherwise an empty string. |
| 287 """ |
| 288 if _PLATFORM != 'windows': |
| 289 return '' |
| 290 SHGetFolderPathW = windll.shell32.SHGetFolderPathW |
| 291 SHGetFolderPathW.argtypes = [wintypes.HWND, |
| 292 ctypes.c_int, |
| 293 wintypes.HANDLE, |
| 294 wintypes.DWORD, |
| 295 wintypes.LPCWSTR] |
| 296 path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH) |
| 297 result = SHGetFolderPathW(0, ftype, 0, 0, path_buf) |
| 298 return str(path_buf.value) |
| 299 |
| 300 def GetInstallTypeString(self): |
| 301 """Returns a string representing the type of Chrome installation.""" |
| 302 install_type = ({_winreg.HKEY_CURRENT_USER: 'user', |
| 303 _winreg.HKEY_LOCAL_MACHINE: 'system'}).get( |
| 304 self.GetChromeInstallType()) |
| 305 return install_type |
| 306 |
| 307 def GetChromeInstallType(self): |
| 308 """Determines Chrome installation type.""" |
| 309 c_type = None |
| 310 key = self._OpenKey(_winreg.HKEY_CURRENT_USER, |
| 311 self.HKEY_USER.replace('ClientState', 'Clients')) |
| 312 if key: |
| 313 if self._GetKeyValue(key, 'pv'): |
| 314 c_type = _winreg.HKEY_CURRENT_USER |
| 315 else: |
| 316 key = self._OpenKey(_winreg.HKEY_LOCAL_MACHINE, |
| 317 self.HKEY_USER.replace('ClientState', 'Clients')) |
| 318 if key: |
| 319 if self._GetKeyValue(key, 'pv'): |
| 320 c_type = _winreg.HKEY_LOCAL_MACHINE |
| 321 if key: |
| 322 _winreg.CloseKey(key) |
| 323 return c_type |
| 324 |
| 325 def GetChromeVersion(self): |
| 326 """Returns current Chrome version.""" |
| 327 chrome_ver = '' |
| 328 key_type = ({'user': _winreg.HKEY_CURRENT_USER, |
| 329 'system' : _winreg.HKEY_LOCAL_MACHINE}).get(self._type) |
| 330 key = self._OpenKey(key_type, self._GetKeyName(key_type)) |
| 331 if key: |
| 332 chrome_ver = self._GetKeyValue(key, 'pv') |
| 333 _winreg.CloseKey(key) |
| 334 return chrome_ver |
| 335 |
| 336 def GetUninstallArgs(self, install_type=_winreg.HKEY_CURRENT_USER): |
| 337 """Gets chrome's uninstall arguments from the registry. |
| 338 |
| 339 Args: |
| 340 install_type: Type of chrome installation (user or system). |
| 341 |
| 342 Returns: |
| 343 A string containing the arguments if successful, otherwise empty string. |
| 344 """ |
| 345 c_args = '' |
| 346 key = self._OpenKey(install_type, self._GetKeyName(install_type, False)) |
| 347 if key: |
| 348 c_args = self._GetKeyValue(key, 'UninstallArguments') |
| 349 _winreg.CloseKey(key) |
| 350 return c_args |
| 351 |
| 352 def GetChromeExePath(self): |
| 353 """Gets Chrome's location, based on install type (i.e. user or system).""" |
| 354 c_path = '' |
| 355 f_id = (lambda _type: _type == 'user' and _CSIDL_COMMON_APPDATA or |
| 356 _CSIDL_PROGRAM_FILESX86)(self._type) |
| 357 if _PLATFORM == 'windows': |
| 358 c_path = os.path.join(self._GetWinLocalFolder(f_id), |
| 359 'Google', 'Chrome', |
| 360 'Application', 'chrome.exe') |
| 361 return c_path |
| 362 |
| 363 def DeleteChromeRegEntries(self): |
| 364 """Deletes chrome registry settings.""" |
| 365 if self._type == 'user': |
| 366 p_key = self.HKEY_USER[: self.HKEY_USER.rfind('\\')] |
| 367 key_name = self.HKEY_USER[self.HKEY_USER.rfind('\\') + 1 :] |
| 368 _type = _winreg.HKEY_CURRENT_USER |
| 369 else: |
| 370 p_key = self.HKEY_LOCAL[: self.HKEY_LOCAL.rfind('\\')] |
| 371 key_name = self.HKEY_LOCAL[self.HKEY_LOCAL.rfind('\\')+1:] |
| 372 _type = _winreg.HKEY_LOCAL_MACHINE |
| 373 key = self._OpenKey(_type, p_key, _winreg.KEY_ALL_ACCESS) |
| 374 try: |
| 375 _winreg.DeleteKey(key, key_name) |
| 376 key.Close() |
| 377 return 0 |
| 378 except _winreg.error, err: |
| 379 print "Warning: could not delete registry settings - ", err |
| 380 return -1 |
| OLD | NEW |