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_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).
     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.
     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 
    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 
    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 
    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 
    221 void DisableAllAutoStartFeatures(const string16& profile_directory) {
    222   DisableForegroundStartAtLogin(profile_directory);
    223   DisableBackgroundStartAtLogin();
    224 }
    225 
    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 
    232 void DisableForegroundStartAtLogin(const string16& profile_directory) {
    233   SetWillLaunchAtLogin(
    234       base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE);
    235 }
    236 
    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 
    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