1// Copyright 2013 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_target_determiner.h"
6
7#include "base/prefs/pref_service.h"
8#include "base/rand_util.h"
9#include "base/strings/stringprintf.h"
10#include "base/time/time.h"
11#include "chrome/browser/download/chrome_download_manager_delegate.h"
12#include "chrome/browser/download/download_crx_util.h"
13#include "chrome/browser/download/download_extensions.h"
14#include "chrome/browser/download/download_prefs.h"
15#include "chrome/browser/extensions/webstore_installer.h"
16#include "chrome/browser/history/history_service.h"
17#include "chrome/browser/history/history_service_factory.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/pref_names.h"
20#include "content/public/browser/browser_context.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/download_interrupt_reasons.h"
23#include "extensions/common/constants.h"
24#include "extensions/common/feature_switch.h"
25#include "grit/generated_resources.h"
26#include "net/base/mime_util.h"
27#include "net/base/net_util.h"
28#include "ui/base/l10n/l10n_util.h"
29
30#if defined(ENABLE_PLUGINS)
31#include "chrome/browser/plugins/plugin_prefs.h"
32#include "content/public/browser/plugin_service.h"
33#include "content/public/common/webplugininfo.h"
34#endif
35
36using content::BrowserThread;
37using content::DownloadItem;
38
39namespace {
40
41const base::FilePath::CharType kCrdownloadSuffix[] =
42    FILE_PATH_LITERAL(".crdownload");
43
44// Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a
45// single bool. A host is considered visited before if prior visible visits were
46// found in history and the first such visit was earlier than the most recent
47// midnight.
48void VisitCountsToVisitedBefore(
49    const base::Callback<void(bool)>& callback,
50    HistoryService::Handle unused_handle,
51    bool found_visits,
52    int count,
53    base::Time first_visit) {
54  callback.Run(
55      found_visits && count > 0 &&
56      (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
57}
58
59} // namespace
60
61DownloadTargetInfo::DownloadTargetInfo()
62    : is_filetype_handled_securely(false) {}
63
64DownloadTargetInfo::~DownloadTargetInfo() {}
65
66DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
67}
68
69DownloadTargetDeterminer::DownloadTargetDeterminer(
70    DownloadItem* download,
71    const base::FilePath& initial_virtual_path,
72    DownloadPrefs* download_prefs,
73    DownloadTargetDeterminerDelegate* delegate,
74    const CompletionCallback& callback)
75    : next_state_(STATE_GENERATE_TARGET_PATH),
76      should_prompt_(false),
77      should_notify_extensions_(false),
78      create_target_directory_(false),
79      conflict_action_(DownloadPathReservationTracker::OVERWRITE),
80      danger_type_(download->GetDangerType()),
81      virtual_path_(initial_virtual_path),
82      is_filetype_handled_securely_(false),
83      download_(download),
84      is_resumption_(download_->GetLastReason() !=
85                         content::DOWNLOAD_INTERRUPT_REASON_NONE &&
86                     !initial_virtual_path.empty()),
87      download_prefs_(download_prefs),
88      delegate_(delegate),
89      completion_callback_(callback),
90      weak_ptr_factory_(this) {
91  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92  DCHECK(download_);
93  DCHECK(delegate);
94  download_->AddObserver(this);
95
96  DoLoop();
97}
98
99DownloadTargetDeterminer::~DownloadTargetDeterminer() {
100  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
101  DCHECK(download_);
102  DCHECK(completion_callback_.is_null());
103  download_->RemoveObserver(this);
104}
105
106void DownloadTargetDeterminer::DoLoop() {
107  Result result = CONTINUE;
108  do {
109    State current_state = next_state_;
110    next_state_ = STATE_NONE;
111
112    switch (current_state) {
113      case STATE_GENERATE_TARGET_PATH:
114        result = DoGenerateTargetPath();
115        break;
116      case STATE_NOTIFY_EXTENSIONS:
117        result = DoNotifyExtensions();
118        break;
119      case STATE_RESERVE_VIRTUAL_PATH:
120        result = DoReserveVirtualPath();
121        break;
122      case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
123        result = DoPromptUserForDownloadPath();
124        break;
125      case STATE_DETERMINE_LOCAL_PATH:
126        result = DoDetermineLocalPath();
127        break;
128      case STATE_DETERMINE_MIME_TYPE:
129        result = DoDetermineMimeType();
130        break;
131      case STATE_DETERMINE_IF_HANDLED_BY_BROWSER:
132        result = DoDetermineIfHandledByBrowser();
133        break;
134      case STATE_CHECK_DOWNLOAD_URL:
135        result = DoCheckDownloadUrl();
136        break;
137      case STATE_DETERMINE_INTERMEDIATE_PATH:
138        result = DoDetermineIntermediatePath();
139        break;
140      case STATE_CHECK_VISITED_REFERRER_BEFORE:
141        result = DoCheckVisitedReferrerBefore();
142        break;
143      case STATE_NONE:
144        NOTREACHED();
145        return;
146    }
147  } while (result == CONTINUE);
148  // Note that if a callback completes synchronously, the handler will still
149  // return QUIT_DOLOOP. In this case, an inner DoLoop() may complete the target
150  // determination and delete |this|.
151
152  if (result == COMPLETE)
153    ScheduleCallbackAndDeleteSelf();
154}
155
156DownloadTargetDeterminer::Result
157    DownloadTargetDeterminer::DoGenerateTargetPath() {
158  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159  DCHECK(local_path_.empty());
160  DCHECK(!should_prompt_);
161  DCHECK(!should_notify_extensions_);
162  DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_);
163  bool is_forced_path = !download_->GetForcedFilePath().empty();
164
165  next_state_ = STATE_NOTIFY_EXTENSIONS;
166
167  if (!virtual_path_.empty() && HasPromptedForPath() && !is_forced_path) {
168    // The download is being resumed and the user has already been prompted for
169    // a path. Assume that it's okay to overwrite the file if there's a conflict
170    // and reuse the selection.
171    should_prompt_ = ShouldPromptForDownload(virtual_path_);
172  } else if (!is_forced_path) {
173    // If we don't have a forced path, we should construct a path for the
174    // download. Forced paths are only specified for programmatic downloads
175    // (WebStore, Drag&Drop). Treat the path as a virtual path. We will
176    // eventually determine whether this is a local path and if not, figure out
177    // a local path.
178    std::string default_filename(
179        l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
180    base::FilePath generated_filename = net::GenerateFileName(
181        download_->GetURL(),
182        download_->GetContentDisposition(),
183        GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset),
184        download_->GetSuggestedFilename(),
185        download_->GetMimeType(),
186        default_filename);
187    should_prompt_ = ShouldPromptForDownload(generated_filename);
188    base::FilePath target_directory;
189    if (should_prompt_) {
190      DCHECK(!download_prefs_->IsDownloadPathManaged());
191      // If the user is going to be prompted and the user has been prompted
192      // before, then always prefer the last directory that the user selected.
193      target_directory = download_prefs_->SaveFilePath();
194    } else {
195      target_directory = download_prefs_->DownloadPath();
196    }
197    virtual_path_ = target_directory.Append(generated_filename);
198    conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
199    should_notify_extensions_ = true;
200  } else {
201    virtual_path_ = download_->GetForcedFilePath();
202    // If this is a resumed download which was previously interrupted due to an
203    // issue with the forced path, the user is still not prompted. If the path
204    // supplied to a programmatic download is invalid, then the caller needs to
205    // intervene.
206  }
207  DCHECK(virtual_path_.IsAbsolute());
208  DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe();
209
210  // If the download is DOA, don't bother going any further. This would be the
211  // case for a download that failed to initialize (e.g. the initial temporary
212  // file couldn't be created because both the downloads directory and the
213  // temporary directory are unwriteable).
214  //
215  // A virtual path is determined for DOA downloads for display purposes. This
216  // is why this check is performed here instead of at the start.
217  if (download_->GetState() != DownloadItem::IN_PROGRESS)
218    return COMPLETE;
219  return CONTINUE;
220}
221
222DownloadTargetDeterminer::Result
223    DownloadTargetDeterminer::DoNotifyExtensions() {
224  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225  DCHECK(!virtual_path_.empty());
226
227  next_state_ = STATE_RESERVE_VIRTUAL_PATH;
228
229  if (!should_notify_extensions_)
230    return CONTINUE;
231
232  delegate_->NotifyExtensions(download_, virtual_path_,
233      base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
234                 weak_ptr_factory_.GetWeakPtr()));
235  return QUIT_DOLOOP;
236}
237
238void DownloadTargetDeterminer::NotifyExtensionsDone(
239    const base::FilePath& suggested_path,
240    DownloadPathReservationTracker::FilenameConflictAction conflict_action) {
241  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242  DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe();
243
244  if (!suggested_path.empty()) {
245    // If an extension overrides the filename, then the target directory will be
246    // forced to download_prefs_->DownloadPath() since extensions cannot place
247    // downloaded files anywhere except there. This prevents subdirectories from
248    // accumulating: if an extension is allowed to say that a file should go in
249    // last_download_path/music/foo.mp3, then last_download_path will accumulate
250    // the subdirectory /music/ so that the next download may end up in
251    // Downloads/music/music/music/bar.mp3.
252    base::FilePath new_path(download_prefs_->DownloadPath().Append(
253        suggested_path).NormalizePathSeparators());
254    // Do not pass a mime type to GenerateSafeFileName so that it does not force
255    // the filename to have an extension if the (Chrome) extension does not
256    // suggest it.
257    net::GenerateSafeFileName(std::string(), false, &new_path);
258    virtual_path_ = new_path;
259    create_target_directory_ = true;
260    conflict_action_ = conflict_action;
261  }
262
263  DoLoop();
264}
265
266DownloadTargetDeterminer::Result
267    DownloadTargetDeterminer::DoReserveVirtualPath() {
268  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269  DCHECK(!virtual_path_.empty());
270
271  next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH;
272
273  delegate_->ReserveVirtualPath(
274      download_, virtual_path_, create_target_directory_, conflict_action_,
275      base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone,
276                 weak_ptr_factory_.GetWeakPtr()));
277  return QUIT_DOLOOP;
278}
279
280void DownloadTargetDeterminer::ReserveVirtualPathDone(
281    const base::FilePath& path, bool verified) {
282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283  DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
284            << " Verified:" << verified;
285  should_prompt_ = (should_prompt_ || !verified);
286  virtual_path_ = path;
287  DoLoop();
288}
289
290DownloadTargetDeterminer::Result
291    DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
292  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
293  DCHECK(!virtual_path_.empty());
294
295  next_state_ = STATE_DETERMINE_LOCAL_PATH;
296
297  if (should_prompt_) {
298    delegate_->PromptUserForDownloadPath(
299        download_,
300        virtual_path_,
301        base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
302                   weak_ptr_factory_.GetWeakPtr()));
303    return QUIT_DOLOOP;
304  }
305  return CONTINUE;
306}
307
308void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
309    const base::FilePath& virtual_path) {
310  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311  DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe();
312  if (virtual_path.empty()) {
313    CancelOnFailureAndDeleteSelf();
314    return;
315  }
316  virtual_path_ = virtual_path;
317  download_prefs_->SetSaveFilePath(virtual_path_.DirName());
318  DoLoop();
319}
320
321DownloadTargetDeterminer::Result
322    DownloadTargetDeterminer::DoDetermineLocalPath() {
323  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324  DCHECK(!virtual_path_.empty());
325  DCHECK(local_path_.empty());
326
327  next_state_ = STATE_DETERMINE_MIME_TYPE;
328
329  delegate_->DetermineLocalPath(
330      download_,
331      virtual_path_,
332      base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone,
333                 weak_ptr_factory_.GetWeakPtr()));
334  return QUIT_DOLOOP;
335}
336
337void DownloadTargetDeterminer::DetermineLocalPathDone(
338    const base::FilePath& local_path) {
339  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
340  DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
341  if (local_path.empty()) {
342    // Path subsitution failed.
343    CancelOnFailureAndDeleteSelf();
344    return;
345  }
346  local_path_ = local_path;
347  DoLoop();
348}
349
350DownloadTargetDeterminer::Result
351    DownloadTargetDeterminer::DoDetermineMimeType() {
352  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353  DCHECK(!virtual_path_.empty());
354  DCHECK(!local_path_.empty());
355  DCHECK(mime_type_.empty());
356
357  next_state_ = STATE_DETERMINE_IF_HANDLED_BY_BROWSER;
358
359  if (virtual_path_ == local_path_) {
360    delegate_->GetFileMimeType(
361        local_path_,
362        base::Bind(&DownloadTargetDeterminer::DetermineMimeTypeDone,
363                   weak_ptr_factory_.GetWeakPtr()));
364    return QUIT_DOLOOP;
365  }
366  return CONTINUE;
367}
368
369void DownloadTargetDeterminer::DetermineMimeTypeDone(
370    const std::string& mime_type) {
371  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372  DVLOG(20) << "MIME type: " << mime_type;
373  mime_type_ = mime_type;
374  DoLoop();
375}
376
377#if defined(ENABLE_PLUGINS)
378// The code below is used by DoDetermineIfHandledByBrowser to determine if the
379// file type is handled by a sandboxed plugin.
380namespace {
381
382typedef std::vector<content::WebPluginInfo> PluginVector;
383
384// Returns true if there is a plugin in |plugins| that is sandboxed and enabled
385// for |profile|.
386bool IsSafePluginAvailableForProfile(scoped_ptr<PluginVector> plugins,
387                                     Profile* profile) {
388  using content::WebPluginInfo;
389  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
390
391  if (plugins->size() == 0)
392    return false;
393
394  scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile);
395  if (!plugin_prefs)
396    return false;
397
398  for (PluginVector::iterator plugin = plugins->begin();
399       plugin != plugins->end(); ++plugin) {
400    if (plugin_prefs->IsPluginEnabled(*plugin) &&
401        (plugin->type == WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS ||
402         plugin->type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS))
403      return true;
404  }
405  return false;
406}
407
408// Returns a callback that determines if a sandboxed plugin is available to
409// handle |mime_type| for a specific profile. The returned callback must be
410// invoked on the UI thread, while this function should be called on the IO
411// thread.
412base::Callback<bool(Profile*)> GetSafePluginChecker(
413    const GURL& url,
414    const std::string& mime_type) {
415  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
416  DCHECK(!mime_type.empty());
417
418  scoped_ptr<PluginVector> plugins(new PluginVector);
419  content::PluginService* plugin_service =
420      content::PluginService::GetInstance();
421  if (plugin_service)
422    plugin_service->GetPluginInfoArray(
423        url, mime_type, false, plugins.get(), NULL);
424  return base::Bind(&IsSafePluginAvailableForProfile, base::Passed(&plugins));
425}
426
427} // namespace
428#endif  // ENABLE_PLUGINS
429
430DownloadTargetDeterminer::Result
431    DownloadTargetDeterminer::DoDetermineIfHandledByBrowser() {
432  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433  DCHECK(!virtual_path_.empty());
434  DCHECK(!local_path_.empty());
435  DCHECK(!is_filetype_handled_securely_);
436
437  next_state_ = STATE_CHECK_DOWNLOAD_URL;
438
439  if (mime_type_.empty())
440    return CONTINUE;
441
442  if (net::IsSupportedMimeType(mime_type_)) {
443    is_filetype_handled_securely_ = true;
444    return CONTINUE;
445  }
446
447#if defined(ENABLE_PLUGINS)
448  BrowserThread::PostTaskAndReplyWithResult(
449      BrowserThread::IO,
450      FROM_HERE,
451      base::Bind(&GetSafePluginChecker,
452                 net::FilePathToFileURL(local_path_), mime_type_),
453      base::Bind(&DownloadTargetDeterminer::DetermineIfHandledByBrowserDone,
454                 weak_ptr_factory_.GetWeakPtr()));
455  return QUIT_DOLOOP;
456#else
457  return CONTINUE;
458#endif
459}
460
461void DownloadTargetDeterminer::DetermineIfHandledByBrowserDone(
462    const base::Callback<bool(Profile*)>& callback) {
463  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
464  is_filetype_handled_securely_ = callback.Run(GetProfile());
465  DVLOG(20) << "Is file type handled securely: "
466            << is_filetype_handled_securely_;
467  DoLoop();
468}
469
470DownloadTargetDeterminer::Result
471    DownloadTargetDeterminer::DoCheckDownloadUrl() {
472  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
473  DCHECK(!virtual_path_.empty());
474  next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE;
475  delegate_->CheckDownloadUrl(
476      download_,
477      virtual_path_,
478      base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone,
479                 weak_ptr_factory_.GetWeakPtr()));
480  return QUIT_DOLOOP;
481}
482
483void DownloadTargetDeterminer::CheckDownloadUrlDone(
484    content::DownloadDangerType danger_type) {
485  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
486  DVLOG(20) << "URL Check Result:" << danger_type;
487  danger_type_ = danger_type;
488  DoLoop();
489}
490
491DownloadTargetDeterminer::Result
492    DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() {
493  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
494
495  next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH;
496
497  // Checking if there are prior visits to the referrer is only necessary if the
498  // danger level of the download depends on the file type. This excludes cases
499  // where the download has already been deemed dangerous, or where the user is
500  // going to be prompted or where this is a programmatic download.
501  if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
502    return CONTINUE;
503
504  // Assume that:
505  // IsDangerousFile(VISITED_REFERRER) => IsDangerousFile(NO_VISITS_...)
506  // I.e. having visited a referrer only lowers a file's danger level.
507  if (IsDangerousFile(NO_VISITS_TO_REFERRER)) {
508    // Only need to ping the history DB if the download would be considered safe
509    // if there are prior visits and is considered dangerous otherwise.
510    if (!IsDangerousFile(VISITED_REFERRER)) {
511      // HistoryServiceFactory redirects incognito profiles to on-record
512      // profiles.  There's no history for on-record profiles in unit_tests.
513      HistoryService* history_service = HistoryServiceFactory::GetForProfile(
514          GetProfile(), Profile::EXPLICIT_ACCESS);
515
516      if (history_service && download_->GetReferrerUrl().is_valid()) {
517        history_service->GetVisibleVisitCountToHost(
518            download_->GetReferrerUrl(), &history_consumer_,
519            base::Bind(&VisitCountsToVisitedBefore, base::Bind(
520                &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone,
521                weak_ptr_factory_.GetWeakPtr())));
522        return QUIT_DOLOOP;
523      }
524    }
525
526    // If the danger level doesn't depend on having visited the refererrer URL
527    // or if original profile doesn't have a HistoryService or the referrer url
528    // is invalid, then assume the referrer has not been visited before.
529    danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
530  }
531  return CONTINUE;
532}
533
534void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
535    bool visited_referrer_before) {
536  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
537  if (IsDangerousFile(
538          visited_referrer_before ? VISITED_REFERRER : NO_VISITS_TO_REFERRER))
539    danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
540  DoLoop();
541}
542
543DownloadTargetDeterminer::Result
544    DownloadTargetDeterminer::DoDetermineIntermediatePath() {
545  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
546  DCHECK(!virtual_path_.empty());
547  DCHECK(!local_path_.empty());
548  DCHECK(intermediate_path_.empty());
549  DCHECK(!virtual_path_.MatchesExtension(kCrdownloadSuffix));
550  DCHECK(!local_path_.MatchesExtension(kCrdownloadSuffix));
551
552  next_state_ = STATE_NONE;
553
554  // Note that the intermediate filename is always uniquified (i.e. if a file by
555  // the same name exists, it is never overwritten). Therefore the code below
556  // does not attempt to find a name that doesn't conflict with an existing
557  // file.
558
559  // If the actual target of the download is a virtual path, then the local path
560  // is considered to point to a temporary path. A separate intermediate path is
561  // unnecessary since the local path already serves that purpose.
562  if (virtual_path_.BaseName() != local_path_.BaseName()) {
563    intermediate_path_ = local_path_;
564    return COMPLETE;
565  }
566
567  // If the download has a forced path and is safe, then just use the
568  // target path. In practice the temporary download file that was created prior
569  // to download filename determination is already named
570  // download_->GetForcedFilePath().
571  if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
572      !download_->GetForcedFilePath().empty()) {
573    DCHECK_EQ(download_->GetForcedFilePath().value(), local_path_.value());
574    intermediate_path_ = local_path_;
575    return COMPLETE;
576  }
577
578  // Other safe downloads get a .crdownload suffix for their intermediate name.
579  if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
580    intermediate_path_ = GetCrDownloadPath(local_path_);
581    return COMPLETE;
582  }
583
584  // If this is a resumed download, then re-use the existing intermediate path
585  // if one is available. A resumed download shouldn't cause a non-dangerous
586  // download to be considered dangerous upon resumption. Therefore the
587  // intermediate file should already be in the correct form.
588  if (is_resumption_ && !download_->GetFullPath().empty() &&
589      local_path_.DirName() == download_->GetFullPath().DirName()) {
590    DCHECK_NE(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
591              download_->GetDangerType());
592    DCHECK_EQ(kCrdownloadSuffix, download_->GetFullPath().Extension());
593    intermediate_path_ = download_->GetFullPath();
594    return COMPLETE;
595  }
596
597  // Dangerous downloads receive a random intermediate name that looks like:
598  // 'Unconfirmed <random>.crdownload'.
599  const base::FilePath::CharType kUnconfirmedFormatSuffix[] =
600      FILE_PATH_LITERAL(" %d.crdownload");
601  // Range of the <random> uniquifier.
602  const int kUnconfirmedUniquifierRange = 1000000;
603#if defined(OS_WIN)
604  base::string16 unconfirmed_format =
605      l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
606#else
607  std::string unconfirmed_format =
608      l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
609#endif
610  unconfirmed_format.append(kUnconfirmedFormatSuffix);
611
612  base::FilePath::StringType file_name = base::StringPrintf(
613      unconfirmed_format.c_str(),
614      base::RandInt(0, kUnconfirmedUniquifierRange));
615  intermediate_path_ = local_path_.DirName().Append(file_name);
616  return COMPLETE;
617}
618
619void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
620  DCHECK(download_);
621  DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
622            << " Local:" << local_path_.AsUTF8Unsafe()
623            << " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
624            << " Should prompt:" << should_prompt_
625            << " Danger type:" << danger_type_;
626  scoped_ptr<DownloadTargetInfo> target_info(new DownloadTargetInfo);
627
628  target_info->target_path = local_path_;
629  target_info->target_disposition =
630      (HasPromptedForPath() || should_prompt_
631           ? DownloadItem::TARGET_DISPOSITION_PROMPT
632           : DownloadItem::TARGET_DISPOSITION_OVERWRITE);
633  target_info->danger_type = danger_type_;
634  target_info->intermediate_path = intermediate_path_;
635  target_info->mime_type = mime_type_;
636  target_info->is_filetype_handled_securely = is_filetype_handled_securely_;
637
638  base::MessageLoop::current()->PostTask(
639      FROM_HERE, base::Bind(completion_callback_, base::Passed(&target_info)));
640  completion_callback_.Reset();
641  delete this;
642}
643
644void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
645  // Path substitution failed.
646  virtual_path_.clear();
647  local_path_.clear();
648  intermediate_path_.clear();
649  ScheduleCallbackAndDeleteSelf();
650}
651
652Profile* DownloadTargetDeterminer::GetProfile() {
653  DCHECK(download_->GetBrowserContext());
654  return Profile::FromBrowserContext(download_->GetBrowserContext());
655}
656
657bool DownloadTargetDeterminer::ShouldPromptForDownload(
658    const base::FilePath& filename) const {
659  if (is_resumption_) {
660    // For resumed downloads, if the target disposition or prefs require
661    // prompting, the user has already been prompted. Try to respect the user's
662    // selection, unless we've discovered that the target path cannot be used
663    // for some reason.
664    content::DownloadInterruptReason reason = download_->GetLastReason();
665    return (reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED ||
666            reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE ||
667            reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE);
668  }
669
670  // If the download path is forced, don't prompt.
671  if (!download_->GetForcedFilePath().empty()) {
672    // 'Save As' downloads shouldn't have a forced path.
673    DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT !=
674           download_->GetTargetDisposition());
675    return false;
676  }
677
678  // Don't ask where to save if the download path is managed. Even if the user
679  // wanted to be prompted for "all" downloads, or if this was a 'Save As'
680  // download.
681  if (download_prefs_->IsDownloadPathManaged())
682    return false;
683
684  // Prompt if this is a 'Save As' download.
685  if (download_->GetTargetDisposition() ==
686      DownloadItem::TARGET_DISPOSITION_PROMPT)
687    return true;
688
689  // Check if the user has the "Always prompt for download location" preference
690  // set. If so we prompt for most downloads except for the following scenarios:
691  // 1) Extension installation. Note that we only care here about the case where
692  //    an extension is installed, not when one is downloaded with "save as...".
693  // 2) Filetypes marked "always open." If the user just wants this file opened,
694  //    don't bother asking where to keep it.
695  if (download_prefs_->PromptForDownload() &&
696      !download_crx_util::IsExtensionDownload(*download_) &&
697      !filename.MatchesExtension(extensions::kExtensionFileExtension) &&
698      !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
699    return true;
700
701  // Otherwise, don't prompt. Note that the user might still be prompted if
702  // there are unresolved conflicts during path reservation (e.g. due to the
703  // target path being unwriteable or because there are too many conflicting
704  // files), or if an extension signals that the user be prompted on a filename
705  // conflict.
706  return false;
707}
708
709bool DownloadTargetDeterminer::HasPromptedForPath() const {
710  return (is_resumption_ && download_->GetTargetDisposition() ==
711                                DownloadItem::TARGET_DISPOSITION_PROMPT);
712}
713
714bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) {
715  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
716
717  // If the user has has been prompted or will be, assume that the user has
718  // approved the download. A programmatic download is considered safe unless it
719  // contains malware.
720  if (HasPromptedForPath() || should_prompt_ ||
721      !download_->GetForcedFilePath().empty())
722    return false;
723
724  const bool is_extension_download =
725      download_crx_util::IsExtensionDownload(*download_);
726
727  // User-initiated extension downloads from pref-whitelisted sources are not
728  // considered dangerous.
729  if (download_->HasUserGesture() &&
730      is_extension_download &&
731      download_crx_util::OffStoreInstallAllowedByPrefs(
732          GetProfile(), *download_)) {
733    return false;
734  }
735
736  // Extensions that are not from the gallery are considered dangerous.
737  // When off-store install is disabled we skip this, since in this case, we
738  // will not offer to install the extension.
739  if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
740      is_extension_download &&
741      !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) {
742    return true;
743  }
744
745  // Anything the user has marked auto-open is OK if it's user-initiated.
746  if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) &&
747      download_->HasUserGesture())
748    return false;
749
750  switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) {
751    case download_util::NOT_DANGEROUS:
752      return false;
753
754    case download_util::ALLOW_ON_USER_GESTURE:
755      // "Allow on user gesture" is OK when we have a user gesture and the
756      // hosting page has been visited before today.
757      if (download_->GetTransitionType() &
758          content::PAGE_TRANSITION_FROM_ADDRESS_BAR) {
759        return false;
760      }
761      return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER;
762
763    case download_util::DANGEROUS:
764      return true;
765  }
766  NOTREACHED();
767  return false;
768}
769
770void DownloadTargetDeterminer::OnDownloadDestroyed(
771    DownloadItem* download) {
772  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
773  DCHECK_EQ(download_, download);
774  CancelOnFailureAndDeleteSelf();
775}
776
777// static
778void DownloadTargetDeterminer::Start(content::DownloadItem* download,
779                                     const base::FilePath& initial_virtual_path,
780                                     DownloadPrefs* download_prefs,
781                                     DownloadTargetDeterminerDelegate* delegate,
782                                     const CompletionCallback& callback) {
783  // DownloadTargetDeterminer owns itself and will self destruct when the job is
784  // complete or the download item is destroyed. The callback is always invoked
785  // asynchronously.
786  new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
787                               delegate, callback);
788}
789
790// static
791base::FilePath DownloadTargetDeterminer::GetCrDownloadPath(
792    const base::FilePath& suggested_path) {
793  return base::FilePath(suggested_path.value() + kCrdownloadSuffix);
794}
795