1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/installer/util/auto_launch_util.h" 6 7 #include "base/command_line.h" 8 #include "base/files/file_path.h" 9 #include "base/logging.h" 10 #include "base/path_service.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/win/win_util.h" 14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_paths.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "chrome/common/chrome_version_info.h" 18 #include "chrome/installer/util/util_constants.h" 19 #include "crypto/sha2.h" 20 21 using base::ASCIIToUTF16; 22 using base::ASCIIToWide; 23 24 namespace auto_launch_util { 25 26 // The prefix of the Chrome Auto-launch key under the Run key. 27 const wchar_t kAutolaunchKeyValue[] = L"GoogleChromeAutoLaunch"; 28 29 // We use one Run key with flags specifying which feature we want to start up. 30 // When we change our Run key we need to specify what we want to do with each 31 // flag. This lists the possible actions we can take with the flags. 32 enum FlagSetting { 33 FLAG_DISABLE, // Disable the flag. 34 FLAG_ENABLE, // Enable the flag. 35 FLAG_PRESERVE, // Preserve the value that the flag has currently. 36 }; 37 38 // A helper function that takes a |profile_directory| and builds a registry key 39 // name to use when deciding where to read/write the auto-launch value 40 // to/from. It takes into account the name of the profile (so that different 41 // installations of Chrome don't conflict, and so the in the future different 42 // profiles can be auto-launched (or not) separately). 43 base::string16 ProfileToKeyName(const base::string16& profile_directory) { 44 base::FilePath path; 45 const bool success = PathService::Get(chrome::DIR_USER_DATA, &path); 46 DCHECK(success); 47 path = path.Append(profile_directory); 48 49 std::string input(path.AsUTF8Unsafe()); 50 uint8 hash[16]; 51 crypto::SHA256HashString(input, hash, sizeof(hash)); 52 std::string hash_string = base::HexEncode(hash, sizeof(hash)); 53 return base::string16(kAutolaunchKeyValue) + ASCIIToWide("_") + 54 ASCIIToWide(hash_string); 55 } 56 57 // Returns whether the Chrome executable specified in |application_path| is set 58 // to auto-launch at computer startup with a given |command_line_switch|. 59 // NOTE: |application_path| is optional and should be blank in most cases (as 60 // it will default to the application path of the current executable). 61 // |profile_directory| is the name of the directory (leaf, not the full path) 62 // that contains the profile that should be opened at computer startup. 63 // |command_line_switch| is the switch we are optionally interested in and, if 64 // not blank, must be present for the function to return true. If blank, it acts 65 // like a wildcard. 66 bool WillLaunchAtLoginWithSwitch(const base::FilePath& application_path, 67 const base::string16& profile_directory, 68 const std::string& command_line_switch) { 69 base::string16 key_name(ProfileToKeyName(profile_directory)); 70 base::string16 autolaunch; 71 if (!base::win::ReadCommandFromAutoRun( 72 HKEY_CURRENT_USER, key_name, &autolaunch)) { 73 return false; 74 } 75 76 base::FilePath chrome_exe(application_path); 77 if (chrome_exe.empty()) { 78 if (!PathService::Get(base::DIR_EXE, &chrome_exe)) { 79 NOTREACHED(); 80 return false; 81 } 82 } 83 chrome_exe = chrome_exe.Append(installer::kChromeExe); 84 85 if (autolaunch.find(chrome_exe.value()) == base::string16::npos) 86 return false; 87 88 return command_line_switch.empty() || 89 autolaunch.find(ASCIIToUTF16(command_line_switch)) != 90 base::string16::npos; 91 } 92 93 bool AutoStartRequested(const base::string16& profile_directory, 94 bool window_requested, 95 const base::FilePath& application_path) { 96 if (window_requested) { 97 return WillLaunchAtLoginWithSwitch(application_path, 98 profile_directory, 99 switches::kAutoLaunchAtStartup); 100 } else { 101 // Background mode isn't profile specific, but is attached to the Run key 102 // for the Default profile. 103 return WillLaunchAtLoginWithSwitch(application_path, 104 ASCIIToUTF16(chrome::kInitialProfile), 105 switches::kNoStartupWindow); 106 } 107 } 108 109 bool CheckAndRemoveDeprecatedBackgroundModeSwitch() { 110 // For backwards compatibility we need to provide a migration path from the 111 // previously used key "chromium" that the BackgroundMode used to set, as it 112 // is incompatible with the new key (can't have two Run keys with 113 // conflicting switches). 114 base::string16 chromium = ASCIIToUTF16("chromium"); 115 base::string16 value; 116 if (base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, chromium, &value)) { 117 if (value.find(ASCIIToUTF16(switches::kNoStartupWindow)) != 118 base::string16::npos) { 119 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, chromium); 120 return true; 121 } 122 } 123 124 return false; 125 } 126 127 void SetWillLaunchAtLogin(const base::FilePath& application_path, 128 const base::string16& profile_directory, 129 FlagSetting foreground_mode, 130 FlagSetting background_mode) { 131 if (CheckAndRemoveDeprecatedBackgroundModeSwitch()) { 132 // We've found the deprecated switch, we must migrate it (unless background 133 // mode is being turned off). 134 if (profile_directory == ASCIIToUTF16(chrome::kInitialProfile) && 135 background_mode == FLAG_PRESERVE) { 136 // Preserve in this case also covers the deprecated value, so we must 137 // explicitly turn the flag on and the rest will be taken care of below. 138 background_mode = FLAG_ENABLE; 139 } else { 140 // When we add support for multiple profiles for foreground mode we need 141 // to think about where to store the background mode switch. I think we 142 // need to store it with the Default profile (call SetWillLaunchAtLogin 143 // again specifying the Default profile), but concerns were raised in 144 // review. 145 NOTREACHED(); 146 } 147 } 148 base::string16 key_name(ProfileToKeyName(profile_directory)); 149 150 // Check which feature should be enabled. 151 bool in_foreground = 152 foreground_mode == FLAG_ENABLE || 153 (foreground_mode == FLAG_PRESERVE && 154 WillLaunchAtLoginWithSwitch(application_path, 155 profile_directory, 156 switches::kAutoLaunchAtStartup)); 157 bool in_background = 158 background_mode == FLAG_ENABLE || 159 (background_mode == FLAG_PRESERVE && 160 WillLaunchAtLoginWithSwitch(application_path, 161 profile_directory, 162 switches::kNoStartupWindow)); 163 164 // TODO(finnur): Convert this into a shortcut, instead of using the Run key. 165 if (in_foreground || in_background) { 166 base::FilePath path(application_path); 167 if (path.empty()) { 168 if (!PathService::Get(base::DIR_EXE, &path)) { 169 NOTREACHED(); 170 return; 171 } 172 } 173 base::string16 cmd_line = ASCIIToUTF16("\""); 174 cmd_line += path.value(); 175 cmd_line += ASCIIToUTF16("\\"); 176 cmd_line += installer::kChromeExe; 177 cmd_line += ASCIIToUTF16("\""); 178 179 if (in_background) { 180 cmd_line += ASCIIToUTF16(" --"); 181 cmd_line += ASCIIToUTF16(switches::kNoStartupWindow); 182 } 183 if (in_foreground) { 184 cmd_line += ASCIIToUTF16(" --"); 185 cmd_line += ASCIIToUTF16(switches::kAutoLaunchAtStartup); 186 187 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 188 if (command_line.HasSwitch(switches::kUserDataDir)) { 189 cmd_line += ASCIIToUTF16(" --"); 190 cmd_line += ASCIIToUTF16(switches::kUserDataDir); 191 cmd_line += ASCIIToUTF16("=\""); 192 cmd_line += 193 command_line.GetSwitchValuePath(switches::kUserDataDir).value(); 194 cmd_line += ASCIIToUTF16("\""); 195 } 196 197 cmd_line += ASCIIToUTF16(" --"); 198 cmd_line += ASCIIToUTF16(switches::kProfileDirectory); 199 cmd_line += ASCIIToUTF16("=\""); 200 cmd_line += profile_directory; 201 cmd_line += ASCIIToUTF16("\""); 202 } 203 204 base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, key_name, cmd_line); 205 } else { 206 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, key_name); 207 } 208 } 209 210 void DisableAllAutoStartFeatures(const base::string16& profile_directory) { 211 DisableForegroundStartAtLogin(profile_directory); 212 DisableBackgroundStartAtLogin(); 213 } 214 215 void EnableForegroundStartAtLogin(const base::string16& profile_directory, 216 const base::FilePath& application_path) { 217 SetWillLaunchAtLogin( 218 application_path, profile_directory, FLAG_ENABLE, FLAG_PRESERVE); 219 } 220 221 void DisableForegroundStartAtLogin(const base::string16& profile_directory) { 222 SetWillLaunchAtLogin( 223 base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE); 224 } 225 226 void EnableBackgroundStartAtLogin() { 227 // Background mode isn't profile specific, but we specify the Default profile 228 // just to have a unique Run key to attach it to. FilePath is blank because 229 // this function is not called from the installer (see comments for 230 // EnableAutoStartAtLogin). 231 SetWillLaunchAtLogin(base::FilePath(), 232 ASCIIToUTF16(chrome::kInitialProfile), 233 FLAG_PRESERVE, 234 FLAG_ENABLE); 235 } 236 237 void DisableBackgroundStartAtLogin() { 238 SetWillLaunchAtLogin(base::FilePath(), 239 ASCIIToUTF16(chrome::kInitialProfile), 240 FLAG_PRESERVE, 241 FLAG_DISABLE); 242 } 243 244 } // namespace auto_launch_util 245