[chromedriver] Make GetElementLocationInView return the top-left coordinate rather...
[chromium-blink-merge.git] / chrome / installer / gcapi / gcapi.cc
blob43b76c7170e0a3f74e4b147270cf884bb7e3c502
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/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"
46 using base::Time;
47 using base::TimeDelta;
48 using base::win::RegKey;
49 using base::win::ScopedCOMInitializer;
50 using base::win::ScopedComPtr;
51 using base::win::ScopedHandle;
53 namespace {
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];
88 DWORD handle = 0;
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))
94 return false;
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))
99 return false;
101 DWORD data_len = 0;
102 LPVOID data = NULL;
103 // Retrieve the language and codepage code if exists.
104 buffer_size = 0;
105 if (!::VerQueryValue(file_version_info, TEXT("\\VarFileInfo\\Translation"),
106 reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
107 return false;
108 if (data_len != 4)
109 return false;
111 wchar_t info_name[256];
112 DWORD lang = 0;
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);
121 data_len = 0;
122 if (!::VerQueryValue(file_version_info, info_name,
123 reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
124 return false;
125 if (data_len <= 0 || data_len >= (out_len / sizeof(wchar_t)))
126 return false;
128 memset(buffer, 0, out_len);
129 ::StringCchCopyN(buffer,
130 (out_len / sizeof(wchar_t)),
131 reinterpret_cast<const wchar_t*>(data),
132 data_len);
133 return true;
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);
141 SYSTEMTIME now;
142 GetLocalTime(&now);
143 now.wMonth += months;
144 if (now.wMonth > 12) {
145 now.wMonth -= 12;
146 now.wYear += 1;
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)
162 return true;
163 if (!GetCompanyName(filename, company, sizeof(company)))
164 return true;
166 bool can_re_offer = true;
167 DWORD disposition = 0;
168 HKEY key = NULL;
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;
186 } else {
187 // Delete the old or invalid value.
188 ::RegDeleteValue(key, company);
189 if (set_flag) {
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,
194 sizeof(DWORD));
198 ::RegCloseKey(key);
201 return can_re_offer;
204 bool IsChromeInstalled(HKEY root_key) {
205 RegKey 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) {
214 RegKey key;
215 DWORD value;
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);
221 bool IsC1FSent() {
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
225 // 4 states.
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;
260 PSID Group;
261 BOOL check = ::AllocateAndInitializeSid(&NtAuthority, 2,
262 SECURITY_BUILTIN_DOMAIN_RID,
263 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0,
264 0, 0, 0,
265 &Group);
266 if (check) {
267 if (!::CheckTokenMembership(NULL, Group, &check))
268 check = FALSE;
270 ::FreeSid(Group);
271 return (check == TRUE);
274 bool VerifyHKLMAccess() {
275 wchar_t str[] = L"test";
276 bool result = false;
277 DWORD disposition = 0;
278 HKEY key = NULL;
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) {
286 result = true;
287 RegDeleteValue(key, str);
290 RegCloseKey(key);
292 // If we create the main key, delete the entire key.
293 if (disposition == REG_CREATED_NEW_KEY)
294 RegDeleteKey(HKEY_LOCAL_MACHINE, kGCAPITempKey);
297 return result;
300 bool IsRunningElevated() {
301 // This method should be called only for Vista or later.
302 if ((GetWindowsVersion() < VERSION_VISTA_OR_HIGHER) ||
303 !VerifyAdminGroup())
304 return false;
306 HANDLE process_token;
307 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
308 return false;
310 TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
311 DWORD size_returned = 0;
312 if (!::GetTokenInformation(process_token, TokenElevationType,
313 &elevation_type, sizeof(elevation_type),
314 &size_returned)) {
315 ::CloseHandle(process_token);
316 return false;
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)
326 return false;
328 HANDLE process_token;
329 bool result = false;
330 if (::OpenProcessToken(process_handle, TOKEN_QUERY, &process_token)) {
331 DWORD size = 0;
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,
338 &actual_size)) &&
339 (actual_size <= size)) {
340 PSID sid = reinterpret_cast<TOKEN_USER*>(token_user)->User.Sid;
341 if (::ConvertSidToStringSid(sid, user_sid))
342 result = true;
344 delete[] token_user;
346 ::CloseHandle(process_token);
348 ::CloseHandle(process_handle);
349 return result;
352 struct SetWindowPosParams {
353 int x;
354 int y;
355 int width;
356 int height;
357 DWORD flags;
358 HWND window_insert_after;
359 bool success;
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
378 return TRUE;
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)) {
388 return false;
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,
398 false /* is_sxs */);
399 return !chrome_exe_path->empty();
402 } // namespace
404 BOOL __stdcall GoogleChromeCompatibilityCheck(BOOL set_flag,
405 int shell_mode,
406 DWORD* reasons) {
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.
438 if (reasons != NULL)
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))
447 return false;
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) {
454 return false;
457 bool impersonation_success = false;
458 if (IsRunningElevated()) {
459 wchar_t* curr_proc_sid;
460 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) {
461 return false;
464 DWORD pid = 0;
465 ::GetWindowThreadProcessId(::GetShellWindow(), &pid);
466 if (pid <= 0) {
467 ::LocalFree(curr_proc_sid);
468 return false;
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,
476 TRUE,
477 pid));
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,
483 &process_token) &&
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;
492 if (user_token)
493 ::CloseHandle(user_token);
494 if (process_token)
495 ::CloseHandle(process_token);
498 ::LocalFree(exp_proc_sid);
501 ::LocalFree(curr_proc_sid);
502 if (!impersonation_success) {
503 return false;
507 base::CommandLine chrome_command(chrome_exe_path);
509 bool ret = false;
510 ScopedComPtr<IProcessLauncher> ipl;
511 if (SUCCEEDED(ipl.CreateInstance(__uuidof(ProcessLauncherClass),
512 NULL,
513 CLSCTX_LOCAL_SERVER))) {
514 if (SUCCEEDED(ipl->LaunchCmdLine(
515 chrome_command.GetCommandLineString().c_str())))
516 ret = true;
517 ipl.Release();
518 } else {
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)
526 ::RevertToSelf();
527 return ret;
530 BOOL __stdcall LaunchGoogleChromeWithDimensions(int x,
531 int y,
532 int width,
533 int height,
534 bool in_background) {
535 if (in_background) {
536 base::FilePath chrome_exe_path;
537 if (!GetGoogleChromePath(&chrome_exe_path))
538 return false;
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
542 // foreground.
543 base::CommandLine chrome_command(chrome_exe_path);
545 ScopedCOMInitializer com_initializer;
546 if (!installer::WMIProcess::Launch(chrome_command.GetCommandLineString(),
547 NULL)) {
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())
551 return false;
554 } else {
555 if (!LaunchGoogleChrome())
556 return false;
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
573 // than that.
574 int ms_elapsed = 0;
575 int timeout = 10000;
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) {
584 found_window = true;
585 timeout = ms_elapsed + 5000;
588 Sleep(10);
589 ms_elapsed += 10;
592 return found_window;
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,
635 int shell_mode,
636 DWORD* error_code) {
637 DCHECK(error_code);
639 if (!brand_code) {
640 if (error_code)
641 *error_code = REACTIVATE_ERROR_INVALID_INPUT;
642 return FALSE;
645 int days_since_last_run = GoogleChromeDaysSinceLastRun();
646 if (days_since_last_run >= 0 &&
647 days_since_last_run < kReactivationMinDaysDormant) {
648 if (error_code)
649 *error_code = REACTIVATE_ERROR_NOTDORMANT;
650 return FALSE;
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)) {
660 if (error_code)
661 *error_code = REACTIVATE_ERROR_NOTINSTALLED;
662 return FALSE;
665 if (HasBeenReactivated()) {
666 if (error_code)
667 *error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED;
668 return FALSE;
672 return TRUE;
675 BOOL __stdcall ReactivateChrome(const wchar_t* brand_code,
676 int shell_mode,
677 DWORD* error_code) {
678 BOOL result = FALSE;
679 if (CanOfferReactivation(brand_code,
680 shell_mode,
681 error_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);
687 result = TRUE;
688 } else {
689 if (error_code)
690 *error_code = REACTIVATE_ERROR_REACTIVATION_FAILED;
694 return result;
697 BOOL __stdcall CanOfferRelaunch(const wchar_t** partner_brandcode_list,
698 int partner_brandcode_list_length,
699 int shell_mode,
700 DWORD* error_code) {
701 DCHECK(error_code);
703 if (!partner_brandcode_list || partner_brandcode_list_length <= 0) {
704 if (error_code)
705 *error_code = RELAUNCH_ERROR_INVALID_INPUT;
706 return FALSE;
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))) {
714 if (error_code)
715 *error_code = RELAUNCH_ERROR_NOTINSTALLED;
716 return FALSE;
719 // b) the installed brandcode should belong to that partner (in
720 // brandcode_list);
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;
727 break;
732 if (!valid_brandcode) {
733 if (error_code)
734 *error_code = RELAUNCH_ERROR_INVALID_PARTNER;
735 return FALSE;
738 // c) C1F ping should not have been sent;
739 if (IsC1FSent()) {
740 if (error_code)
741 *error_code = RELAUNCH_ERROR_PINGS_SENT;
742 return FALSE;
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) {
749 if (error_code)
750 *error_code = RELAUNCH_ERROR_NOTDORMANT;
751 return FALSE;
754 // e) a minimum period (6 months) must have passed since the previous
755 // relaunch offer for the current user;
756 RegKey key;
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) {
764 if (error_code)
765 *error_code = RELAUNCH_ERROR_ALREADY_RELAUNCHED;
766 return FALSE;
769 return TRUE;
772 BOOL __stdcall SetRelaunchOffered(const wchar_t** partner_brandcode_list,
773 int partner_brandcode_list_length,
774 const wchar_t* relaunch_brandcode,
775 int shell_mode,
776 DWORD* error_code) {
777 if (!CanOfferRelaunch(partner_brandcode_list, partner_brandcode_list_length,
778 shell_mode, error_code))
779 return FALSE;
781 // Store the relaunched brand code and the minimum date for relaunch (6 months
782 // from now), and set the Omaha experiment label.
783 RegKey key;
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)) {
792 if (error_code)
793 *error_code = RELAUNCH_ERROR_RELAUNCH_FAILED;
794 return FALSE;
797 return TRUE;