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.h"
6
7#include "base/bind.h"
8#include "base/files/file_util_proxy.h"
9#include "base/metrics/histogram.h"
10#include "base/task_runner.h"
11#include "chrome/common/safe_browsing/csd.pb.h"
12#include "net/base/net_errors.h"
13
14namespace safe_browsing {
15
16namespace {
17
18// This enum is used by histograms.  Do not change the ordering or remove items.
19enum UploadResultType {
20  UPLOAD_SUCCESS,
21  UPLOAD_CANCELLED,
22  UPLOAD_METADATA_NET_ERROR,
23  UPLOAD_METADATA_RESPONSE_ERROR,
24  UPLOAD_FILE_NET_ERROR,
25  UPLOAD_FILE_RESPONSE_ERROR,
26  UPLOAD_COMPLETE_RESPONSE_ERROR,
27  // Memory space for histograms is determined by the max.
28  // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
29  UPLOAD_RESULT_MAX
30};
31
32// Handles the uploading of a single downloaded binary to the safebrowsing
33// download feedback service.
34class DownloadFeedbackImpl : public DownloadFeedback {
35 public:
36  DownloadFeedbackImpl(net::URLRequestContextGetter* request_context_getter,
37                       base::TaskRunner* file_task_runner,
38                       const base::FilePath& file_path,
39                       const std::string& ping_request,
40                       const std::string& ping_response);
41  virtual ~DownloadFeedbackImpl();
42
43  virtual void Start(const base::Closure& finish_callback) OVERRIDE;
44
45  virtual const std::string& GetPingRequestForTesting() const OVERRIDE {
46    return ping_request_;
47  }
48
49  virtual const std::string& GetPingResponseForTesting() const OVERRIDE {
50    return ping_response_;
51  }
52
53 private:
54  // Callback for TwoPhaseUploader completion.  Relays the result to the
55  // |finish_callback|.
56  void FinishedUpload(base::Closure finish_callback,
57                      TwoPhaseUploader::State state,
58                      int net_error,
59                      int response_code,
60                      const std::string& response);
61
62  void RecordUploadResult(UploadResultType result);
63
64  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
65  scoped_refptr<base::TaskRunner> file_task_runner_;
66  const base::FilePath file_path_;
67  int64 file_size_;
68
69  // The safebrowsing request and response of checking that this binary is
70  // unsafe.
71  std::string ping_request_;
72  std::string ping_response_;
73
74  scoped_ptr<TwoPhaseUploader> uploader_;
75
76  DISALLOW_COPY_AND_ASSIGN(DownloadFeedbackImpl);
77};
78
79DownloadFeedbackImpl::DownloadFeedbackImpl(
80    net::URLRequestContextGetter* request_context_getter,
81    base::TaskRunner* file_task_runner,
82    const base::FilePath& file_path,
83    const std::string& ping_request,
84    const std::string& ping_response)
85    : request_context_getter_(request_context_getter),
86      file_task_runner_(file_task_runner),
87      file_path_(file_path),
88      file_size_(-1),
89      ping_request_(ping_request),
90      ping_response_(ping_response) {
91  DVLOG(1) << "DownloadFeedback constructed " << this << " for "
92           << file_path.AsUTF8Unsafe();
93}
94
95DownloadFeedbackImpl::~DownloadFeedbackImpl() {
96  DCHECK(CalledOnValidThread());
97  DVLOG(1) << "DownloadFeedback destructed " << this;
98
99  if (uploader_) {
100    RecordUploadResult(UPLOAD_CANCELLED);
101    // Destroy the uploader before attempting to delete the file.
102    uploader_.reset();
103  }
104
105  base::FileUtilProxy::DeleteFile(file_task_runner_.get(),
106                                  file_path_,
107                                  false,
108                                  base::FileUtilProxy::StatusCallback());
109}
110
111void DownloadFeedbackImpl::Start(const base::Closure& finish_callback) {
112  DCHECK(CalledOnValidThread());
113  DCHECK(!uploader_);
114
115  ClientDownloadReport report_metadata;
116
117  bool r = report_metadata.mutable_download_request()->ParseFromString(
118      ping_request_);
119  DCHECK(r);
120  r = report_metadata.mutable_download_response()->ParseFromString(
121      ping_response_);
122  DCHECK(r);
123  file_size_ = report_metadata.download_request().length();
124
125  std::string metadata_string;
126  bool ok = report_metadata.SerializeToString(&metadata_string);
127  DCHECK(ok);
128  uploader_.reset(
129      TwoPhaseUploader::Create(request_context_getter_.get(),
130                               file_task_runner_.get(),
131                               GURL(kSbFeedbackURL),
132                               metadata_string,
133                               file_path_,
134                               TwoPhaseUploader::ProgressCallback(),
135                               base::Bind(&DownloadFeedbackImpl::FinishedUpload,
136                                          base::Unretained(this),
137                                          finish_callback)));
138  uploader_->Start();
139}
140
141void DownloadFeedbackImpl::FinishedUpload(base::Closure finish_callback,
142                                          TwoPhaseUploader::State state,
143                                          int net_error,
144                                          int response_code,
145                                          const std::string& response_data) {
146  DCHECK(CalledOnValidThread());
147  DVLOG(1) << __FUNCTION__ << " " << state << " rlen=" << response_data.size();
148
149  switch (state) {
150    case TwoPhaseUploader::STATE_SUCCESS: {
151      ClientUploadResponse response;
152      if (!response.ParseFromString(response_data) ||
153          response.status() != ClientUploadResponse::SUCCESS)
154        RecordUploadResult(UPLOAD_COMPLETE_RESPONSE_ERROR);
155      else
156        RecordUploadResult(UPLOAD_SUCCESS);
157      break;
158    }
159    case TwoPhaseUploader::UPLOAD_FILE:
160      if (net_error != net::OK)
161        RecordUploadResult(UPLOAD_FILE_NET_ERROR);
162      else
163        RecordUploadResult(UPLOAD_FILE_RESPONSE_ERROR);
164      break;
165    case TwoPhaseUploader::UPLOAD_METADATA:
166      if (net_error != net::OK)
167        RecordUploadResult(UPLOAD_METADATA_NET_ERROR);
168      else
169        RecordUploadResult(UPLOAD_METADATA_RESPONSE_ERROR);
170      break;
171    default:
172      NOTREACHED();
173  }
174
175  uploader_.reset();
176
177  finish_callback.Run();
178  // We may be deleted here.
179}
180
181void DownloadFeedbackImpl::RecordUploadResult(UploadResultType result) {
182  if (result == UPLOAD_SUCCESS)
183    UMA_HISTOGRAM_CUSTOM_COUNTS(
184        "SBDownloadFeedback.SizeSuccess", file_size_, 1, kMaxUploadSize, 50);
185  else
186    UMA_HISTOGRAM_CUSTOM_COUNTS(
187        "SBDownloadFeedback.SizeFailure", file_size_, 1, kMaxUploadSize, 50);
188  UMA_HISTOGRAM_ENUMERATION(
189      "SBDownloadFeedback.UploadResult", result, UPLOAD_RESULT_MAX);
190}
191
192}  // namespace
193
194// static
195const int64 DownloadFeedback::kMaxUploadSize = 50 * 1024 * 1024;
196
197// static
198const char DownloadFeedback::kSbFeedbackURL[] =
199    "https://safebrowsing.google.com/safebrowsing/uploads/chrome";
200
201// static
202DownloadFeedbackFactory* DownloadFeedback::factory_ = NULL;
203
204// static
205DownloadFeedback* DownloadFeedback::Create(
206    net::URLRequestContextGetter* request_context_getter,
207    base::TaskRunner* file_task_runner,
208    const base::FilePath& file_path,
209    const std::string& ping_request,
210    const std::string& ping_response) {
211  if (!DownloadFeedback::factory_)
212    return new DownloadFeedbackImpl(
213        request_context_getter, file_task_runner, file_path, ping_request,
214        ping_response);
215  return DownloadFeedback::factory_->CreateDownloadFeedback(
216        request_context_getter, file_task_runner, file_path, ping_request,
217        ping_response);
218}
219
220}  // namespace safe_browsing
221
222