Home | History | Annotate | Download | only in download
      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/browser/download/download_prefs.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/file_util.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/logging.h"
     15 #include "base/path_service.h"
     16 #include "base/prefs/pref_service.h"
     17 #include "base/strings/string_split.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/sys_string_conversions.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "chrome/browser/download/chrome_download_manager_delegate.h"
     22 #include "chrome/browser/download/download_extensions.h"
     23 #include "chrome/browser/download/download_service.h"
     24 #include "chrome/browser/download/download_service_factory.h"
     25 #include "chrome/browser/profiles/profile.h"
     26 #include "chrome/browser/profiles/profile_manager.h"
     27 #include "chrome/common/chrome_paths.h"
     28 #include "chrome/common/pref_names.h"
     29 #include "components/user_prefs/pref_registry_syncable.h"
     30 #include "content/public/browser/browser_thread.h"
     31 #include "content/public/browser/download_manager.h"
     32 #include "content/public/browser/save_page_type.h"
     33 
     34 #if defined(OS_CHROMEOS)
     35 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
     36 #include "chrome/browser/chromeos/drive/file_system_util.h"
     37 #endif
     38 
     39 using content::BrowserContext;
     40 using content::BrowserThread;
     41 using content::DownloadManager;
     42 
     43 namespace {
     44 
     45 // Consider downloads 'dangerous' if they go to the home directory on Linux and
     46 // to the desktop on any platform.
     47 bool DownloadPathIsDangerous(const base::FilePath& download_path) {
     48 #if defined(OS_LINUX)
     49   base::FilePath home_dir = base::GetHomeDir();
     50   if (download_path == home_dir) {
     51     return true;
     52   }
     53 #endif
     54 
     55 #if defined(OS_ANDROID)
     56   // Android does not have a desktop dir.
     57   return false;
     58 #else
     59   base::FilePath desktop_dir;
     60   if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_dir)) {
     61     NOTREACHED();
     62     return false;
     63   }
     64   return (download_path == desktop_dir);
     65 #endif
     66 }
     67 
     68 class DefaultDownloadDirectory {
     69  public:
     70   const base::FilePath& path() const { return path_; }
     71 
     72  private:
     73   friend struct base::DefaultLazyInstanceTraits<DefaultDownloadDirectory>;
     74 
     75   DefaultDownloadDirectory() {
     76     if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) {
     77       NOTREACHED();
     78     }
     79     if (DownloadPathIsDangerous(path_)) {
     80       // This is only useful on platforms that support
     81       // DIR_DEFAULT_DOWNLOADS_SAFE.
     82       if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path_)) {
     83         NOTREACHED();
     84       }
     85     }
     86   }
     87 
     88   base::FilePath path_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(DefaultDownloadDirectory);
     91 };
     92 
     93 static base::LazyInstance<DefaultDownloadDirectory>
     94     g_default_download_directory = LAZY_INSTANCE_INITIALIZER;
     95 
     96 }  // namespace
     97 
     98 DownloadPrefs::DownloadPrefs(Profile* profile) : profile_(profile) {
     99   PrefService* prefs = profile->GetPrefs();
    100 
    101   // If the download path is dangerous we forcefully reset it. But if we do
    102   // so we set a flag to make sure we only do it once, to avoid fighting
    103   // the user if he really wants it on an unsafe place such as the desktop.
    104   if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
    105     base::FilePath current_download_dir = prefs->GetFilePath(
    106         prefs::kDownloadDefaultDirectory);
    107     if (DownloadPathIsDangerous(current_download_dir)) {
    108       prefs->SetFilePath(prefs::kDownloadDefaultDirectory,
    109                          GetDefaultDownloadDirectory());
    110     }
    111     prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
    112   }
    113 
    114   prompt_for_download_.Init(prefs::kPromptForDownload, prefs);
    115   download_path_.Init(prefs::kDownloadDefaultDirectory, prefs);
    116   save_file_path_.Init(prefs::kSaveFileDefaultDirectory, prefs);
    117   save_file_type_.Init(prefs::kSaveFileType, prefs);
    118 
    119   // We store any file extension that should be opened automatically at
    120   // download completion in this pref.
    121   std::string extensions_to_open =
    122       prefs->GetString(prefs::kDownloadExtensionsToOpen);
    123   std::vector<std::string> extensions;
    124   base::SplitString(extensions_to_open, ':', &extensions);
    125 
    126   for (size_t i = 0; i < extensions.size(); ++i) {
    127 #if defined(OS_POSIX)
    128     base::FilePath path(extensions[i]);
    129 #elif defined(OS_WIN)
    130     base::FilePath path(UTF8ToWide(extensions[i]));
    131 #endif
    132     if (!extensions[i].empty() &&
    133         download_util::GetFileDangerLevel(path) == download_util::NOT_DANGEROUS)
    134       auto_open_.insert(path.value());
    135   }
    136 }
    137 
    138 DownloadPrefs::~DownloadPrefs() {}
    139 
    140 // static
    141 void DownloadPrefs::RegisterProfilePrefs(
    142     user_prefs::PrefRegistrySyncable* registry) {
    143   registry->RegisterBooleanPref(
    144       prefs::kPromptForDownload,
    145       false,
    146       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
    147   registry->RegisterStringPref(
    148       prefs::kDownloadExtensionsToOpen,
    149       std::string(),
    150       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    151   registry->RegisterBooleanPref(
    152       prefs::kDownloadDirUpgraded,
    153       false,
    154       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    155   registry->RegisterIntegerPref(
    156       prefs::kSaveFileType,
    157       content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
    158       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    159 
    160   // The default download path is userprofile\download.
    161   const base::FilePath& default_download_path = GetDefaultDownloadDirectory();
    162   registry->RegisterFilePathPref(
    163       prefs::kDownloadDefaultDirectory,
    164       default_download_path,
    165       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    166   registry->RegisterFilePathPref(
    167       prefs::kSaveFileDefaultDirectory,
    168       default_download_path,
    169       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    170 
    171 #if defined(OS_CHROMEOS)
    172   // Ensure that the download directory specified in the preferences exists.
    173   BrowserThread::PostTask(
    174       BrowserThread::FILE, FROM_HERE,
    175       base::Bind(base::IgnoreResult(&base::CreateDirectory),
    176                  default_download_path));
    177 #endif  // defined(OS_CHROMEOS)
    178 }
    179 
    180 // static
    181 const base::FilePath& DownloadPrefs::GetDefaultDownloadDirectory() {
    182   return g_default_download_directory.Get().path();
    183 }
    184 
    185 // static
    186 DownloadPrefs* DownloadPrefs::FromDownloadManager(
    187     DownloadManager* download_manager) {
    188   ChromeDownloadManagerDelegate* delegate =
    189       static_cast<ChromeDownloadManagerDelegate*>(
    190           download_manager->GetDelegate());
    191   return delegate->download_prefs();
    192 }
    193 
    194 // static
    195 DownloadPrefs* DownloadPrefs::FromBrowserContext(
    196     content::BrowserContext* context) {
    197   return FromDownloadManager(BrowserContext::GetDownloadManager(context));
    198 }
    199 
    200 base::FilePath DownloadPrefs::DownloadPath() const {
    201 #if defined(OS_CHROMEOS)
    202   // If the download path is under /drive, and DriveIntegrationService isn't
    203   // available (which it isn't for incognito mode, for instance), use the
    204   // default download directory (/Downloads).
    205   if (drive::util::IsUnderDriveMountPoint(*download_path_)) {
    206     drive::DriveIntegrationService* integration_service =
    207         drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
    208     if (!integration_service || !integration_service->is_enabled())
    209       return GetDefaultDownloadDirectory();
    210   }
    211 #endif
    212   return *download_path_;
    213 }
    214 
    215 void DownloadPrefs::SetDownloadPath(const base::FilePath& path) {
    216   download_path_.SetValue(path);
    217   SetSaveFilePath(path);
    218 }
    219 
    220 base::FilePath DownloadPrefs::SaveFilePath() const {
    221   return *save_file_path_;
    222 }
    223 
    224 void DownloadPrefs::SetSaveFilePath(const base::FilePath& path) {
    225   save_file_path_.SetValue(path);
    226 }
    227 
    228 void DownloadPrefs::SetSaveFileType(int type) {
    229   save_file_type_.SetValue(type);
    230 }
    231 
    232 bool DownloadPrefs::PromptForDownload() const {
    233   // If the DownloadDirectory policy is set, then |prompt_for_download_| should
    234   // always be false.
    235   DCHECK(!download_path_.IsManaged() || !prompt_for_download_.GetValue());
    236   return *prompt_for_download_;
    237 }
    238 
    239 bool DownloadPrefs::IsDownloadPathManaged() const {
    240   return download_path_.IsManaged();
    241 }
    242 
    243 bool DownloadPrefs::IsAutoOpenUsed() const {
    244   return !auto_open_.empty();
    245 }
    246 
    247 bool DownloadPrefs::IsAutoOpenEnabledBasedOnExtension(
    248     const base::FilePath& path) const {
    249   base::FilePath::StringType extension = path.Extension();
    250   if (extension.empty())
    251     return false;
    252   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    253   extension.erase(0, 1);
    254   return auto_open_.find(extension) != auto_open_.end();
    255 }
    256 
    257 bool DownloadPrefs::EnableAutoOpenBasedOnExtension(
    258     const base::FilePath& file_name) {
    259   base::FilePath::StringType extension = file_name.Extension();
    260   if (extension.empty())
    261     return false;
    262   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    263   extension.erase(0, 1);
    264 
    265   auto_open_.insert(extension);
    266   SaveAutoOpenState();
    267   return true;
    268 }
    269 
    270 void DownloadPrefs::DisableAutoOpenBasedOnExtension(
    271     const base::FilePath& file_name) {
    272   base::FilePath::StringType extension = file_name.Extension();
    273   if (extension.empty())
    274     return;
    275   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    276   extension.erase(0, 1);
    277   auto_open_.erase(extension);
    278   SaveAutoOpenState();
    279 }
    280 
    281 void DownloadPrefs::ResetAutoOpen() {
    282   auto_open_.clear();
    283   SaveAutoOpenState();
    284 }
    285 
    286 void DownloadPrefs::SaveAutoOpenState() {
    287   std::string extensions;
    288   for (AutoOpenSet::iterator it = auto_open_.begin();
    289        it != auto_open_.end(); ++it) {
    290 #if defined(OS_POSIX)
    291     std::string this_extension = *it;
    292 #elif defined(OS_WIN)
    293     // TODO(phajdan.jr): Why we're using Sys conversion here, but not in ctor?
    294     std::string this_extension = base::SysWideToUTF8(*it);
    295 #endif
    296     extensions += this_extension + ":";
    297   }
    298   if (!extensions.empty())
    299     extensions.erase(extensions.size() - 1);
    300 
    301   profile_->GetPrefs()->SetString(prefs::kDownloadExtensionsToOpen, extensions);
    302 }
    303 
    304 bool DownloadPrefs::AutoOpenCompareFunctor::operator()(
    305     const base::FilePath::StringType& a,
    306     const base::FilePath::StringType& b) const {
    307   return base::FilePath::CompareLessIgnoreCase(a, b);
    308 }
    309