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