1 // Copyright 2015 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.
5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/process/launch.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_io_data.h"
22 #include "chrome/browser/safe_browsing/srt_field_trial_win.h"
23 #include "chrome/browser/safe_browsing/srt_global_error_win.h"
24 #include "chrome/browser/ui/browser_finder.h"
25 #include "chrome/browser/ui/browser_list.h"
26 #include "chrome/browser/ui/browser_list_observer.h"
27 #include "chrome/browser/ui/global_error/global_error_service.h"
28 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
29 #include "components/component_updater/pref_names.h"
30 #include "components/variations/net/variations_http_header_provider.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/base/load_flags.h"
33 #include "net/http/http_status_code.h"
34 #include "net/url_request/url_fetcher.h"
35 #include "net/url_request/url_fetcher_delegate.h"
36 #include "net/url_request/url_request_context_getter.h"
38 using content::BrowserThread
;
40 namespace safe_browsing
{
44 // Overrides for the reporter launcher and prompt triggers free function, used
46 ReporterLauncher g_reporter_launcher_
;
47 PromptTrigger g_prompt_trigger_
;
49 void DisplaySRTPrompt(const base::FilePath
& download_path
) {
50 // Find the last active browser, which may be NULL, in which case we won't
51 // show the prompt this time and will wait until the next run of the
52 // reporter. We can't use other ways of finding a browser because we don't
54 chrome::HostDesktopType desktop_type
= chrome::GetActiveDesktop();
55 Browser
* browser
= chrome::FindLastActiveWithHostDesktopType(desktop_type
);
59 Profile
* profile
= browser
->profile();
62 // Make sure we have a tabbed browser since we need to anchor the bubble to
63 // the toolbar's wrench menu. Create one if none exist already.
64 if (browser
->type() != Browser::TYPE_TABBED
) {
65 browser
= chrome::FindTabbedBrowser(profile
, false, desktop_type
);
67 browser
= new Browser(Browser::CreateParams(profile
, desktop_type
));
69 GlobalErrorService
* global_error_service
=
70 GlobalErrorServiceFactory::GetForProfile(profile
);
71 SRTGlobalError
* global_error
=
72 new SRTGlobalError(global_error_service
, download_path
);
74 // Ownership of |global_error| is passed to the service. The error removes
75 // itself from the service and self-destructs when done.
76 global_error_service
->AddGlobalError(global_error
);
78 bool show_bubble
= true;
79 PrefService
* local_state
= g_browser_process
->local_state();
80 if (local_state
&& local_state
->GetBoolean(prefs::kSwReporterPendingPrompt
)) {
81 // Don't show the bubble if there's already a pending prompt to only be
82 // sown in the Chrome menu.
83 RecordReporterStepHistogram(SW_REPORTER_ADDED_TO_MENU
);
86 // Do not try to show bubble if another GlobalError is already showing
87 // one. The bubble will be shown once the others have been dismissed.
88 for (GlobalError
* error
: global_error_service
->errors()) {
89 if (error
->GetBubbleView()) {
96 global_error
->ShowBubbleView(browser
);
99 // This function is called from a worker thread to launch the SwReporter and
100 // wait for termination to collect its exit code. This task could be
101 // interrupted by a shutdown at any time, so it shouldn't depend on anything
102 // external that could be shut down beforehand.
103 int LaunchAndWaitForExit(const base::FilePath
& exe_path
,
104 const std::string
& version
) {
105 if (!g_reporter_launcher_
.is_null())
106 return g_reporter_launcher_
.Run(exe_path
, version
);
107 base::Process reporter_process
=
108 base::LaunchProcess(exe_path
.value(), base::LaunchOptions());
109 // This exit code is used to identify that a reporter run didn't happen, so
110 // the result should be ignored and a rerun scheduled for the usual delay.
111 int exit_code
= kReporterFailureExitCode
;
112 if (reporter_process
.IsValid()) {
113 RecordReporterStepHistogram(SW_REPORTER_START_EXECUTION
);
114 bool success
= reporter_process
.WaitForExit(&exit_code
);
117 RecordReporterStepHistogram(SW_REPORTER_FAILED_TO_START
);
122 // Class that will attempt to download the SRT, showing the SRT notification
123 // bubble when the download operation is complete. Instances of SRTFetcher own
124 // themselves, they will self-delete on completion of the network request when
125 // OnURLFetchComplete is called.
126 class SRTFetcher
: public net::URLFetcherDelegate
{
128 explicit SRTFetcher(Profile
* profile
)
130 url_fetcher_(net::URLFetcher::Create(0,
131 GURL(GetSRTDownloadURL()),
132 net::URLFetcher::GET
,
134 url_fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
135 url_fetcher_
->SetMaxRetriesOn5xx(3);
136 url_fetcher_
->SaveResponseToTemporaryFile(
137 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
138 url_fetcher_
->SetRequestContext(
139 g_browser_process
->system_request_context());
140 // Adds the UMA bit to the download request if the user is enrolled in UMA.
141 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(
142 profile_
->GetResourceContext());
143 net::HttpRequestHeaders headers
;
144 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
145 url_fetcher_
->GetOriginalURL(),
146 io_data
->IsOffTheRecord(),
147 io_data
->GetMetricsEnabledStateOnIOThread(),
149 url_fetcher_
->SetExtraRequestHeaders(headers
.ToString());
150 url_fetcher_
->Start();
153 // net::URLFetcherDelegate:
154 void OnURLFetchComplete(const net::URLFetcher
* source
) override
{
155 // Take ownership of the fetcher in this scope (source == url_fetcher_).
156 DCHECK_EQ(url_fetcher_
.get(), source
);
158 base::FilePath download_path
;
159 if (source
->GetStatus().is_success() &&
160 source
->GetResponseCode() == net::HTTP_OK
) {
161 if (source
->GetResponseAsFilePath(true, &download_path
)) {
162 DCHECK(!download_path
.empty());
166 // As long as the fetch didn't fail due to HTTP_NOT_FOUND, show a prompt
167 // (either offering the tool directly or pointing to the download page).
168 // If the fetch failed to find the file, don't prompt the user since the
169 // tool is not currently available.
170 // TODO(mad): Consider implementing another layer of retries / alternate
171 // fetching mechanisms. http://crbug.com/460293
172 // TODO(mad): In the event the browser is closed before the prompt displays,
173 // we will wait until the next scanner run to re-display it.
174 // Improve this. http://crbug.com/460295
175 if (source
->GetResponseCode() != net::HTTP_NOT_FOUND
)
176 DisplaySRTPrompt(download_path
);
178 RecordSRTPromptHistogram(SRT_PROMPT_DOWNLOAD_UNAVAILABLE
);
180 // Explicitly destroy the url_fetcher_ to avoid destruction races.
181 url_fetcher_
.reset();
183 // At this point, the url_fetcher_ is gone and this SRTFetcher instance is
189 ~SRTFetcher() override
{}
194 // The underlying URL fetcher. The instance is alive from construction through
195 // OnURLFetchComplete.
196 scoped_ptr
<net::URLFetcher
> url_fetcher_
;
198 DISALLOW_COPY_AND_ASSIGN(SRTFetcher
);
201 // Try to fetch the SRT, and on success, show the prompt to run it.
202 void MaybeFetchSRT(Browser
* browser
, const std::string
& reporter_version
) {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
205 if (!g_prompt_trigger_
.is_null()) {
206 g_prompt_trigger_
.Run(browser
, reporter_version
);
209 Profile
* profile
= browser
->profile();
211 PrefService
* prefs
= profile
->GetPrefs();
214 // Don't show the prompt again if it's been shown before for this profile
215 // and for the current variations seed, unless there's a pending prompt to
216 // show in the Chrome menu.
217 std::string incoming_seed
= GetIncomingSRTSeed();
218 std::string old_seed
= prefs
->GetString(prefs::kSwReporterPromptSeed
);
219 PrefService
* local_state
= g_browser_process
->local_state();
220 bool pending_prompt
=
221 local_state
&& local_state
->GetBoolean(prefs::kSwReporterPendingPrompt
);
222 if (!incoming_seed
.empty() && incoming_seed
== old_seed
&& !pending_prompt
) {
223 RecordReporterStepHistogram(SW_REPORTER_ALREADY_PROMPTED
);
227 if (!incoming_seed
.empty())
228 prefs
->SetString(prefs::kSwReporterPromptSeed
, incoming_seed
);
229 prefs
->SetString(prefs::kSwReporterPromptVersion
, reporter_version
);
232 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START
);
234 // All the work happens in the self-deleting class below.
235 new SRTFetcher(profile
);
238 // This class tries to run the reporter and reacts to its exit code. It
239 // schedules subsequent runs as needed, or retries as soon as a browser is
240 // available when none is on first try.
241 class ReporterRunner
: public chrome::BrowserListObserver
{
243 // Starts the sequence of attempts to run the reporter.
245 const base::FilePath
& exe_path
,
246 const std::string
& version
,
247 const scoped_refptr
<base::TaskRunner
>& main_thread_task_runner
,
248 const scoped_refptr
<base::TaskRunner
>& blocking_task_runner
) {
250 instance_
= new ReporterRunner
;
251 DCHECK(instance_
->thread_checker_
.CalledOnValidThread());
252 // There's nothing to do if the path and version of the reporter has not
253 // changed, we just keep running the tasks that are running now.
254 if (instance_
->exe_path_
== exe_path
&& instance_
->version_
== version
)
256 bool first_run
= instance_
->exe_path_
.empty();
258 instance_
->exe_path_
= exe_path
;
259 instance_
->version_
= version
;
260 instance_
->main_thread_task_runner_
= main_thread_task_runner
;
261 instance_
->blocking_task_runner_
= blocking_task_runner
;
264 instance_
->TryToRun();
269 ~ReporterRunner() override
{}
271 // BrowserListObserver.
272 void OnBrowserSetLastActive(Browser
* browser
) override
{}
273 void OnBrowserRemoved(Browser
* browser
) override
{}
274 void OnBrowserAdded(Browser
* browser
) override
{
275 DCHECK(thread_checker_
.CalledOnValidThread());
277 if (browser
->host_desktop_type() == chrome::GetActiveDesktop()) {
278 MaybeFetchSRT(browser
, version_
);
279 BrowserList::RemoveObserver(this);
283 // This method is called on the UI thread when the reporter run has completed.
284 // This is run as a task posted from an interruptible worker thread so should
285 // be resilient to unexpected shutdown.
286 void ReporterDone(int exit_code
) {
287 DCHECK(thread_checker_
.CalledOnValidThread());
289 // Don't continue when the reporter process failed to launch, but still try
290 // again after the regular delay. It's not worth retrying earlier, risking
291 // running too often if it always fails, since not many users fail here.
292 main_thread_task_runner_
->PostDelayedTask(
294 base::Bind(&ReporterRunner::TryToRun
, base::Unretained(this)),
295 base::TimeDelta::FromDays(days_between_reporter_runs_
));
296 if (exit_code
== kReporterFailureExitCode
)
299 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code
);
300 PrefService
* local_state
= g_browser_process
->local_state();
302 local_state
->SetInteger(prefs::kSwReporterLastExitCode
, exit_code
);
303 local_state
->SetInt64(prefs::kSwReporterLastTimeTriggered
,
304 base::Time::Now().ToInternalValue());
307 if (!IsInSRTPromptFieldTrialGroups()) {
308 // Knowing about disabled field trial is more important than reporter not
309 // finding anything to remove, so check this case first.
310 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL
);
314 if (exit_code
!= kSwReporterPostRebootCleanupNeeded
&&
315 exit_code
!= kSwReporterCleanupNeeded
) {
316 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_NEEDED
);
320 // Find the last active browser, which may be NULL, in which case we need
321 // to wait for one to be available. We can't use other ways of finding a
322 // browser because we don't have a profile. And we need a browser to get to
323 // a profile, which we need, to tell whether we should prompt or not.
324 // TODO(mad): crbug.com/503269, investigate whether we should change how we
325 // decide when it's time to download the SRT and when to display the prompt.
326 chrome::HostDesktopType desktop_type
= chrome::GetActiveDesktop();
327 Browser
* browser
= chrome::FindLastActiveWithHostDesktopType(desktop_type
);
329 RecordReporterStepHistogram(SW_REPORTER_NO_BROWSER
);
330 BrowserList::AddObserver(this);
332 MaybeFetchSRT(browser
, version_
);
337 DCHECK(thread_checker_
.CalledOnValidThread());
338 PrefService
* local_state
= g_browser_process
->local_state();
339 if (version_
.empty() || !local_state
) {
340 DCHECK(exe_path_
.empty());
344 // Run the reporter if it hasn't been triggered in the last
345 // |days_between_reporter_runs_| days, which depends if there is a pending
346 // prompt to be added to Chrome's menu.
347 if (local_state
->GetBoolean(prefs::kSwReporterPendingPrompt
)) {
348 days_between_reporter_runs_
= kDaysBetweenSwReporterRunsForPendingPrompt
;
349 safe_browsing::RecordReporterStepHistogram(
350 safe_browsing::SW_REPORTER_RAN_DAILY
);
352 days_between_reporter_runs_
= kDaysBetweenSuccessfulSwReporterRuns
;
354 const base::Time last_time_triggered
= base::Time::FromInternalValue(
355 local_state
->GetInt64(prefs::kSwReporterLastTimeTriggered
));
356 base::TimeDelta
next_trigger_delay(
357 last_time_triggered
+
358 base::TimeDelta::FromDays(days_between_reporter_runs_
) -
360 if (next_trigger_delay
.ToInternalValue() <= 0 ||
361 // Also make sure the kSwReporterLastTimeTriggered value is not set in
363 last_time_triggered
> base::Time::Now()) {
364 // It's OK to simply |PostTaskAndReplyWithResult| so that
365 // |LaunchAndWaitForExit| doesn't need to access
366 // |main_thread_task_runner_| since the callback is not delayed and the
367 // test task runner won't need to force it.
368 base::PostTaskAndReplyWithResult(
369 blocking_task_runner_
.get(), FROM_HERE
,
370 base::Bind(&LaunchAndWaitForExit
, exe_path_
, version_
),
371 base::Bind(&ReporterRunner::ReporterDone
, base::Unretained(this)));
373 main_thread_task_runner_
->PostDelayedTask(
375 base::Bind(&ReporterRunner::TryToRun
, base::Unretained(this)),
380 base::FilePath exe_path_
;
381 std::string version_
;
382 scoped_refptr
<base::TaskRunner
> main_thread_task_runner_
;
383 scoped_refptr
<base::TaskRunner
> blocking_task_runner_
;
385 // This value is used to identify how long to wait before starting a new run
386 // of the reporter. It's initialized with the default value and may be changed
387 // to a different value when a prompt is pending and the reporter should be
388 // run before adding the global error to the Chrome menu.
389 int days_between_reporter_runs_
= kDaysBetweenSuccessfulSwReporterRuns
;
391 // A single leaky instance.
392 static ReporterRunner
* instance_
;
394 base::ThreadChecker thread_checker_
;
395 DISALLOW_COPY_AND_ASSIGN(ReporterRunner
);
398 ReporterRunner
* ReporterRunner::instance_
= nullptr;
403 const base::FilePath
& exe_path
,
404 const std::string
& version
,
405 const scoped_refptr
<base::TaskRunner
>& main_thread_task_runner
,
406 const scoped_refptr
<base::TaskRunner
>& blocking_task_runner
) {
407 ReporterRunner::Run(exe_path
, version
, main_thread_task_runner
,
408 blocking_task_runner
);
411 void SetReporterLauncherForTesting(const ReporterLauncher
& reporter_launcher
) {
412 g_reporter_launcher_
= reporter_launcher
;
415 void SetPromptTriggerForTesting(const PromptTrigger
& prompt_trigger
) {
416 g_prompt_trigger_
= prompt_trigger
;
419 } // namespace safe_browsing