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).
ProfileToKeyName(const string16 & profile_directory)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.
WillLaunchAtLoginWithSwitch(const base::FilePath & application_path,const string16 & profile_directory,const std::string & command_line_switch)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
AutoStartRequested(const string16 & profile_directory,bool window_requested,const base::FilePath & application_path)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
CheckAndRemoveDeprecatedBackgroundModeSwitch()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
SetWillLaunchAtLogin(const base::FilePath & application_path,const string16 & profile_directory,FlagSetting foreground_mode,FlagSetting background_mode)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
DisableAllAutoStartFeatures(const string16 & profile_directory)221 void DisableAllAutoStartFeatures(const string16& profile_directory) {
222 DisableForegroundStartAtLogin(profile_directory);
223 DisableBackgroundStartAtLogin();
224 }
225
EnableForegroundStartAtLogin(const string16 & profile_directory,const base::FilePath & application_path)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
DisableForegroundStartAtLogin(const string16 & profile_directory)232 void DisableForegroundStartAtLogin(const string16& profile_directory) {
233 SetWillLaunchAtLogin(
234 base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE);
235 }
236
EnableBackgroundStartAtLogin()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
DisableBackgroundStartAtLogin()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