// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This code is a legacy utility API for partners to check whether
// Chrome can be installed and launched. Recent updates are being made
// to add new functionality. These updates use code from Chromium, the old
// coded against the win32 api directly. If you have an itch to shave a
// yak, feel free to re-write the old code too.
#include "chrome/installer/gcapi/gcapi.h"
#include <sddl.h>
#define STRSAFE_NO_DEPRECATE
#include <windows.h>
#include <strsafe.h>
#include <tlhelp32.h>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <set>
#include <string>
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/process/launch.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_handle.h"
#include "chrome/installer/gcapi/gcapi_omaha_experiment.h"
#include "chrome/installer/gcapi/gcapi_reactivation.h"
#include "chrome/installer/launcher_support/chrome_launcher_support.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/wmi.h"
#include "google_update/google_update_idl.h"
using base::Time;
using base::TimeDelta;
using base::win::RegKey;
using base::win::ScopedCOMInitializer;
using base::win::ScopedComPtr;
using base::win::ScopedHandle;
namespace {
const wchar_t kChromeRegClientsKey[] =
L"Software\\Google\\Update\\Clients\\"
L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
const wchar_t kChromeRegClientStateKey[] =
L"Software\\Google\\Update\\ClientState\\"
L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
const wchar_t kChromeRegClientStateMediumKey[] =
L"Software\\Google\\Update\\ClientStateMedium\\"
L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
const wchar_t kGCAPITempKey[] = L"Software\\Google\\GCAPITemp";
const wchar_t kChromeRegLaunchCmd[] = L"InstallerSuccessLaunchCmdLine";
const wchar_t kChromeRegLastLaunchCmd[] = L"LastInstallerSuccessLaunchCmdLine";
const wchar_t kChromeRegVersion[] = L"pv";
const wchar_t kNoChromeOfferUntil[] =
L"SOFTWARE\\Google\\No Chrome Offer Until";
const wchar_t kC1FPendingKey[] =
L"Software\\Google\\Common\\Rlz\\Events\\C";
const wchar_t kC1FSentKey[] =
L"Software\\Google\\Common\\Rlz\\StatefulEvents\\C";
const wchar_t kC1FKey[] = L"C1F";
const wchar_t kRelaunchBrandcodeValue[] = L"RelaunchBrandcode";
const wchar_t kRelaunchAllowedAfterValue[] = L"RelaunchAllowedAfter";
// Prefix used to match the window class for Chrome windows.
const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
// Return the company name specified in the file version info resource.
bool GetCompanyName(const wchar_t* filename, wchar_t* buffer, DWORD out_len) {
wchar_t file_version_info[8192];
DWORD handle = 0;
DWORD buffer_size = 0;
buffer_size = ::GetFileVersionInfoSize(filename, &handle);
// Cannot stats the file or our buffer size is too small (very unlikely).
if (buffer_size == 0 || buffer_size > _countof(file_version_info))
return false;
buffer_size = _countof(file_version_info);
memset(file_version_info, 0, buffer_size);
if (!::GetFileVersionInfo(filename, handle, buffer_size, file_version_info))
return false;
DWORD data_len = 0;
LPVOID data = NULL;
// Retrieve the language and codepage code if exists.
buffer_size = 0;
if (!::VerQueryValue(file_version_info, TEXT("\\VarFileInfo\\Translation"),
reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
return false;
if (data_len != 4)
return false;
wchar_t info_name[256];
DWORD lang = 0;
// Formulate the string to retrieve the company name of the specific
// language codepage.
memcpy(&lang, data, 4);
::StringCchPrintf(info_name, _countof(info_name),
L"\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName",
(lang & 0xff00)>>8, (lang & 0xff), (lang & 0xff000000)>>24,
(lang & 0xff0000)>>16);
data_len = 0;
if (!::VerQueryValue(file_version_info, info_name,
reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
return false;
if (data_len <= 0 || data_len >= (out_len / sizeof(wchar_t)))
return false;
memset(buffer, 0, out_len);
::StringCchCopyN(buffer,
(out_len / sizeof(wchar_t)),
reinterpret_cast<const wchar_t*>(data),
data_len);
return true;
}
// Offsets the current date by |months|. |months| must be between 0 and 12.
// The returned date is in the YYYYMMDD format.
DWORD FormatDateOffsetByMonths(int months) {
DCHECK(months >= 0 && months <= 12);
SYSTEMTIME now;
GetLocalTime(&now);
now.wMonth += months;
if (now.wMonth > 12) {
now.wMonth -= 12;
now.wYear += 1;
}
return now.wYear * 10000 + now.wMonth * 100 + now.wDay;
}
// Return true if we can re-offer Chrome; false, otherwise.
// Each partner can only offer Chrome once every six months.
bool CanReOfferChrome(BOOL set_flag) {
wchar_t filename[MAX_PATH+1];
wchar_t company[MAX_PATH];
// If we cannot retrieve the version info of the executable or company
// name, we allow the Chrome to be offered because there is no past
// history to be found.
if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0)
return true;
if (!GetCompanyName(filename, company, sizeof(company)))
return true;
bool can_re_offer = true;
DWORD disposition = 0;
HKEY key = NULL;
if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kNoChromeOfferUntil,
0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
NULL, &key, &disposition) == ERROR_SUCCESS) {
// Get today's date, and format it as YYYYMMDD numeric value.
DWORD today = FormatDateOffsetByMonths(0);
// Cannot re-offer, if the timer already exists and is not expired yet.
DWORD value_type = REG_DWORD;
DWORD value_data = 0;
DWORD value_length = sizeof(DWORD);
if (::RegQueryValueEx(key, company, 0, &value_type,
reinterpret_cast<LPBYTE>(&value_data),
&value_length) == ERROR_SUCCESS &&
REG_DWORD == value_type &&
value_data > today) {
// The time has not expired, we cannot offer Chrome.
can_re_offer = false;
} else {
// Delete the old or invalid value.
::RegDeleteValue(key, company);
if (set_flag) {
// Set expiration date for offer as six months from today,
// represented as a YYYYMMDD numeric value.
DWORD value = FormatDateOffsetByMonths(6);
::RegSetValueEx(key, company, 0, REG_DWORD, (LPBYTE)&value,
sizeof(DWORD));
}
}
::RegCloseKey(key);
}
return can_re_offer;
}
bool IsChromeInstalled(HKEY root_key) {
RegKey key;
return key.Open(root_key, kChromeRegClientsKey, KEY_READ) == ERROR_SUCCESS &&
key.HasValue(kChromeRegVersion);
}
// Returns true if the |subkey| in |root| has the kC1FKey entry set to 1.
bool RegKeyHasC1F(HKEY root, const wchar_t* subkey) {
RegKey key;
DWORD value;
return key.Open(root, subkey, KEY_READ) == ERROR_SUCCESS &&
key.ReadValueDW(kC1FKey, &value) == ERROR_SUCCESS &&
value == static_cast<DWORD>(1);
}
bool IsC1FSent() {
// The C1F RLZ key can either be in HKCU or in HKLM (the HKLM RLZ key is made
// readable to all-users via rlz_lib::CreateMachineState()) and can either be
// in sent or pending state. Return true if there is a match for any of these
// 4 states.
return RegKeyHasC1F(HKEY_CURRENT_USER, kC1FSentKey) ||
RegKeyHasC1F(HKEY_CURRENT_USER, kC1FPendingKey) ||
RegKeyHasC1F(HKEY_LOCAL_MACHINE, kC1FSentKey) ||
RegKeyHasC1F(HKEY_LOCAL_MACHINE, kC1FPendingKey);
}
enum WindowsVersion {
VERSION_BELOW_XP_SP2,
VERSION_XP_SP2_UP_TO_VISTA, // "but not including"
VERSION_VISTA_OR_HIGHER,
};
WindowsVersion GetWindowsVersion() {
OSVERSIONINFOEX version_info = { sizeof version_info };
GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
// Windows Vista is version 6.0.
if (version_info.dwMajorVersion >= 6)
return VERSION_VISTA_OR_HIGHER;
// Windows XP is version 5.1. (5.2 is Windows Server 2003/XP Pro x64.)
if ((version_info.dwMajorVersion < 5) || (version_info.dwMinorVersion < 1))
return VERSION_BELOW_XP_SP2;
// For XP itself, we only support SP2 and above.
return ((version_info.dwMinorVersion > 1) ||
(version_info.wServicePackMajor >= 2)) ?
VERSION_XP_SP2_UP_TO_VISTA : VERSION_BELOW_XP_SP2;
}
// Note this function should not be called on old Windows versions where these
// Windows API are not available. We always invoke this function after checking
// that current OS is Vista or later.
bool VerifyAdminGroup() {
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID Group;
BOOL check = ::AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0,
0, 0, 0,
&Group);
if (check) {
if (!::CheckTokenMembership(NULL, Group, &check))
check = FALSE;
}
::FreeSid(Group);
return (check == TRUE);
}
bool VerifyHKLMAccess() {
wchar_t str[] = L"test";
bool result = false;
DWORD disposition = 0;
HKEY key = NULL;
if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kGCAPITempKey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL,
&key, &disposition) == ERROR_SUCCESS) {
if (::RegSetValueEx(key, str, 0, REG_SZ, (LPBYTE)str,
(DWORD)lstrlen(str)) == ERROR_SUCCESS) {
result = true;
RegDeleteValue(key, str);
}
RegCloseKey(key);
// If we create the main key, delete the entire key.
if (disposition == REG_CREATED_NEW_KEY)
RegDeleteKey(HKEY_LOCAL_MACHINE, kGCAPITempKey);
}
return result;
}
bool IsRunningElevated() {
// This method should be called only for Vista or later.
if ((GetWindowsVersion() < VERSION_VISTA_OR_HIGHER) ||
!VerifyAdminGroup())
return false;
HANDLE process_token;
if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
return false;
TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
DWORD size_returned = 0;
if (!::GetTokenInformation(process_token, TokenElevationType,
&elevation_type, sizeof(elevation_type),
&size_returned)) {
::CloseHandle(process_token);
return false;
}
::CloseHandle(process_token);
return (elevation_type == TokenElevationTypeFull);
}
bool GetUserIdForProcess(size_t pid, wchar_t** user_sid) {
HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid);
if (process_handle == NULL)
return false;
HANDLE process_token;
bool result = false;
if (::OpenProcessToken(process_handle, TOKEN_QUERY, &process_token)) {
DWORD size = 0;
::GetTokenInformation(process_token, TokenUser, NULL, 0, &size);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
::GetLastError() == ERROR_SUCCESS) {
DWORD actual_size = 0;
BYTE* token_user = new BYTE[size];
if ((::GetTokenInformation(process_token, TokenUser, token_user, size,
&actual_size)) &&
(actual_size <= size)) {
PSID sid = reinterpret_cast<TOKEN_USER*>(token_user)->User.Sid;
if (::ConvertSidToStringSid(sid, user_sid))
result = true;
}
delete[] token_user;
}
::CloseHandle(process_token);
}
::CloseHandle(process_handle);
return result;
}
struct SetWindowPosParams {
int x;
int y;
int width;
int height;
DWORD flags;
HWND window_insert_after;
bool success;
std::set<HWND> shunted_hwnds;
};
BOOL CALLBACK ChromeWindowEnumProc(HWND hwnd, LPARAM lparam) {
wchar_t window_class[MAX_PATH] = {};
SetWindowPosParams* params = reinterpret_cast<SetWindowPosParams*>(lparam);
if (!params->shunted_hwnds.count(hwnd) &&
::GetClassName(hwnd, window_class, arraysize(window_class)) &&
StartsWith(window_class, kChromeWindowClassPrefix, false) &&
::SetWindowPos(hwnd, params->window_insert_after, params->x,
params->y, params->width, params->height, params->flags)) {
params->shunted_hwnds.insert(hwnd);
params->success = true;
}
// Return TRUE to ensure we hit all possible top-level Chrome windows as per
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633498.aspx
return TRUE;
}
// Returns true and populates |chrome_exe_path| with the path to chrome.exe if
// a valid installation can be found.
bool GetGoogleChromePath(base::FilePath* chrome_exe_path) {
HKEY install_key = HKEY_LOCAL_MACHINE;
if (!IsChromeInstalled(install_key)) {
install_key = HKEY_CURRENT_USER;
if (!IsChromeInstalled(install_key)) {
return false;
}
}
// Now grab the uninstall string from the appropriate ClientState key
// and use that as the base for a path to chrome.exe.
*chrome_exe_path =
chrome_launcher_support::GetChromePathForInstallationLevel(
install_key == HKEY_LOCAL_MACHINE ?
chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION :
chrome_launcher_support::USER_LEVEL_INSTALLATION);
return !chrome_exe_path->empty();
}
} // namespace
BOOL __stdcall GoogleChromeCompatibilityCheck(BOOL set_flag,
int shell_mode,
DWORD* reasons) {
DWORD local_reasons = 0;
WindowsVersion windows_version = GetWindowsVersion();
// System requirements?
if (windows_version == VERSION_BELOW_XP_SP2)
local_reasons |= GCCC_ERROR_OSNOTSUPPORTED;
if (IsChromeInstalled(HKEY_LOCAL_MACHINE))
local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT;
if (IsChromeInstalled(HKEY_CURRENT_USER))
local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT;
if (shell_mode == GCAPI_INVOKED_UAC_ELEVATION) {
// Only check that we have HKLM write permissions if we specify that
// GCAPI is being invoked from an elevated shell, or in admin mode
if (!VerifyHKLMAccess()) {
local_reasons |= GCCC_ERROR_ACCESSDENIED;
} else if ((windows_version == VERSION_VISTA_OR_HIGHER) &&
!VerifyAdminGroup()) {
// For Vista or later check for elevation since even for admin user we could
// be running in non-elevated mode. We require integrity level High.
local_reasons |= GCCC_ERROR_INTEGRITYLEVEL;
}
}
// Then only check whether we can re-offer, if everything else is OK.
if (local_reasons == 0 && !CanReOfferChrome(set_flag))
local_reasons |= GCCC_ERROR_ALREADYOFFERED;
// Done. Copy/return results.
if (reasons != NULL)
*reasons = local_reasons;
return (local_reasons == 0);
}
BOOL __stdcall LaunchGoogleChrome() {
base::FilePath chrome_exe_path;
if (!GetGoogleChromePath(&chrome_exe_path))
return false;
ScopedCOMInitializer com_initializer;
if (::CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IDENTIFY, NULL,
EOAC_DYNAMIC_CLOAKING, NULL) != S_OK) {
return false;
}
bool impersonation_success = false;
if (IsRunningElevated()) {
wchar_t* curr_proc_sid;
if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) {
return false;
}
DWORD pid = 0;
::GetWindowThreadProcessId(::GetShellWindow(), &pid);
if (pid <= 0) {
::LocalFree(curr_proc_sid);
return false;
}
wchar_t* exp_proc_sid;
if (GetUserIdForProcess(pid, &exp_proc_sid)) {
if (_wcsicmp(curr_proc_sid, exp_proc_sid) == 0) {
ScopedHandle process_handle(
::OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
TRUE,
pid));
if (process_handle.IsValid()) {
HANDLE process_token = NULL;
HANDLE user_token = NULL;
if (::OpenProcessToken(process_handle, TOKEN_DUPLICATE | TOKEN_QUERY,
&process_token) &&
::DuplicateTokenEx(process_token,
TOKEN_IMPERSONATE | TOKEN_QUERY |
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
NULL, SecurityImpersonation,
TokenPrimary, &user_token) &&
(::ImpersonateLoggedOnUser(user_token) != 0)) {
impersonation_success = true;
}
if (user_token)
::CloseHandle(user_token);
if (process_token)
::CloseHandle(process_token);
}
}
::LocalFree(exp_proc_sid);
}
::LocalFree(curr_proc_sid);
if (!impersonation_success) {
return false;
}
}
bool ret = false;
ScopedComPtr<IProcessLauncher> ipl;
if (SUCCEEDED(ipl.CreateInstance(__uuidof(ProcessLauncherClass),
NULL,
CLSCTX_LOCAL_SERVER))) {
if (SUCCEEDED(ipl->LaunchCmdLine(chrome_exe_path.value().c_str())))
ret = true;
ipl.Release();
} else {
// Couldn't get Omaha's process launcher, Omaha may not be installed at
// system level. Try just running Chrome instead.
ret = base::LaunchProcess(chrome_exe_path.value(),
base::LaunchOptions(),
NULL);
}
if (impersonation_success)
::RevertToSelf();
return ret;
}
BOOL __stdcall LaunchGoogleChromeWithDimensions(int x,
int y,
int width,
int height,
bool in_background) {
if (in_background) {
base::FilePath chrome_exe_path;
if (!GetGoogleChromePath(&chrome_exe_path))
return false;
// When launching in the background, use WMI to ensure that chrome.exe is
// is not our child process. This prevents it from pushing itself to
// foreground.
CommandLine chrome_command(chrome_exe_path);
ScopedCOMInitializer com_initializer;
if (!installer::WMIProcess::Launch(chrome_command.GetCommandLineString(),
NULL)) {
// For some reason WMI failed. Try and launch the old fashioned way,
// knowing that visual glitches will occur when the window pops up.
if (!LaunchGoogleChrome())
return false;
}
} else {
if (!LaunchGoogleChrome())
return false;
}
HWND hwnd_insert_after = in_background ? HWND_BOTTOM : NULL;
DWORD set_window_flags = in_background ? SWP_NOACTIVATE : SWP_NOZORDER;
if (x == -1 && y == -1)
set_window_flags |= SWP_NOMOVE;
if (width == -1 && height == -1)
set_window_flags |= SWP_NOSIZE;
SetWindowPosParams enum_params = { x, y, width, height, set_window_flags,
hwnd_insert_after, false };
// Chrome may have been launched, but the window may not have appeared
// yet. Wait for it to appear for 10 seconds, but exit if it takes longer
// than that.
int ms_elapsed = 0;
int timeout = 10000;
bool found_window = false;
while (ms_elapsed < timeout) {
// Enum all top-level windows looking for Chrome windows.
::EnumWindows(ChromeWindowEnumProc, reinterpret_cast<LPARAM>(&enum_params));
// Give it five more seconds after finding the first window until we stop
// shoving new windows into the background.
if (!found_window && enum_params.success) {
found_window = true;
timeout = ms_elapsed + 5000;
}
Sleep(10);
ms_elapsed += 10;
}
return found_window;
}
BOOL __stdcall LaunchGoogleChromeInBackground() {
return LaunchGoogleChromeWithDimensions(-1, -1, -1, -1, true);
}
int __stdcall GoogleChromeDaysSinceLastRun() {
int days_since_last_run = std::numeric_limits<int>::max();
if (IsChromeInstalled(HKEY_LOCAL_MACHINE) ||
IsChromeInstalled(HKEY_CURRENT_USER)) {
RegKey client_state(
HKEY_CURRENT_USER, kChromeRegClientStateKey, KEY_QUERY_VALUE);
if (client_state.Valid()) {
std::wstring last_run;
int64 last_run_value = 0;
if (client_state.ReadValue(google_update::kRegLastRunTimeField,
&last_run) == ERROR_SUCCESS &&
base::StringToInt64(last_run, &last_run_value)) {
Time last_run_time = Time::FromInternalValue(last_run_value);
TimeDelta difference = Time::NowFromSystemTime() - last_run_time;
// We can end up with negative numbers here, given changes in system
// clock time or due to TimeDelta's int64 -> int truncation.
int new_days_since_last_run = difference.InDays();
if (new_days_since_last_run >= 0 &&
new_days_since_last_run < days_since_last_run) {
days_since_last_run = new_days_since_last_run;
}
}
}
}
if (days_since_last_run == std::numeric_limits<int>::max()) {
days_since_last_run = -1;
}
return days_since_last_run;
}
BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code,
int shell_mode,
DWORD* error_code) {
DCHECK(error_code);
if (!brand_code) {
if (error_code)
*error_code = REACTIVATE_ERROR_INVALID_INPUT;
return FALSE;
}
int days_since_last_run = GoogleChromeDaysSinceLastRun();
if (days_since_last_run >= 0 &&
days_since_last_run < kReactivationMinDaysDormant) {
if (error_code)
*error_code = REACTIVATE_ERROR_NOTDORMANT;
return FALSE;
}
// Only run the code below when this function is invoked from a standard,
// non-elevated cmd shell. This is because this section of code looks at
// values in HKEY_CURRENT_USER, and we only want to look at the logged-in
// user's HKCU, not the admin user's HKCU.
if (shell_mode == GCAPI_INVOKED_STANDARD_SHELL) {
if (!IsChromeInstalled(HKEY_LOCAL_MACHINE) &&
!IsChromeInstalled(HKEY_CURRENT_USER)) {
if (error_code)
*error_code = REACTIVATE_ERROR_NOTINSTALLED;
return FALSE;
}
if (HasBeenReactivated()) {
if (error_code)
*error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED;
return FALSE;
}
}
return TRUE;
}
BOOL __stdcall ReactivateChrome(wchar_t* brand_code,
int shell_mode,
DWORD* error_code) {
BOOL result = FALSE;
if (CanOfferReactivation(brand_code,
shell_mode,
error_code)) {
if (SetReactivationBrandCode(brand_code, shell_mode)) {
// Currently set this as a best-effort thing. We return TRUE if
// reactivation succeeded regardless of the experiment label result.
SetReactivationExperimentLabels(brand_code, shell_mode);
result = TRUE;
} else {
if (error_code)
*error_code = REACTIVATE_ERROR_REACTIVATION_FAILED;
}
}
return result;
}
BOOL __stdcall CanOfferRelaunch(const wchar_t** partner_brandcode_list,
int partner_brandcode_list_length,
int shell_mode,
DWORD* error_code) {
DCHECK(error_code);
if (!partner_brandcode_list || partner_brandcode_list_length <= 0) {
if (error_code)
*error_code = RELAUNCH_ERROR_INVALID_INPUT;
return FALSE;
}
// These conditions need to be satisfied for relaunch:
// a) Chrome should be installed;
if (!IsChromeInstalled(HKEY_LOCAL_MACHINE) &&
(shell_mode != GCAPI_INVOKED_STANDARD_SHELL ||
!IsChromeInstalled(HKEY_CURRENT_USER))) {
if (error_code)
*error_code = RELAUNCH_ERROR_NOTINSTALLED;
return FALSE;
}
// b) the installed brandcode should belong to that partner (in
// brandcode_list);
std::wstring installed_brandcode;
bool valid_brandcode = false;
if (GoogleUpdateSettings::GetBrand(&installed_brandcode)) {
for (int i = 0; i < partner_brandcode_list_length; ++i) {
if (!_wcsicmp(installed_brandcode.c_str(), partner_brandcode_list[i])) {
valid_brandcode = true;
break;
}
}
}
if (!valid_brandcode) {
if (error_code)
*error_code = RELAUNCH_ERROR_INVALID_PARTNER;
return FALSE;
}
// c) C1F ping should not have been sent;
if (IsC1FSent()) {
if (error_code)
*error_code = RELAUNCH_ERROR_PINGS_SENT;
return FALSE;
}
// d) a minimum period (30 days) must have passed since Chrome was last used;
int days_since_last_run = GoogleChromeDaysSinceLastRun();
if (days_since_last_run >= 0 &&
days_since_last_run < kRelaunchMinDaysDormant) {
if (error_code)
*error_code = RELAUNCH_ERROR_NOTDORMANT;
return FALSE;
}
// e) a minimum period (6 months) must have passed since the previous
// relaunch offer for the current user;
RegKey key;
DWORD min_relaunch_date;
if (key.Open(HKEY_CURRENT_USER, kChromeRegClientStateKey,
KEY_QUERY_VALUE) == ERROR_SUCCESS &&
key.ReadValueDW(kRelaunchAllowedAfterValue,
&min_relaunch_date) == ERROR_SUCCESS &&
FormatDateOffsetByMonths(0) < min_relaunch_date) {
if (error_code)
*error_code = RELAUNCH_ERROR_ALREADY_RELAUNCHED;
return FALSE;
}
return TRUE;
}
BOOL __stdcall SetRelaunchOffered(const wchar_t** partner_brandcode_list,
int partner_brandcode_list_length,
const wchar_t* relaunch_brandcode,
int shell_mode,
DWORD* error_code) {
if (!CanOfferRelaunch(partner_brandcode_list, partner_brandcode_list_length,
shell_mode, error_code))
return FALSE;
// Store the relaunched brand code and the minimum date for relaunch (6 months
// from now), and set the Omaha experiment label.
RegKey key;
if (key.Create(HKEY_CURRENT_USER, kChromeRegClientStateKey,
KEY_SET_VALUE) != ERROR_SUCCESS ||
key.WriteValue(kRelaunchBrandcodeValue,
relaunch_brandcode) != ERROR_SUCCESS ||
key.WriteValue(kRelaunchAllowedAfterValue,
FormatDateOffsetByMonths(6)) != ERROR_SUCCESS ||
!SetRelaunchExperimentLabels(relaunch_brandcode, shell_mode)) {
if (error_code)
*error_code = RELAUNCH_ERROR_RELAUNCH_FAILED;
return FALSE;
}
return TRUE;
}