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