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
21using base::ASCIIToUTF16;
22using base::ASCIIToWide;
23
24namespace auto_launch_util {
25
26// The prefix of the Chrome Auto-launch key under the Run key.
27const 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.
32enum 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).
43base::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.
66bool 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
93bool 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
109bool 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
127void 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
210void DisableAllAutoStartFeatures(const base::string16& profile_directory) {
211  DisableForegroundStartAtLogin(profile_directory);
212  DisableBackgroundStartAtLogin();
213}
214
215void 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
221void DisableForegroundStartAtLogin(const base::string16& profile_directory) {
222  SetWillLaunchAtLogin(
223      base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE);
224}
225
226void 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
237void DisableBackgroundStartAtLogin() {
238  SetWillLaunchAtLogin(base::FilePath(),
239                       ASCIIToUTF16(chrome::kInitialProfile),
240                       FLAG_PRESERVE,
241                       FLAG_DISABLE);
242}
243
244}  // namespace auto_launch_util
245