// 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.
#include "chrome/installer/util/auto_launch_util.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/installer/util/util_constants.h"
#include "crypto/sha2.h"
using base::ASCIIToUTF16;
using base::ASCIIToWide;
namespace auto_launch_util {
// The prefix of the Chrome Auto-launch key under the Run key.
const wchar_t kAutolaunchKeyValue[] = L"GoogleChromeAutoLaunch";
// We use one Run key with flags specifying which feature we want to start up.
// When we change our Run key we need to specify what we want to do with each
// flag. This lists the possible actions we can take with the flags.
enum FlagSetting {
FLAG_DISABLE, // Disable the flag.
FLAG_ENABLE, // Enable the flag.
FLAG_PRESERVE, // Preserve the value that the flag has currently.
};
// A helper function that takes a |profile_directory| and builds a registry key
// name to use when deciding where to read/write the auto-launch value
// to/from. It takes into account the name of the profile (so that different
// installations of Chrome don't conflict, and so the in the future different
// profiles can be auto-launched (or not) separately).
base::string16 ProfileToKeyName(const base::string16& profile_directory) {
base::FilePath path;
const bool success = PathService::Get(chrome::DIR_USER_DATA, &path);
DCHECK(success);
path = path.Append(profile_directory);
std::string input(path.AsUTF8Unsafe());
uint8 hash[16];
crypto::SHA256HashString(input, hash, sizeof(hash));
std::string hash_string = base::HexEncode(hash, sizeof(hash));
return base::string16(kAutolaunchKeyValue) + ASCIIToWide("_") +
ASCIIToWide(hash_string);
}
// Returns whether the Chrome executable specified in |application_path| is set
// to auto-launch at computer startup with a given |command_line_switch|.
// NOTE: |application_path| is optional and should be blank in most cases (as
// it will default to the application path of the current executable).
// |profile_directory| is the name of the directory (leaf, not the full path)
// that contains the profile that should be opened at computer startup.
// |command_line_switch| is the switch we are optionally interested in and, if
// not blank, must be present for the function to return true. If blank, it acts
// like a wildcard.
bool WillLaunchAtLoginWithSwitch(const base::FilePath& application_path,
const base::string16& profile_directory,
const std::string& command_line_switch) {
base::string16 key_name(ProfileToKeyName(profile_directory));
base::string16 autolaunch;
if (!base::win::ReadCommandFromAutoRun(
HKEY_CURRENT_USER, key_name, &autolaunch)) {
return false;
}
base::FilePath chrome_exe(application_path);
if (chrome_exe.empty()) {
if (!PathService::Get(base::DIR_EXE, &chrome_exe)) {
NOTREACHED();
return false;
}
}
chrome_exe = chrome_exe.Append(installer::kChromeExe);
if (autolaunch.find(chrome_exe.value()) == base::string16::npos)
return false;
return command_line_switch.empty() ||
autolaunch.find(ASCIIToUTF16(command_line_switch)) !=
base::string16::npos;
}
bool AutoStartRequested(const base::string16& profile_directory,
bool window_requested,
const base::FilePath& application_path) {
if (window_requested) {
return WillLaunchAtLoginWithSwitch(application_path,
profile_directory,
switches::kAutoLaunchAtStartup);
} else {
// Background mode isn't profile specific, but is attached to the Run key
// for the Default profile.
return WillLaunchAtLoginWithSwitch(application_path,
ASCIIToUTF16(chrome::kInitialProfile),
switches::kNoStartupWindow);
}
}
bool CheckAndRemoveDeprecatedBackgroundModeSwitch() {
// For backwards compatibility we need to provide a migration path from the
// previously used key "chromium" that the BackgroundMode used to set, as it
// is incompatible with the new key (can't have two Run keys with
// conflicting switches).
base::string16 chromium = ASCIIToUTF16("chromium");
base::string16 value;
if (base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, chromium, &value)) {
if (value.find(ASCIIToUTF16(switches::kNoStartupWindow)) !=
base::string16::npos) {
base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, chromium);
return true;
}
}
return false;
}
void SetWillLaunchAtLogin(const base::FilePath& application_path,
const base::string16& profile_directory,
FlagSetting foreground_mode,
FlagSetting background_mode) {
if (CheckAndRemoveDeprecatedBackgroundModeSwitch()) {
// We've found the deprecated switch, we must migrate it (unless background
// mode is being turned off).
if (profile_directory == ASCIIToUTF16(chrome::kInitialProfile) &&
background_mode == FLAG_PRESERVE) {
// Preserve in this case also covers the deprecated value, so we must
// explicitly turn the flag on and the rest will be taken care of below.
background_mode = FLAG_ENABLE;
} else {
// When we add support for multiple profiles for foreground mode we need
// to think about where to store the background mode switch. I think we
// need to store it with the Default profile (call SetWillLaunchAtLogin
// again specifying the Default profile), but concerns were raised in
// review.
NOTREACHED();
}
}
base::string16 key_name(ProfileToKeyName(profile_directory));
// Check which feature should be enabled.
bool in_foreground =
foreground_mode == FLAG_ENABLE ||
(foreground_mode == FLAG_PRESERVE &&
WillLaunchAtLoginWithSwitch(application_path,
profile_directory,
switches::kAutoLaunchAtStartup));
bool in_background =
background_mode == FLAG_ENABLE ||
(background_mode == FLAG_PRESERVE &&
WillLaunchAtLoginWithSwitch(application_path,
profile_directory,
switches::kNoStartupWindow));
// TODO(finnur): Convert this into a shortcut, instead of using the Run key.
if (in_foreground || in_background) {
base::FilePath path(application_path);
if (path.empty()) {
if (!PathService::Get(base::DIR_EXE, &path)) {
NOTREACHED();
return;
}
}
base::string16 cmd_line = ASCIIToUTF16("\"");
cmd_line += path.value();
cmd_line += ASCIIToUTF16("\\");
cmd_line += installer::kChromeExe;
cmd_line += ASCIIToUTF16("\"");
if (in_background) {
cmd_line += ASCIIToUTF16(" --");
cmd_line += ASCIIToUTF16(switches::kNoStartupWindow);
}
if (in_foreground) {
cmd_line += ASCIIToUTF16(" --");
cmd_line += ASCIIToUTF16(switches::kAutoLaunchAtStartup);
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kUserDataDir)) {
cmd_line += ASCIIToUTF16(" --");
cmd_line += ASCIIToUTF16(switches::kUserDataDir);
cmd_line += ASCIIToUTF16("=\"");
cmd_line +=
command_line.GetSwitchValuePath(switches::kUserDataDir).value();
cmd_line += ASCIIToUTF16("\"");
}
cmd_line += ASCIIToUTF16(" --");
cmd_line += ASCIIToUTF16(switches::kProfileDirectory);
cmd_line += ASCIIToUTF16("=\"");
cmd_line += profile_directory;
cmd_line += ASCIIToUTF16("\"");
}
base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, key_name, cmd_line);
} else {
base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, key_name);
}
}
void DisableAllAutoStartFeatures(const base::string16& profile_directory) {
DisableForegroundStartAtLogin(profile_directory);
DisableBackgroundStartAtLogin();
}
void EnableForegroundStartAtLogin(const base::string16& profile_directory,
const base::FilePath& application_path) {
SetWillLaunchAtLogin(
application_path, profile_directory, FLAG_ENABLE, FLAG_PRESERVE);
}
void DisableForegroundStartAtLogin(const base::string16& profile_directory) {
SetWillLaunchAtLogin(
base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE);
}
void EnableBackgroundStartAtLogin() {
// Background mode isn't profile specific, but we specify the Default profile
// just to have a unique Run key to attach it to. FilePath is blank because
// this function is not called from the installer (see comments for
// EnableAutoStartAtLogin).
SetWillLaunchAtLogin(base::FilePath(),
ASCIIToUTF16(chrome::kInitialProfile),
FLAG_PRESERVE,
FLAG_ENABLE);
}
void DisableBackgroundStartAtLogin() {
SetWillLaunchAtLogin(base::FilePath(),
ASCIIToUTF16(chrome::kInitialProfile),
FLAG_PRESERVE,
FLAG_DISABLE);
}
} // namespace auto_launch_util