blob: 5129bcfc9d94bf93773b9206174f4cc5e44bd8ff [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/download/download_item_model.h"
#include <string>
#include "base/functional/bind.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/rtl.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_commands.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_item_warning_data.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/download/download_target_determiner.h"
#include "chrome/browser/download/download_ui_model.h"
#include "chrome/browser/download/offline_item_utils.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_item_rename_handler.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
#include "components/safe_browsing/content/common/proto/download_file_types.pb.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/text/bytes_formatting.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#endif
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "ui/views/vector_icons.h"
#endif
#if BUILDFLAG(FULL_SAFE_BROWSING)
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#endif
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
#include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h"
#include "chrome/browser/enterprise/connectors/connectors_manager.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/safe_browsing/download_protection/deep_scanning_request.h"
#endif // !BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "components/safe_browsing/content/common/file_type_policies.h"
#endif
using DangerUiPattern = DownloadUIModel::DangerUiPattern;
using download::DownloadItem;
using InsecureDownloadStatus = download::DownloadItem::InsecureDownloadStatus;
using safe_browsing::DownloadFileType;
using ReportThreatDetailsResult =
safe_browsing::PingManager::ReportThreatDetailsResult;
using TailoredVerdict = safe_browsing::ClientDownloadResponse::TailoredVerdict;
using TailoredWarningType = DownloadUIModel::TailoredWarningType;
namespace {
// Per DownloadItem data used by DownloadItemModel. The model doesn't keep any
// state since there could be multiple models associated with a single
// DownloadItem, and the lifetime of the model is shorter than the DownloadItem.
class DownloadItemModelData : public base::SupportsUserData::Data {
public:
~DownloadItemModelData() override = default;
// Get the DownloadItemModelData object for |download|. Returns NULL if
// there's no model data.
static const DownloadItemModelData* Get(const DownloadItem* download);
// Get the DownloadItemModelData object for |download|. Creates a model data
// object if not found. Always returns a non-NULL pointer, unless OOM.
static DownloadItemModelData* GetOrCreate(DownloadItem* download);
// Whether the download should be displayed in the download UI on desktop
// platforms. True by default.
bool should_show_in_ui_ = true;
// Whether the UI has been notified about this download.
bool was_ui_notified_ = false;
// Whether the download should be opened in the browser vs. the system handler
// for the file type.
std::optional<bool> should_prefer_opening_in_browser_;
// Danger level of the file determined based on the file type and whether
// there was a user action associated with the download.
DownloadFileType::DangerLevel danger_level_ = DownloadFileType::NOT_DANGEROUS;
// Whether the download is currently being revived.
bool is_being_revived_ = false;
// Whether the safe browsing download warning was shown (and recorded) earlier
// on the UI.
bool was_ui_warning_shown_ = false;
// Tracks when an ephemeral warning was first displayed on the UI. Does not
// persist on restart, though ephemeral warning downloads are canceled by
// then as all in-progress downloads are.
std::optional<base::Time> ephemeral_warning_ui_shown_time_;
// Was the UI actioned on. This defaults to true so that we don't show
// extraneous items in the partial view the first time the bubble pops up
// after a browser restart.
bool actioned_on_ = true;
private:
DownloadItemModelData();
static const char kKey[];
};
// static
const char DownloadItemModelData::kKey[] = "DownloadItemModelData key";
// static
const DownloadItemModelData* DownloadItemModelData::Get(
const DownloadItem* download) {
return static_cast<const DownloadItemModelData*>(download->GetUserData(kKey));
}
// static
DownloadItemModelData* DownloadItemModelData::GetOrCreate(
DownloadItem* download) {
DownloadItemModelData* data =
static_cast<DownloadItemModelData*>(download->GetUserData(kKey));
if (data == nullptr) {
data = new DownloadItemModelData();
data->should_show_in_ui_ = !download->IsTransient();
download->SetUserData(kKey, base::WrapUnique(data));
}
return data;
}
DownloadItemModelData::DownloadItemModelData() = default;
// This is for sending download reports from the download bubble UI on
// desktop, so it is not needed on Android.
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) && !BUILDFLAG(IS_ANDROID)
void MaybeSendDownloadReport(bool did_proceed,
download::DownloadItem* download) {
if (safe_browsing::SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service()) {
sb_service->SendDownloadReport(
download,
safe_browsing::ClientSafeBrowsingReportRequest::
DANGEROUS_DOWNLOAD_WARNING,
did_proceed, /*show_download_in_folder=*/std::nullopt);
}
}
#endif
} // namespace
// -----------------------------------------------------------------------------
// DownloadItemModel
// static
DownloadUIModel::DownloadUIModelPtr DownloadItemModel::Wrap(
download::DownloadItem* download) {
return std::make_unique<DownloadItemModel>(download);
}
// static
DownloadUIModel::DownloadUIModelPtr DownloadItemModel::Wrap(
download::DownloadItem* download,
std::unique_ptr<DownloadUIModel::StatusTextBuilderBase>
status_text_builder) {
return std::make_unique<DownloadItemModel>(download,
std::move(status_text_builder));
}
DownloadItemModel::DownloadItemModel(DownloadItem* download)
: DownloadItemModel(download, std::make_unique<StatusTextBuilder>()) {}
DownloadItemModel::DownloadItemModel(
download::DownloadItem* download,
std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> status_text_builder)
: DownloadUIModel(std::move(status_text_builder)), download_(download) {
download_->AddObserver(this);
}
DownloadItemModel::~DownloadItemModel() {
if (download_)
download_->RemoveObserver(this);
}
ContentId DownloadItemModel::GetContentId() const {
return OfflineItemUtils::GetContentIdForDownload(download_);
}
Profile* DownloadItemModel::profile() const {
return Profile::FromBrowserContext(
content::DownloadItemUtils::GetBrowserContext(download_));
}
std::u16string DownloadItemModel::GetTabProgressStatusText() const {
int64_t total = GetTotalBytes();
int64_t size;
auto* renamer = download_->GetRenameHandler();
if (renamer && renamer->ShowRenameProgress()) {
size = static_cast<int>(
(download_->GetReceivedBytes() + download_->GetUploadedBytes()) * 0.5);
} else {
size = download_->GetReceivedBytes();
}
std::u16string received_size = ui::FormatBytes(size);
std::u16string amount = received_size;
// Adjust both strings for the locale direction since we don't yet know which
// string we'll end up using for constructing the final progress string.
base::i18n::AdjustStringForLocaleDirection(&amount);
if (total) {
std::u16string total_text = ui::FormatBytes(total);
base::i18n::AdjustStringForLocaleDirection(&total_text);
base::i18n::AdjustStringForLocaleDirection(&received_size);
amount = l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_SIZE,
received_size, total_text);
} else {
amount.assign(received_size);
}
int64_t current_speed = download_->CurrentSpeed();
std::u16string speed_text = ui::FormatSpeed(current_speed);
base::i18n::AdjustStringForLocaleDirection(&speed_text);
base::TimeDelta remaining;
std::u16string time_remaining;
if (download_->IsPaused()) {
time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED);
} else if (download_->TimeRemaining(&remaining)) {
time_remaining =
ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
ui::TimeFormat::LENGTH_SHORT, remaining);
}
if (time_remaining.empty()) {
base::i18n::AdjustStringForLocaleDirection(&amount);
return l10n_util::GetStringFUTF16(
IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount);
}
return l10n_util::GetStringFUTF16(IDS_DOWNLOAD_TAB_PROGRESS_STATUS,
speed_text, amount, time_remaining);
}
int64_t DownloadItemModel::GetCompletedBytes() const {
auto* renamer = download_->GetRenameHandler();
if (renamer && renamer->ShowRenameProgress()) {
return static_cast<int>(
(download_->GetReceivedBytes() + download_->GetUploadedBytes()) * 0.5);
}
return download_->GetReceivedBytes();
}
int64_t DownloadItemModel::GetTotalBytes() const {
return download_->AllDataSaved() ? download_->GetReceivedBytes()
: download_->GetTotalBytes();
}
int64_t DownloadItemModel::GetUploadedBytes() const {
return download_->GetUploadedBytes();
}
// TODO(asanka,rdsmith): Once 'open' moves exclusively to the
// ChromeDownloadManagerDelegate, we should calculate the percentage here
// instead of calling into the DownloadItem.
int DownloadItemModel::PercentComplete() const {
auto* renamer = download_->GetRenameHandler();
if (renamer && renamer->ShowRenameProgress()) {
return static_cast<int>(
((download_->GetReceivedBytes() + download_->GetUploadedBytes()) * 0.5 *
100.0) /
GetTotalBytes());
}
return download_->PercentComplete();
}
bool DownloadItemModel::IsDangerous() const {
return download_->IsDangerous();
}
bool DownloadItemModel::MightBeMalicious() const {
return IsDangerous() && (download_->GetDangerType() !=
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
}
// If you change this definition of malicious, also update
// DownloadManagerImpl::BlockingShutdownCount.
bool DownloadItemModel::IsMalicious() const {
if (!MightBeMalicious())
return false;
switch (download_->GetDangerType()) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
return true;
case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
case download::DOWNLOAD_DANGER_TYPE_MAX:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
// We shouldn't get any of these due to the MightBeMalicious() test above.
NOTREACHED();
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
return false;
}
NOTREACHED();
}
bool DownloadItemModel::IsInsecure() const {
return download_->IsInsecure();
}
bool DownloadItemModel::ShouldShowDownloadStartedAnimation() const {
return !download_->IsSavePackageDownload() &&
!download_crx_util::IsTrustedExtensionDownload(profile(), *download_);
}
bool DownloadItemModel::ShouldNotifyUI() const {
if (download_->IsTransient())
return false;
// The browser is only interested in new active downloads. History downloads
// that are completed or interrupted are not displayed in the UI. The
// downloads page independently listens for new downloads when it is active.
// Note that the UI will be notified of downloads even if they are not meant
// to be displayed in the desktop downloads UI (i.e. ShouldShowInUi() returns
// false). This is because:
// * The desktop UI (download bubble) isn't the only UI. E.g. on Android,
// there is a separate download UI implemented in Java.
// * There are other UI activities that need to be performed. E.g. if the
// download was initiated from a new tab, then that tab should be closed.
return download_->GetDownloadCreationType() !=
download::DownloadItem::DownloadCreationType::
TYPE_HISTORY_IMPORT ||
download_->GetState() == download::DownloadItem::IN_PROGRESS;
}
bool DownloadItemModel::WasUINotified() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
return data && data->was_ui_notified_;
}
void DownloadItemModel::SetWasUINotified(bool was_ui_notified) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->was_ui_notified_ = was_ui_notified;
}
bool DownloadItemModel::WasActionedOn() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
if (!data) {
return DownloadUIModel::WasActionedOn();
}
return data && data->actioned_on_;
}
void DownloadItemModel::SetActionedOn(bool actioned_on) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->actioned_on_ = actioned_on;
}
bool DownloadItemModel::WasUIWarningShown() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
return data && data->was_ui_warning_shown_;
}
void DownloadItemModel::SetWasUIWarningShown(bool was_ui_warning_shown) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->was_ui_warning_shown_ = was_ui_warning_shown;
}
std::optional<base::Time> DownloadItemModel::GetEphemeralWarningUiShownTime()
const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
return data ? data->ephemeral_warning_ui_shown_time_
: std::optional<base::Time>();
}
void DownloadItemModel::SetEphemeralWarningUiShownTime(
std::optional<base::Time> ephemeral_warning_ui_shown_time) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->ephemeral_warning_ui_shown_time_ = ephemeral_warning_ui_shown_time;
}
bool DownloadItemModel::ShouldPreferOpeningInBrowser() {
const DownloadItemModelData* data =
DownloadItemModelData::GetOrCreate(download_);
#if !BUILDFLAG(IS_ANDROID)
if (!data->should_prefer_opening_in_browser_) {
base::FilePath path = GetTargetFilePath();
std::string mime_type = GetMimeType();
DetermineAndSetShouldPreferOpeningInBrowser(
path,
DownloadTargetDeterminer::DetermineIfHandledSafelyHelperSynchronous(
download_, path, mime_type));
}
#endif // !BUILDFLAG(IS_ANDROID)
return data->should_prefer_opening_in_browser_.value_or(false);
}
void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->should_prefer_opening_in_browser_ = preference;
}
DownloadFileType::DangerLevel DownloadItemModel::GetDangerLevel() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
return data ? data->danger_level_ : DownloadFileType::NOT_DANGEROUS;
}
void DownloadItemModel::SetDangerLevel(
DownloadFileType::DangerLevel danger_level) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->danger_level_ = danger_level;
}
download::DownloadItem::InsecureDownloadStatus
DownloadItemModel::GetInsecureDownloadStatus() const {
return download_->GetInsecureDownloadStatus();
}
bool DownloadItemModel::IsBeingRevived() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
return data && data->is_being_revived_;
}
void DownloadItemModel::SetIsBeingRevived(bool is_being_revived) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->is_being_revived_ = is_being_revived;
}
const download::DownloadItem* DownloadItemModel::GetDownloadItem() const {
return download_;
}
base::FilePath DownloadItemModel::GetFileNameToReportUser() const {
return download_->GetFileNameToReportUser();
}
base::FilePath DownloadItemModel::GetTargetFilePath() const {
return download_->GetTargetFilePath();
}
void DownloadItemModel::OpenDownload() {
download_->OpenDownload();
}
download::DownloadItem::DownloadState DownloadItemModel::GetState() const {
return download_->GetState();
}
bool DownloadItemModel::IsPaused() const {
return download_->IsPaused();
}
download::DownloadDangerType DownloadItemModel::GetDangerType() const {
return download_->GetDangerType();
}
bool DownloadItemModel::GetOpenWhenComplete() const {
return download_->GetOpenWhenComplete();
}
bool DownloadItemModel::IsOpenWhenCompleteByPolicy() const {
return download_->ShouldOpenFileByPolicyBasedOnExtension();
}
bool DownloadItemModel::TimeRemaining(base::TimeDelta* remaining) const {
return download_->TimeRemaining(remaining);
}
base::Time DownloadItemModel::GetStartTime() const {
return download_->GetStartTime();
}
base::Time DownloadItemModel::GetEndTime() const {
return download_->GetEndTime();
}
bool DownloadItemModel::GetOpened() const {
return download_->GetOpened();
}
void DownloadItemModel::SetOpened(bool opened) {
download_->SetOpened(opened);
}
bool DownloadItemModel::IsDone() const {
return download_->IsDone();
}
void DownloadItemModel::Pause() {
download_->Pause();
}
void DownloadItemModel::Resume() {
download_->Resume(true /* has_user_gesture */);
}
void DownloadItemModel::Cancel(bool user_cancel) {
download_->Cancel(user_cancel);
}
void DownloadItemModel::Remove() {
download_->Remove();
}
void DownloadItemModel::SetOpenWhenComplete(bool open) {
download_->SetOpenWhenComplete(open);
}
base::FilePath DownloadItemModel::GetFullPath() const {
return download_->GetFullPath();
}
bool DownloadItemModel::CanResume() const {
return download_->CanResume();
}
bool DownloadItemModel::AllDataSaved() const {
return download_->AllDataSaved();
}
bool DownloadItemModel::GetFileExternallyRemoved() const {
return download_->GetFileExternallyRemoved();
}
GURL DownloadItemModel::GetURL() const {
return download_->GetURL();
}
bool DownloadItemModel::HasUserGesture() const {
return download_->HasUserGesture();
}
void DownloadItemModel::OnDownloadUpdated(DownloadItem* download) {
if (delegate_)
delegate_->OnDownloadUpdated();
}
void DownloadItemModel::OnDownloadOpened(DownloadItem* download) {
if (delegate_)
delegate_->OnDownloadOpened();
}
void DownloadItemModel::OnDownloadDestroyed(DownloadItem* download) {
ContentId id = GetContentId();
download_->RemoveObserver(this);
download_ = nullptr;
// The object could get deleted after this.
if (delegate_)
delegate_->OnDownloadDestroyed(id);
}
void DownloadItemModel::OpenUsingPlatformHandler() {
DownloadCoreService* download_core_service =
DownloadCoreServiceFactory::GetForBrowserContext(
content::DownloadItemUtils::GetBrowserContext(download_));
if (!download_core_service)
return;
ChromeDownloadManagerDelegate* delegate =
download_core_service->GetDownloadManagerDelegate();
if (!delegate)
return;
delegate->OpenDownloadUsingPlatformHandler(download_);
RecordDownloadOpen(DOWNLOAD_OPEN_METHOD_USER_PLATFORM,
download_->GetMimeType());
}
#if BUILDFLAG(IS_CHROMEOS)
std::optional<DownloadCommands::Command>
DownloadItemModel::MaybeGetMediaAppAction() const {
std::string mime_type = GetMimeType();
if (mime_type == "application/pdf") {
return DownloadCommands::EDIT_WITH_MEDIA_APP;
}
if (base::StartsWith(mime_type, "audio/", base::CompareCase::SENSITIVE) ||
base::StartsWith(mime_type, "video/", base::CompareCase::SENSITIVE)) {
return DownloadCommands::OPEN_WITH_MEDIA_APP;
}
return std::nullopt;
}
void DownloadItemModel::OpenUsingMediaApp() {
ash::SystemAppLaunchParams params;
params.launch_paths.push_back(GetFullPath());
ash::LaunchSystemWebAppAsync(profile(), ash::SystemWebAppType::MEDIA, params);
RecordDownloadOpen(DOWNLOAD_OPEN_METHOD_MEDIA_APP, GetMimeType());
}
#endif
#if !BUILDFLAG(IS_ANDROID)
bool DownloadItemModel::IsCommandEnabled(
const DownloadCommands* download_commands,
DownloadCommands::Command command) const {
switch (command) {
case DownloadCommands::SHOW_IN_FOLDER:
return download_->CanShowInFolder();
case DownloadCommands::OPEN_WHEN_COMPLETE:
return download_->CanOpenDownload() &&
!download_crx_util::IsExtensionDownload(*download_);
case DownloadCommands::PLATFORM_OPEN:
return download_->CanOpenDownload() &&
!download_crx_util::IsExtensionDownload(*download_);
case DownloadCommands::ALWAYS_OPEN_TYPE:
// For temporary downloads, the target filename might be a temporary
// filename. Don't base an "Always open" decision based on it. Also
// exclude extensions.
return download_->CanOpenDownload() &&
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
safe_browsing::FileTypePolicies::GetInstance()
->IsAllowedToOpenAutomatically(
download_->GetTargetFilePath()) &&
#endif
!download_crx_util::IsExtensionDownload(*download_);
case DownloadCommands::PAUSE:
return !download_->IsSavePackageDownload() &&
DownloadUIModel::IsCommandEnabled(download_commands, command);
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP: {
#if BUILDFLAG(IS_CHROMEOS)
std::optional<DownloadCommands::Command> media_app_command =
MaybeGetMediaAppAction();
return media_app_command == command && download_->CanOpenDownload() &&
!download_crx_util::IsExtensionDownload(*download_);
#else
return false;
#endif
}
case DownloadCommands::CANCEL:
case DownloadCommands::RESUME:
case DownloadCommands::COPY_TO_CLIPBOARD:
case DownloadCommands::DISCARD:
case DownloadCommands::KEEP:
case DownloadCommands::LEARN_MORE_SCANNING:
case DownloadCommands::LEARN_MORE_INTERRUPTED:
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
case DownloadCommands::DEEP_SCAN:
case DownloadCommands::BYPASS_DEEP_SCANNING:
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::CANCEL_DEEP_SCAN:
return DownloadUIModel::IsCommandEnabled(download_commands, command);
}
NOTREACHED();
}
bool DownloadItemModel::IsCommandChecked(
const DownloadCommands* download_commands,
DownloadCommands::Command command) const {
switch (command) {
case DownloadCommands::OPEN_WHEN_COMPLETE:
return download_->GetOpenWhenComplete() ||
download_crx_util::IsExtensionDownload(*download_);
case DownloadCommands::ALWAYS_OPEN_TYPE:
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)
if (download_commands->CanOpenPdfInSystemViewer()) {
DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext(profile());
return prefs->ShouldOpenPdfInSystemReader();
}
#endif
return download_->ShouldOpenFileBasedOnExtension();
case DownloadCommands::PAUSE:
case DownloadCommands::RESUME:
return IsPaused();
case DownloadCommands::SHOW_IN_FOLDER:
case DownloadCommands::PLATFORM_OPEN:
case DownloadCommands::CANCEL:
case DownloadCommands::DISCARD:
case DownloadCommands::KEEP:
case DownloadCommands::LEARN_MORE_SCANNING:
case DownloadCommands::LEARN_MORE_INTERRUPTED:
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
case DownloadCommands::COPY_TO_CLIPBOARD:
case DownloadCommands::DEEP_SCAN:
case DownloadCommands::BYPASS_DEEP_SCANNING:
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::CANCEL_DEEP_SCAN:
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP:
return false;
}
return false;
}
void DownloadItemModel::ExecuteCommand(DownloadCommands* download_commands,
DownloadCommands::Command command) {
switch (command) {
case DownloadCommands::SHOW_IN_FOLDER:
download_->ShowDownloadInShell();
break;
case DownloadCommands::OPEN_WHEN_COMPLETE:
download_->OpenDownload();
break;
case DownloadCommands::ALWAYS_OPEN_TYPE: {
bool is_checked = IsCommandChecked(download_commands,
DownloadCommands::ALWAYS_OPEN_TYPE);
DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext(profile());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)
if (download_commands->CanOpenPdfInSystemViewer()) {
prefs->SetShouldOpenPdfInSystemReader(!is_checked);
SetShouldPreferOpeningInBrowser(is_checked);
break;
}
#endif
base::FilePath path = download_->GetTargetFilePath();
if (is_checked)
prefs->DisableAutoOpenByUserBasedOnExtension(path);
else
prefs->EnableAutoOpenByUserBasedOnExtension(path);
break;
}
case DownloadCommands::BYPASS_DEEP_SCANNING_AND_OPEN:
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
SetOpenWhenComplete(true);
#endif
[[fallthrough]];
case DownloadCommands::BYPASS_DEEP_SCANNING:
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
CompleteSafeBrowsingScan();
if (download_->GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING ||
download_->GetDangerType() ==
download::
DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING) {
safe_browsing::LogLocalDecryptionEvent(
safe_browsing::DeepScanEvent::kPromptBypassed);
} else {
LogDeepScanEvent(download_,
safe_browsing::DeepScanEvent::kPromptBypassed);
}
#endif
[[fallthrough]];
case DownloadCommands::KEEP:
if (IsInsecure()) {
download_->ValidateInsecureDownload();
break;
}
if (GetDangerType() == download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING) {
break;
}
DCHECK(IsDangerous());
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
MaybeSendDownloadReport(/*did_proceed=*/true, download_);
#endif
download_->ValidateDangerousDownload();
break;
case DownloadCommands::DISCARD:
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
MaybeSendDownloadReport(/*did_proceed=*/false, download_);
if (GetDangerType() == download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING) {
LogDeepScanEvent(download_, safe_browsing::DeepScanEvent::kScanDeleted);
}
#endif
DownloadUIModel::ExecuteCommand(download_commands, command);
break;
case DownloadCommands::LEARN_MORE_SCANNING: {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
using safe_browsing::DownloadProtectionService;
safe_browsing::SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service();
DownloadProtectionService* protection_service =
(sb_service ? sb_service->download_protection_service() : nullptr);
if (protection_service)
protection_service->ShowDetailsForDownload(
download_, download_commands->GetBrowser());
break;
#else
// Should only be getting invoked if we are using safe browsing.
NOTREACHED();
#endif
}
case DownloadCommands::PLATFORM_OPEN:
case DownloadCommands::CANCEL:
case DownloadCommands::LEARN_MORE_INTERRUPTED:
case DownloadCommands::LEARN_MORE_INSECURE_DOWNLOAD:
case DownloadCommands::LEARN_MORE_DOWNLOAD_BLOCKED:
case DownloadCommands::OPEN_SAFE_BROWSING_SETTING:
case DownloadCommands::PAUSE:
case DownloadCommands::RESUME:
case DownloadCommands::COPY_TO_CLIPBOARD:
case DownloadCommands::REVIEW:
case DownloadCommands::RETRY:
case DownloadCommands::OPEN_WITH_MEDIA_APP:
case DownloadCommands::EDIT_WITH_MEDIA_APP:
DownloadUIModel::ExecuteCommand(download_commands, command);
break;
case DownloadCommands::DEEP_SCAN: {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) && !BUILDFLAG(IS_ANDROID)
// Deep scanning is not supported on Android.
safe_browsing::DownloadProtectionService::UploadForConsumerDeepScanning(
download_,
DownloadItemWarningData::DeepScanTrigger::TRIGGER_CONSUMER_PROMPT,
/*password=*/std::nullopt);
#endif
break;
}
case DownloadCommands::CANCEL_DEEP_SCAN: {
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
DownloadCoreService* download_core_service =
DownloadCoreServiceFactory::GetForBrowserContext(
content::DownloadItemUtils::GetBrowserContext(download_));
DCHECK(download_core_service);
ChromeDownloadManagerDelegate* delegate =
download_core_service->GetDownloadManagerDelegate();
DCHECK(delegate);
LogDeepScanEvent(download_, safe_browsing::DeepScanEvent::kScanCanceled);
delegate->CheckClientDownloadDone(
download_->GetId(),
safe_browsing::DownloadCheckResult::PROMPT_FOR_SCANNING);
#endif
break;
}
}
}
TailoredWarningType DownloadItemModel::GetTailoredWarningType() const {
#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
download::DownloadDangerType danger_type = GetDangerType();
TailoredVerdict tailored_verdict = safe_browsing::DownloadProtectionService::
GetDownloadProtectionTailoredVerdict(download_);
if (danger_type == download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT &&
tailored_verdict.tailored_verdict_type() ==
TailoredVerdict::SUSPICIOUS_ARCHIVE) {
return TailoredWarningType::kSuspiciousArchive;
}
if (danger_type ==
download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE &&
tailored_verdict.tailored_verdict_type() ==
TailoredVerdict::COOKIE_THEFT) {
return TailoredWarningType::kCookieTheft;
}
#endif
return TailoredWarningType::kNoTailoredWarning;
}
DangerUiPattern DownloadItemModel::GetDangerUiPattern() const {
// Keep logic here in sync with DownloadBubbleRowViewInfo and
// IconAndColor code in download_bubble_info_utils.cc, and
// chrome://downloads WebUI frontend code.
DownloadItem::DownloadState state = GetState();
// Error conditions, including cancellations, have a "download off" icon or
// some combination of "info" icon and red or gray.
if (state == DownloadItem::CANCELLED || state == DownloadItem::INTERRUPTED) {
return DangerUiPattern::kOther;
} else if (state == DownloadItem::MAX_DOWNLOAD_STATE) {
NOTREACHED();
}
switch (GetInsecureDownloadStatus()) {
case DownloadItem::InsecureDownloadStatus::BLOCK:
case DownloadItem::InsecureDownloadStatus::WARN:
return DangerUiPattern::kSuspicious;
case DownloadItem::InsecureDownloadStatus::UNKNOWN:
case DownloadItem::InsecureDownloadStatus::SAFE:
case DownloadItem::InsecureDownloadStatus::VALIDATED:
case DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
break;
}
switch (GetTailoredWarningType()) {
case TailoredWarningType::kCookieTheft:
return DangerUiPattern::kDangerous;
case TailoredWarningType::kSuspiciousArchive:
return DangerUiPattern::kSuspicious;
case TailoredWarningType::kNoTailoredWarning:
break;
}
switch (GetDangerType()) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
return DangerUiPattern::kDangerous;
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
return DangerUiPattern::kSuspicious;
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
return DangerUiPattern::kOther;
// TODO(crbug.com/329254526): The following two may be wrong.
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
break;
case download::DOWNLOAD_DANGER_TYPE_MAX:
NOTREACHED();
}
return DangerUiPattern::kNormal;
}
bool DownloadItemModel::ShouldShowInUi() const {
const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
if (data) {
return data->should_show_in_ui_;
}
return !download_->IsTransient();
}
void DownloadItemModel::SetShouldShowInUi(bool should_show) {
DownloadItemModelData* data = DownloadItemModelData::GetOrCreate(download_);
data->should_show_in_ui_ = should_show;
}
bool DownloadItemModel::ShouldShowInBubble() const {
// Downloads blocked by local policies should be notified, otherwise users
// won't get any feedback that the download has failed.
bool should_notify =
download_->GetLastReason() ==
download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED &&
download_->GetInsecureDownloadStatus() !=
download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK;
// Wait until the target path is determined.
if (download_->GetTargetFilePath().empty() && !should_notify) {
return false;
}
if (IsEphemeralWarning()) {
// Ephemeral warnings become canceled if the browser shuts down (or an hour
// after being displayed if the user hasn't acted on them). These should no
// longer be shown, regardless of what the shown time is set to.
if (download_->GetState() == download::DownloadItem::CANCELLED) {
return false;
}
// If the user hasn't acted on an ephemeral warning within 5 minutes, it
// should no longer be shown in the bubble. (IsEphemeralWarning no longer
// returns true once the user has acted on the warning.)
auto warning_shown_time = GetEphemeralWarningUiShownTime();
if (warning_shown_time.has_value() &&
base::Time::Now() - warning_shown_time.value() >
kEphemeralWarningLifetimeOnBubble) {
return false;
}
}
return DownloadUIModel::ShouldShowInBubble();
}
#endif // !BUILDFLAG(IS_ANDROID)
bool DownloadItemModel::IsEphemeralWarning() const {
// On Android, insecure downloads display a InsecureDownloadDialog prior to
// the download and do not display any warning in the UI, so there is no
// associated warning message to hide/cancel.
#if !BUILDFLAG(IS_ANDROID)
switch (GetInsecureDownloadStatus()) {
case download::DownloadItem::InsecureDownloadStatus::BLOCK:
case download::DownloadItem::InsecureDownloadStatus::WARN:
return true;
case download::DownloadItem::InsecureDownloadStatus::UNKNOWN:
case download::DownloadItem::InsecureDownloadStatus::SAFE:
case download::DownloadItem::InsecureDownloadStatus::VALIDATED:
case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
break;
}
#endif
switch (GetDangerType()) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
return true;
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
case download::DOWNLOAD_DANGER_TYPE_MAX:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
return false;
}
}
offline_items_collection::FailState DownloadItemModel::GetLastFailState()
const {
return OfflineItemUtils::ConvertDownloadInterruptReasonToFailState(
download_->GetLastReason());
}
std::string DownloadItemModel::GetMimeType() const {
return download_->GetMimeType();
}
bool DownloadItemModel::IsExtensionDownload() const {
return download_crx_util::IsExtensionDownload(*download_);
}
#if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
void DownloadItemModel::CompleteSafeBrowsingScan() {
if (download_->IsSavePackageDownload()) {
download_->OnAsyncScanningCompleted(
download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED);
enterprise_connectors::RunSavePackageScanningCallback(download_, true);
} else {
ChromeDownloadManagerDelegate::SafeBrowsingState* state =
static_cast<ChromeDownloadManagerDelegate::SafeBrowsingState*>(
download_->GetUserData(
&ChromeDownloadManagerDelegate::SafeBrowsingState::
kSafeBrowsingUserDataKey));
state->CompleteDownload();
}
}
void DownloadItemModel::ReviewScanningVerdict(
content::WebContents* web_contents) {
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
auto command_callback =
[](std::unique_ptr<DownloadItemModel> model,
std::unique_ptr<DownloadCommands> download_commands,
DownloadCommands::Command command) {
model->ExecuteCommand(download_commands.get(), command);
};
enterprise_connectors::ShowDownloadReviewDialog(
GetFileNameToReportUser().LossyDisplayName(), profile(), download_,
web_contents,
base::BindOnce(
command_callback, std::make_unique<DownloadItemModel>(download_),
std::make_unique<DownloadCommands>(DownloadUIModel::GetWeakPtr()),
DownloadCommands::KEEP),
base::BindOnce(
command_callback, std::make_unique<DownloadItemModel>(download_),
std::make_unique<DownloadCommands>(DownloadUIModel::GetWeakPtr()),
DownloadCommands::DISCARD));
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
}
#endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION)
bool DownloadItemModel::ShouldShowDropdown() const {
// We don't show the dropdown for dangerous file types or for files
// blocked by enterprise policy.
if (IsDangerous() && GetState() != DownloadItem::CANCELLED &&
!MightBeMalicious()) {
return false;
}
if (GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK ||
GetDangerType() ==
download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED ||
GetDangerType() == download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE ||
GetDangerType() == download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED) {
return false;
}
return true;
}
void DownloadItemModel::DetermineAndSetShouldPreferOpeningInBrowser(
const base::FilePath& target_path,
bool is_filetype_handled_safely) {
DownloadCoreService* download_core_service =
DownloadCoreServiceFactory::GetForBrowserContext(
content::DownloadItemUtils::GetBrowserContext(download_));
if (!download_core_service)
return;
ChromeDownloadManagerDelegate* delegate =
download_core_service->GetDownloadManagerDelegate();
if (!delegate)
return;
if (!target_path.empty() &&
delegate->IsOpenInBrowserPreferredForFile(target_path) &&
is_filetype_handled_safely) {
SetShouldPreferOpeningInBrowser(true);
return;
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
if (download_->GetOriginalMimeType() == "application/x-x509-user-cert") {
SetShouldPreferOpeningInBrowser(true);
return;
}
#endif
SetShouldPreferOpeningInBrowser(false);
}
bool DownloadItemModel::IsTopLevelEncryptedArchive() const {
return DownloadItemWarningData::IsTopLevelEncryptedArchive(download_);
}