auto_launch_util.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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    path = product.GetUserDataPath();
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 base::string16(kAutolaunchKeyValue) + ASCIIToWide("_") +
65         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.
77bool WillLaunchAtLoginWithSwitch(const base::FilePath& application_path,
78                                 const base::string16& profile_directory,
79                                 const std::string& command_line_switch) {
80  base::string16 key_name(ProfileToKeyName(profile_directory));
81  base::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()) == base::string16::npos)
97    return false;
98
99  return command_line_switch.empty() ||
100         autolaunch.find(ASCIIToUTF16(command_line_switch)) !=
101             base::string16::npos;
102}
103
104bool AutoStartRequested(const base::string16& profile_directory,
105                        bool window_requested,
106                        const base::FilePath& application_path) {
107  if (window_requested) {
108    return WillLaunchAtLoginWithSwitch(application_path,
109                                       profile_directory,
110                                       switches::kAutoLaunchAtStartup);
111  } else {
112    // Background mode isn't profile specific, but is attached to the Run key
113    // for the Default profile.
114    return WillLaunchAtLoginWithSwitch(application_path,
115                                       ASCIIToUTF16(chrome::kInitialProfile),
116                                       switches::kNoStartupWindow);
117  }
118}
119
120bool CheckAndRemoveDeprecatedBackgroundModeSwitch() {
121  // For backwards compatibility we need to provide a migration path from the
122  // previously used key "chromium" that the BackgroundMode used to set, as it
123  // is incompatible with the new key (can't have two Run keys with
124  // conflicting switches).
125  base::string16 chromium = ASCIIToUTF16("chromium");
126  base::string16 value;
127  if (base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, chromium, &value)) {
128    if (value.find(ASCIIToUTF16(switches::kNoStartupWindow)) !=
129        base::string16::npos) {
130      base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, chromium);
131      return true;
132    }
133  }
134
135  return false;
136}
137
138void SetWillLaunchAtLogin(const base::FilePath& application_path,
139                          const base::string16& profile_directory,
140                          FlagSetting foreground_mode,
141                          FlagSetting background_mode) {
142  if (CheckAndRemoveDeprecatedBackgroundModeSwitch()) {
143    // We've found the deprecated switch, we must migrate it (unless background
144    // mode is being turned off).
145    if (profile_directory == ASCIIToUTF16(chrome::kInitialProfile) &&
146        background_mode == FLAG_PRESERVE) {
147      // Preserve in this case also covers the deprecated value, so we must
148      // explicitly turn the flag on and the rest will be taken care of below.
149      background_mode = FLAG_ENABLE;
150    } else {
151      // When we add support for multiple profiles for foreground mode we need
152      // to think about where to store the background mode switch. I think we
153      // need to store it with the Default profile (call SetWillLaunchAtLogin
154      // again specifying the Default profile), but concerns were raised in
155      // review.
156      NOTREACHED();
157    }
158  }
159  base::string16 key_name(ProfileToKeyName(profile_directory));
160
161  // Check which feature should be enabled.
162  bool in_foreground =
163      foreground_mode == FLAG_ENABLE ||
164      (foreground_mode == FLAG_PRESERVE &&
165          WillLaunchAtLoginWithSwitch(application_path,
166                                      profile_directory,
167                                      switches::kAutoLaunchAtStartup));
168  bool in_background =
169      background_mode == FLAG_ENABLE ||
170      (background_mode == FLAG_PRESERVE &&
171          WillLaunchAtLoginWithSwitch(application_path,
172                                      profile_directory,
173                                      switches::kNoStartupWindow));
174
175  // TODO(finnur): Convert this into a shortcut, instead of using the Run key.
176  if (in_foreground || in_background) {
177    base::FilePath path(application_path);
178    if (path.empty()) {
179      if (!PathService::Get(base::DIR_EXE, &path)) {
180        NOTREACHED();
181        return;
182      }
183    }
184    base::string16 cmd_line = ASCIIToUTF16("\"");
185    cmd_line += path.value();
186    cmd_line += ASCIIToUTF16("\\");
187    cmd_line += installer::kChromeExe;
188    cmd_line += ASCIIToUTF16("\"");
189
190    if (in_background) {
191      cmd_line += ASCIIToUTF16(" --");
192      cmd_line += ASCIIToUTF16(switches::kNoStartupWindow);
193    }
194    if (in_foreground) {
195      cmd_line += ASCIIToUTF16(" --");
196      cmd_line += ASCIIToUTF16(switches::kAutoLaunchAtStartup);
197
198      const CommandLine& command_line = *CommandLine::ForCurrentProcess();
199      if (command_line.HasSwitch(switches::kUserDataDir)) {
200        cmd_line += ASCIIToUTF16(" --");
201        cmd_line += ASCIIToUTF16(switches::kUserDataDir);
202        cmd_line += ASCIIToUTF16("=\"");
203        cmd_line +=
204            command_line.GetSwitchValuePath(switches::kUserDataDir).value();
205        cmd_line += ASCIIToUTF16("\"");
206      }
207
208      cmd_line += ASCIIToUTF16(" --");
209      cmd_line += ASCIIToUTF16(switches::kProfileDirectory);
210      cmd_line += ASCIIToUTF16("=\"");
211      cmd_line += profile_directory;
212      cmd_line += ASCIIToUTF16("\"");
213    }
214
215    base::win::AddCommandToAutoRun(
216        HKEY_CURRENT_USER, key_name, cmd_line);
217  } else {
218    base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, key_name);
219  }
220}
221
222void DisableAllAutoStartFeatures(const base::string16& profile_directory) {
223  DisableForegroundStartAtLogin(profile_directory);
224  DisableBackgroundStartAtLogin();
225}
226
227void EnableForegroundStartAtLogin(const base::string16& profile_directory,
228                                  const base::FilePath& application_path) {
229  SetWillLaunchAtLogin(
230      application_path, profile_directory, FLAG_ENABLE, FLAG_PRESERVE);
231}
232
233void DisableForegroundStartAtLogin(const base::string16& profile_directory) {
234  SetWillLaunchAtLogin(
235      base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE);
236}
237
238void EnableBackgroundStartAtLogin() {
239  // Background mode isn't profile specific, but we specify the Default profile
240  // just to have a unique Run key to attach it to. FilePath is blank because
241  // this function is not called from the installer (see comments for
242  // EnableAutoStartAtLogin).
243  SetWillLaunchAtLogin(base::FilePath(),
244                       ASCIIToUTF16(chrome::kInitialProfile),
245                       FLAG_PRESERVE,
246                       FLAG_ENABLE);
247}
248
249void DisableBackgroundStartAtLogin() {
250  SetWillLaunchAtLogin(base::FilePath(),
251                       ASCIIToUTF16(chrome::kInitialProfile),
252                       FLAG_PRESERVE,
253                       FLAG_DISABLE);
254}
255
256}  // namespace auto_launch_util
257