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