Home | History | Annotate | Download | only in safe_browsing
      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