| OLD | NEW |
| (Empty) | |
| 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/stringprintf.h" |
| 10 #include "base/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_file_picker.h" |
| 15 #include "chrome/browser/download/download_prefs.h" |
| 16 #include "chrome/browser/download/download_service.h" |
| 17 #include "chrome/browser/download/download_service_factory.h" |
| 18 #include "chrome/browser/download/download_util.h" |
| 19 #include "chrome/browser/extensions/api/downloads/downloads_api.h" |
| 20 #include "chrome/browser/extensions/webstore_installer.h" |
| 21 #include "chrome/browser/history/history_service.h" |
| 22 #include "chrome/browser/history/history_service_factory.h" |
| 23 #include "chrome/browser/profiles/profile.h" |
| 24 #include "chrome/browser/safe_browsing/download_protection_service.h" |
| 25 #include "chrome/common/extensions/feature_switch.h" |
| 26 #include "chrome/common/pref_names.h" |
| 27 #include "content/public/browser/browser_context.h" |
| 28 #include "content/public/browser/browser_thread.h" |
| 29 #include "grit/generated_resources.h" |
| 30 #include "net/base/net_util.h" |
| 31 #include "ui/base/l10n/l10n_util.h" |
| 32 |
| 33 #if defined(OS_CHROMEOS) |
| 34 #include "chrome/browser/chromeos/drive/drive_download_handler.h" |
| 35 #include "chrome/browser/chromeos/drive/drive_file_system_util.h" |
| 36 #endif |
| 37 |
| 38 using content::BrowserThread; |
| 39 using content::DownloadItem; |
| 40 using safe_browsing::DownloadProtectionService; |
| 41 |
| 42 namespace { |
| 43 |
| 44 // Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a |
| 45 // single bool. A host is considered visited before if prior visible vists were |
| 46 // found in history and the first such visit was earlier than the most recent |
| 47 // midnight. |
| 48 void 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 |
| 61 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() { |
| 62 } |
| 63 |
| 64 DownloadTargetDeterminer::DownloadTargetDeterminer( |
| 65 DownloadItem* download, |
| 66 DownloadPrefs* download_prefs, |
| 67 DownloadFilePickerFactory* file_picker_factory, |
| 68 const base::FilePath& last_selected_directory, |
| 69 DownloadTargetDeterminerDelegate* delegate, |
| 70 const CompletionCallback& callback) |
| 71 : next_state_(STATE_GENERATE_TARGET_PATH), |
| 72 should_prompt_(false), |
| 73 should_overwrite_(!download->GetForcedFilePath().empty()), |
| 74 danger_type_(download->GetDangerType()), |
| 75 download_(download), |
| 76 download_prefs_(download_prefs), |
| 77 file_picker_factory_(file_picker_factory), |
| 78 delegate_(delegate), |
| 79 last_selected_directory_(last_selected_directory), |
| 80 completion_callback_(callback), |
| 81 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
| 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 83 DCHECK(download_); |
| 84 DCHECK(delegate); |
| 85 download_->AddObserver(this); |
| 86 |
| 87 DoLoop(); |
| 88 } |
| 89 |
| 90 DownloadTargetDeterminer::~DownloadTargetDeterminer() { |
| 91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 92 DCHECK(download_); |
| 93 DCHECK(completion_callback_.is_null()); |
| 94 download_->RemoveObserver(this); |
| 95 } |
| 96 |
| 97 void DownloadTargetDeterminer::DoLoop() { |
| 98 Result result = CONTINUE; |
| 99 do { |
| 100 State current_state = next_state_; |
| 101 next_state_ = STATE_NONE; |
| 102 |
| 103 switch (current_state) { |
| 104 case STATE_GENERATE_TARGET_PATH: |
| 105 result = DoGenerateTargetPath(); |
| 106 break; |
| 107 case STATE_NOTIFY_EXTENSIONS: |
| 108 result = DoNotifyExtensions(); |
| 109 break; |
| 110 case STATE_RESERVE_VIRTUAL_PATH: |
| 111 result = DoReserveVirtualPath(); |
| 112 break; |
| 113 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH: |
| 114 result = DoPromptUserForDownloadPath(); |
| 115 break; |
| 116 case STATE_DETERMINE_LOCAL_PATH: |
| 117 result = DoDetermineLocalPath(); |
| 118 break; |
| 119 case STATE_CHECK_DOWNLOAD_URL: |
| 120 result = DoCheckDownloadUrl(); |
| 121 break; |
| 122 case STATE_DETERMINE_DANGER_TYPE: |
| 123 result = DoDetermineDangerType(); |
| 124 break; |
| 125 case STATE_DETERMINE_INTERMEDIATE_PATH: |
| 126 result = DoDetermineIntermediatePath(); |
| 127 break; |
| 128 case STATE_CHECK_VISITED_REFERRER_BEFORE: |
| 129 result = DoCheckVisitedReferrerBefore(); |
| 130 break; |
| 131 case STATE_NONE: |
| 132 NOTREACHED(); |
| 133 return; |
| 134 } |
| 135 } while (result == CONTINUE); |
| 136 // Note that if a callback completes synchronously, the handler will still |
| 137 // return QUIT_DOLOOP. In this case, an inner DoLoop() may complete the target |
| 138 // determination and delete |this|. |
| 139 |
| 140 if (result == COMPLETE) |
| 141 ScheduleCallbackAndDeleteSelf(); |
| 142 } |
| 143 |
| 144 DownloadTargetDeterminer::Result |
| 145 DownloadTargetDeterminer::DoGenerateTargetPath() { |
| 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 147 DCHECK(virtual_path_.empty()); |
| 148 DCHECK(local_path_.empty()); |
| 149 bool is_forced_path = !download_->GetForcedFilePath().empty(); |
| 150 |
| 151 next_state_ = STATE_NOTIFY_EXTENSIONS; |
| 152 |
| 153 // If we don't have a forced path, we should construct a path for the |
| 154 // download. Forced paths are only specified for programmatic downloads |
| 155 // (WebStore, Drag&Drop). Treat the path as a virtual path. We will eventually |
| 156 // determine whether this is a local path and if not, figure out a local path. |
| 157 if (!is_forced_path) { |
| 158 std::string default_filename( |
| 159 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); |
| 160 base::FilePath generated_filename = net::GenerateFileName( |
| 161 download_->GetURL(), |
| 162 download_->GetContentDisposition(), |
| 163 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset), |
| 164 download_->GetSuggestedFilename(), |
| 165 download_->GetMimeType(), |
| 166 default_filename); |
| 167 should_prompt_ = ShouldPromptForDownload(generated_filename); |
| 168 base::FilePath target_directory; |
| 169 if (should_prompt_ && !last_selected_directory_.empty()) { |
| 170 DCHECK(!download_prefs_->IsDownloadPathManaged()); |
| 171 // If the user is going to be prompted and the user has been prompted |
| 172 // before, then always prefer the last directory that the user selected. |
| 173 target_directory = last_selected_directory_; |
| 174 } else { |
| 175 target_directory = download_prefs_->DownloadPath(); |
| 176 } |
| 177 virtual_path_ = target_directory.Append(generated_filename); |
| 178 } else { |
| 179 DCHECK(!should_prompt_); |
| 180 virtual_path_ = download_->GetForcedFilePath(); |
| 181 } |
| 182 DCHECK(virtual_path_.IsAbsolute()); |
| 183 DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe(); |
| 184 |
| 185 // If the download is DOA, don't bother going any further. This would be the |
| 186 // case for a download that failed to initialize (e.g. the initial temporary |
| 187 // file couldn't be created because both the downloads directory and the |
| 188 // temporary directory are unwriteable). |
| 189 if (!download_->IsInProgress()) |
| 190 return COMPLETE; |
| 191 return CONTINUE; |
| 192 } |
| 193 |
| 194 DownloadTargetDeterminer::Result |
| 195 DownloadTargetDeterminer::DoNotifyExtensions() { |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 197 DCHECK(!virtual_path_.empty()); |
| 198 |
| 199 next_state_ = STATE_RESERVE_VIRTUAL_PATH; |
| 200 |
| 201 // Don't notify extensions of forced file paths. |
| 202 if (!download_->GetForcedFilePath().empty()) |
| 203 return CONTINUE; |
| 204 |
| 205 ExtensionDownloadsEventRouter* router = |
| 206 DownloadServiceFactory::GetForProfile(GetProfile())-> |
| 207 GetExtensionEventRouter(); |
| 208 if (!router) |
| 209 return CONTINUE; |
| 210 |
| 211 base::Closure original_path_callback = |
| 212 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone, |
| 213 weak_ptr_factory_.GetWeakPtr(), base::FilePath(), false); |
| 214 ExtensionDownloadsEventRouter::FilenameChangedCallback override_callback = |
| 215 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone, |
| 216 weak_ptr_factory_.GetWeakPtr()); |
| 217 router->OnDeterminingFilename(download_, virtual_path_.BaseName(), |
| 218 original_path_callback, |
| 219 override_callback); |
| 220 return QUIT_DOLOOP; |
| 221 } |
| 222 |
| 223 void DownloadTargetDeterminer::NotifyExtensionsDone( |
| 224 const base::FilePath& suggested_path, |
| 225 bool should_overwrite) { |
| 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 227 DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe(); |
| 228 |
| 229 if (!suggested_path.empty()) { |
| 230 // TODO(asanka, benjhayden): The suggested path may contain path fragments. |
| 231 // We need to validate each component individually. http://crbug.com/181332. |
| 232 |
| 233 // If an extension overrides the filename, then the target directory will be |
| 234 // forced to download_prefs_->DownloadPath() since extensions cannot place |
| 235 // downloaded files anywhere except there. This prevents subdirectories from |
| 236 // accumulating: if an extension is allowed to say that a file should go in |
| 237 // last_download_path/music/foo.mp3, then last_download_path will accumulate |
| 238 // the subdirectory /music/ so that the next download may end up in |
| 239 // Downloads/music/music/music/bar.mp3. |
| 240 base::FilePath new_path(download_prefs_->DownloadPath().Append( |
| 241 suggested_path).NormalizePathSeparators()); |
| 242 // Do not pass a mime type to GenerateSafeFileName so that it does not force |
| 243 // the filename to have an extension if the (Chrome) extension does not |
| 244 // suggest it. |
| 245 net::GenerateSafeFileName(std::string(), false, &new_path); |
| 246 virtual_path_ = new_path; |
| 247 |
| 248 // If |is_forced_path| were true, then extensions would not have been |
| 249 // consulted, so use |overwrite| instead of |is_forced_path|. This does NOT |
| 250 // set DownloadItem::GetForcedFilePath()! |
| 251 should_overwrite_ = should_overwrite; |
| 252 } |
| 253 |
| 254 DoLoop(); |
| 255 } |
| 256 |
| 257 DownloadTargetDeterminer::Result |
| 258 DownloadTargetDeterminer::DoReserveVirtualPath() { |
| 259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 260 DCHECK(!virtual_path_.empty()); |
| 261 |
| 262 next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH; |
| 263 |
| 264 delegate_->ReserveVirtualPath( |
| 265 download_, virtual_path_, !should_overwrite_, |
| 266 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone, |
| 267 weak_ptr_factory_.GetWeakPtr())); |
| 268 return QUIT_DOLOOP; |
| 269 } |
| 270 |
| 271 void DownloadTargetDeterminer::ReserveVirtualPathDone( |
| 272 const base::FilePath& path, bool verified) { |
| 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 274 DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe() |
| 275 << " Verified:" << verified; |
| 276 should_prompt_ = (should_prompt_ || !verified); |
| 277 if (verified) |
| 278 virtual_path_ = path; |
| 279 DoLoop(); |
| 280 } |
| 281 |
| 282 DownloadTargetDeterminer::Result |
| 283 DownloadTargetDeterminer::DoPromptUserForDownloadPath() { |
| 284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 285 DCHECK(!virtual_path_.empty()); |
| 286 |
| 287 if (should_prompt_) { |
| 288 // If we are prompting, then delegate_->PromptUserForDownloadPathDone() |
| 289 // returns both a local and virtual path, so we don't need to determine a |
| 290 // local path in that case. |
| 291 next_state_ = STATE_CHECK_DOWNLOAD_URL; |
| 292 file_picker_factory_->Create( |
| 293 download_, |
| 294 virtual_path_, |
| 295 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone, |
| 296 weak_ptr_factory_.GetWeakPtr())); |
| 297 return QUIT_DOLOOP; |
| 298 } |
| 299 |
| 300 next_state_ = STATE_DETERMINE_LOCAL_PATH; |
| 301 return CONTINUE; |
| 302 } |
| 303 |
| 304 void DownloadTargetDeterminer::PromptUserForDownloadPathDone( |
| 305 const base::FilePath& virtual_path, |
| 306 const base::FilePath& local_path) { |
| 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 308 DVLOG(20) << "User selected paths (virtual):" << virtual_path.AsUTF8Unsafe() |
| 309 << " (local):" << local_path.AsUTF8Unsafe(); |
| 310 if (virtual_path.empty()) { |
| 311 CancelOnFailureAndDeleteSelf(); |
| 312 return; |
| 313 } |
| 314 virtual_path_ = virtual_path; |
| 315 local_path_ = local_path; |
| 316 |
| 317 DCHECK_EQ(STATE_CHECK_DOWNLOAD_URL, next_state_); |
| 318 DoLoop(); |
| 319 } |
| 320 |
| 321 DownloadTargetDeterminer::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_CHECK_DOWNLOAD_URL; |
| 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 |
| 337 void DownloadTargetDeterminer::DetermineLocalPathDone( |
| 338 const base::FilePath& local_path) { |
| 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 340 DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe(); |
| 341 local_path_ = local_path; |
| 342 if (local_path_.empty()) { |
| 343 // Path subsitution failed. |
| 344 CancelOnFailureAndDeleteSelf(); |
| 345 return; |
| 346 } |
| 347 DoLoop(); |
| 348 } |
| 349 |
| 350 DownloadTargetDeterminer::Result |
| 351 DownloadTargetDeterminer::DoCheckDownloadUrl() { |
| 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 353 |
| 354 next_state_ = STATE_DETERMINE_DANGER_TYPE; |
| 355 |
| 356 #if defined(FULL_SAFE_BROWSING) |
| 357 safe_browsing::DownloadProtectionService* service = |
| 358 delegate_->GetDownloadProtectionService(); |
| 359 if (service) { |
| 360 VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = " |
| 361 << download_->DebugString(false); |
| 362 service->CheckDownloadUrl( |
| 363 *download_, |
| 364 base::Bind( |
| 365 &DownloadTargetDeterminer::CheckDownloadUrlDone, |
| 366 weak_ptr_factory_.GetWeakPtr())); |
| 367 return QUIT_DOLOOP; |
| 368 } |
| 369 #endif |
| 370 return CONTINUE; |
| 371 } |
| 372 |
| 373 void DownloadTargetDeterminer::CheckDownloadUrlDone( |
| 374 DownloadProtectionService::DownloadCheckResult result) { |
| 375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 376 DVLOG(20) << "URL Check Result:" << result; |
| 377 if (result != DownloadProtectionService::SAFE) |
| 378 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL; |
| 379 DoLoop(); |
| 380 } |
| 381 |
| 382 DownloadTargetDeterminer::Result |
| 383 DownloadTargetDeterminer::DoDetermineDangerType() { |
| 384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 385 DCHECK(!virtual_path_.empty()); |
| 386 |
| 387 next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE; |
| 388 |
| 389 #if defined(FULL_SAFE_BROWSING) |
| 390 // If the download hasn't already been marked dangerous (could be |
| 391 // DANGEROUS_URL), check if it is a dangerous file. |
| 392 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { |
| 393 // If this type of files is handled by the enhanced SafeBrowsing download |
| 394 // protection, mark it as potentially dangerous content until we are done |
| 395 // with scanning it. |
| 396 safe_browsing::DownloadProtectionService* service = |
| 397 delegate_->GetDownloadProtectionService(); |
| 398 if (service && service->IsSupportedDownload(*download_, virtual_path_)) { |
| 399 // TODO(noelutz): if the user changes the extension name in the UI to |
| 400 // something like .exe SafeBrowsing will currently *not* check if the |
| 401 // download is malicious. |
| 402 danger_type_ = content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT; |
| 403 } |
| 404 } else { |
| 405 DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type_); |
| 406 } |
| 407 #endif |
| 408 |
| 409 return CONTINUE; |
| 410 } |
| 411 |
| 412 DownloadTargetDeterminer::Result |
| 413 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() { |
| 414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 415 |
| 416 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH; |
| 417 |
| 418 // Checking if there are prior visits to the referrer is only necessary if the |
| 419 // danger level of the download depends on the file type. This excludes cases |
| 420 // where the download has already been deemed dangerous, or where the user is |
| 421 // going to be prompted or where this is a programmatic download. |
| 422 if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || |
| 423 should_prompt_ || |
| 424 !download_->GetForcedFilePath().empty()) { |
| 425 return CONTINUE; |
| 426 } |
| 427 |
| 428 // Only ping the history DB if the download would be considered safe if there |
| 429 // are prior visits and is considered dangerous otherwise. |
| 430 if (!IsDangerousFile(VISITED_REFERRER) && |
| 431 IsDangerousFile(NO_VISITS_TO_REFERRER)) { |
| 432 // HistoryServiceFactory redirects incognito profiles to on-record profiles. |
| 433 // There's no history for on-record profiles in unit_tests. |
| 434 HistoryService* history_service = HistoryServiceFactory::GetForProfile( |
| 435 GetProfile(), Profile::EXPLICIT_ACCESS); |
| 436 |
| 437 if (history_service && download_->GetReferrerUrl().is_valid()) { |
| 438 history_service->GetVisibleVisitCountToHost( |
| 439 download_->GetReferrerUrl(), &history_consumer_, |
| 440 base::Bind(&VisitCountsToVisitedBefore, base::Bind( |
| 441 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone, |
| 442 weak_ptr_factory_.GetWeakPtr()))); |
| 443 return QUIT_DOLOOP; |
| 444 } |
| 445 } |
| 446 |
| 447 // If the danger level doesn't depend on having visited the refererrer URL or |
| 448 // if original profile doesn't have a HistoryService or the referrer url is |
| 449 // invalid, then assume the referrer has not been visited before. |
| 450 if (IsDangerousFile(NO_VISITS_TO_REFERRER)) |
| 451 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; |
| 452 return CONTINUE; |
| 453 } |
| 454 |
| 455 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone( |
| 456 bool visited_referrer_before) { |
| 457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 458 if (IsDangerousFile( |
| 459 visited_referrer_before ? VISITED_REFERRER : NO_VISITS_TO_REFERRER)) |
| 460 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE; |
| 461 DoLoop(); |
| 462 } |
| 463 |
| 464 DownloadTargetDeterminer::Result |
| 465 DownloadTargetDeterminer::DoDetermineIntermediatePath() { |
| 466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 467 DCHECK(!virtual_path_.empty()); |
| 468 DCHECK(!local_path_.empty()); |
| 469 DCHECK(intermediate_path_.empty()); |
| 470 |
| 471 next_state_ = STATE_NONE; |
| 472 |
| 473 if (virtual_path_.BaseName() != local_path_.BaseName() || |
| 474 (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS && |
| 475 !download_->GetForcedFilePath().empty())) { |
| 476 // If the actual target of the download is a virtual path, then the local |
| 477 // path is considered to point to a temporary path. A separate intermediate |
| 478 // path is unnecessary since the local path already serves that purpose. |
| 479 // |
| 480 // Also, if the download has a forced path and is safe, then just use the |
| 481 // target path. |
| 482 intermediate_path_ = local_path_; |
| 483 } else if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { |
| 484 // If the download is not dangerous, just append .crdownload to the target |
| 485 // path. |
| 486 intermediate_path_ = download_util::GetCrDownloadPath(local_path_); |
| 487 } else { |
| 488 // If the download is potentially dangerous we create a filename of the form |
| 489 // 'Unconfirmed <random>.crdownload'. |
| 490 base::FilePath::StringType file_name; |
| 491 base::FilePath dir = local_path_.DirName(); |
| 492 #if defined(OS_WIN) |
| 493 string16 unconfirmed_prefix = |
| 494 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); |
| 495 #else |
| 496 std::string unconfirmed_prefix = |
| 497 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); |
| 498 #endif |
| 499 base::SStringPrintf( |
| 500 &file_name, |
| 501 unconfirmed_prefix.append( |
| 502 FILE_PATH_LITERAL(" %d.crdownload")).c_str(), |
| 503 base::RandInt(0, 1000000)); |
| 504 intermediate_path_ = dir.Append(file_name); |
| 505 } |
| 506 |
| 507 return COMPLETE; |
| 508 } |
| 509 |
| 510 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() { |
| 511 DCHECK(download_); |
| 512 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe() |
| 513 << " Local:" << local_path_.AsUTF8Unsafe() |
| 514 << " Intermediate:" << intermediate_path_.AsUTF8Unsafe() |
| 515 << " Should prompt:" << should_prompt_ |
| 516 << " Danger type:" << danger_type_; |
| 517 MessageLoop::current()->PostTask( |
| 518 FROM_HERE, |
| 519 base::Bind(completion_callback_, |
| 520 virtual_path_, |
| 521 local_path_, |
| 522 intermediate_path_, |
| 523 (should_prompt_ ? DownloadItem::TARGET_DISPOSITION_PROMPT : |
| 524 DownloadItem::TARGET_DISPOSITION_OVERWRITE), |
| 525 danger_type_)); |
| 526 completion_callback_.Reset(); |
| 527 delete this; |
| 528 } |
| 529 |
| 530 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() { |
| 531 // Path substitution failed. |
| 532 virtual_path_.clear(); |
| 533 local_path_.clear(); |
| 534 intermediate_path_.clear(); |
| 535 ScheduleCallbackAndDeleteSelf(); |
| 536 } |
| 537 |
| 538 Profile* DownloadTargetDeterminer::GetProfile() { |
| 539 DCHECK(download_->GetBrowserContext()); |
| 540 return Profile::FromBrowserContext(download_->GetBrowserContext()); |
| 541 } |
| 542 |
| 543 bool DownloadTargetDeterminer::ShouldPromptForDownload( |
| 544 const base::FilePath& filename) { |
| 545 // If the download path is forced, don't prompt. |
| 546 if (!download_->GetForcedFilePath().empty()) { |
| 547 // 'Save As' downloads shouldn't have a forced path. |
| 548 DCHECK_NE(DownloadItem::TARGET_DISPOSITION_PROMPT, |
| 549 download_->GetTargetDisposition()); |
| 550 return false; |
| 551 } |
| 552 |
| 553 // Don't ask where to save if the download path is managed. Even if the user |
| 554 // wanted to be prompted for "all" downloads, or if this was a 'Save As' |
| 555 // download. |
| 556 if (download_prefs_->IsDownloadPathManaged()) |
| 557 return false; |
| 558 |
| 559 // Prompt if this is a 'Save As' download. |
| 560 if (download_->GetTargetDisposition() == |
| 561 DownloadItem::TARGET_DISPOSITION_PROMPT) |
| 562 return true; |
| 563 |
| 564 // Check if the user has the "Always prompt for download location" preference |
| 565 // set. If so we prompt for most downloads except for the following scenarios: |
| 566 // 1) Extension installation. Note that we only care here about the case where |
| 567 // an extension is installed, not when one is downloaded with "save as...". |
| 568 // 2) Filetypes marked "always open." If the user just wants this file opened, |
| 569 // don't bother asking where to keep it. |
| 570 if (download_prefs_->PromptForDownload() && |
| 571 !download_crx_util::IsExtensionDownload(*download_) && |
| 572 !extensions::Extension::IsExtension(filename) && |
| 573 !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename)) |
| 574 return true; |
| 575 |
| 576 // Otherwise, don't prompt. |
| 577 return false; |
| 578 } |
| 579 |
| 580 bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) { |
| 581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 582 const bool is_extension_download = |
| 583 download_crx_util::IsExtensionDownload(*download_); |
| 584 |
| 585 // User-initiated extension downloads from pref-whitelisted sources are not |
| 586 // considered dangerous. |
| 587 if (download_->HasUserGesture() && |
| 588 is_extension_download && |
| 589 download_crx_util::OffStoreInstallAllowedByPrefs( |
| 590 GetProfile(), *download_)) { |
| 591 return false; |
| 592 } |
| 593 |
| 594 // Extensions that are not from the gallery are considered dangerous. |
| 595 // When off-store install is disabled we skip this, since in this case, we |
| 596 // will not offer to install the extension. |
| 597 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() && |
| 598 is_extension_download && |
| 599 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) { |
| 600 return true; |
| 601 } |
| 602 |
| 603 // Anything the user has marked auto-open is OK if it's user-initiated. |
| 604 if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) && |
| 605 download_->HasUserGesture()) |
| 606 return false; |
| 607 |
| 608 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) { |
| 609 case download_util::NotDangerous: |
| 610 return false; |
| 611 |
| 612 case download_util::AllowOnUserGesture: |
| 613 // "Allow on user gesture" is OK when we have a user gesture and the |
| 614 // hosting page has been visited before today. |
| 615 if (download_->GetTransitionType() & |
| 616 content::PAGE_TRANSITION_FROM_ADDRESS_BAR) { |
| 617 return false; |
| 618 } |
| 619 return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER; |
| 620 |
| 621 case download_util::Dangerous: |
| 622 return true; |
| 623 } |
| 624 NOTREACHED(); |
| 625 return false; |
| 626 } |
| 627 |
| 628 void DownloadTargetDeterminer::OnDownloadDestroyed( |
| 629 DownloadItem* download) { |
| 630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 631 DCHECK_EQ(download_, download); |
| 632 CancelOnFailureAndDeleteSelf(); |
| 633 } |
| 634 |
| 635 // static |
| 636 void DownloadTargetDeterminer::Start( |
| 637 content::DownloadItem* download, |
| 638 DownloadPrefs* download_prefs, |
| 639 DownloadFilePickerFactory* file_picker_factory, |
| 640 const base::FilePath& last_selected_directory, |
| 641 DownloadTargetDeterminerDelegate* delegate, |
| 642 const CompletionCallback& callback) { |
| 643 // DownloadTargetDeterminer owns itself and will self destruct when the job is |
| 644 // complete or the download item is destroyed. The callback is always invoked |
| 645 // asynchronously. |
| 646 new DownloadTargetDeterminer(download, download_prefs, file_picker_factory, |
| 647 last_selected_directory, delegate, callback); |
| 648 } |
| OLD | NEW |