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 17 namespace safe_browsing { 18 19 namespace { 20 21 const void* kPingKey = &kPingKey; 22 23 class 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 52 DownloadFeedbackPings::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 59 void 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 69 DownloadFeedbackPings* DownloadFeedbackPings::FromDownload( 70 const content::DownloadItem& download) { 71 return static_cast<DownloadFeedbackPings*>(download.GetUserData(kPingKey)); 72 } 73 74 } // namespace 75 76 DownloadFeedbackService::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 84 DownloadFeedbackService::~DownloadFeedbackService() { 85 DCHECK(CalledOnValidThread()); 86 } 87 88 // static 89 void 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 106 bool DownloadFeedbackService::IsEnabledForDownload( 107 const content::DownloadItem& download) { 108 return !!DownloadFeedbackPings::FromDownload(download); 109 } 110 111 // static 112 bool 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 126 void 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 134 void 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 154 void 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 170 void DownloadFeedbackService::StartPendingFeedback() { 171 DCHECK(!active_feedback_.empty()); 172 active_feedback_.front()->Start(base::Bind( 173 &DownloadFeedbackService::FeedbackComplete, base::Unretained(this))); 174 } 175 176 void 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 195 void 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