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 // NOTE: This code is a legacy utility API for partners to check whether 6 // Chrome can be installed and launched. Recent updates are being made 7 // to add new functionality. These updates use code from Chromium, the old 8 // coded against the win32 api directly. If you have an itch to shave a 9 // yak, feel free to re-write the old code too. 10 11 #include "chrome/installer/gcapi/gcapi.h" 12 13 #include <sddl.h> 14 #define STRSAFE_NO_DEPRECATE 15 #include <windows.h> 16 #include <strsafe.h> 17 #include <tlhelp32.h> 18 19 #include <cstdlib> 20 #include <iterator> 21 #include <limits> 22 #include <set> 23 #include <string> 24 25 #include "base/basictypes.h" 26 #include "base/command_line.h" 27 #include "base/files/file_path.h" 28 #include "base/process/launch.h" 29 #include "base/strings/string16.h" 30 #include "base/strings/string_number_conversions.h" 31 #include "base/strings/string_util.h" 32 #include "base/time/time.h" 33 #include "base/win/registry.h" 34 #include "base/win/scoped_com_initializer.h" 35 #include "base/win/scoped_comptr.h" 36 #include "base/win/scoped_handle.h" 37 #include "chrome/installer/gcapi/gcapi_omaha_experiment.h" 38 #include "chrome/installer/gcapi/gcapi_reactivation.h" 39 #include "chrome/installer/launcher_support/chrome_launcher_support.h" 40 #include "chrome/installer/util/google_update_constants.h" 41 #include "chrome/installer/util/util_constants.h" 42 #include "chrome/installer/util/wmi.h" 43 #include "google_update/google_update_idl.h" 44 45 using base::Time; 46 using base::TimeDelta; 47 using base::win::RegKey; 48 using base::win::ScopedCOMInitializer; 49 using base::win::ScopedComPtr; 50 using base::win::ScopedHandle; 51 52 namespace { 53 54 const wchar_t kChromeRegClientsKey[] = 55 L"Software\\Google\\Update\\Clients\\" 56 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; 57 const wchar_t kChromeRegClientStateKey[] = 58 L"Software\\Google\\Update\\ClientState\\" 59 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; 60 const wchar_t kChromeRegClientStateMediumKey[] = 61 L"Software\\Google\\Update\\ClientStateMedium\\" 62 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; 63 64 const wchar_t kGCAPITempKey[] = L"Software\\Google\\GCAPITemp"; 65 66 const wchar_t kChromeRegLaunchCmd[] = L"InstallerSuccessLaunchCmdLine"; 67 const wchar_t kChromeRegLastLaunchCmd[] = L"LastInstallerSuccessLaunchCmdLine"; 68 const wchar_t kChromeRegVersion[] = L"pv"; 69 const wchar_t kNoChromeOfferUntil[] = 70 L"SOFTWARE\\Google\\No Chrome Offer Until"; 71 72 // Prefix used to match the window class for Chrome windows. 73 const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_"; 74 75 // Return the company name specified in the file version info resource. 76 bool GetCompanyName(const wchar_t* filename, wchar_t* buffer, DWORD out_len) { 77 wchar_t file_version_info[8192]; 78 DWORD handle = 0; 79 DWORD buffer_size = 0; 80 81 buffer_size = ::GetFileVersionInfoSize(filename, &handle); 82 // Cannot stats the file or our buffer size is too small (very unlikely). 83 if (buffer_size == 0 || buffer_size > _countof(file_version_info)) 84 return false; 85 86 buffer_size = _countof(file_version_info); 87 memset(file_version_info, 0, buffer_size); 88 if (!::GetFileVersionInfo(filename, handle, buffer_size, file_version_info)) 89 return false; 90 91 DWORD data_len = 0; 92 LPVOID data = NULL; 93 // Retrieve the language and codepage code if exists. 94 buffer_size = 0; 95 if (!::VerQueryValue(file_version_info, TEXT("\\VarFileInfo\\Translation"), 96 reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len))) 97 return false; 98 if (data_len != 4) 99 return false; 100 101 wchar_t info_name[256]; 102 DWORD lang = 0; 103 // Formulate the string to retrieve the company name of the specific 104 // language codepage. 105 memcpy(&lang, data, 4); 106 ::StringCchPrintf(info_name, _countof(info_name), 107 L"\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName", 108 (lang & 0xff00)>>8, (lang & 0xff), (lang & 0xff000000)>>24, 109 (lang & 0xff0000)>>16); 110 111 data_len = 0; 112 if (!::VerQueryValue(file_version_info, info_name, 113 reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len))) 114 return false; 115 if (data_len <= 0 || data_len >= (out_len / sizeof(wchar_t))) 116 return false; 117 118 memset(buffer, 0, out_len); 119 ::StringCchCopyN(buffer, 120 (out_len / sizeof(wchar_t)), 121 reinterpret_cast<const wchar_t*>(data), 122 data_len); 123 return true; 124 } 125 126 // Return true if we can re-offer Chrome; false, otherwise. 127 // Each partner can only offer Chrome once every six months. 128 bool CanReOfferChrome(BOOL set_flag) { 129 wchar_t filename[MAX_PATH+1]; 130 wchar_t company[MAX_PATH]; 131 132 // If we cannot retrieve the version info of the executable or company 133 // name, we allow the Chrome to be offered because there is no past 134 // history to be found. 135 if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0) 136 return true; 137 if (!GetCompanyName(filename, company, sizeof(company))) 138 return true; 139 140 bool can_re_offer = true; 141 DWORD disposition = 0; 142 HKEY key = NULL; 143 if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kNoChromeOfferUntil, 144 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, 145 NULL, &key, &disposition) == ERROR_SUCCESS) { 146 // Get today's date, and format it as YYYYMMDD numeric value. 147 SYSTEMTIME now; 148 GetLocalTime(&now); 149 DWORD today = now.wYear * 10000 + now.wMonth * 100 + now.wDay; 150 151 // Cannot re-offer, if the timer already exists and is not expired yet. 152 DWORD value_type = REG_DWORD; 153 DWORD value_data = 0; 154 DWORD value_length = sizeof(DWORD); 155 if (::RegQueryValueEx(key, company, 0, &value_type, 156 reinterpret_cast<LPBYTE>(&value_data), 157 &value_length) == ERROR_SUCCESS && 158 REG_DWORD == value_type && 159 value_data > today) { 160 // The time has not expired, we cannot offer Chrome. 161 can_re_offer = false; 162 } else { 163 // Delete the old or invalid value. 164 ::RegDeleteValue(key, company); 165 if (set_flag) { 166 // Set expiration date for offer as six months from today, 167 // represented as a YYYYMMDD numeric value. 168 SYSTEMTIME timer = now; 169 timer.wMonth = timer.wMonth + 6; 170 if (timer.wMonth > 12) { 171 timer.wMonth = timer.wMonth - 12; 172 timer.wYear = timer.wYear + 1; 173 } 174 DWORD value = timer.wYear * 10000 + timer.wMonth * 100 + timer.wDay; 175 ::RegSetValueEx(key, company, 0, REG_DWORD, (LPBYTE)&value, 176 sizeof(DWORD)); 177 } 178 } 179 180 ::RegCloseKey(key); 181 } 182 183 return can_re_offer; 184 } 185 186 // Helper function to read a value from registry. Returns true if value 187 // is read successfully and stored in parameter value. Returns false otherwise. 188 bool ReadValueFromRegistry(HKEY root_key, const wchar_t* sub_key, 189 const wchar_t* value_name, wchar_t* value, 190 size_t* size) { 191 HKEY key; 192 if ((::RegOpenKeyEx(root_key, sub_key, NULL, 193 KEY_READ, &key) == ERROR_SUCCESS) && 194 (::RegQueryValueEx(key, value_name, NULL, NULL, 195 reinterpret_cast<LPBYTE>(value), 196 reinterpret_cast<LPDWORD>(size)) == ERROR_SUCCESS)) { 197 ::RegCloseKey(key); 198 return true; 199 } 200 return false; 201 } 202 203 bool IsChromeInstalled(HKEY root_key) { 204 wchar_t version[64]; 205 size_t size = _countof(version); 206 return ReadValueFromRegistry(root_key, kChromeRegClientsKey, 207 kChromeRegVersion, version, &size); 208 } 209 210 enum WindowsVersion { 211 VERSION_BELOW_XP_SP2, 212 VERSION_XP_SP2_UP_TO_VISTA, // "but not including" 213 VERSION_VISTA_OR_HIGHER, 214 }; 215 WindowsVersion GetWindowsVersion() { 216 OSVERSIONINFOEX version_info = { sizeof version_info }; 217 GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info)); 218 219 // Windows Vista is version 6.0. 220 if (version_info.dwMajorVersion >= 6) 221 return VERSION_VISTA_OR_HIGHER; 222 223 // Windows XP is version 5.1. (5.2 is Windows Server 2003/XP Pro x64.) 224 if ((version_info.dwMajorVersion < 5) || (version_info.dwMinorVersion < 1)) 225 return VERSION_BELOW_XP_SP2; 226 227 // For XP itself, we only support SP2 and above. 228 return ((version_info.dwMinorVersion > 1) || 229 (version_info.wServicePackMajor >= 2)) ? 230 VERSION_XP_SP2_UP_TO_VISTA : VERSION_BELOW_XP_SP2; 231 } 232 233 // Note this function should not be called on old Windows versions where these 234 // Windows API are not available. We always invoke this function after checking 235 // that current OS is Vista or later. 236 bool VerifyAdminGroup() { 237 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; 238 PSID Group; 239 BOOL check = ::AllocateAndInitializeSid(&NtAuthority, 2, 240 SECURITY_BUILTIN_DOMAIN_RID, 241 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 242 0, 0, 0, 243 &Group); 244 if (check) { 245 if (!::CheckTokenMembership(NULL, Group, &check)) 246 check = FALSE; 247 } 248 ::FreeSid(Group); 249 return (check == TRUE); 250 } 251 252 bool VerifyHKLMAccess() { 253 wchar_t str[] = L"test"; 254 bool result = false; 255 DWORD disposition = 0; 256 HKEY key = NULL; 257 258 if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kGCAPITempKey, 0, NULL, 259 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, 260 &key, &disposition) == ERROR_SUCCESS) { 261 if (::RegSetValueEx(key, str, 0, REG_SZ, (LPBYTE)str, 262 (DWORD)lstrlen(str)) == ERROR_SUCCESS) { 263 result = true; 264 RegDeleteValue(key, str); 265 } 266 267 RegCloseKey(key); 268 269 // If we create the main key, delete the entire key. 270 if (disposition == REG_CREATED_NEW_KEY) 271 RegDeleteKey(HKEY_LOCAL_MACHINE, kGCAPITempKey); 272 } 273 274 return result; 275 } 276 277 bool IsRunningElevated() { 278 // This method should be called only for Vista or later. 279 if ((GetWindowsVersion() < VERSION_VISTA_OR_HIGHER) || 280 !VerifyAdminGroup()) 281 return false; 282 283 HANDLE process_token; 284 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token)) 285 return false; 286 287 TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault; 288 DWORD size_returned = 0; 289 if (!::GetTokenInformation(process_token, TokenElevationType, 290 &elevation_type, sizeof(elevation_type), 291 &size_returned)) { 292 ::CloseHandle(process_token); 293 return false; 294 } 295 296 ::CloseHandle(process_token); 297 return (elevation_type == TokenElevationTypeFull); 298 } 299 300 bool GetUserIdForProcess(size_t pid, wchar_t** user_sid) { 301 HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid); 302 if (process_handle == NULL) 303 return false; 304 305 HANDLE process_token; 306 bool result = false; 307 if (::OpenProcessToken(process_handle, TOKEN_QUERY, &process_token)) { 308 DWORD size = 0; 309 ::GetTokenInformation(process_token, TokenUser, NULL, 0, &size); 310 if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER || 311 ::GetLastError() == ERROR_SUCCESS) { 312 DWORD actual_size = 0; 313 BYTE* token_user = new BYTE[size]; 314 if ((::GetTokenInformation(process_token, TokenUser, token_user, size, 315 &actual_size)) && 316 (actual_size <= size)) { 317 PSID sid = reinterpret_cast<TOKEN_USER*>(token_user)->User.Sid; 318 if (::ConvertSidToStringSid(sid, user_sid)) 319 result = true; 320 } 321 delete[] token_user; 322 } 323 ::CloseHandle(process_token); 324 } 325 ::CloseHandle(process_handle); 326 return result; 327 } 328 329 struct SetWindowPosParams { 330 int x; 331 int y; 332 int width; 333 int height; 334 DWORD flags; 335 HWND window_insert_after; 336 bool success; 337 std::set<HWND> shunted_hwnds; 338 }; 339 340 BOOL CALLBACK ChromeWindowEnumProc(HWND hwnd, LPARAM lparam) { 341 wchar_t window_class[MAX_PATH] = {}; 342 SetWindowPosParams* params = reinterpret_cast<SetWindowPosParams*>(lparam); 343 344 if (!params->shunted_hwnds.count(hwnd) && 345 ::GetClassName(hwnd, window_class, arraysize(window_class)) && 346 StartsWith(window_class, kChromeWindowClassPrefix, false) && 347 ::SetWindowPos(hwnd, params->window_insert_after, params->x, 348 params->y, params->width, params->height, params->flags)) { 349 params->shunted_hwnds.insert(hwnd); 350 params->success = true; 351 } 352 353 // Return TRUE to ensure we hit all possible top-level Chrome windows as per 354 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633498.aspx 355 return TRUE; 356 } 357 358 // Returns true and populates |chrome_exe_path| with the path to chrome.exe if 359 // a valid installation can be found. 360 bool GetGoogleChromePath(base::FilePath* chrome_exe_path) { 361 HKEY install_key = HKEY_LOCAL_MACHINE; 362 if (!IsChromeInstalled(install_key)) { 363 install_key = HKEY_CURRENT_USER; 364 if (!IsChromeInstalled(install_key)) { 365 return false; 366 } 367 } 368 369 // Now grab the uninstall string from the appropriate ClientState key 370 // and use that as the base for a path to chrome.exe. 371 *chrome_exe_path = 372 chrome_launcher_support::GetChromePathForInstallationLevel( 373 install_key == HKEY_LOCAL_MACHINE ? 374 chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION : 375 chrome_launcher_support::USER_LEVEL_INSTALLATION); 376 return !chrome_exe_path->empty(); 377 } 378 379 } // namespace 380 381 BOOL __stdcall GoogleChromeCompatibilityCheck(BOOL set_flag, 382 int shell_mode, 383 DWORD* reasons) { 384 DWORD local_reasons = 0; 385 386 WindowsVersion windows_version = GetWindowsVersion(); 387 // System requirements? 388 if (windows_version == VERSION_BELOW_XP_SP2) 389 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED; 390 391 if (IsChromeInstalled(HKEY_LOCAL_MACHINE)) 392 local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT; 393 394 if (IsChromeInstalled(HKEY_CURRENT_USER)) 395 local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT; 396 397 if (shell_mode == GCAPI_INVOKED_UAC_ELEVATION) { 398 // Only check that we have HKLM write permissions if we specify that 399 // GCAPI is being invoked from an elevated shell, or in admin mode 400 if (!VerifyHKLMAccess()) { 401 local_reasons |= GCCC_ERROR_ACCESSDENIED; 402 } else if ((windows_version == VERSION_VISTA_OR_HIGHER) && 403 !VerifyAdminGroup()) { 404 // For Vista or later check for elevation since even for admin user we could 405 // be running in non-elevated mode. We require integrity level High. 406 local_reasons |= GCCC_ERROR_INTEGRITYLEVEL; 407 } 408 } 409 410 // Then only check whether we can re-offer, if everything else is OK. 411 if (local_reasons == 0 && !CanReOfferChrome(set_flag)) 412 local_reasons |= GCCC_ERROR_ALREADYOFFERED; 413 414 // Done. Copy/return results. 415 if (reasons != NULL) 416 *reasons = local_reasons; 417 418 return (local_reasons == 0); 419 } 420 421 BOOL __stdcall LaunchGoogleChrome() { 422 base::FilePath chrome_exe_path; 423 if (!GetGoogleChromePath(&chrome_exe_path)) 424 return false; 425 426 ScopedCOMInitializer com_initializer; 427 if (::CoInitializeSecurity(NULL, -1, NULL, NULL, 428 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, 429 RPC_C_IMP_LEVEL_IDENTIFY, NULL, 430 EOAC_DYNAMIC_CLOAKING, NULL) != S_OK) { 431 return false; 432 } 433 434 bool impersonation_success = false; 435 if (IsRunningElevated()) { 436 wchar_t* curr_proc_sid; 437 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) { 438 return false; 439 } 440 441 DWORD pid = 0; 442 ::GetWindowThreadProcessId(::GetShellWindow(), &pid); 443 if (pid <= 0) { 444 ::LocalFree(curr_proc_sid); 445 return false; 446 } 447 448 wchar_t* exp_proc_sid; 449 if (GetUserIdForProcess(pid, &exp_proc_sid)) { 450 if (_wcsicmp(curr_proc_sid, exp_proc_sid) == 0) { 451 ScopedHandle process_handle( 452 ::OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 453 TRUE, 454 pid)); 455 if (process_handle.IsValid()) { 456 HANDLE process_token = NULL; 457 HANDLE user_token = NULL; 458 if (::OpenProcessToken(process_handle, TOKEN_DUPLICATE | TOKEN_QUERY, 459 &process_token) && 460 ::DuplicateTokenEx(process_token, 461 TOKEN_IMPERSONATE | TOKEN_QUERY | 462 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, 463 NULL, SecurityImpersonation, 464 TokenPrimary, &user_token) && 465 (::ImpersonateLoggedOnUser(user_token) != 0)) { 466 impersonation_success = true; 467 } 468 if (user_token) 469 ::CloseHandle(user_token); 470 if (process_token) 471 ::CloseHandle(process_token); 472 } 473 } 474 ::LocalFree(exp_proc_sid); 475 } 476 477 ::LocalFree(curr_proc_sid); 478 if (!impersonation_success) { 479 return false; 480 } 481 } 482 483 bool ret = false; 484 ScopedComPtr<IProcessLauncher> ipl; 485 if (SUCCEEDED(ipl.CreateInstance(__uuidof(ProcessLauncherClass), 486 NULL, 487 CLSCTX_LOCAL_SERVER))) { 488 if (SUCCEEDED(ipl->LaunchCmdLine(chrome_exe_path.value().c_str()))) 489 ret = true; 490 ipl.Release(); 491 } else { 492 // Couldn't get Omaha's process launcher, Omaha may not be installed at 493 // system level. Try just running Chrome instead. 494 ret = base::LaunchProcess(chrome_exe_path.value(), 495 base::LaunchOptions(), 496 NULL); 497 } 498 499 if (impersonation_success) 500 ::RevertToSelf(); 501 return ret; 502 } 503 504 BOOL __stdcall LaunchGoogleChromeWithDimensions(int x, 505 int y, 506 int width, 507 int height, 508 bool in_background) { 509 if (in_background) { 510 base::FilePath chrome_exe_path; 511 if (!GetGoogleChromePath(&chrome_exe_path)) 512 return false; 513 514 // When launching in the background, use WMI to ensure that chrome.exe is 515 // is not our child process. This prevents it from pushing itself to 516 // foreground. 517 CommandLine chrome_command(chrome_exe_path); 518 519 ScopedCOMInitializer com_initializer; 520 if (!installer::WMIProcess::Launch(chrome_command.GetCommandLineString(), 521 NULL)) { 522 // For some reason WMI failed. Try and launch the old fashioned way, 523 // knowing that visual glitches will occur when the window pops up. 524 if (!LaunchGoogleChrome()) 525 return false; 526 } 527 528 } else { 529 if (!LaunchGoogleChrome()) 530 return false; 531 } 532 533 HWND hwnd_insert_after = in_background ? HWND_BOTTOM : NULL; 534 DWORD set_window_flags = in_background ? SWP_NOACTIVATE : SWP_NOZORDER; 535 536 if (x == -1 && y == -1) 537 set_window_flags |= SWP_NOMOVE; 538 539 if (width == -1 && height == -1) 540 set_window_flags |= SWP_NOSIZE; 541 542 SetWindowPosParams enum_params = { x, y, width, height, set_window_flags, 543 hwnd_insert_after, false }; 544 545 // Chrome may have been launched, but the window may not have appeared 546 // yet. Wait for it to appear for 10 seconds, but exit if it takes longer 547 // than that. 548 int ms_elapsed = 0; 549 int timeout = 10000; 550 bool found_window = false; 551 while (ms_elapsed < timeout) { 552 // Enum all top-level windows looking for Chrome windows. 553 ::EnumWindows(ChromeWindowEnumProc, reinterpret_cast<LPARAM>(&enum_params)); 554 555 // Give it five more seconds after finding the first window until we stop 556 // shoving new windows into the background. 557 if (!found_window && enum_params.success) { 558 found_window = true; 559 timeout = ms_elapsed + 5000; 560 } 561 562 Sleep(10); 563 ms_elapsed += 10; 564 } 565 566 return found_window; 567 } 568 569 BOOL __stdcall LaunchGoogleChromeInBackground() { 570 return LaunchGoogleChromeWithDimensions(-1, -1, -1, -1, true); 571 } 572 573 int __stdcall GoogleChromeDaysSinceLastRun() { 574 int days_since_last_run = std::numeric_limits<int>::max(); 575 576 if (IsChromeInstalled(HKEY_LOCAL_MACHINE) || 577 IsChromeInstalled(HKEY_CURRENT_USER)) { 578 RegKey client_state( 579 HKEY_CURRENT_USER, kChromeRegClientStateKey, KEY_QUERY_VALUE); 580 if (client_state.Valid()) { 581 std::wstring last_run; 582 int64 last_run_value = 0; 583 if (client_state.ReadValue(google_update::kRegLastRunTimeField, 584 &last_run) == ERROR_SUCCESS && 585 base::StringToInt64(last_run, &last_run_value)) { 586 Time last_run_time = Time::FromInternalValue(last_run_value); 587 TimeDelta difference = Time::NowFromSystemTime() - last_run_time; 588 589 // We can end up with negative numbers here, given changes in system 590 // clock time or due to TimeDelta's int64 -> int truncation. 591 int new_days_since_last_run = difference.InDays(); 592 if (new_days_since_last_run >= 0 && 593 new_days_since_last_run < days_since_last_run) { 594 days_since_last_run = new_days_since_last_run; 595 } 596 } 597 } 598 } 599 600 if (days_since_last_run == std::numeric_limits<int>::max()) { 601 days_since_last_run = -1; 602 } 603 604 return days_since_last_run; 605 } 606 607 BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code, 608 int shell_mode, 609 DWORD* error_code) { 610 DCHECK(error_code); 611 612 if (!brand_code) { 613 if (error_code) 614 *error_code = REACTIVATE_ERROR_INVALID_INPUT; 615 return FALSE; 616 } 617 618 int days_since_last_run = GoogleChromeDaysSinceLastRun(); 619 if (days_since_last_run >= 0 && 620 days_since_last_run < kReactivationMinDaysDormant) { 621 if (error_code) 622 *error_code = REACTIVATE_ERROR_NOTDORMANT; 623 return FALSE; 624 } 625 626 // Only run the code below when this function is invoked from a standard, 627 // non-elevated cmd shell. This is because this section of code looks at 628 // values in HKEY_CURRENT_USER, and we only want to look at the logged-in 629 // user's HKCU, not the admin user's HKCU. 630 if (shell_mode == GCAPI_INVOKED_STANDARD_SHELL) { 631 if (!IsChromeInstalled(HKEY_LOCAL_MACHINE) && 632 !IsChromeInstalled(HKEY_CURRENT_USER)) { 633 if (error_code) 634 *error_code = REACTIVATE_ERROR_NOTINSTALLED; 635 return FALSE; 636 } 637 638 if (HasBeenReactivated()) { 639 if (error_code) 640 *error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED; 641 return FALSE; 642 } 643 } 644 645 return TRUE; 646 } 647 648 BOOL __stdcall ReactivateChrome(wchar_t* brand_code, 649 int shell_mode, 650 DWORD* error_code) { 651 BOOL result = FALSE; 652 if (CanOfferReactivation(brand_code, 653 shell_mode, 654 error_code)) { 655 if (SetReactivationBrandCode(brand_code, shell_mode)) { 656 // Currently set this as a best-effort thing. We return TRUE if 657 // reactivation succeeded regardless of the experiment label result. 658 SetReactivationExperimentLabels(brand_code, shell_mode); 659 660 result = TRUE; 661 } else { 662 if (error_code) 663 *error_code = REACTIVATE_ERROR_REACTIVATION_FAILED; 664 } 665 } 666 667 return result; 668 } 669