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/files/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