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/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