• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // See the corresponding header file for description of the functions in this
6 // file.
7 
8 #include "chrome/installer/util/install_util.h"
9 
10 #include <shellapi.h>
11 #include <shlobj.h>
12 #include <shlwapi.h>
13 
14 #include <algorithm>
15 
16 #include "base/command_line.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/path_service.h"
21 #include "base/process/launch.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/sys_info.h"
25 #include "base/values.h"
26 #include "base/version.h"
27 #include "base/win/metro.h"
28 #include "base/win/registry.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/installer/util/browser_distribution.h"
33 #include "chrome/installer/util/google_update_constants.h"
34 #include "chrome/installer/util/helper.h"
35 #include "chrome/installer/util/installation_state.h"
36 #include "chrome/installer/util/l10n_string_util.h"
37 #include "chrome/installer/util/util_constants.h"
38 #include "chrome/installer/util/work_item_list.h"
39 
40 using base::win::RegKey;
41 using installer::ProductState;
42 
43 namespace {
44 
45 const wchar_t kStageBinaryPatching[] = L"binary_patching";
46 const wchar_t kStageBuilding[] = L"building";
47 const wchar_t kStageConfiguringAutoLaunch[] = L"configuring_auto_launch";
48 const wchar_t kStageCopyingPreferencesFile[] = L"copying_prefs";
49 const wchar_t kStageCreatingShortcuts[] = L"creating_shortcuts";
50 const wchar_t kStageEnsemblePatching[] = L"ensemble_patching";
51 const wchar_t kStageExecuting[] = L"executing";
52 const wchar_t kStageFinishing[] = L"finishing";
53 const wchar_t kStagePreconditions[] = L"preconditions";
54 const wchar_t kStageRefreshingPolicy[] = L"refreshing_policy";
55 const wchar_t kStageRegisteringChrome[] = L"registering_chrome";
56 const wchar_t kStageRemovingOldVersions[] = L"removing_old_ver";
57 const wchar_t kStageRollingback[] = L"rollingback";
58 const wchar_t kStageUncompressing[] = L"uncompressing";
59 const wchar_t kStageUnpacking[] = L"unpacking";
60 const wchar_t kStageUpdatingChannels[] = L"updating_channels";
61 const wchar_t kStageCreatingVisualManifest[] = L"creating_visual_manifest";
62 const wchar_t kStageDeferringToHigherVersion[] = L"deferring_to_higher_version";
63 const wchar_t kStageUninstallingBinaries[] = L"uninstalling_binaries";
64 const wchar_t kStageUninstallingChromeFrame[] = L"uninstalling_chrome_frame";
65 
66 const wchar_t* const kStages[] = {
67   NULL,
68   kStagePreconditions,
69   kStageUncompressing,
70   kStageEnsemblePatching,
71   kStageBinaryPatching,
72   kStageUnpacking,
73   kStageBuilding,
74   kStageExecuting,
75   kStageRollingback,
76   kStageRefreshingPolicy,
77   kStageUpdatingChannels,
78   kStageCopyingPreferencesFile,
79   kStageCreatingShortcuts,
80   kStageRegisteringChrome,
81   kStageRemovingOldVersions,
82   kStageFinishing,
83   kStageConfiguringAutoLaunch,
84   kStageCreatingVisualManifest,
85   kStageDeferringToHigherVersion,
86   kStageUninstallingBinaries,
87   kStageUninstallingChromeFrame,
88 };
89 
90 COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages),
91                kStages_disagrees_with_Stage_comma_they_must_match_bang);
92 
93 // Creates a zero-sized non-decorated foreground window that doesn't appear
94 // in the taskbar. This is used as a parent window for calls to ShellExecuteEx
95 // in order for the UAC dialog to appear in the foreground and for focus
96 // to be returned to this process once the UAC task is dismissed. Returns
97 // NULL on failure, a handle to the UAC window on success.
CreateUACForegroundWindow()98 HWND CreateUACForegroundWindow() {
99   HWND foreground_window = ::CreateWindowEx(WS_EX_TOOLWINDOW,
100                                             L"STATIC",
101                                             NULL,
102                                             WS_POPUP | WS_VISIBLE,
103                                             0, 0, 0, 0,
104                                             NULL, NULL,
105                                             ::GetModuleHandle(NULL),
106                                             NULL);
107   if (foreground_window) {
108     HMONITOR monitor = ::MonitorFromWindow(foreground_window,
109                                            MONITOR_DEFAULTTONEAREST);
110     if (monitor) {
111       MONITORINFO mi = {0};
112       mi.cbSize = sizeof(mi);
113       ::GetMonitorInfo(monitor, &mi);
114       RECT screen_rect = mi.rcWork;
115       int x_offset = (screen_rect.right - screen_rect.left) / 2;
116       int y_offset = (screen_rect.bottom - screen_rect.top) / 2;
117       ::MoveWindow(foreground_window,
118                    screen_rect.left + x_offset,
119                    screen_rect.top + y_offset,
120                    0, 0, FALSE);
121     } else {
122       NOTREACHED() << "Unable to get default monitor";
123     }
124     ::SetForegroundWindow(foreground_window);
125   }
126   return foreground_window;
127 }
128 
129 }  // namespace
130 
GetActiveSetupPath(BrowserDistribution * dist)131 base::string16 InstallUtil::GetActiveSetupPath(BrowserDistribution* dist) {
132   static const wchar_t kInstalledComponentsPath[] =
133       L"Software\\Microsoft\\Active Setup\\Installed Components\\";
134   return kInstalledComponentsPath + dist->GetActiveSetupGuid();
135 }
136 
TriggerActiveSetupCommand()137 void InstallUtil::TriggerActiveSetupCommand() {
138   base::string16 active_setup_reg(
139       GetActiveSetupPath(BrowserDistribution::GetDistribution()));
140   base::win::RegKey active_setup_key(
141       HKEY_LOCAL_MACHINE, active_setup_reg.c_str(), KEY_QUERY_VALUE);
142   base::string16 cmd_str;
143   LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str);
144   if (read_status != ERROR_SUCCESS) {
145     LOG(ERROR) << active_setup_reg << ", " << read_status;
146     // This should never fail if Chrome is registered at system-level, but if it
147     // does there is not much else to be done.
148     return;
149   }
150 
151   CommandLine cmd(CommandLine::FromString(cmd_str));
152   // Force creation of shortcuts as the First Run beacon might land between now
153   // and the time setup.exe checks for it.
154   cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings);
155 
156   base::LaunchOptions launch_options;
157   if (base::win::IsMetroProcess())
158     launch_options.force_breakaway_from_job_ = true;
159   if (!base::LaunchProcess(cmd.GetCommandLineString(), launch_options, NULL))
160     PLOG(ERROR) << cmd.GetCommandLineString();
161 }
162 
ExecuteExeAsAdmin(const CommandLine & cmd,DWORD * exit_code)163 bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) {
164   base::FilePath::StringType program(cmd.GetProgram().value());
165   DCHECK(!program.empty());
166   DCHECK_NE(program[0], L'\"');
167 
168   CommandLine::StringType params(cmd.GetCommandLineString());
169   if (params[0] == '"') {
170     DCHECK_EQ('"', params[program.length() + 1]);
171     DCHECK_EQ(program, params.substr(1, program.length()));
172     params = params.substr(program.length() + 2);
173   } else {
174     DCHECK_EQ(program, params.substr(0, program.length()));
175     params = params.substr(program.length());
176   }
177 
178   base::TrimWhitespace(params, base::TRIM_ALL, &params);
179 
180   HWND uac_foreground_window = CreateUACForegroundWindow();
181 
182   SHELLEXECUTEINFO info = {0};
183   info.cbSize = sizeof(SHELLEXECUTEINFO);
184   info.fMask = SEE_MASK_NOCLOSEPROCESS;
185   info.hwnd = uac_foreground_window;
186   info.lpVerb = L"runas";
187   info.lpFile = program.c_str();
188   info.lpParameters = params.c_str();
189   info.nShow = SW_SHOW;
190 
191   bool success = false;
192   if (::ShellExecuteEx(&info) == TRUE) {
193     ::WaitForSingleObject(info.hProcess, INFINITE);
194     DWORD ret_val = 0;
195     if (::GetExitCodeProcess(info.hProcess, &ret_val)) {
196       success = true;
197       if (exit_code)
198         *exit_code = ret_val;
199     }
200   }
201 
202   if (uac_foreground_window) {
203     DestroyWindow(uac_foreground_window);
204   }
205 
206   return success;
207 }
208 
GetChromeUninstallCmd(bool system_install,BrowserDistribution::Type distribution_type)209 CommandLine InstallUtil::GetChromeUninstallCmd(
210     bool system_install, BrowserDistribution::Type distribution_type) {
211   ProductState state;
212   if (state.Initialize(system_install, distribution_type)) {
213     return state.uninstall_command();
214   }
215   return CommandLine(CommandLine::NO_PROGRAM);
216 }
217 
GetChromeVersion(BrowserDistribution * dist,bool system_install,Version * version)218 void InstallUtil::GetChromeVersion(BrowserDistribution* dist,
219                                    bool system_install,
220                                    Version* version) {
221   DCHECK(dist);
222   RegKey key;
223   HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
224   LONG result = key.Open(reg_root,
225                          dist->GetVersionKey().c_str(),
226                          KEY_QUERY_VALUE | KEY_WOW64_32KEY);
227 
228   base::string16 version_str;
229   if (result == ERROR_SUCCESS)
230     result = key.ReadValue(google_update::kRegVersionField, &version_str);
231 
232   *version = Version();
233   if (result == ERROR_SUCCESS && !version_str.empty()) {
234     VLOG(1) << "Existing " << dist->GetDisplayName() << " version found "
235             << version_str;
236     *version = Version(base::UTF16ToASCII(version_str));
237   } else {
238     DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
239     VLOG(1) << "No existing " << dist->GetDisplayName()
240             << " install found.";
241   }
242 }
243 
GetCriticalUpdateVersion(BrowserDistribution * dist,bool system_install,Version * version)244 void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution* dist,
245                                            bool system_install,
246                                            Version* version) {
247   DCHECK(dist);
248   RegKey key;
249   HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
250   LONG result = key.Open(reg_root,
251                          dist->GetVersionKey().c_str(),
252                          KEY_QUERY_VALUE | KEY_WOW64_32KEY);
253 
254   base::string16 version_str;
255   if (result == ERROR_SUCCESS)
256     result = key.ReadValue(google_update::kRegCriticalVersionField,
257                            &version_str);
258 
259   *version = Version();
260   if (result == ERROR_SUCCESS && !version_str.empty()) {
261     VLOG(1) << "Critical Update version for " << dist->GetDisplayName()
262             << " found " << version_str;
263     *version = Version(base::UTF16ToASCII(version_str));
264   } else {
265     DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
266     VLOG(1) << "No existing " << dist->GetDisplayName()
267             << " install found.";
268   }
269 }
270 
IsOSSupported()271 bool InstallUtil::IsOSSupported() {
272   // We do not support Win2K or older, or XP without service pack 2.
273   VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
274           << base::SysInfo::OperatingSystemVersion();
275   base::win::Version version = base::win::GetVersion();
276   return (version > base::win::VERSION_XP) ||
277       ((version == base::win::VERSION_XP) &&
278        (base::win::OSInfo::GetInstance()->service_pack().major >= 2));
279 }
280 
AddInstallerResultItems(bool system_install,const base::string16 & state_key,installer::InstallStatus status,int string_resource_id,const base::string16 * const launch_cmd,WorkItemList * install_list)281 void InstallUtil::AddInstallerResultItems(
282     bool system_install,
283     const base::string16& state_key,
284     installer::InstallStatus status,
285     int string_resource_id,
286     const base::string16* const launch_cmd,
287     WorkItemList* install_list) {
288   DCHECK(install_list);
289   const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
290   DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1;
291   install_list->AddCreateRegKeyWorkItem(root, state_key, KEY_WOW64_32KEY);
292   install_list->AddSetRegValueWorkItem(root,
293                                        state_key,
294                                        KEY_WOW64_32KEY,
295                                        installer::kInstallerResult,
296                                        installer_result,
297                                        true);
298   install_list->AddSetRegValueWorkItem(root,
299                                        state_key,
300                                        KEY_WOW64_32KEY,
301                                        installer::kInstallerError,
302                                        static_cast<DWORD>(status),
303                                        true);
304   if (string_resource_id != 0) {
305     base::string16 msg = installer::GetLocalizedString(string_resource_id);
306     install_list->AddSetRegValueWorkItem(root,
307                                          state_key,
308                                          KEY_WOW64_32KEY,
309                                          installer::kInstallerResultUIString,
310                                          msg,
311                                          true);
312   }
313   if (launch_cmd != NULL && !launch_cmd->empty()) {
314     install_list->AddSetRegValueWorkItem(
315         root,
316         state_key,
317         KEY_WOW64_32KEY,
318         installer::kInstallerSuccessLaunchCmdLine,
319         *launch_cmd,
320         true);
321   }
322 }
323 
UpdateInstallerStage(bool system_install,const base::string16 & state_key_path,installer::InstallerStage stage)324 void InstallUtil::UpdateInstallerStage(bool system_install,
325                                        const base::string16& state_key_path,
326                                        installer::InstallerStage stage) {
327   DCHECK_LE(static_cast<installer::InstallerStage>(0), stage);
328   DCHECK_GT(installer::NUM_STAGES, stage);
329   const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
330   RegKey state_key;
331   LONG result =
332       state_key.Open(root,
333                      state_key_path.c_str(),
334                      KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
335   if (result == ERROR_SUCCESS) {
336     if (stage == installer::NO_STAGE) {
337       result = state_key.DeleteValue(installer::kInstallerExtraCode1);
338       LOG_IF(ERROR, result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
339           << "Failed deleting installer stage from " << state_key_path
340           << "; result: " << result;
341     } else {
342       const DWORD extra_code_1 = static_cast<DWORD>(stage);
343       result = state_key.WriteValue(installer::kInstallerExtraCode1,
344                                     extra_code_1);
345       LOG_IF(ERROR, result != ERROR_SUCCESS)
346           << "Failed writing installer stage to " << state_key_path
347           << "; result: " << result;
348     }
349     // TODO(grt): Remove code below here once we're convinced that our use of
350     // Google Update's new InstallerExtraCode1 value is good.
351     installer::ChannelInfo channel_info;
352     // This will return false if the "ap" value isn't present, which is fine.
353     channel_info.Initialize(state_key);
354     if (channel_info.SetStage(kStages[stage]) &&
355         !channel_info.Write(&state_key)) {
356       LOG(ERROR) << "Failed writing installer stage to " << state_key_path;
357     }
358   } else {
359     LOG(ERROR) << "Failed opening " << state_key_path
360                << " to update installer stage; result: " << result;
361   }
362 }
363 
IsPerUserInstall(const wchar_t * const exe_path)364 bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) {
365   wchar_t program_files_path[MAX_PATH] = {0};
366   if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
367                                 SHGFP_TYPE_CURRENT, program_files_path))) {
368     return !StartsWith(exe_path, program_files_path, false);
369   } else {
370     NOTREACHED();
371   }
372   return true;
373 }
374 
IsMultiInstall(BrowserDistribution * dist,bool system_install)375 bool InstallUtil::IsMultiInstall(BrowserDistribution* dist,
376                                  bool system_install) {
377   DCHECK(dist);
378   ProductState state;
379   return state.Initialize(system_install, dist) && state.is_multi_install();
380 }
381 
CheckIsChromeSxSProcess()382 bool CheckIsChromeSxSProcess() {
383   CommandLine* command_line = CommandLine::ForCurrentProcess();
384   CHECK(command_line);
385 
386   if (command_line->HasSwitch(installer::switches::kChromeSxS))
387     return true;
388 
389   // Also return true if we are running from Chrome SxS installed path.
390   base::FilePath exe_dir;
391   PathService::Get(base::DIR_EXE, &exe_dir);
392   base::string16 chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2);
393   chrome_sxs_dir.append(installer::kSxSSuffix);
394 
395   // This is SxS if current EXE is in or under (possibly multiple levels under)
396   // |chrome_sxs_dir|\|installer::kInstallBinaryDir|
397   std::vector<base::FilePath::StringType> components;
398   exe_dir.GetComponents(&components);
399   // We need at least 1 element in the array for the behavior of the following
400   // loop to be defined.  This should always be true, since we're splitting the
401   // path to our executable and one of the components will be the drive letter.
402   DCHECK(!components.empty());
403   typedef std::vector<base::FilePath::StringType>::const_reverse_iterator
404       ComponentsIterator;
405   for (ComponentsIterator current = components.rbegin(), parent = current + 1;
406        parent != components.rend(); current = parent++) {
407     if (base::FilePath::CompareEqualIgnoreCase(
408             *current, installer::kInstallBinaryDir) &&
409         base::FilePath::CompareEqualIgnoreCase(*parent, chrome_sxs_dir)) {
410       return true;
411     }
412   }
413 
414   return false;
415 }
416 
IsChromeSxSProcess()417 bool InstallUtil::IsChromeSxSProcess() {
418   static bool sxs = CheckIsChromeSxSProcess();
419   return sxs;
420 }
421 
422 // static
IsFirstRunSentinelPresent()423 bool InstallUtil::IsFirstRunSentinelPresent() {
424   // TODO(msw): Consolidate with first_run::internal::IsFirstRunSentinelPresent.
425   base::FilePath user_data_dir;
426   return !PathService::Get(chrome::DIR_USER_DATA, &user_data_dir) ||
427          base::PathExists(user_data_dir.Append(chrome::kFirstRunSentinel));
428 }
429 
430 // static
GetEULASentinelFilePath(base::FilePath * path)431 bool InstallUtil::GetEULASentinelFilePath(base::FilePath* path) {
432   base::FilePath user_data_dir;
433   if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
434     return false;
435   *path = user_data_dir.Append(installer::kEULASentinelFile);
436   return true;
437 }
438 
439 // This method tries to delete a registry key and logs an error message
440 // in case of failure. It returns true if deletion is successful (or the key did
441 // not exist), otherwise false.
DeleteRegistryKey(HKEY root_key,const base::string16 & key_path,REGSAM wow64_access)442 bool InstallUtil::DeleteRegistryKey(HKEY root_key,
443                                     const base::string16& key_path,
444                                     REGSAM wow64_access) {
445   VLOG(1) << "Deleting registry key " << key_path;
446   RegKey target_key;
447   LONG result = target_key.Open(root_key, key_path.c_str(),
448                                 KEY_READ | KEY_WRITE | wow64_access);
449 
450   if (result == ERROR_FILE_NOT_FOUND)
451     return true;
452 
453   if (result == ERROR_SUCCESS)
454     result = target_key.DeleteKey(L"");
455 
456   if (result != ERROR_SUCCESS) {
457     LOG(ERROR) << "Failed to delete registry key: " << key_path
458                << " error: " << result;
459     return false;
460   }
461   return true;
462 }
463 
464 // This method tries to delete a registry value and logs an error message
465 // in case of failure. It returns true if deletion is successful (or the key did
466 // not exist), otherwise false.
DeleteRegistryValue(HKEY reg_root,const base::string16 & key_path,REGSAM wow64_access,const base::string16 & value_name)467 bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
468                                       const base::string16& key_path,
469                                       REGSAM wow64_access,
470                                       const base::string16& value_name) {
471   RegKey key;
472   LONG result = key.Open(reg_root, key_path.c_str(),
473                          KEY_SET_VALUE | wow64_access);
474   if (result == ERROR_SUCCESS)
475     result = key.DeleteValue(value_name.c_str());
476   if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
477     LOG(ERROR) << "Failed to delete registry value: " << value_name
478                << " error: " << result;
479     return false;
480   }
481   return true;
482 }
483 
484 // static
DeleteRegistryKeyIf(HKEY root_key,const base::string16 & key_to_delete_path,const base::string16 & key_to_test_path,const REGSAM wow64_access,const wchar_t * value_name,const RegistryValuePredicate & predicate)485 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf(
486     HKEY root_key,
487     const base::string16& key_to_delete_path,
488     const base::string16& key_to_test_path,
489     const REGSAM wow64_access,
490     const wchar_t* value_name,
491     const RegistryValuePredicate& predicate) {
492   DCHECK(root_key);
493   ConditionalDeleteResult delete_result = NOT_FOUND;
494   RegKey key;
495   base::string16 actual_value;
496   if (key.Open(root_key, key_to_test_path.c_str(),
497                KEY_QUERY_VALUE | wow64_access) == ERROR_SUCCESS &&
498       key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
499       predicate.Evaluate(actual_value)) {
500     key.Close();
501     delete_result = DeleteRegistryKey(root_key,
502                                       key_to_delete_path,
503                                       wow64_access)
504         ? DELETED : DELETE_FAILED;
505   }
506   return delete_result;
507 }
508 
509 // static
DeleteRegistryValueIf(HKEY root_key,const wchar_t * key_path,REGSAM wow64_access,const wchar_t * value_name,const RegistryValuePredicate & predicate)510 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(
511     HKEY root_key,
512     const wchar_t* key_path,
513     REGSAM wow64_access,
514     const wchar_t* value_name,
515     const RegistryValuePredicate& predicate) {
516   DCHECK(root_key);
517   DCHECK(key_path);
518   ConditionalDeleteResult delete_result = NOT_FOUND;
519   RegKey key;
520   base::string16 actual_value;
521   if (key.Open(root_key, key_path,
522                KEY_QUERY_VALUE | KEY_SET_VALUE | wow64_access)
523           == ERROR_SUCCESS &&
524       key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
525       predicate.Evaluate(actual_value)) {
526     LONG result = key.DeleteValue(value_name);
527     if (result != ERROR_SUCCESS) {
528       LOG(ERROR) << "Failed to delete registry value: "
529                  << (value_name ? value_name : L"(Default)")
530                  << " error: " << result;
531       delete_result = DELETE_FAILED;
532     }
533     delete_result = DELETED;
534   }
535   return delete_result;
536 }
537 
Evaluate(const base::string16 & value) const538 bool InstallUtil::ValueEquals::Evaluate(const base::string16& value) const {
539   return value == value_to_match_;
540 }
541 
542 // static
GetInstallReturnCode(installer::InstallStatus status)543 int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
544   switch (status) {
545     case installer::FIRST_INSTALL_SUCCESS:
546     case installer::INSTALL_REPAIRED:
547     case installer::NEW_VERSION_UPDATED:
548     case installer::IN_USE_UPDATED:
549     case installer::UNUSED_BINARIES_UNINSTALLED:
550       return 0;
551     default:
552       return status;
553   }
554 }
555 
556 // static
MakeUninstallCommand(const base::string16 & program,const base::string16 & arguments,CommandLine * command_line)557 void InstallUtil::MakeUninstallCommand(const base::string16& program,
558                                        const base::string16& arguments,
559                                        CommandLine* command_line) {
560   *command_line = CommandLine::FromString(L"\"" + program + L"\" " + arguments);
561 }
562 
563 // static
GetCurrentDate()564 base::string16 InstallUtil::GetCurrentDate() {
565   static const wchar_t kDateFormat[] = L"yyyyMMdd";
566   wchar_t date_str[arraysize(kDateFormat)] = {0};
567   int len = GetDateFormatW(LOCALE_INVARIANT, 0, NULL, kDateFormat,
568                            date_str, arraysize(date_str));
569   if (len) {
570     --len;  // Subtract terminating \0.
571   } else {
572     PLOG(DFATAL) << "GetDateFormat";
573   }
574 
575   return base::string16(date_str, len);
576 }
577 
578 // Open |path| with minimal access to obtain information about it, returning
579 // true and populating |file| on success.
580 // static
OpenForInfo(const base::FilePath & path,base::File * file)581 bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path,
582                                               base::File* file) {
583   DCHECK(file);
584   file->Initialize(path, base::File::FLAG_OPEN);
585   return file->IsValid();
586 }
587 
588 // Populate |info| for |file|, returning true on success.
589 // static
GetInfo(const base::File & file,BY_HANDLE_FILE_INFORMATION * info)590 bool InstallUtil::ProgramCompare::GetInfo(const base::File& file,
591                                           BY_HANDLE_FILE_INFORMATION* info) {
592   DCHECK(file.IsValid());
593   return GetFileInformationByHandle(file.GetPlatformFile(), info) != 0;
594 }
595 
ProgramCompare(const base::FilePath & path_to_match)596 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
597     : path_to_match_(path_to_match),
598       file_info_() {
599   DCHECK(!path_to_match_.empty());
600   if (!OpenForInfo(path_to_match_, &file_)) {
601     PLOG(WARNING) << "Failed opening " << path_to_match_.value()
602                   << "; falling back to path string comparisons.";
603   } else if (!GetInfo(file_, &file_info_)) {
604     PLOG(WARNING) << "Failed getting information for "
605                   << path_to_match_.value()
606                   << "; falling back to path string comparisons.";
607     file_.Close();
608   }
609 }
610 
~ProgramCompare()611 InstallUtil::ProgramCompare::~ProgramCompare() {
612 }
613 
Evaluate(const base::string16 & value) const614 bool InstallUtil::ProgramCompare::Evaluate(const base::string16& value) const {
615   // Suss out the exe portion of the value, which is expected to be a command
616   // line kinda (or exactly) like:
617   // "c:\foo\bar\chrome.exe" -- "%1"
618   base::FilePath program(CommandLine::FromString(value).GetProgram());
619   if (program.empty()) {
620     LOG(WARNING) << "Failed to parse an executable name from command line: \""
621                  << value << "\"";
622     return false;
623   }
624 
625   return EvaluatePath(program);
626 }
627 
EvaluatePath(const base::FilePath & path) const628 bool InstallUtil::ProgramCompare::EvaluatePath(
629     const base::FilePath& path) const {
630   // Try the simple thing first: do the paths happen to match?
631   if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(),
632                                              path.value()))
633     return true;
634 
635   // If the paths don't match and we couldn't open the expected file, we've done
636   // our best.
637   if (!file_.IsValid())
638     return false;
639 
640   // Open the program and see if it references the expected file.
641   base::File file;
642   BY_HANDLE_FILE_INFORMATION info = {};
643 
644   return (OpenForInfo(path, &file) &&
645           GetInfo(file, &info) &&
646           info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
647           info.nFileIndexHigh == file_info_.nFileIndexHigh &&
648           info.nFileIndexLow == file_info_.nFileIndexLow);
649 }
650