// 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