15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NOTE: This code is a legacy utility API for partners to check whether
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       Chrome can be installed and launched. Recent updates are being made
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       to add new functionality. These updates use code from Chromium, the old
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       coded against the win32 api directly. If you have an itch to shave a
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       yak, feel free to re-write the old code too.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/gcapi/gcapi.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sddl.h>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define STRSAFE_NO_DEPRECATE
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <windows.h>
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <strsafe.h>
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <tlhelp32.h>
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cstdlib>
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <iterator>
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits>
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
28a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/launch.h"
297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string16.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/registry.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_com_initializer.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_handle.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/gcapi/gcapi_omaha_experiment.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/gcapi/gcapi_reactivation.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/launcher_support/chrome_launcher_support.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/google_update_constants.h"
413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "chrome/installer/util/google_update_settings.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/util_constants.h"
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/wmi.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_update/google_update_idl.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::Time;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeDelta;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::win::RegKey;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::win::ScopedCOMInitializer;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::win::ScopedComPtr;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::win::ScopedHandle;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegClientsKey[] =
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"Software\\Google\\Update\\Clients\\"
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegClientStateKey[] =
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"Software\\Google\\Update\\ClientState\\"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegClientStateMediumKey[] =
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"Software\\Google\\Update\\ClientStateMedium\\"
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kGCAPITempKey[] = L"Software\\Google\\GCAPITemp";
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegLaunchCmd[] = L"InstallerSuccessLaunchCmdLine";
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegLastLaunchCmd[] = L"LastInstallerSuccessLaunchCmdLine";
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeRegVersion[] = L"pv";
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kNoChromeOfferUntil[] =
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L"SOFTWARE\\Google\\No Chrome Offer Until";
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const wchar_t kC1FPendingKey[] =
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    L"Software\\Google\\Common\\Rlz\\Events\\C";
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const wchar_t kC1FSentKey[] =
763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    L"Software\\Google\\Common\\Rlz\\StatefulEvents\\C";
773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const wchar_t kC1FKey[] = L"C1F";
783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const wchar_t kRelaunchBrandcodeValue[] = L"RelaunchBrandcode";
803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const wchar_t kRelaunchAllowedAfterValue[] = L"RelaunchAllowedAfter";
813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Prefix used to match the window class for Chrome windows.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return the company name specified in the file version info resource.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetCompanyName(const wchar_t* filename, wchar_t* buffer, DWORD out_len) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t file_version_info[8192];
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD handle = 0;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD buffer_size = 0;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_size = ::GetFileVersionInfoSize(filename, &handle);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Cannot stats the file or our buffer size is too small (very unlikely).
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (buffer_size == 0 || buffer_size > _countof(file_version_info))
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_size = _countof(file_version_info);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(file_version_info, 0, buffer_size);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::GetFileVersionInfo(filename, handle, buffer_size, file_version_info))
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD data_len = 0;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LPVOID data = NULL;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve the language and codepage code if exists.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_size = 0;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::VerQueryValue(file_version_info, TEXT("\\VarFileInfo\\Translation"),
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_len != 4)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t info_name[256];
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD lang = 0;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Formulate the string to retrieve the company name of the specific
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // language codepage.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(&lang, data, 4);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::StringCchPrintf(info_name, _countof(info_name),
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      L"\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName",
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (lang & 0xff00)>>8, (lang & 0xff), (lang & 0xff000000)>>24,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (lang & 0xff0000)>>16);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_len = 0;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::VerQueryValue(file_version_info, info_name,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<LPVOID *>(&data), reinterpret_cast<UINT *>(&data_len)))
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_len <= 0 || data_len >= (out_len / sizeof(wchar_t)))
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(buffer, 0, out_len);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::StringCchCopyN(buffer,
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   (out_len / sizeof(wchar_t)),
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   reinterpret_cast<const wchar_t*>(data),
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   data_len);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// Offsets the current date by |months|. |months| must be between 0 and 12.
1373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// The returned date is in the YYYYMMDD format.
1383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)DWORD FormatDateOffsetByMonths(int months) {
1393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DCHECK(months >= 0 && months <= 12);
1403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  SYSTEMTIME now;
1423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  GetLocalTime(&now);
1433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  now.wMonth += months;
1443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (now.wMonth > 12) {
1453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    now.wMonth -= 12;
1463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    now.wYear += 1;
1473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
1483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return now.wYear * 10000 + now.wMonth * 100 + now.wDay;
1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true if we can re-offer Chrome; false, otherwise.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Each partner can only offer Chrome once every six months.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CanReOfferChrome(BOOL set_flag) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t filename[MAX_PATH+1];
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t company[MAX_PATH];
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we cannot retrieve the version info of the executable or company
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // name, we allow the Chrome to be offered because there is no past
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // history to be found.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetCompanyName(filename, company, sizeof(company)))
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool can_re_offer = true;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD disposition = 0;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HKEY key = NULL;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kNoChromeOfferUntil,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL, &key, &disposition) == ERROR_SUCCESS) {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get today's date, and format it as YYYYMMDD numeric value.
1733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    DWORD today = FormatDateOffsetByMonths(0);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cannot re-offer, if the timer already exists and is not expired yet.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD value_type = REG_DWORD;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD value_data = 0;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD value_length = sizeof(DWORD);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::RegQueryValueEx(key, company, 0, &value_type,
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          reinterpret_cast<LPBYTE>(&value_data),
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          &value_length) == ERROR_SUCCESS &&
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        REG_DWORD == value_type &&
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value_data > today) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The time has not expired, we cannot offer Chrome.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      can_re_offer = false;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Delete the old or invalid value.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::RegDeleteValue(key, company);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (set_flag) {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Set expiration date for offer as six months from today,
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // represented as a YYYYMMDD numeric value.
1923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        DWORD value = FormatDateOffsetByMonths(6);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ::RegSetValueEx(key, company, 0, REG_DWORD, (LPBYTE)&value,
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        sizeof(DWORD));
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::RegCloseKey(key);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return can_re_offer;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool IsChromeInstalled(HKEY root_key) {
2053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  RegKey key;
206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return key.Open(root_key,
207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  kChromeRegClientsKey,
208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  KEY_READ | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
2093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)         key.HasValue(kChromeRegVersion);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// Returns true if the |subkey| in |root| has the kC1FKey entry set to 1.
2133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool RegKeyHasC1F(HKEY root, const wchar_t* subkey) {
2143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  RegKey key;
2153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DWORD value;
216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return key.Open(root, subkey, KEY_READ | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
2173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      key.ReadValueDW(kC1FKey, &value) == ERROR_SUCCESS &&
2183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      value == static_cast<DWORD>(1);
2193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
2203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool IsC1FSent() {
2223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // The C1F RLZ key can either be in HKCU or in HKLM (the HKLM RLZ key is made
2233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // readable to all-users via rlz_lib::CreateMachineState()) and can either be
2243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // in sent or pending state. Return true if there is a match for any of these
2253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // 4 states.
2263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return RegKeyHasC1F(HKEY_CURRENT_USER, kC1FSentKey) ||
2273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      RegKeyHasC1F(HKEY_CURRENT_USER, kC1FPendingKey) ||
2283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      RegKeyHasC1F(HKEY_LOCAL_MACHINE, kC1FSentKey) ||
2293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      RegKeyHasC1F(HKEY_LOCAL_MACHINE, kC1FPendingKey);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum WindowsVersion {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VERSION_BELOW_XP_SP2,
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VERSION_XP_SP2_UP_TO_VISTA,  // "but not including"
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VERSION_VISTA_OR_HIGHER,
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WindowsVersion GetWindowsVersion() {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSVERSIONINFOEX version_info = { sizeof version_info };
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Windows Vista is version 6.0.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (version_info.dwMajorVersion >= 6)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return VERSION_VISTA_OR_HIGHER;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Windows XP is version 5.1.  (5.2 is Windows Server 2003/XP Pro x64.)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((version_info.dwMajorVersion < 5) || (version_info.dwMinorVersion < 1))
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return VERSION_BELOW_XP_SP2;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For XP itself, we only support SP2 and above.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ((version_info.dwMinorVersion > 1) ||
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (version_info.wServicePackMajor >= 2)) ?
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VERSION_XP_SP2_UP_TO_VISTA : VERSION_BELOW_XP_SP2;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note this function should not be called on old Windows versions where these
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Windows API are not available. We always invoke this function after checking
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// that current OS is Vista or later.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool VerifyAdminGroup() {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PSID Group;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL check = ::AllocateAndInitializeSid(&NtAuthority, 2,
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          SECURITY_BUILTIN_DOMAIN_RID,
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          0, 0, 0,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          &Group);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (check) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!::CheckTokenMembership(NULL, Group, &check))
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      check = FALSE;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::FreeSid(Group);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (check == TRUE);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool VerifyHKLMAccess() {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t str[] = L"test";
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool result = false;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD disposition = 0;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HKEY key = NULL;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::RegCreateKeyEx(HKEY_LOCAL_MACHINE, kGCAPITempKey, 0, NULL,
281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                       REG_OPTION_NON_VOLATILE,
282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                       KEY_READ | KEY_WRITE | KEY_WOW64_32KEY, NULL,
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       &key, &disposition) == ERROR_SUCCESS) {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::RegSetValueEx(key, str, 0, REG_SZ, (LPBYTE)str,
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (DWORD)lstrlen(str)) == ERROR_SUCCESS) {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result = true;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RegDeleteValue(key, str);
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RegCloseKey(key);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //  If we create the main key, delete the entire key.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (disposition == REG_CREATED_NEW_KEY)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RegDeleteKey(HKEY_LOCAL_MACHINE, kGCAPITempKey);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsRunningElevated() {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This method should be called only for Vista or later.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((GetWindowsVersion() < VERSION_VISTA_OR_HIGHER) ||
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !VerifyAdminGroup())
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HANDLE process_token;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD size_returned = 0;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::GetTokenInformation(process_token, TokenElevationType,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             &elevation_type, sizeof(elevation_type),
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             &size_returned)) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::CloseHandle(process_token);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::CloseHandle(process_token);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (elevation_type == TokenElevationTypeFull);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetUserIdForProcess(size_t pid, wchar_t** user_sid) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (process_handle == NULL)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HANDLE process_token;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool result = false;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::OpenProcessToken(process_handle, TOKEN_QUERY, &process_token)) {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD size = 0;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::GetTokenInformation(process_token, TokenUser, NULL, 0, &size);
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ::GetLastError() == ERROR_SUCCESS) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DWORD actual_size = 0;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BYTE* token_user = new BYTE[size];
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((::GetTokenInformation(process_token, TokenUser, token_user, size,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &actual_size)) &&
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (actual_size <= size)) {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        PSID sid = reinterpret_cast<TOKEN_USER*>(token_user)->User.Sid;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (::ConvertSidToStringSid(sid, user_sid))
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          result = true;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete[] token_user;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::CloseHandle(process_token);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::CloseHandle(process_handle);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct SetWindowPosParams {
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int y;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int height;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD flags;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HWND window_insert_after;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<HWND> shunted_hwnds;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL CALLBACK ChromeWindowEnumProc(HWND hwnd, LPARAM lparam) {
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wchar_t window_class[MAX_PATH] = {};
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetWindowPosParams* params = reinterpret_cast<SetWindowPosParams*>(lparam);
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!params->shunted_hwnds.count(hwnd) &&
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::GetClassName(hwnd, window_class, arraysize(window_class)) &&
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartsWith(window_class, kChromeWindowClassPrefix, false) &&
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::SetWindowPos(hwnd, params->window_insert_after, params->x,
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     params->y, params->width, params->height, params->flags)) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params->shunted_hwnds.insert(hwnd);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params->success = true;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return TRUE to ensure we hit all possible top-level Chrome windows as per
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633498.aspx
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns true and populates |chrome_exe_path| with the path to chrome.exe if
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// a valid installation can be found.
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool GetGoogleChromePath(base::FilePath* chrome_exe_path) {
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HKEY install_key = HKEY_LOCAL_MACHINE;
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!IsChromeInstalled(install_key)) {
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    install_key = HKEY_CURRENT_USER;
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!IsChromeInstalled(install_key)) {
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Now grab the uninstall string from the appropriate ClientState key
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // and use that as the base for a path to chrome.exe.
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *chrome_exe_path =
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome_launcher_support::GetChromePathForInstallationLevel(
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          install_key == HKEY_LOCAL_MACHINE ?
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION :
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              chrome_launcher_support::USER_LEVEL_INSTALLATION);
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !chrome_exe_path->empty();
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL __stdcall GoogleChromeCompatibilityCheck(BOOL set_flag,
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int shell_mode,
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              DWORD* reasons) {
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD local_reasons = 0;
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WindowsVersion windows_version = GetWindowsVersion();
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // System requirements?
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (windows_version == VERSION_BELOW_XP_SP2)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_OSNOTSUPPORTED;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsChromeInstalled(HKEY_LOCAL_MACHINE))
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_SYSTEMLEVELALREADYPRESENT;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsChromeInstalled(HKEY_CURRENT_USER))
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_USERLEVELALREADYPRESENT;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (shell_mode == GCAPI_INVOKED_UAC_ELEVATION) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only check that we have HKLM write permissions if we specify that
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // GCAPI is being invoked from an elevated shell, or in admin mode
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!VerifyHKLMAccess()) {
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_ACCESSDENIED;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if ((windows_version == VERSION_VISTA_OR_HIGHER) &&
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         !VerifyAdminGroup()) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // For Vista or later check for elevation since even for admin user we could
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // be running in non-elevated mode. We require integrity level High.
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_INTEGRITYLEVEL;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Then only check whether we can re-offer, if everything else is OK.
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (local_reasons == 0 && !CanReOfferChrome(set_flag))
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local_reasons |= GCCC_ERROR_ALREADYOFFERED;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Done. Copy/return results.
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reasons != NULL)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *reasons = local_reasons;
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (local_reasons == 0);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL __stdcall LaunchGoogleChrome() {
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe_path;
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GetGoogleChromePath(&chrome_exe_path))
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedCOMInitializer com_initializer;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::CoInitializeSecurity(NULL, -1, NULL, NULL,
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             RPC_C_IMP_LEVEL_IDENTIFY, NULL,
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             EOAC_DYNAMIC_CLOAKING, NULL) != S_OK) {
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool impersonation_success = false;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsRunningElevated()) {
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wchar_t* curr_proc_sid;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) {
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD pid = 0;
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::GetWindowThreadProcessId(::GetShellWindow(), &pid);
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (pid <= 0) {
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::LocalFree(curr_proc_sid);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wchar_t* exp_proc_sid;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (GetUserIdForProcess(pid, &exp_proc_sid)) {
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (_wcsicmp(curr_proc_sid, exp_proc_sid) == 0) {
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ScopedHandle process_handle(
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ::OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          TRUE,
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          pid));
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (process_handle.IsValid()) {
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          HANDLE process_token = NULL;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          HANDLE user_token = NULL;
4811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          if (::OpenProcessToken(process_handle.Get(),
4821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 TOKEN_DUPLICATE | TOKEN_QUERY,
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 &process_token) &&
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              ::DuplicateTokenEx(process_token,
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 TOKEN_IMPERSONATE | TOKEN_QUERY |
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 NULL, SecurityImpersonation,
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 TokenPrimary, &user_token) &&
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (::ImpersonateLoggedOnUser(user_token) != 0)) {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            impersonation_success = true;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (user_token)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ::CloseHandle(user_token);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (process_token)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ::CloseHandle(process_token);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::LocalFree(exp_proc_sid);
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::LocalFree(curr_proc_sid);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!impersonation_success) {
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CommandLine chrome_command(chrome_exe_path);
5086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ret = false;
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IProcessLauncher> ipl;
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SUCCEEDED(ipl.CreateInstance(__uuidof(ProcessLauncherClass),
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   NULL,
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   CLSCTX_LOCAL_SERVER))) {
5146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (SUCCEEDED(ipl->LaunchCmdLine(
5156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            chrome_command.GetCommandLineString().c_str())))
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ret = true;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ipl.Release();
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Couldn't get Omaha's process launcher, Omaha may not be installed at
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // system level. Try just running Chrome instead.
5216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    ret = base::LaunchProcess(chrome_command.GetCommandLineString(),
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              base::LaunchOptions(),
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              NULL);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (impersonation_success)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::RevertToSelf();
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ret;
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL __stdcall LaunchGoogleChromeWithDimensions(int x,
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                int y,
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                int width,
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                int height,
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                bool in_background) {
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (in_background) {
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath chrome_exe_path;
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!GetGoogleChromePath(&chrome_exe_path))
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // When launching in the background, use WMI to ensure that chrome.exe is
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // is not our child process. This prevents it from pushing itself to
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // foreground.
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CommandLine chrome_command(chrome_exe_path);
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ScopedCOMInitializer com_initializer;
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!installer::WMIProcess::Launch(chrome_command.GetCommandLineString(),
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       NULL)) {
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // For some reason WMI failed. Try and launch the old fashioned way,
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // knowing that visual glitches will occur when the window pops up.
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!LaunchGoogleChrome())
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return false;
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!LaunchGoogleChrome())
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HWND hwnd_insert_after = in_background ? HWND_BOTTOM : NULL;
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD set_window_flags = in_background ? SWP_NOACTIVATE : SWP_NOZORDER;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (x == -1 && y == -1)
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    set_window_flags |= SWP_NOMOVE;
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (width == -1 && height == -1)
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    set_window_flags |= SWP_NOSIZE;
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetWindowPosParams enum_params = { x, y, width, height, set_window_flags,
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     hwnd_insert_after, false };
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Chrome may have been launched, but the window may not have appeared
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // yet. Wait for it to appear for 10 seconds, but exit if it takes longer
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than that.
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int ms_elapsed = 0;
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int timeout = 10000;
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool found_window = false;
5782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (ms_elapsed < timeout) {
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Enum all top-level windows looking for Chrome windows.
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::EnumWindows(ChromeWindowEnumProc, reinterpret_cast<LPARAM>(&enum_params));
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Give it five more seconds after finding the first window until we stop
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // shoving new windows into the background.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!found_window && enum_params.success) {
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      found_window = true;
5862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      timeout = ms_elapsed + 5000;
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Sleep(10);
5902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ms_elapsed += 10;
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return found_window;
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)BOOL __stdcall LaunchGoogleChromeInBackground() {
5972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return LaunchGoogleChromeWithDimensions(-1, -1, -1, -1, true);
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int __stdcall GoogleChromeDaysSinceLastRun() {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int days_since_last_run = std::numeric_limits<int>::max();
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsChromeInstalled(HKEY_LOCAL_MACHINE) ||
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IsChromeInstalled(HKEY_CURRENT_USER)) {
605cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    RegKey client_state(HKEY_CURRENT_USER,
606cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        kChromeRegClientStateKey,
607cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        KEY_QUERY_VALUE | KEY_WOW64_32KEY);
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (client_state.Valid()) {
609116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::string16 last_run;
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int64 last_run_value = 0;
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (client_state.ReadValue(google_update::kRegLastRunTimeField,
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 &last_run) == ERROR_SUCCESS &&
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::StringToInt64(last_run, &last_run_value)) {
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Time last_run_time = Time::FromInternalValue(last_run_value);
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        TimeDelta difference = Time::NowFromSystemTime() - last_run_time;
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // We can end up with negative numbers here, given changes in system
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // clock time or due to TimeDelta's int64 -> int truncation.
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int new_days_since_last_run = difference.InDays();
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (new_days_since_last_run >= 0 &&
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            new_days_since_last_run < days_since_last_run) {
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          days_since_last_run = new_days_since_last_run;
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (days_since_last_run == std::numeric_limits<int>::max()) {
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    days_since_last_run = -1;
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return days_since_last_run;
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code,
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    int shell_mode,
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    DWORD* error_code) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(error_code);
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!brand_code) {
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (error_code)
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error_code = REACTIVATE_ERROR_INVALID_INPUT;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return FALSE;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int days_since_last_run = GoogleChromeDaysSinceLastRun();
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (days_since_last_run >= 0 &&
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      days_since_last_run < kReactivationMinDaysDormant) {
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (error_code)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error_code = REACTIVATE_ERROR_NOTDORMANT;
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return FALSE;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only run the code below when this function is invoked from a standard,
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // non-elevated cmd shell.  This is because this section of code looks at
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // values in HKEY_CURRENT_USER, and we only want to look at the logged-in
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // user's HKCU, not the admin user's HKCU.
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (shell_mode == GCAPI_INVOKED_STANDARD_SHELL) {
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!IsChromeInstalled(HKEY_LOCAL_MACHINE) &&
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !IsChromeInstalled(HKEY_CURRENT_USER)) {
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (error_code)
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *error_code = REACTIVATE_ERROR_NOTINSTALLED;
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return FALSE;
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (HasBeenReactivated()) {
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (error_code)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED;
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return FALSE;
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciBOOL __stdcall ReactivateChrome(const wchar_t* brand_code,
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                int shell_mode,
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                DWORD* error_code) {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = FALSE;
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (CanOfferReactivation(brand_code,
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           shell_mode,
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           error_code)) {
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (SetReactivationBrandCode(brand_code, shell_mode)) {
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Currently set this as a best-effort thing. We return TRUE if
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // reactivation succeeded regardless of the experiment label result.
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetReactivationExperimentLabels(brand_code, shell_mode);
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result = TRUE;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (error_code)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *error_code = REACTIVATE_ERROR_REACTIVATION_FAILED;
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)BOOL __stdcall CanOfferRelaunch(const wchar_t** partner_brandcode_list,
6993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                int partner_brandcode_list_length,
7003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                int shell_mode,
7013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                DWORD* error_code) {
7023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DCHECK(error_code);
7033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (!partner_brandcode_list || partner_brandcode_list_length <= 0) {
7053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_INVALID_INPUT;
7073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // These conditions need to be satisfied for relaunch:
7113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // a) Chrome should be installed;
7123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (!IsChromeInstalled(HKEY_LOCAL_MACHINE) &&
7133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      (shell_mode != GCAPI_INVOKED_STANDARD_SHELL ||
7143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          !IsChromeInstalled(HKEY_CURRENT_USER))) {
7153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_NOTINSTALLED;
7173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // b) the installed brandcode should belong to that partner (in
7213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // brandcode_list);
722116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  base::string16 installed_brandcode;
7233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  bool valid_brandcode = false;
7243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (GoogleUpdateSettings::GetBrand(&installed_brandcode)) {
7253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    for (int i = 0; i < partner_brandcode_list_length; ++i) {
7263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      if (!_wcsicmp(installed_brandcode.c_str(), partner_brandcode_list[i])) {
7273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        valid_brandcode = true;
7283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        break;
7293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      }
7303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    }
7313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (!valid_brandcode) {
7343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_INVALID_PARTNER;
7363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // c) C1F ping should not have been sent;
7403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (IsC1FSent()) {
7413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_PINGS_SENT;
7433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // d) a minimum period (30 days) must have passed since Chrome was last used;
7473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  int days_since_last_run = GoogleChromeDaysSinceLastRun();
7483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (days_since_last_run >= 0 &&
7493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      days_since_last_run < kRelaunchMinDaysDormant) {
7503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_NOTDORMANT;
7523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // e) a minimum period (6 months) must have passed since the previous
7563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // relaunch offer for the current user;
7573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  RegKey key;
7583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DWORD min_relaunch_date;
759cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (key.Open(HKEY_CURRENT_USER,
760cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)               kChromeRegClientStateKey,
761cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)               KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
7623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      key.ReadValueDW(kRelaunchAllowedAfterValue,
7633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                      &min_relaunch_date) == ERROR_SUCCESS &&
7643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      FormatDateOffsetByMonths(0) < min_relaunch_date) {
7653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_ALREADY_RELAUNCHED;
7673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return TRUE;
7713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
7723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)BOOL __stdcall SetRelaunchOffered(const wchar_t** partner_brandcode_list,
7743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                  int partner_brandcode_list_length,
7753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                  const wchar_t* relaunch_brandcode,
7763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                  int shell_mode,
7773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                  DWORD* error_code) {
7783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (!CanOfferRelaunch(partner_brandcode_list, partner_brandcode_list_length,
7793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                        shell_mode, error_code))
7803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Store the relaunched brand code and the minimum date for relaunch (6 months
7833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // from now), and set the Omaha experiment label.
7843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  RegKey key;
785cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (key.Create(HKEY_CURRENT_USER,
786cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 kChromeRegClientStateKey,
787cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 KEY_SET_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS ||
7883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      key.WriteValue(kRelaunchBrandcodeValue,
7893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                     relaunch_brandcode) != ERROR_SUCCESS ||
7903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      key.WriteValue(kRelaunchAllowedAfterValue,
7913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                     FormatDateOffsetByMonths(6)) != ERROR_SUCCESS ||
7923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      !SetRelaunchExperimentLabels(relaunch_brandcode, shell_mode)) {
7933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (error_code)
7943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      *error_code = RELAUNCH_ERROR_RELAUNCH_FAILED;
7953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return FALSE;
7963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
7973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return TRUE;
7993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
800