download_prefs.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
39using content::BrowserContext;
40using content::BrowserThread;
41using content::DownloadManager;
42
43namespace {
44
45// Consider downloads 'dangerous' if they go to the home directory on Linux and
46// to the desktop on any platform.
47bool 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
68class 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
93static base::LazyInstance<DefaultDownloadDirectory>
94    g_default_download_directory = LAZY_INSTANCE_INITIALIZER;
95
96}  // namespace
97
98DownloadPrefs::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
138DownloadPrefs::~DownloadPrefs() {}
139
140// static
141void 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
181const base::FilePath& DownloadPrefs::GetDefaultDownloadDirectory() {
182  return g_default_download_directory.Get().path();
183}
184
185// static
186DownloadPrefs* 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
195DownloadPrefs* DownloadPrefs::FromBrowserContext(
196    content::BrowserContext* context) {
197  return FromDownloadManager(BrowserContext::GetDownloadManager(context));
198}
199
200base::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
215void DownloadPrefs::SetDownloadPath(const base::FilePath& path) {
216  download_path_.SetValue(path);
217  SetSaveFilePath(path);
218}
219
220base::FilePath DownloadPrefs::SaveFilePath() const {
221  return *save_file_path_;
222}
223
224void DownloadPrefs::SetSaveFilePath(const base::FilePath& path) {
225  save_file_path_.SetValue(path);
226}
227
228void DownloadPrefs::SetSaveFileType(int type) {
229  save_file_type_.SetValue(type);
230}
231
232bool 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
239bool DownloadPrefs::IsDownloadPathManaged() const {
240  return download_path_.IsManaged();
241}
242
243bool DownloadPrefs::IsAutoOpenUsed() const {
244  return !auto_open_.empty();
245}
246
247bool 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
257bool 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
270void 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
281void DownloadPrefs::ResetAutoOpen() {
282  auto_open_.clear();
283  SaveAutoOpenState();
284}
285
286void 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
304bool 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