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/safe_browsing/download_feedback_service.h" 6 7#include "base/bind.h" 8#include "base/files/file_path.h" 9#include "base/files/file_util_proxy.h" 10#include "base/metrics/histogram.h" 11#include "base/supports_user_data.h" 12#include "base/task_runner.h" 13#include "chrome/browser/safe_browsing/download_feedback.h" 14#include "content/public/browser/download_danger_type.h" 15#include "content/public/browser/download_item.h" 16 17namespace safe_browsing { 18 19namespace { 20 21const void* kPingKey = &kPingKey; 22 23class DownloadFeedbackPings : public base::SupportsUserData::Data { 24 public: 25 DownloadFeedbackPings(const std::string& ping_request, 26 const std::string& ping_response); 27 28 // Stores the ping data in the given |download|. 29 static void CreateForDownload(content::DownloadItem* download, 30 const std::string& ping_request, 31 const std::string& ping_response); 32 33 // Returns the DownloadFeedbackPings object associated with |download|. May 34 // return NULL. 35 static DownloadFeedbackPings* FromDownload( 36 const content::DownloadItem& download); 37 38 39 const std::string& ping_request() const { 40 return ping_request_; 41 } 42 43 const std::string& ping_response() const { 44 return ping_response_; 45 } 46 47 private: 48 std::string ping_request_; 49 std::string ping_response_; 50}; 51 52DownloadFeedbackPings::DownloadFeedbackPings(const std::string& ping_request, 53 const std::string& ping_response) 54 : ping_request_(ping_request), 55 ping_response_(ping_response) { 56} 57 58// static 59void DownloadFeedbackPings::CreateForDownload( 60 content::DownloadItem* download, 61 const std::string& ping_request, 62 const std::string& ping_response) { 63 DownloadFeedbackPings* pings = new DownloadFeedbackPings(ping_request, 64 ping_response); 65 download->SetUserData(kPingKey, pings); 66} 67 68// static 69DownloadFeedbackPings* DownloadFeedbackPings::FromDownload( 70 const content::DownloadItem& download) { 71 return static_cast<DownloadFeedbackPings*>(download.GetUserData(kPingKey)); 72} 73 74} // namespace 75 76DownloadFeedbackService::DownloadFeedbackService( 77 net::URLRequestContextGetter* request_context_getter, 78 base::TaskRunner* file_task_runner) 79 : request_context_getter_(request_context_getter), 80 file_task_runner_(file_task_runner), 81 weak_ptr_factory_(this) { 82} 83 84DownloadFeedbackService::~DownloadFeedbackService() { 85 DCHECK(CalledOnValidThread()); 86} 87 88// static 89void DownloadFeedbackService::MaybeStorePingsForDownload( 90 DownloadProtectionService::DownloadCheckResult result, 91 content::DownloadItem* download, 92 const std::string& ping, 93 const std::string& response) { 94 if (result != DownloadProtectionService::UNCOMMON && 95 result != DownloadProtectionService::DANGEROUS_HOST) 96 return; 97 UMA_HISTOGRAM_COUNTS("SBDownloadFeedback.SizeEligibleKB", 98 download->GetReceivedBytes() / 1024); 99 if (download->GetReceivedBytes() > DownloadFeedback::kMaxUploadSize) 100 return; 101 102 DownloadFeedbackPings::CreateForDownload(download, ping, response); 103} 104 105// static 106bool DownloadFeedbackService::IsEnabledForDownload( 107 const content::DownloadItem& download) { 108 return !!DownloadFeedbackPings::FromDownload(download); 109} 110 111// static 112bool DownloadFeedbackService::GetPingsForDownloadForTesting( 113 const content::DownloadItem& download, 114 std::string* ping, 115 std::string* response) { 116 DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(download); 117 if (!pings) 118 return false; 119 120 *ping = pings->ping_request(); 121 *response = pings->ping_response(); 122 return true; 123} 124 125// static 126void DownloadFeedbackService::RecordEligibleDownloadShown( 127 content::DownloadDangerType danger_type) { 128 UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Eligible", 129 danger_type, 130 content::DOWNLOAD_DANGER_TYPE_MAX); 131} 132 133 134void DownloadFeedbackService::BeginFeedbackForDownload( 135 content::DownloadItem* download) { 136 DCHECK(CalledOnValidThread()); 137 138 UMA_HISTOGRAM_ENUMERATION("SBDownloadFeedback.Activations", 139 download->GetDangerType(), 140 content::DOWNLOAD_DANGER_TYPE_MAX); 141 142 DownloadFeedbackPings* pings = DownloadFeedbackPings::FromDownload(*download); 143 DCHECK(pings); 144 145 download->StealDangerousDownload( 146 base::Bind(&DownloadFeedbackService::BeginFeedbackOrDeleteFile, 147 file_task_runner_, 148 weak_ptr_factory_.GetWeakPtr(), 149 pings->ping_request(), 150 pings->ping_response())); 151} 152 153// static 154void DownloadFeedbackService::BeginFeedbackOrDeleteFile( 155 const scoped_refptr<base::TaskRunner>& file_task_runner, 156 const base::WeakPtr<DownloadFeedbackService>& service, 157 const std::string& ping_request, 158 const std::string& ping_response, 159 const base::FilePath& path) { 160 if (service) { 161 service->BeginFeedback(ping_request, ping_response, path); 162 } else { 163 base::FileUtilProxy::DeleteFile(file_task_runner.get(), 164 path, 165 false, 166 base::FileUtilProxy::StatusCallback()); 167 } 168} 169 170void DownloadFeedbackService::StartPendingFeedback() { 171 DCHECK(!active_feedback_.empty()); 172 active_feedback_.front()->Start(base::Bind( 173 &DownloadFeedbackService::FeedbackComplete, base::Unretained(this))); 174} 175 176void DownloadFeedbackService::BeginFeedback( 177 const std::string& ping_request, 178 const std::string& ping_response, 179 const base::FilePath& path) { 180 DCHECK(CalledOnValidThread()); 181 DownloadFeedback* feedback = 182 DownloadFeedback::Create(request_context_getter_.get(), 183 file_task_runner_.get(), 184 path, 185 ping_request, 186 ping_response); 187 active_feedback_.push_back(feedback); 188 UMA_HISTOGRAM_COUNTS_100("SBDownloadFeedback.ActiveFeedbacks", 189 active_feedback_.size()); 190 191 if (active_feedback_.size() == 1) 192 StartPendingFeedback(); 193} 194 195void DownloadFeedbackService::FeedbackComplete() { 196 DVLOG(1) << __FUNCTION__; 197 DCHECK(CalledOnValidThread()); 198 DCHECK(!active_feedback_.empty()); 199 active_feedback_.erase(active_feedback_.begin()); 200 if (!active_feedback_.empty()) 201 StartPendingFeedback(); 202} 203 204} // namespace safe_browsing 205