Suppression for crbug/241044.
[chromium-blink-merge.git] / chrome / installer / gcapi / gcapi.cc
blob6341570a672e8f2987c0c4b800138441ae8ca257
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.
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.
11 #include "chrome/installer/gcapi/gcapi.h"
13 #include <sddl.h>
14 #define STRSAFE_NO_DEPRECATE
15 #include <windows.h>
16 #include <strsafe.h>
17 #include <tlhelp32.h>
19 #include <cstdlib>
20 #include <iterator>
21 #include <limits>
22 #include <set>
23 #include <string>
25 #include "base/basictypes.h"
26 #include "base/command_line.h"
27 #include "base/files/file_path.h"
28 #include "base/process_util.h"
29 #include "base/string16.h"
30 #include "base/string_util.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/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"
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;
52 namespace {
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}";
64 const wchar_t kGCAPITempKey[] = L"Software\\Google\\GCAPITemp";
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";
72 // Prefix used to match the window class for Chrome windows.
73 const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
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;
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;
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;
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;
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);
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;
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;
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];
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;
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;
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;
174 DWORD value = timer.wYear * 10000 + timer.wMonth * 100 + timer.wDay;
175 ::RegSetValueEx(key, company, 0, REG_DWORD, (LPBYTE)&value,
176 sizeof(DWORD));
180 ::RegCloseKey(key);
183 return can_re_offer;
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;
200 return false;
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);
210 enum WindowsVersion {
211 VERSION_BELOW_XP_SP2,
212 VERSION_XP_SP2_UP_TO_VISTA, // "but not including"
213 VERSION_VISTA_OR_HIGHER,
215 WindowsVersion GetWindowsVersion() {
216 OSVERSIONINFOEX version_info = { sizeof version_info };
217 GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
219 // Windows Vista is version 6.0.
220 if (version_info.dwMajorVersion >= 6)
221 return VERSION_VISTA_OR_HIGHER;
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;
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;
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;
248 ::FreeSid(Group);
249 return (check == TRUE);
252 bool VerifyHKLMAccess() {
253 wchar_t str[] = L"test";
254 bool result = false;
255 DWORD disposition = 0;
256 HKEY key = NULL;
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);
267 RegCloseKey(key);
269 // If we create the main key, delete the entire key.
270 if (disposition == REG_CREATED_NEW_KEY)
271 RegDeleteKey(HKEY_LOCAL_MACHINE, kGCAPITempKey);
274 return result;
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;
283 HANDLE process_token;
284 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
285 return false;
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;
296 ::CloseHandle(process_token);
297 return (elevation_type == TokenElevationTypeFull);
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;
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;
321 delete[] token_user;
323 ::CloseHandle(process_token);
325 ::CloseHandle(process_handle);
326 return result;
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;
340 BOOL CALLBACK ChromeWindowEnumProc(HWND hwnd, LPARAM lparam) {
341 wchar_t window_class[MAX_PATH] = {};
342 SetWindowPosParams* params = reinterpret_cast<SetWindowPosParams*>(lparam);
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;
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;
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;
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();
379 } // namespace
381 BOOL __stdcall GoogleChromeCompatibilityCheck(BOOL set_flag,
382 int shell_mode,
383 DWORD* reasons) {
384 DWORD local_reasons = 0;
386 WindowsVersion windows_version = GetWindowsVersion();
387 // System requirements?
388 if (windows_version == VERSION_BELOW_XP_SP2)
389 local_reasons |= GCCC_ERROR_OSNOTSUPPORTED;
391 if (IsChromeInstalled(HKEY_LOCAL_MACHINE))
392 local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT;
394 if (IsChromeInstalled(HKEY_CURRENT_USER))
395 local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT;
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;
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;
414 // Done. Copy/return results.
415 if (reasons != NULL)
416 *reasons = local_reasons;
418 return (local_reasons == 0);
421 BOOL __stdcall LaunchGoogleChrome() {
422 base::FilePath chrome_exe_path;
423 if (!GetGoogleChromePath(&chrome_exe_path))
424 return false;
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;
434 bool impersonation_success = false;
435 if (IsRunningElevated()) {
436 wchar_t* curr_proc_sid;
437 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) {
438 return false;
441 DWORD pid = 0;
442 ::GetWindowThreadProcessId(::GetShellWindow(), &pid);
443 if (pid <= 0) {
444 ::LocalFree(curr_proc_sid);
445 return false;
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;
468 if (user_token)
469 ::CloseHandle(user_token);
470 if (process_token)
471 ::CloseHandle(process_token);
474 ::LocalFree(exp_proc_sid);
477 ::LocalFree(curr_proc_sid);
478 if (!impersonation_success) {
479 return false;
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);
499 if (impersonation_success)
500 ::RevertToSelf();
501 return ret;
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;
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);
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;
528 } else {
529 if (!LaunchGoogleChrome())
530 return false;
533 HWND hwnd_insert_after = in_background ? HWND_BOTTOM : NULL;
534 DWORD set_window_flags = in_background ? SWP_NOACTIVATE : SWP_NOZORDER;
536 if (x == -1 && y == -1)
537 set_window_flags |= SWP_NOMOVE;
539 if (width == -1 && height == -1)
540 set_window_flags |= SWP_NOSIZE;
542 SetWindowPosParams enum_params = { x, y, width, height, set_window_flags,
543 hwnd_insert_after, false };
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));
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;
562 Sleep(10);
563 ms_elapsed += 10;
566 return found_window;
569 BOOL __stdcall LaunchGoogleChromeInBackground() {
570 return LaunchGoogleChromeWithDimensions(-1, -1, -1, -1, true);
573 int __stdcall GoogleChromeDaysSinceLastRun() {
574 int days_since_last_run = std::numeric_limits<int>::max();
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;
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;
600 if (days_since_last_run == std::numeric_limits<int>::max()) {
601 days_since_last_run = -1;
604 return days_since_last_run;
607 BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code,
608 int shell_mode,
609 DWORD* error_code) {
610 DCHECK(error_code);
612 if (!brand_code) {
613 if (error_code)
614 *error_code = REACTIVATE_ERROR_INVALID_INPUT;
615 return FALSE;
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;
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;
638 if (HasBeenReactivated()) {
639 if (error_code)
640 *error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED;
641 return FALSE;
645 return TRUE;
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);
660 result = TRUE;
661 } else {
662 if (error_code)
663 *error_code = REACTIVATE_ERROR_REACTIVATION_FAILED;
667 return result;