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"
14 #define STRSAFE_NO_DEPRECATE
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/google_update_settings.h"
42 #include "chrome/installer/util/util_constants.h"
43 #include "chrome/installer/util/wmi.h"
44 #include "google_update/google_update_idl.h"
47 using base::TimeDelta
;
48 using base::win::RegKey
;
49 using base::win::ScopedCOMInitializer
;
50 using base::win::ScopedComPtr
;
51 using base::win::ScopedHandle
;
55 const wchar_t kChromeRegClientsKey
[] =
56 L
"Software\\Google\\Update\\Clients\\"
57 L
"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
58 const wchar_t kChromeRegClientStateKey
[] =
59 L
"Software\\Google\\Update\\ClientState\\"
60 L
"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
61 const wchar_t kChromeRegClientStateMediumKey
[] =
62 L
"Software\\Google\\Update\\ClientStateMedium\\"
63 L
"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
65 const wchar_t kGCAPITempKey
[] = L
"Software\\Google\\GCAPITemp";
67 const wchar_t kChromeRegLaunchCmd
[] = L
"InstallerSuccessLaunchCmdLine";
68 const wchar_t kChromeRegLastLaunchCmd
[] = L
"LastInstallerSuccessLaunchCmdLine";
69 const wchar_t kChromeRegVersion
[] = L
"pv";
70 const wchar_t kNoChromeOfferUntil
[] =
71 L
"SOFTWARE\\Google\\No Chrome Offer Until";
73 const wchar_t kC1FPendingKey
[] =
74 L
"Software\\Google\\Common\\Rlz\\Events\\C";
75 const wchar_t kC1FSentKey
[] =
76 L
"Software\\Google\\Common\\Rlz\\StatefulEvents\\C";
77 const wchar_t kC1FKey
[] = L
"C1F";
79 const wchar_t kRelaunchBrandcodeValue
[] = L
"RelaunchBrandcode";
80 const wchar_t kRelaunchAllowedAfterValue
[] = L
"RelaunchAllowedAfter";
82 // Prefix used to match the window class for Chrome windows.
83 const wchar_t kChromeWindowClassPrefix
[] = L
"Chrome_WidgetWin_";
85 // Return the company name specified in the file version info resource.
86 bool GetCompanyName(const wchar_t* filename
, wchar_t* buffer
, DWORD out_len
) {
87 wchar_t file_version_info
[8192];
89 DWORD buffer_size
= 0;
91 buffer_size
= ::GetFileVersionInfoSize(filename
, &handle
);
92 // Cannot stats the file or our buffer size is too small (very unlikely).
93 if (buffer_size
== 0 || buffer_size
> _countof(file_version_info
))
96 buffer_size
= _countof(file_version_info
);
97 memset(file_version_info
, 0, buffer_size
);
98 if (!::GetFileVersionInfo(filename
, handle
, buffer_size
, file_version_info
))
103 // Retrieve the language and codepage code if exists.
105 if (!::VerQueryValue(file_version_info
, TEXT("\\VarFileInfo\\Translation"),
106 reinterpret_cast<LPVOID
*>(&data
), reinterpret_cast<UINT
*>(&data_len
)))
111 wchar_t info_name
[256];
113 // Formulate the string to retrieve the company name of the specific
114 // language codepage.
115 memcpy(&lang
, data
, 4);
116 ::StringCchPrintf(info_name
, _countof(info_name
),
117 L
"\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName",
118 (lang
& 0xff00)>>8, (lang
& 0xff), (lang
& 0xff000000)>>24,
119 (lang
& 0xff0000)>>16);
122 if (!::VerQueryValue(file_version_info
, info_name
,
123 reinterpret_cast<LPVOID
*>(&data
), reinterpret_cast<UINT
*>(&data_len
)))
125 if (data_len
<= 0 || data_len
>= (out_len
/ sizeof(wchar_t)))
128 memset(buffer
, 0, out_len
);
129 ::StringCchCopyN(buffer
,
130 (out_len
/ sizeof(wchar_t)),
131 reinterpret_cast<const wchar_t*>(data
),
136 // Offsets the current date by |months|. |months| must be between 0 and 12.
137 // The returned date is in the YYYYMMDD format.
138 DWORD
FormatDateOffsetByMonths(int months
) {
139 DCHECK(months
>= 0 && months
<= 12);
143 now
.wMonth
+= months
;
144 if (now
.wMonth
> 12) {
149 return now
.wYear
* 10000 + now
.wMonth
* 100 + now
.wDay
;
152 // Return true if we can re-offer Chrome; false, otherwise.
153 // Each partner can only offer Chrome once every six months.
154 bool CanReOfferChrome(BOOL set_flag
) {
155 wchar_t filename
[MAX_PATH
+1];
156 wchar_t company
[MAX_PATH
];
158 // If we cannot retrieve the version info of the executable or company
159 // name, we allow the Chrome to be offered because there is no past
160 // history to be found.
161 if (::GetModuleFileName(NULL
, filename
, MAX_PATH
) == 0)
163 if (!GetCompanyName(filename
, company
, sizeof(company
)))
166 bool can_re_offer
= true;
167 DWORD disposition
= 0;
169 if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE
, kNoChromeOfferUntil
,
170 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_READ
| KEY_WRITE
,
171 NULL
, &key
, &disposition
) == ERROR_SUCCESS
) {
172 // Get today's date, and format it as YYYYMMDD numeric value.
173 DWORD today
= FormatDateOffsetByMonths(0);
175 // Cannot re-offer, if the timer already exists and is not expired yet.
176 DWORD value_type
= REG_DWORD
;
177 DWORD value_data
= 0;
178 DWORD value_length
= sizeof(DWORD
);
179 if (::RegQueryValueEx(key
, company
, 0, &value_type
,
180 reinterpret_cast<LPBYTE
>(&value_data
),
181 &value_length
) == ERROR_SUCCESS
&&
182 REG_DWORD
== value_type
&&
183 value_data
> today
) {
184 // The time has not expired, we cannot offer Chrome.
185 can_re_offer
= false;
187 // Delete the old or invalid value.
188 ::RegDeleteValue(key
, company
);
190 // Set expiration date for offer as six months from today,
191 // represented as a YYYYMMDD numeric value.
192 DWORD value
= FormatDateOffsetByMonths(6);
193 ::RegSetValueEx(key
, company
, 0, REG_DWORD
, (LPBYTE
)&value
,
204 bool IsChromeInstalled(HKEY root_key
) {
206 return key
.Open(root_key
,
207 kChromeRegClientsKey
,
208 KEY_READ
| KEY_WOW64_32KEY
) == ERROR_SUCCESS
&&
209 key
.HasValue(kChromeRegVersion
);
212 // Returns true if the |subkey| in |root| has the kC1FKey entry set to 1.
213 bool RegKeyHasC1F(HKEY root
, const wchar_t* subkey
) {
216 return key
.Open(root
, subkey
, KEY_READ
| KEY_WOW64_32KEY
) == ERROR_SUCCESS
&&
217 key
.ReadValueDW(kC1FKey
, &value
) == ERROR_SUCCESS
&&
218 value
== static_cast<DWORD
>(1);
222 // The C1F RLZ key can either be in HKCU or in HKLM (the HKLM RLZ key is made
223 // readable to all-users via rlz_lib::CreateMachineState()) and can either be
224 // in sent or pending state. Return true if there is a match for any of these
226 return RegKeyHasC1F(HKEY_CURRENT_USER
, kC1FSentKey
) ||
227 RegKeyHasC1F(HKEY_CURRENT_USER
, kC1FPendingKey
) ||
228 RegKeyHasC1F(HKEY_LOCAL_MACHINE
, kC1FSentKey
) ||
229 RegKeyHasC1F(HKEY_LOCAL_MACHINE
, kC1FPendingKey
);
232 enum WindowsVersion
{
233 VERSION_BELOW_XP_SP2
,
234 VERSION_XP_SP2_UP_TO_VISTA
, // "but not including"
235 VERSION_VISTA_OR_HIGHER
,
237 WindowsVersion
GetWindowsVersion() {
238 OSVERSIONINFOEX version_info
= { sizeof version_info
};
239 GetVersionEx(reinterpret_cast<OSVERSIONINFO
*>(&version_info
));
241 // Windows Vista is version 6.0.
242 if (version_info
.dwMajorVersion
>= 6)
243 return VERSION_VISTA_OR_HIGHER
;
245 // Windows XP is version 5.1. (5.2 is Windows Server 2003/XP Pro x64.)
246 if ((version_info
.dwMajorVersion
< 5) || (version_info
.dwMinorVersion
< 1))
247 return VERSION_BELOW_XP_SP2
;
249 // For XP itself, we only support SP2 and above.
250 return ((version_info
.dwMinorVersion
> 1) ||
251 (version_info
.wServicePackMajor
>= 2)) ?
252 VERSION_XP_SP2_UP_TO_VISTA
: VERSION_BELOW_XP_SP2
;
255 // Note this function should not be called on old Windows versions where these
256 // Windows API are not available. We always invoke this function after checking
257 // that current OS is Vista or later.
258 bool VerifyAdminGroup() {
259 SID_IDENTIFIER_AUTHORITY NtAuthority
= SECURITY_NT_AUTHORITY
;
261 BOOL check
= ::AllocateAndInitializeSid(&NtAuthority
, 2,
262 SECURITY_BUILTIN_DOMAIN_RID
,
263 DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0,
267 if (!::CheckTokenMembership(NULL
, Group
, &check
))
271 return (check
== TRUE
);
274 bool VerifyHKLMAccess() {
275 wchar_t str
[] = L
"test";
277 DWORD disposition
= 0;
280 if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE
, kGCAPITempKey
, 0, NULL
,
281 REG_OPTION_NON_VOLATILE
,
282 KEY_READ
| KEY_WRITE
| KEY_WOW64_32KEY
, NULL
,
283 &key
, &disposition
) == ERROR_SUCCESS
) {
284 if (::RegSetValueEx(key
, str
, 0, REG_SZ
, (LPBYTE
)str
,
285 (DWORD
)lstrlen(str
)) == ERROR_SUCCESS
) {
287 RegDeleteValue(key
, str
);
292 // If we create the main key, delete the entire key.
293 if (disposition
== REG_CREATED_NEW_KEY
)
294 RegDeleteKey(HKEY_LOCAL_MACHINE
, kGCAPITempKey
);
300 bool IsRunningElevated() {
301 // This method should be called only for Vista or later.
302 if ((GetWindowsVersion() < VERSION_VISTA_OR_HIGHER
) ||
306 HANDLE process_token
;
307 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
, &process_token
))
310 TOKEN_ELEVATION_TYPE elevation_type
= TokenElevationTypeDefault
;
311 DWORD size_returned
= 0;
312 if (!::GetTokenInformation(process_token
, TokenElevationType
,
313 &elevation_type
, sizeof(elevation_type
),
315 ::CloseHandle(process_token
);
319 ::CloseHandle(process_token
);
320 return (elevation_type
== TokenElevationTypeFull
);
323 bool GetUserIdForProcess(size_t pid
, wchar_t** user_sid
) {
324 HANDLE process_handle
= ::OpenProcess(PROCESS_QUERY_INFORMATION
, TRUE
, pid
);
325 if (process_handle
== NULL
)
328 HANDLE process_token
;
330 if (::OpenProcessToken(process_handle
, TOKEN_QUERY
, &process_token
)) {
332 ::GetTokenInformation(process_token
, TokenUser
, NULL
, 0, &size
);
333 if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER
||
334 ::GetLastError() == ERROR_SUCCESS
) {
335 DWORD actual_size
= 0;
336 BYTE
* token_user
= new BYTE
[size
];
337 if ((::GetTokenInformation(process_token
, TokenUser
, token_user
, size
,
339 (actual_size
<= size
)) {
340 PSID sid
= reinterpret_cast<TOKEN_USER
*>(token_user
)->User
.Sid
;
341 if (::ConvertSidToStringSid(sid
, user_sid
))
346 ::CloseHandle(process_token
);
348 ::CloseHandle(process_handle
);
352 struct SetWindowPosParams
{
358 HWND window_insert_after
;
360 std::set
<HWND
> shunted_hwnds
;
363 BOOL CALLBACK
ChromeWindowEnumProc(HWND hwnd
, LPARAM lparam
) {
364 wchar_t window_class
[MAX_PATH
] = {};
365 SetWindowPosParams
* params
= reinterpret_cast<SetWindowPosParams
*>(lparam
);
367 if (!params
->shunted_hwnds
.count(hwnd
) &&
368 ::GetClassName(hwnd
, window_class
, arraysize(window_class
)) &&
369 StartsWith(window_class
, kChromeWindowClassPrefix
, false) &&
370 ::SetWindowPos(hwnd
, params
->window_insert_after
, params
->x
,
371 params
->y
, params
->width
, params
->height
, params
->flags
)) {
372 params
->shunted_hwnds
.insert(hwnd
);
373 params
->success
= true;
376 // Return TRUE to ensure we hit all possible top-level Chrome windows as per
377 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633498.aspx
381 // Returns true and populates |chrome_exe_path| with the path to chrome.exe if
382 // a valid installation can be found.
383 bool GetGoogleChromePath(base::FilePath
* chrome_exe_path
) {
384 HKEY install_key
= HKEY_LOCAL_MACHINE
;
385 if (!IsChromeInstalled(install_key
)) {
386 install_key
= HKEY_CURRENT_USER
;
387 if (!IsChromeInstalled(install_key
)) {
392 // Now grab the uninstall string from the appropriate ClientState key
393 // and use that as the base for a path to chrome.exe.
394 *chrome_exe_path
= chrome_launcher_support::GetChromePathForInstallationLevel(
395 install_key
== HKEY_LOCAL_MACHINE
396 ? chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION
397 : chrome_launcher_support::USER_LEVEL_INSTALLATION
,
399 return !chrome_exe_path
->empty();
404 BOOL __stdcall
GoogleChromeCompatibilityCheck(BOOL set_flag
,
407 DWORD local_reasons
= 0;
409 WindowsVersion windows_version
= GetWindowsVersion();
410 // System requirements?
411 if (windows_version
== VERSION_BELOW_XP_SP2
)
412 local_reasons
|= GCCC_ERROR_OSNOTSUPPORTED
;
414 if (IsChromeInstalled(HKEY_LOCAL_MACHINE
))
415 local_reasons
|= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT
;
417 if (IsChromeInstalled(HKEY_CURRENT_USER
))
418 local_reasons
|= GCCC_ERROR_USERLEVELALREADYPRESENT
;
420 if (shell_mode
== GCAPI_INVOKED_UAC_ELEVATION
) {
421 // Only check that we have HKLM write permissions if we specify that
422 // GCAPI is being invoked from an elevated shell, or in admin mode
423 if (!VerifyHKLMAccess()) {
424 local_reasons
|= GCCC_ERROR_ACCESSDENIED
;
425 } else if ((windows_version
== VERSION_VISTA_OR_HIGHER
) &&
426 !VerifyAdminGroup()) {
427 // For Vista or later check for elevation since even for admin user we could
428 // be running in non-elevated mode. We require integrity level High.
429 local_reasons
|= GCCC_ERROR_INTEGRITYLEVEL
;
433 // Then only check whether we can re-offer, if everything else is OK.
434 if (local_reasons
== 0 && !CanReOfferChrome(set_flag
))
435 local_reasons
|= GCCC_ERROR_ALREADYOFFERED
;
437 // Done. Copy/return results.
439 *reasons
= local_reasons
;
441 return (local_reasons
== 0);
444 BOOL __stdcall
LaunchGoogleChrome() {
445 base::FilePath chrome_exe_path
;
446 if (!GetGoogleChromePath(&chrome_exe_path
))
449 ScopedCOMInitializer com_initializer
;
450 if (::CoInitializeSecurity(NULL
, -1, NULL
, NULL
,
451 RPC_C_AUTHN_LEVEL_PKT_PRIVACY
,
452 RPC_C_IMP_LEVEL_IDENTIFY
, NULL
,
453 EOAC_DYNAMIC_CLOAKING
, NULL
) != S_OK
) {
457 bool impersonation_success
= false;
458 if (IsRunningElevated()) {
459 wchar_t* curr_proc_sid
;
460 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid
)) {
465 ::GetWindowThreadProcessId(::GetShellWindow(), &pid
);
467 ::LocalFree(curr_proc_sid
);
471 wchar_t* exp_proc_sid
;
472 if (GetUserIdForProcess(pid
, &exp_proc_sid
)) {
473 if (_wcsicmp(curr_proc_sid
, exp_proc_sid
) == 0) {
474 ScopedHandle
process_handle(
475 ::OpenProcess(PROCESS_DUP_HANDLE
| PROCESS_QUERY_INFORMATION
,
478 if (process_handle
.IsValid()) {
479 HANDLE process_token
= NULL
;
480 HANDLE user_token
= NULL
;
481 if (::OpenProcessToken(process_handle
.Get(),
482 TOKEN_DUPLICATE
| TOKEN_QUERY
,
484 ::DuplicateTokenEx(process_token
,
485 TOKEN_IMPERSONATE
| TOKEN_QUERY
|
486 TOKEN_ASSIGN_PRIMARY
| TOKEN_DUPLICATE
,
487 NULL
, SecurityImpersonation
,
488 TokenPrimary
, &user_token
) &&
489 (::ImpersonateLoggedOnUser(user_token
) != 0)) {
490 impersonation_success
= true;
493 ::CloseHandle(user_token
);
495 ::CloseHandle(process_token
);
498 ::LocalFree(exp_proc_sid
);
501 ::LocalFree(curr_proc_sid
);
502 if (!impersonation_success
) {
507 base::CommandLine
chrome_command(chrome_exe_path
);
510 ScopedComPtr
<IProcessLauncher
> ipl
;
511 if (SUCCEEDED(ipl
.CreateInstance(__uuidof(ProcessLauncherClass
),
513 CLSCTX_LOCAL_SERVER
))) {
514 if (SUCCEEDED(ipl
->LaunchCmdLine(
515 chrome_command
.GetCommandLineString().c_str())))
519 // Couldn't get Omaha's process launcher, Omaha may not be installed at
520 // system level. Try just running Chrome instead.
521 ret
= base::LaunchProcess(chrome_command
.GetCommandLineString(),
522 base::LaunchOptions()).IsValid();
525 if (impersonation_success
)
530 BOOL __stdcall
LaunchGoogleChromeWithDimensions(int x
,
534 bool in_background
) {
536 base::FilePath chrome_exe_path
;
537 if (!GetGoogleChromePath(&chrome_exe_path
))
540 // When launching in the background, use WMI to ensure that chrome.exe is
541 // is not our child process. This prevents it from pushing itself to
543 base::CommandLine
chrome_command(chrome_exe_path
);
545 ScopedCOMInitializer com_initializer
;
546 if (!installer::WMIProcess::Launch(chrome_command
.GetCommandLineString(),
548 // For some reason WMI failed. Try and launch the old fashioned way,
549 // knowing that visual glitches will occur when the window pops up.
550 if (!LaunchGoogleChrome())
555 if (!LaunchGoogleChrome())
559 HWND hwnd_insert_after
= in_background
? HWND_BOTTOM
: NULL
;
560 DWORD set_window_flags
= in_background
? SWP_NOACTIVATE
: SWP_NOZORDER
;
562 if (x
== -1 && y
== -1)
563 set_window_flags
|= SWP_NOMOVE
;
565 if (width
== -1 && height
== -1)
566 set_window_flags
|= SWP_NOSIZE
;
568 SetWindowPosParams enum_params
= { x
, y
, width
, height
, set_window_flags
,
569 hwnd_insert_after
, false };
571 // Chrome may have been launched, but the window may not have appeared
572 // yet. Wait for it to appear for 10 seconds, but exit if it takes longer
576 bool found_window
= false;
577 while (ms_elapsed
< timeout
) {
578 // Enum all top-level windows looking for Chrome windows.
579 ::EnumWindows(ChromeWindowEnumProc
, reinterpret_cast<LPARAM
>(&enum_params
));
581 // Give it five more seconds after finding the first window until we stop
582 // shoving new windows into the background.
583 if (!found_window
&& enum_params
.success
) {
585 timeout
= ms_elapsed
+ 5000;
595 BOOL __stdcall
LaunchGoogleChromeInBackground() {
596 return LaunchGoogleChromeWithDimensions(-1, -1, -1, -1, true);
599 int __stdcall
GoogleChromeDaysSinceLastRun() {
600 int days_since_last_run
= std::numeric_limits
<int>::max();
602 if (IsChromeInstalled(HKEY_LOCAL_MACHINE
) ||
603 IsChromeInstalled(HKEY_CURRENT_USER
)) {
604 RegKey
client_state(HKEY_CURRENT_USER
,
605 kChromeRegClientStateKey
,
606 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
);
607 if (client_state
.Valid()) {
608 base::string16 last_run
;
609 int64 last_run_value
= 0;
610 if (client_state
.ReadValue(google_update::kRegLastRunTimeField
,
611 &last_run
) == ERROR_SUCCESS
&&
612 base::StringToInt64(last_run
, &last_run_value
)) {
613 Time last_run_time
= Time::FromInternalValue(last_run_value
);
614 TimeDelta difference
= Time::NowFromSystemTime() - last_run_time
;
616 // We can end up with negative numbers here, given changes in system
617 // clock time or due to TimeDelta's int64 -> int truncation.
618 int new_days_since_last_run
= difference
.InDays();
619 if (new_days_since_last_run
>= 0 &&
620 new_days_since_last_run
< days_since_last_run
) {
621 days_since_last_run
= new_days_since_last_run
;
627 if (days_since_last_run
== std::numeric_limits
<int>::max()) {
628 days_since_last_run
= -1;
631 return days_since_last_run
;
634 BOOL __stdcall
CanOfferReactivation(const wchar_t* brand_code
,
641 *error_code
= REACTIVATE_ERROR_INVALID_INPUT
;
645 int days_since_last_run
= GoogleChromeDaysSinceLastRun();
646 if (days_since_last_run
>= 0 &&
647 days_since_last_run
< kReactivationMinDaysDormant
) {
649 *error_code
= REACTIVATE_ERROR_NOTDORMANT
;
653 // Only run the code below when this function is invoked from a standard,
654 // non-elevated cmd shell. This is because this section of code looks at
655 // values in HKEY_CURRENT_USER, and we only want to look at the logged-in
656 // user's HKCU, not the admin user's HKCU.
657 if (shell_mode
== GCAPI_INVOKED_STANDARD_SHELL
) {
658 if (!IsChromeInstalled(HKEY_LOCAL_MACHINE
) &&
659 !IsChromeInstalled(HKEY_CURRENT_USER
)) {
661 *error_code
= REACTIVATE_ERROR_NOTINSTALLED
;
665 if (HasBeenReactivated()) {
667 *error_code
= REACTIVATE_ERROR_ALREADY_REACTIVATED
;
675 BOOL __stdcall
ReactivateChrome(const wchar_t* brand_code
,
679 if (CanOfferReactivation(brand_code
,
682 if (SetReactivationBrandCode(brand_code
, shell_mode
)) {
683 // Currently set this as a best-effort thing. We return TRUE if
684 // reactivation succeeded regardless of the experiment label result.
685 SetReactivationExperimentLabels(brand_code
, shell_mode
);
690 *error_code
= REACTIVATE_ERROR_REACTIVATION_FAILED
;
697 BOOL __stdcall
CanOfferRelaunch(const wchar_t** partner_brandcode_list
,
698 int partner_brandcode_list_length
,
703 if (!partner_brandcode_list
|| partner_brandcode_list_length
<= 0) {
705 *error_code
= RELAUNCH_ERROR_INVALID_INPUT
;
709 // These conditions need to be satisfied for relaunch:
710 // a) Chrome should be installed;
711 if (!IsChromeInstalled(HKEY_LOCAL_MACHINE
) &&
712 (shell_mode
!= GCAPI_INVOKED_STANDARD_SHELL
||
713 !IsChromeInstalled(HKEY_CURRENT_USER
))) {
715 *error_code
= RELAUNCH_ERROR_NOTINSTALLED
;
719 // b) the installed brandcode should belong to that partner (in
721 base::string16 installed_brandcode
;
722 bool valid_brandcode
= false;
723 if (GoogleUpdateSettings::GetBrand(&installed_brandcode
)) {
724 for (int i
= 0; i
< partner_brandcode_list_length
; ++i
) {
725 if (!_wcsicmp(installed_brandcode
.c_str(), partner_brandcode_list
[i
])) {
726 valid_brandcode
= true;
732 if (!valid_brandcode
) {
734 *error_code
= RELAUNCH_ERROR_INVALID_PARTNER
;
738 // c) C1F ping should not have been sent;
741 *error_code
= RELAUNCH_ERROR_PINGS_SENT
;
745 // d) a minimum period (30 days) must have passed since Chrome was last used;
746 int days_since_last_run
= GoogleChromeDaysSinceLastRun();
747 if (days_since_last_run
>= 0 &&
748 days_since_last_run
< kRelaunchMinDaysDormant
) {
750 *error_code
= RELAUNCH_ERROR_NOTDORMANT
;
754 // e) a minimum period (6 months) must have passed since the previous
755 // relaunch offer for the current user;
757 DWORD min_relaunch_date
;
758 if (key
.Open(HKEY_CURRENT_USER
,
759 kChromeRegClientStateKey
,
760 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
) == ERROR_SUCCESS
&&
761 key
.ReadValueDW(kRelaunchAllowedAfterValue
,
762 &min_relaunch_date
) == ERROR_SUCCESS
&&
763 FormatDateOffsetByMonths(0) < min_relaunch_date
) {
765 *error_code
= RELAUNCH_ERROR_ALREADY_RELAUNCHED
;
772 BOOL __stdcall
SetRelaunchOffered(const wchar_t** partner_brandcode_list
,
773 int partner_brandcode_list_length
,
774 const wchar_t* relaunch_brandcode
,
777 if (!CanOfferRelaunch(partner_brandcode_list
, partner_brandcode_list_length
,
778 shell_mode
, error_code
))
781 // Store the relaunched brand code and the minimum date for relaunch (6 months
782 // from now), and set the Omaha experiment label.
784 if (key
.Create(HKEY_CURRENT_USER
,
785 kChromeRegClientStateKey
,
786 KEY_SET_VALUE
| KEY_WOW64_32KEY
) != ERROR_SUCCESS
||
787 key
.WriteValue(kRelaunchBrandcodeValue
,
788 relaunch_brandcode
) != ERROR_SUCCESS
||
789 key
.WriteValue(kRelaunchAllowedAfterValue
,
790 FormatDateOffsetByMonths(6)) != ERROR_SUCCESS
||
791 !SetRelaunchExperimentLabels(relaunch_brandcode
, shell_mode
)) {
793 *error_code
= RELAUNCH_ERROR_RELAUNCH_FAILED
;