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/chrome_download_manager_delegate.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/callback.h" 12 #include "base/file_util.h" 13 #include "base/prefs/pref_member.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/rand_util.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/time/time.h" 19 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/chrome_notification_types.h" 21 #include "chrome/browser/download/download_completion_blocker.h" 22 #include "chrome/browser/download/download_crx_util.h" 23 #include "chrome/browser/download/download_file_picker.h" 24 #include "chrome/browser/download/download_history.h" 25 #include "chrome/browser/download/download_path_reservation_tracker.h" 26 #include "chrome/browser/download/download_prefs.h" 27 #include "chrome/browser/download/download_service.h" 28 #include "chrome/browser/download/download_service_factory.h" 29 #include "chrome/browser/download/download_target_determiner.h" 30 #include "chrome/browser/download/download_util.h" 31 #include "chrome/browser/download/save_package_file_picker.h" 32 #include "chrome/browser/extensions/api/downloads/downloads_api.h" 33 #include "chrome/browser/extensions/crx_installer.h" 34 #include "chrome/browser/platform_util.h" 35 #include "chrome/browser/profiles/profile.h" 36 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 37 #include "chrome/common/chrome_constants.h" 38 #include "chrome/common/pref_names.h" 39 #include "components/user_prefs/pref_registry_syncable.h" 40 #include "content/public/browser/download_item.h" 41 #include "content/public/browser/download_manager.h" 42 #include "content/public/browser/notification_source.h" 43 #include "extensions/common/constants.h" 44 45 #if defined(OS_CHROMEOS) 46 #include "chrome/browser/chromeos/drive/download_handler.h" 47 #include "chrome/browser/chromeos/drive/file_system_util.h" 48 #endif 49 50 using content::BrowserThread; 51 using content::DownloadItem; 52 using content::DownloadManager; 53 using safe_browsing::DownloadProtectionService; 54 55 namespace { 56 57 // String pointer used for identifying safebrowing data associated with 58 // a download item. 59 static const char safe_browsing_id[] = "Safe Browsing ID"; 60 61 // The state of a safebrowsing check. 62 class SafeBrowsingState : public DownloadCompletionBlocker { 63 public: 64 SafeBrowsingState() 65 : verdict_(DownloadProtectionService::SAFE) { 66 } 67 68 virtual ~SafeBrowsingState(); 69 70 // The verdict that we got from calling CheckClientDownload. Only valid to 71 // call if |is_complete()|. 72 DownloadProtectionService::DownloadCheckResult verdict() const { 73 return verdict_; 74 } 75 76 void SetVerdict(DownloadProtectionService::DownloadCheckResult result) { 77 verdict_ = result; 78 CompleteDownload(); 79 } 80 81 private: 82 DownloadProtectionService::DownloadCheckResult verdict_; 83 84 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingState); 85 }; 86 87 SafeBrowsingState::~SafeBrowsingState() {} 88 89 // Used with GetPlatformDownloadPath() to indicate which platform path to 90 // return. 91 enum PlatformDownloadPathType { 92 // Return the platform specific target path. 93 PLATFORM_TARGET_PATH, 94 95 // Return the platform specific current path. If the download is in-progress 96 // and the download location is a local filesystem path, then 97 // GetPlatformDownloadPath will return the path to the intermediate file. 98 PLATFORM_CURRENT_PATH 99 }; 100 101 // Returns a path in the form that that is expected by platform_util::OpenItem / 102 // platform_util::ShowItemInFolder / DownloadTargetDeterminer. 103 // 104 // DownloadItems corresponding to Drive downloads use a temporary file as the 105 // target path. The paths returned by DownloadItem::GetFullPath() / 106 // GetTargetFilePath() refer to this temporary file. This function looks up the 107 // corresponding path in Drive for these downloads. 108 // 109 // How the platform path is determined is based on PlatformDownloadPathType. 110 base::FilePath GetPlatformDownloadPath(Profile* profile, 111 const DownloadItem* download, 112 PlatformDownloadPathType path_type) { 113 #if defined(OS_CHROMEOS) 114 // Drive downloads always return the target path for all types. 115 drive::DownloadHandler* drive_download_handler = 116 drive::DownloadHandler::GetForProfile(profile); 117 if (drive_download_handler && 118 drive_download_handler->IsDriveDownload(download)) 119 return drive_download_handler->GetTargetPath(download); 120 #endif 121 122 if (path_type == PLATFORM_TARGET_PATH) 123 return download->GetTargetFilePath(); 124 return download->GetFullPath(); 125 } 126 127 // Callback invoked by DownloadProtectionService::CheckClientDownload. 128 // |is_content_check_supported| is true if the SB service supports scanning the 129 // download for malicious content. 130 // |callback| is invoked with a danger type determined as follows: 131 // 132 // Danger type is (in order of preference): 133 // * DANGEROUS_URL, if the URL is a known malware site. 134 // * MAYBE_DANGEROUS_CONTENT, if the content will be scanned for 135 // malware. I.e. |is_content_check_supported| is true. 136 // * NOT_DANGEROUS. 137 void CheckDownloadUrlDone( 138 const DownloadTargetDeterminerDelegate::CheckDownloadUrlCallback& callback, 139 bool is_content_check_supported, 140 DownloadProtectionService::DownloadCheckResult result) { 141 content::DownloadDangerType danger_type; 142 if (result == DownloadProtectionService::SAFE) { 143 // If this type of files is handled by the enhanced SafeBrowsing download 144 // protection, mark it as potentially dangerous content until we are done 145 // with scanning it. 146 if (is_content_check_supported) 147 danger_type = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT; 148 else 149 danger_type = content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS; 150 } else { 151 // If the URL is malicious, we'll use that as the danger type. The results 152 // of the content check, if one is performed, will be ignored. 153 danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL; 154 } 155 callback.Run(danger_type); 156 } 157 158 } // namespace 159 160 ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile) 161 : profile_(profile), 162 next_download_id_(content::DownloadItem::kInvalidId), 163 download_prefs_(new DownloadPrefs(profile)) { 164 } 165 166 ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() { 167 } 168 169 void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) { 170 download_manager_ = dm; 171 } 172 173 void ChromeDownloadManagerDelegate::Shutdown() { 174 download_prefs_.reset(); 175 } 176 177 void ChromeDownloadManagerDelegate::SetNextId(uint32 next_id) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 179 DCHECK(!profile_->IsOffTheRecord()); 180 DCHECK_NE(content::DownloadItem::kInvalidId, next_id); 181 next_download_id_ = next_id; 182 183 IdCallbackVector callbacks; 184 id_callbacks_.swap(callbacks); 185 for (IdCallbackVector::const_iterator it = callbacks.begin(); 186 it != callbacks.end(); ++it) { 187 ReturnNextId(*it); 188 } 189 } 190 191 void ChromeDownloadManagerDelegate::GetNextId( 192 const content::DownloadIdCallback& callback) { 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 194 if (profile_->IsOffTheRecord()) { 195 content::BrowserContext::GetDownloadManager( 196 profile_->GetOriginalProfile())->GetDelegate()->GetNextId(callback); 197 return; 198 } 199 if (next_download_id_ == content::DownloadItem::kInvalidId) { 200 id_callbacks_.push_back(callback); 201 return; 202 } 203 ReturnNextId(callback); 204 } 205 206 void ChromeDownloadManagerDelegate::ReturnNextId( 207 const content::DownloadIdCallback& callback) { 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 209 DCHECK(!profile_->IsOffTheRecord()); 210 DCHECK_NE(content::DownloadItem::kInvalidId, next_download_id_); 211 callback.Run(next_download_id_++); 212 } 213 214 bool ChromeDownloadManagerDelegate::DetermineDownloadTarget( 215 DownloadItem* download, 216 const content::DownloadTargetCallback& callback) { 217 DownloadTargetDeterminer::Start( 218 download, 219 GetPlatformDownloadPath( 220 profile_, download, PLATFORM_TARGET_PATH), 221 download_prefs_.get(), 222 this, 223 callback); 224 return true; 225 } 226 227 bool ChromeDownloadManagerDelegate::ShouldOpenFileBasedOnExtension( 228 const base::FilePath& path) { 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 230 if (path.Extension().empty()) 231 return false; 232 // TODO(asanka): This determination is done based on |path|, while 233 // ShouldOpenDownload() detects extension downloads based on the 234 // characteristics of the download. Reconcile this. http://crbug.com/167702 235 if (path.MatchesExtension(extensions::kExtensionFileExtension)) 236 return false; 237 return download_prefs_->IsAutoOpenEnabledBasedOnExtension(path); 238 } 239 240 // static 241 void ChromeDownloadManagerDelegate::DisableSafeBrowsing(DownloadItem* item) { 242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 243 #if defined(FULL_SAFE_BROWSING) 244 SafeBrowsingState* state = static_cast<SafeBrowsingState*>( 245 item->GetUserData(&safe_browsing_id)); 246 if (!state) { 247 state = new SafeBrowsingState(); 248 item->SetUserData(&safe_browsing_id, state); 249 } 250 state->SetVerdict(DownloadProtectionService::SAFE); 251 #endif 252 } 253 254 bool ChromeDownloadManagerDelegate::IsDownloadReadyForCompletion( 255 DownloadItem* item, 256 const base::Closure& internal_complete_callback) { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258 #if defined(FULL_SAFE_BROWSING) 259 SafeBrowsingState* state = static_cast<SafeBrowsingState*>( 260 item->GetUserData(&safe_browsing_id)); 261 if (!state) { 262 // Begin the safe browsing download protection check. 263 DownloadProtectionService* service = GetDownloadProtectionService(); 264 if (service) { 265 VLOG(2) << __FUNCTION__ << "() Start SB download check for download = " 266 << item->DebugString(false); 267 state = new SafeBrowsingState(); 268 state->set_callback(internal_complete_callback); 269 item->SetUserData(&safe_browsing_id, state); 270 service->CheckClientDownload( 271 item, 272 base::Bind( 273 &ChromeDownloadManagerDelegate::CheckClientDownloadDone, 274 this, 275 item->GetId())); 276 return false; 277 } 278 } else if (!state->is_complete()) { 279 // Don't complete the download until we have an answer. 280 state->set_callback(internal_complete_callback); 281 return false; 282 } 283 #endif 284 return true; 285 } 286 287 void ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal( 288 uint32 download_id, 289 const base::Closure& user_complete_callback) { 290 DownloadItem* item = download_manager_->GetDownload(download_id); 291 if (!item) 292 return; 293 if (ShouldCompleteDownload(item, user_complete_callback)) 294 user_complete_callback.Run(); 295 } 296 297 bool ChromeDownloadManagerDelegate::ShouldCompleteDownload( 298 DownloadItem* item, 299 const base::Closure& user_complete_callback) { 300 return IsDownloadReadyForCompletion(item, base::Bind( 301 &ChromeDownloadManagerDelegate::ShouldCompleteDownloadInternal, 302 this, item->GetId(), user_complete_callback)); 303 } 304 305 bool ChromeDownloadManagerDelegate::ShouldOpenDownload( 306 DownloadItem* item, const content::DownloadOpenDelayedCallback& callback) { 307 if (download_crx_util::IsExtensionDownload(*item)) { 308 scoped_refptr<extensions::CrxInstaller> crx_installer = 309 download_crx_util::OpenChromeExtension(profile_, *item); 310 311 // CRX_INSTALLER_DONE will fire when the install completes. At that 312 // time, Observe() will call the passed callback. 313 registrar_.Add( 314 this, 315 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 316 content::Source<extensions::CrxInstaller>(crx_installer.get())); 317 318 crx_installers_[crx_installer.get()] = callback; 319 // The status text and percent complete indicator will change now 320 // that we are installing a CRX. Update observers so that they pick 321 // up the change. 322 item->UpdateObservers(); 323 return false; 324 } 325 326 return true; 327 } 328 329 bool ChromeDownloadManagerDelegate::GenerateFileHash() { 330 #if defined(FULL_SAFE_BROWSING) 331 return profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled) && 332 g_browser_process->safe_browsing_service()->DownloadBinHashNeeded(); 333 #else 334 return false; 335 #endif 336 } 337 338 void ChromeDownloadManagerDelegate::GetSaveDir( 339 content::BrowserContext* browser_context, 340 base::FilePath* website_save_dir, 341 base::FilePath* download_save_dir, 342 bool* skip_dir_check) { 343 *website_save_dir = download_prefs_->SaveFilePath(); 344 DCHECK(!website_save_dir->empty()); 345 *download_save_dir = download_prefs_->DownloadPath(); 346 *skip_dir_check = false; 347 #if defined(OS_CHROMEOS) 348 *skip_dir_check = drive::util::IsUnderDriveMountPoint(*website_save_dir); 349 #endif 350 } 351 352 void ChromeDownloadManagerDelegate::ChooseSavePath( 353 content::WebContents* web_contents, 354 const base::FilePath& suggested_path, 355 const base::FilePath::StringType& default_extension, 356 bool can_save_as_complete, 357 const content::SavePackagePathPickedCallback& callback) { 358 // Deletes itself. 359 new SavePackageFilePicker( 360 web_contents, 361 suggested_path, 362 default_extension, 363 can_save_as_complete, 364 download_prefs_.get(), 365 callback); 366 } 367 368 void ChromeDownloadManagerDelegate::OpenDownload(DownloadItem* download) { 369 DCHECK_EQ(DownloadItem::COMPLETE, download->GetState()); 370 if (!download->CanOpenDownload()) 371 return; 372 base::FilePath platform_path( 373 GetPlatformDownloadPath(profile_, download, PLATFORM_TARGET_PATH)); 374 DCHECK(!platform_path.empty()); 375 platform_util::OpenItem(platform_path); 376 } 377 378 void ChromeDownloadManagerDelegate::ShowDownloadInShell( 379 DownloadItem* download) { 380 if (!download->CanShowInFolder()) 381 return; 382 base::FilePath platform_path( 383 GetPlatformDownloadPath(profile_, download, PLATFORM_CURRENT_PATH)); 384 DCHECK(!platform_path.empty()); 385 platform_util::ShowItemInFolder(platform_path); 386 } 387 388 void ChromeDownloadManagerDelegate::CheckForFileExistence( 389 DownloadItem* download, 390 const content::CheckForFileExistenceCallback& callback) { 391 #if defined(OS_CHROMEOS) 392 drive::DownloadHandler* drive_download_handler = 393 drive::DownloadHandler::GetForProfile(profile_); 394 if (drive_download_handler && 395 drive_download_handler->IsDriveDownload(download)) { 396 drive_download_handler->CheckForFileExistence(download, callback); 397 return; 398 } 399 #endif 400 BrowserThread::PostTaskAndReplyWithResult( 401 BrowserThread::FILE, FROM_HERE, 402 base::Bind(&base::PathExists, download->GetTargetFilePath()), 403 callback); 404 } 405 406 std::string 407 ChromeDownloadManagerDelegate::ApplicationClientIdForFileScanning() const { 408 return std::string(chrome::kApplicationClientIDStringForAVScanning); 409 } 410 411 DownloadProtectionService* 412 ChromeDownloadManagerDelegate::GetDownloadProtectionService() { 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 414 #if defined(FULL_SAFE_BROWSING) 415 SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service(); 416 if (sb_service && sb_service->download_protection_service() && 417 profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { 418 return sb_service->download_protection_service(); 419 } 420 #endif 421 return NULL; 422 } 423 424 void ChromeDownloadManagerDelegate::NotifyExtensions( 425 DownloadItem* download, 426 const base::FilePath& virtual_path, 427 const NotifyExtensionsCallback& callback) { 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 429 #if !defined(OS_ANDROID) 430 ExtensionDownloadsEventRouter* router = 431 DownloadServiceFactory::GetForBrowserContext(profile_)-> 432 GetExtensionEventRouter(); 433 if (router) { 434 base::Closure original_path_callback = 435 base::Bind(callback, base::FilePath(), 436 DownloadPathReservationTracker::UNIQUIFY); 437 router->OnDeterminingFilename(download, virtual_path.BaseName(), 438 original_path_callback, 439 callback); 440 return; 441 } 442 #endif 443 callback.Run(base::FilePath(), DownloadPathReservationTracker::UNIQUIFY); 444 } 445 446 void ChromeDownloadManagerDelegate::ReserveVirtualPath( 447 content::DownloadItem* download, 448 const base::FilePath& virtual_path, 449 bool create_directory, 450 DownloadPathReservationTracker::FilenameConflictAction conflict_action, 451 const DownloadTargetDeterminerDelegate::ReservedPathCallback& callback) { 452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 453 DCHECK(!virtual_path.empty()); 454 #if defined(OS_CHROMEOS) 455 // TODO(asanka): Handle path reservations for virtual paths as well. 456 // http://crbug.com/151618 457 if (drive::util::IsUnderDriveMountPoint(virtual_path)) { 458 callback.Run(virtual_path, true); 459 return; 460 } 461 #endif 462 DownloadPathReservationTracker::GetReservedPath( 463 download, 464 virtual_path, 465 download_prefs_->DownloadPath(), 466 create_directory, 467 conflict_action, 468 callback); 469 } 470 471 void ChromeDownloadManagerDelegate::PromptUserForDownloadPath( 472 DownloadItem* download, 473 const base::FilePath& suggested_path, 474 const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) { 475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 476 DownloadFilePicker::ShowFilePicker(download, suggested_path, callback); 477 } 478 479 void ChromeDownloadManagerDelegate::DetermineLocalPath( 480 DownloadItem* download, 481 const base::FilePath& virtual_path, 482 const DownloadTargetDeterminerDelegate::LocalPathCallback& callback) { 483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 484 #if defined(OS_CHROMEOS) 485 drive::DownloadHandler* drive_download_handler = 486 drive::DownloadHandler::GetForProfile(profile_); 487 if (drive_download_handler) { 488 drive_download_handler->SubstituteDriveDownloadPath( 489 virtual_path, download, callback); 490 return; 491 } 492 #endif 493 callback.Run(virtual_path); 494 } 495 496 void ChromeDownloadManagerDelegate::CheckDownloadUrl( 497 DownloadItem* download, 498 const base::FilePath& suggested_path, 499 const CheckDownloadUrlCallback& callback) { 500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 501 502 #if defined(FULL_SAFE_BROWSING) 503 safe_browsing::DownloadProtectionService* service = 504 GetDownloadProtectionService(); 505 if (service) { 506 bool is_content_check_supported = 507 service->IsSupportedDownload(*download, suggested_path); 508 VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = " 509 << download->DebugString(false); 510 service->CheckDownloadUrl(*download, 511 base::Bind(&CheckDownloadUrlDone, 512 callback, 513 is_content_check_supported)); 514 return; 515 } 516 #endif 517 callback.Run(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); 518 } 519 520 void ChromeDownloadManagerDelegate::CheckClientDownloadDone( 521 uint32 download_id, 522 DownloadProtectionService::DownloadCheckResult result) { 523 DownloadItem* item = download_manager_->GetDownload(download_id); 524 if (!item || (item->GetState() != DownloadItem::IN_PROGRESS)) 525 return; 526 527 VLOG(2) << __FUNCTION__ << "() download = " << item->DebugString(false) 528 << " verdict = " << result; 529 // We only mark the content as being dangerous if the download's safety state 530 // has not been set to DANGEROUS yet. We don't want to show two warnings. 531 if (item->GetDangerType() == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || 532 item->GetDangerType() == 533 content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) { 534 content::DownloadDangerType danger_type = 535 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS; 536 switch (result) { 537 case DownloadProtectionService::SAFE: 538 // Do nothing. 539 break; 540 case DownloadProtectionService::DANGEROUS: 541 danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT; 542 break; 543 case DownloadProtectionService::UNCOMMON: 544 danger_type = content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT; 545 break; 546 case DownloadProtectionService::DANGEROUS_HOST: 547 danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST; 548 break; 549 case DownloadProtectionService::POTENTIALLY_UNWANTED: 550 danger_type = content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED; 551 break; 552 } 553 554 if (danger_type != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) 555 item->OnContentCheckCompleted(danger_type); 556 } 557 558 SafeBrowsingState* state = static_cast<SafeBrowsingState*>( 559 item->GetUserData(&safe_browsing_id)); 560 state->SetVerdict(result); 561 } 562 563 // content::NotificationObserver implementation. 564 void ChromeDownloadManagerDelegate::Observe( 565 int type, 566 const content::NotificationSource& source, 567 const content::NotificationDetails& details) { 568 DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE); 569 570 registrar_.Remove(this, 571 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 572 source); 573 574 scoped_refptr<extensions::CrxInstaller> installer = 575 content::Source<extensions::CrxInstaller>(source).ptr(); 576 content::DownloadOpenDelayedCallback callback = 577 crx_installers_[installer.get()]; 578 crx_installers_.erase(installer.get()); 579 callback.Run(installer->did_handle_successfully()); 580 } 581