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