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