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/download/download_target_determiner.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/browser/profiles/profile_manager.h"
     28 #include "chrome/common/chrome_paths.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "components/pref_registry/pref_registry_syncable.h"
     31 #include "content/public/browser/browser_thread.h"
     32 #include "content/public/browser/download_manager.h"
     33 #include "content/public/browser/save_page_type.h"
     34 
     35 #if defined(OS_CHROMEOS)
     36 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
     37 #include "chrome/browser/chromeos/drive/file_system_util.h"
     38 #include "chrome/browser/chromeos/file_manager/path_util.h"
     39 #endif
     40 
     41 #if defined(OS_WIN)
     42 #include "chrome/browser/ui/pdf/adobe_reader_info_win.h"
     43 #endif
     44 
     45 using content::BrowserContext;
     46 using content::BrowserThread;
     47 using content::DownloadManager;
     48 
     49 namespace {
     50 
     51 // Consider downloads 'dangerous' if they go to the home directory on Linux and
     52 // to the desktop on any platform.
     53 bool DownloadPathIsDangerous(const base::FilePath& download_path) {
     54 #if defined(OS_LINUX)
     55   base::FilePath home_dir = base::GetHomeDir();
     56   if (download_path == home_dir) {
     57     return true;
     58   }
     59 #endif
     60 
     61 #if defined(OS_ANDROID)
     62   // Android does not have a desktop dir.
     63   return false;
     64 #else
     65   base::FilePath desktop_dir;
     66   if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_dir)) {
     67     NOTREACHED();
     68     return false;
     69   }
     70   return (download_path == desktop_dir);
     71 #endif
     72 }
     73 
     74 class DefaultDownloadDirectory {
     75  public:
     76   const base::FilePath& path() const { return path_; }
     77 
     78  private:
     79   friend struct base::DefaultLazyInstanceTraits<DefaultDownloadDirectory>;
     80 
     81   DefaultDownloadDirectory() {
     82     if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) {
     83       NOTREACHED();
     84     }
     85     if (DownloadPathIsDangerous(path_)) {
     86       // This is only useful on platforms that support
     87       // DIR_DEFAULT_DOWNLOADS_SAFE.
     88       if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path_)) {
     89         NOTREACHED();
     90       }
     91     }
     92   }
     93 
     94   base::FilePath path_;
     95 
     96   DISALLOW_COPY_AND_ASSIGN(DefaultDownloadDirectory);
     97 };
     98 
     99 base::LazyInstance<DefaultDownloadDirectory>
    100     g_default_download_directory = LAZY_INSTANCE_INITIALIZER;
    101 
    102 }  // namespace
    103 
    104 DownloadPrefs::DownloadPrefs(Profile* profile) : profile_(profile) {
    105   PrefService* prefs = profile->GetPrefs();
    106 
    107 #if defined(OS_CHROMEOS)
    108   // On Chrome OS, the default download directory is different for each profile.
    109   // If the profile-unaware default path (from GetDefaultDownloadDirectory())
    110   // is set (this happens during the initial preference registration in static
    111   // RegisterProfilePrefs()), alter by GetDefaultDownloadDirectoryForProfile().
    112   // file_manager::util::MigratePathFromOldFormat will do this.
    113   const char* path_pref[] = {
    114       prefs::kSaveFileDefaultDirectory,
    115       prefs::kDownloadDefaultDirectory
    116   };
    117   for (size_t i = 0; i < arraysize(path_pref); ++i) {
    118     const base::FilePath current = prefs->GetFilePath(path_pref[i]);
    119     base::FilePath migrated;
    120     if (!current.empty() &&
    121         file_manager::util::MigratePathFromOldFormat(
    122             profile_, current, &migrated)) {
    123       prefs->SetFilePath(path_pref[i], migrated);
    124     }
    125   }
    126 
    127   // Ensure that the default download directory exists.
    128   BrowserThread::PostTask(
    129       BrowserThread::FILE, FROM_HERE,
    130       base::Bind(base::IgnoreResult(&base::CreateDirectory),
    131                  GetDefaultDownloadDirectoryForProfile()));
    132 #endif  // defined(OS_CHROMEOS)
    133 
    134 #if defined(OS_WIN)
    135   should_open_pdf_in_adobe_reader_ =
    136       prefs->GetBoolean(prefs::kOpenPdfDownloadInAdobeReader);
    137 #endif
    138 
    139   // If the download path is dangerous we forcefully reset it. But if we do
    140   // so we set a flag to make sure we only do it once, to avoid fighting
    141   // the user if he really wants it on an unsafe place such as the desktop.
    142   if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
    143     base::FilePath current_download_dir = prefs->GetFilePath(
    144         prefs::kDownloadDefaultDirectory);
    145     if (DownloadPathIsDangerous(current_download_dir)) {
    146       prefs->SetFilePath(prefs::kDownloadDefaultDirectory,
    147                          GetDefaultDownloadDirectoryForProfile());
    148     }
    149     prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
    150   }
    151 
    152   prompt_for_download_.Init(prefs::kPromptForDownload, prefs);
    153   download_path_.Init(prefs::kDownloadDefaultDirectory, prefs);
    154   save_file_path_.Init(prefs::kSaveFileDefaultDirectory, prefs);
    155   save_file_type_.Init(prefs::kSaveFileType, prefs);
    156 
    157   // We store any file extension that should be opened automatically at
    158   // download completion in this pref.
    159   std::string extensions_to_open =
    160       prefs->GetString(prefs::kDownloadExtensionsToOpen);
    161   std::vector<std::string> extensions;
    162   base::SplitString(extensions_to_open, ':', &extensions);
    163 
    164   for (size_t i = 0; i < extensions.size(); ++i) {
    165 #if defined(OS_POSIX)
    166     base::FilePath path(extensions[i]);
    167 #elif defined(OS_WIN)
    168     base::FilePath path(base::UTF8ToWide(extensions[i]));
    169 #endif
    170     if (!extensions[i].empty() &&
    171         download_util::GetFileDangerLevel(path) == download_util::NOT_DANGEROUS)
    172       auto_open_.insert(path.value());
    173   }
    174 }
    175 
    176 DownloadPrefs::~DownloadPrefs() {}
    177 
    178 // static
    179 void DownloadPrefs::RegisterProfilePrefs(
    180     user_prefs::PrefRegistrySyncable* registry) {
    181   registry->RegisterBooleanPref(
    182       prefs::kPromptForDownload,
    183       false,
    184       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
    185   registry->RegisterStringPref(
    186       prefs::kDownloadExtensionsToOpen,
    187       std::string(),
    188       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    189   registry->RegisterBooleanPref(
    190       prefs::kDownloadDirUpgraded,
    191       false,
    192       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    193   registry->RegisterIntegerPref(
    194       prefs::kSaveFileType,
    195       content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
    196       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    197 
    198   const base::FilePath& default_download_path = GetDefaultDownloadDirectory();
    199   registry->RegisterFilePathPref(
    200       prefs::kDownloadDefaultDirectory,
    201       default_download_path,
    202       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    203   registry->RegisterFilePathPref(
    204       prefs::kSaveFileDefaultDirectory,
    205       default_download_path,
    206       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    207 #if defined(OS_WIN)
    208   registry->RegisterBooleanPref(
    209       prefs::kOpenPdfDownloadInAdobeReader,
    210       false,
    211       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    212 #endif
    213 }
    214 
    215 base::FilePath DownloadPrefs::GetDefaultDownloadDirectoryForProfile() const {
    216 #if defined(OS_CHROMEOS)
    217   return file_manager::util::GetDownloadsFolderForProfile(profile_);
    218 #else
    219   return GetDefaultDownloadDirectory();
    220 #endif
    221 }
    222 
    223 // static
    224 const base::FilePath& DownloadPrefs::GetDefaultDownloadDirectory() {
    225   return g_default_download_directory.Get().path();
    226 }
    227 
    228 // static
    229 DownloadPrefs* DownloadPrefs::FromDownloadManager(
    230     DownloadManager* download_manager) {
    231   ChromeDownloadManagerDelegate* delegate =
    232       static_cast<ChromeDownloadManagerDelegate*>(
    233           download_manager->GetDelegate());
    234   return delegate->download_prefs();
    235 }
    236 
    237 // static
    238 DownloadPrefs* DownloadPrefs::FromBrowserContext(
    239     content::BrowserContext* context) {
    240   return FromDownloadManager(BrowserContext::GetDownloadManager(context));
    241 }
    242 
    243 base::FilePath DownloadPrefs::DownloadPath() const {
    244 #if defined(OS_CHROMEOS)
    245   // If the download path is under /drive, and DriveIntegrationService isn't
    246   // available (which it isn't for incognito mode, for instance), use the
    247   // default download directory (/Downloads).
    248   if (drive::util::IsUnderDriveMountPoint(*download_path_)) {
    249     drive::DriveIntegrationService* integration_service =
    250         drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
    251     if (!integration_service || !integration_service->is_enabled())
    252       return GetDefaultDownloadDirectoryForProfile();
    253   }
    254 #endif
    255   return *download_path_;
    256 }
    257 
    258 void DownloadPrefs::SetDownloadPath(const base::FilePath& path) {
    259   download_path_.SetValue(path);
    260   SetSaveFilePath(path);
    261 }
    262 
    263 base::FilePath DownloadPrefs::SaveFilePath() const {
    264   return *save_file_path_;
    265 }
    266 
    267 void DownloadPrefs::SetSaveFilePath(const base::FilePath& path) {
    268   save_file_path_.SetValue(path);
    269 }
    270 
    271 void DownloadPrefs::SetSaveFileType(int type) {
    272   save_file_type_.SetValue(type);
    273 }
    274 
    275 bool DownloadPrefs::PromptForDownload() const {
    276   // If the DownloadDirectory policy is set, then |prompt_for_download_| should
    277   // always be false.
    278   DCHECK(!download_path_.IsManaged() || !prompt_for_download_.GetValue());
    279   return *prompt_for_download_;
    280 }
    281 
    282 bool DownloadPrefs::IsDownloadPathManaged() const {
    283   return download_path_.IsManaged();
    284 }
    285 
    286 bool DownloadPrefs::IsAutoOpenUsed() const {
    287 #if defined(OS_WIN)
    288   if (ShouldOpenPdfInAdobeReader())
    289     return true;
    290 #endif
    291   return !auto_open_.empty();
    292 }
    293 
    294 bool DownloadPrefs::IsAutoOpenEnabledBasedOnExtension(
    295     const base::FilePath& path) const {
    296   base::FilePath::StringType extension = path.Extension();
    297   if (extension.empty())
    298     return false;
    299   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    300   extension.erase(0, 1);
    301 #if defined(OS_WIN)
    302   if (extension == FILE_PATH_LITERAL("pdf") &&
    303       DownloadTargetDeterminer::IsAdobeReaderUpToDate() &&
    304       ShouldOpenPdfInAdobeReader())
    305     return true;
    306 #endif
    307   return auto_open_.find(extension) != auto_open_.end();
    308 }
    309 
    310 bool DownloadPrefs::EnableAutoOpenBasedOnExtension(
    311     const base::FilePath& file_name) {
    312   base::FilePath::StringType extension = file_name.Extension();
    313   if (extension.empty())
    314     return false;
    315   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    316   extension.erase(0, 1);
    317 
    318   auto_open_.insert(extension);
    319   SaveAutoOpenState();
    320   return true;
    321 }
    322 
    323 void DownloadPrefs::DisableAutoOpenBasedOnExtension(
    324     const base::FilePath& file_name) {
    325   base::FilePath::StringType extension = file_name.Extension();
    326   if (extension.empty())
    327     return;
    328   DCHECK(extension[0] == base::FilePath::kExtensionSeparator);
    329   extension.erase(0, 1);
    330   auto_open_.erase(extension);
    331   SaveAutoOpenState();
    332 }
    333 
    334 #if defined(OS_WIN)
    335 void DownloadPrefs::SetShouldOpenPdfInAdobeReader(bool should_open) {
    336   if (should_open_pdf_in_adobe_reader_ == should_open)
    337     return;
    338   should_open_pdf_in_adobe_reader_ = should_open;
    339   profile_->GetPrefs()->SetBoolean(prefs::kOpenPdfDownloadInAdobeReader,
    340                                    should_open);
    341 }
    342 
    343 bool DownloadPrefs::ShouldOpenPdfInAdobeReader() const {
    344   return should_open_pdf_in_adobe_reader_;
    345 }
    346 #endif
    347 
    348 void DownloadPrefs::ResetAutoOpen() {
    349 #if defined(OS_WIN)
    350   SetShouldOpenPdfInAdobeReader(false);
    351 #endif
    352   auto_open_.clear();
    353   SaveAutoOpenState();
    354 }
    355 
    356 void DownloadPrefs::SaveAutoOpenState() {
    357   std::string extensions;
    358   for (AutoOpenSet::iterator it = auto_open_.begin();
    359        it != auto_open_.end(); ++it) {
    360 #if defined(OS_POSIX)
    361     std::string this_extension = *it;
    362 #elif defined(OS_WIN)
    363     // TODO(phajdan.jr): Why we're using Sys conversion here, but not in ctor?
    364     std::string this_extension = base::SysWideToUTF8(*it);
    365 #endif
    366     extensions += this_extension + ":";
    367   }
    368   if (!extensions.empty())
    369     extensions.erase(extensions.size() - 1);
    370 
    371   profile_->GetPrefs()->SetString(prefs::kDownloadExtensionsToOpen, extensions);
    372 }
    373 
    374 bool DownloadPrefs::AutoOpenCompareFunctor::operator()(
    375     const base::FilePath::StringType& a,
    376     const base::FilePath::StringType& b) const {
    377   return base::FilePath::CompareLessIgnoreCase(a, b);
    378 }
    379