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