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
ping_request() const39 const std::string& ping_request() const {
40 return ping_request_;
41 }
42
ping_response() const43 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
DownloadFeedbackPings(const std::string & ping_request,const std::string & ping_response)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
CreateForDownload(content::DownloadItem * download,const std::string & ping_request,const std::string & ping_response)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
FromDownload(const content::DownloadItem & download)69 DownloadFeedbackPings* DownloadFeedbackPings::FromDownload(
70 const content::DownloadItem& download) {
71 return static_cast<DownloadFeedbackPings*>(download.GetUserData(kPingKey));
72 }
73
74 } // namespace
75
DownloadFeedbackService(net::URLRequestContextGetter * request_context_getter,base::TaskRunner * file_task_runner)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
~DownloadFeedbackService()84 DownloadFeedbackService::~DownloadFeedbackService() {
85 DCHECK(CalledOnValidThread());
86 }
87
88 // static
MaybeStorePingsForDownload(DownloadProtectionService::DownloadCheckResult result,content::DownloadItem * download,const std::string & ping,const std::string & response)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
IsEnabledForDownload(const content::DownloadItem & download)106 bool DownloadFeedbackService::IsEnabledForDownload(
107 const content::DownloadItem& download) {
108 return !!DownloadFeedbackPings::FromDownload(download);
109 }
110
111 // static
GetPingsForDownloadForTesting(const content::DownloadItem & download,std::string * ping,std::string * response)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
RecordEligibleDownloadShown(content::DownloadDangerType danger_type)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
BeginFeedbackForDownload(content::DownloadItem * download)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
BeginFeedbackOrDeleteFile(const scoped_refptr<base::TaskRunner> & file_task_runner,const base::WeakPtr<DownloadFeedbackService> & service,const std::string & ping_request,const std::string & ping_response,const base::FilePath & path)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
StartPendingFeedback()170 void DownloadFeedbackService::StartPendingFeedback() {
171 DCHECK(!active_feedback_.empty());
172 active_feedback_.front()->Start(base::Bind(
173 &DownloadFeedbackService::FeedbackComplete, base::Unretained(this)));
174 }
175
BeginFeedback(const std::string & ping_request,const std::string & ping_response,const base::FilePath & path)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
FeedbackComplete()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