// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/safe_browsing/download_feedback_service.h" #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util_proxy.h" #include "base/metrics/histogram.h" #include "base/supports_user_data.h" #include "base/task_runner.h" #include "chrome/browser/safe_browsing/download_feedback.h" #include "content/public/browser/download_danger_type.h" #include "content/public/browser/download_item.h" namespace safe_browsing { namespace { const void* kPingKey = &kPingKey; class DownloadFeedbackPings : public base::SupportsUserData::Data { public: DownloadFeedbackPings(const std::string& ping_request, const std::string& ping_response); // Stores the ping data in the given |download|. static void CreateForDownload(content::DownloadItem* download, const std::string& ping_request, const std::string& ping_response); // Returns the DownloadFeedbackPings object associated with |download|. May // return NULL. static DownloadFeedbackPings* FromDownload( const content::DownloadItem& download); const std::string& ping_request() const { return ping_request_; } const std::string& ping_response() const { return ping_response_; } private: std::string ping_request_; std::string ping_response_; }; DownloadFeedbackPings::DownloadFeedbackPings(const std::string& ping_request, const std::string& ping_response) : ping_request_(ping_request), ping_response_(ping_response) { } // static void DownloadFeedbackPings::CreateForDownload( content::DownloadItem* download, const std::string& ping_request, const std::string& ping_response) { DownloadFeedbackPings* pings = new DownloadFeedbackPings(ping_request, ping_response); download->SetUserData(kPingKey, pings); } // static DownloadFeedbackPings* DownloadFeedbackPings::FromDownload( const content::DownloadItem& download) { return static_cast(download.GetUserData(kPingKey)); } } // namespace DownloadFeedbackService::DownloadFeedbackService( net::URLRequestContextGetter* request_context_getter, base::TaskRunner* file_task_runner) : request_context_getter_(request_context_getter), file_task_runner_(file_task_runner), weak_ptr_factory_(this) { } DownloadFeedbackService::~DownloadFeedbackService() { DCHECK(CalledOnValidThread()); } // static void DownloadFeedbackService::MaybeStorePingsForDownload( DownloadProtectionService::DownloadCheckResult result, content::DownloadItem* download, const std::string& ping, const std::string& response) { if (result != DownloadProtectionService::UNCOMMON && result != DownloadProtectionService::DANGEROUS_HOST) return; UMA_HISTOGRAM_COUNTS("SBDownloadFeedback.SizeEligibleKB", download->GetReceivedBytes() / 1024); if (download->GetReceivedBytes() > DownloadFeedback::kMaxUploadSize) return; DownloadFeedbackPings::CreateForDownload(download, ping, response); } // static bool DownloadFeedbackService::IsEnabledForDownload( const content::DownloadItem& download) { return !!DownloadFeedbackPings::FromDownload(download); } // static bool DownloadFeedbackService::GetPingsForDownloadForTesting( const content::DownloadItem& download, std::string* ping, std::string* response) { DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(download); if (!pings) return false; *ping = pings->ping_request(); *response = pings->ping_response(); return true; } // static void DownloadFeedbackService::RecordEligibleDownloadShown( content::DownloadDangerType danger_type) { UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Eligible", danger_type, content::DOWNLOAD_DANGER_TYPE_MAX); } void DownloadFeedbackService::BeginFeedbackForDownload( content::DownloadItem* download) { DCHECK(CalledOnValidThread()); UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Activations", download->GetDangerType(), content::DOWNLOAD_DANGER_TYPE_MAX); DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(*download); DCHECK(pings); download->StealDangerousDownload( base::Bind(&DownloadFeedbackService::BeginFeedbackOrDeleteFile, file_task_runner_, weak_ptr_factory_.GetWeakPtr(), pings->ping_request(), pings->ping_response())); } // static void DownloadFeedbackService::BeginFeedbackOrDeleteFile( const scoped_refptr& file_task_runner, const base::WeakPtr& service, const std::string& ping_request, const std::string& ping_response, const base::FilePath& path) { if (service) { service->BeginFeedback(ping_request, ping_response, path); } else { base::FileUtilProxy::DeleteFile(file_task_runner.get(), path, false, base::FileUtilProxy::StatusCallback()); } } void DownloadFeedbackService::StartPendingFeedback() { DCHECK(!active_feedback_.empty()); active_feedback_.front()->Start(base::Bind( &DownloadFeedbackService::FeedbackComplete, base::Unretained(this))); } void DownloadFeedbackService::BeginFeedback( const std::string& ping_request, const std::string& ping_response, const base::FilePath& path) { DCHECK(CalledOnValidThread()); DownloadFeedback* feedback = DownloadFeedback::Create(request_context_getter_.get(), file_task_runner_.get(), path, ping_request, ping_response); active_feedback_.push_back(feedback); UMA_HISTOGRAM_COUNTS_100("SBDownloadFeedback.ActiveFeedbacks", active_feedback_.size()); if (active_feedback_.size() == 1) StartPendingFeedback(); } void DownloadFeedbackService::FeedbackComplete() { DVLOG(1) << __FUNCTION__; DCHECK(CalledOnValidThread()); DCHECK(!active_feedback_.empty()); active_feedback_.erase(active_feedback_.begin()); if (!active_feedback_.empty()) StartPendingFeedback(); } } // namespace safe_browsing