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/memory/singleton.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/process/launch.h"
17 #include "base/threading/worker_pool.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/safe_browsing/srt_field_trial_win.h"
22 #include "chrome/browser/safe_browsing/srt_global_error_win.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_list.h"
25 #include "chrome/browser/ui/browser_list_observer.h"
26 #include "chrome/browser/ui/global_error/global_error_service.h"
27 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
28 #include "components/component_updater/pref_names.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "net/base/load_flags.h"
31 #include "net/http/http_status_code.h"
32 #include "net/url_request/url_fetcher.h"
33 #include "net/url_request/url_fetcher_delegate.h"
34 #include "net/url_request/url_request_context_getter.h"
36 using content::BrowserThread
;
38 namespace safe_browsing
{
42 // The number of days to wait before triggering another sw_reporter run.
43 const int kDaysBetweenSuccessfulSwReporterRuns
= 7;
45 void DisplaySRTPrompt(const base::FilePath
& download_path
) {
46 // Find the last active browser, which may be NULL, in which case we won't
47 // show the prompt this time and will wait until the next run of the
48 // reporter. We can't use other ways of finding a browser because we don't
50 chrome::HostDesktopType desktop_type
= chrome::GetActiveDesktop();
51 Browser
* browser
= chrome::FindLastActiveWithHostDesktopType(desktop_type
);
55 Profile
* profile
= browser
->profile();
58 // Make sure we have a tabbed browser since we need to anchor the bubble to
59 // the toolbar's wrench menu. Create one if none exist already.
60 if (browser
->type() != Browser::TYPE_TABBED
) {
61 browser
= chrome::FindTabbedBrowser(profile
, false, desktop_type
);
63 browser
= new Browser(Browser::CreateParams(profile
, desktop_type
));
65 GlobalErrorService
* global_error_service
=
66 GlobalErrorServiceFactory::GetForProfile(profile
);
67 SRTGlobalError
* global_error
=
68 new SRTGlobalError(global_error_service
, download_path
);
70 // Ownership of |global_error| is passed to the service. The error removes
71 // itself from the service and self-destructs when done.
72 global_error_service
->AddGlobalError(global_error
);
74 // Do not try to show bubble if another GlobalError is already showing
75 // one. The bubble will be shown once the others have been dismissed.
76 bool need_to_show_bubble
= true;
77 for (GlobalError
* error
: global_error_service
->errors()) {
78 if (error
->GetBubbleView()) {
79 need_to_show_bubble
= false;
83 if (need_to_show_bubble
)
84 global_error
->ShowBubbleView(browser
);
87 // Class that will attempt to download the SRT, showing the SRT notification
88 // bubble when the download operation is complete. Instances of SRTFetcher own
89 // themselves, they will self-delete on completion of the network request when
90 // OnURLFetchComplete is called.
91 class SRTFetcher
: public net::URLFetcherDelegate
{
94 : url_fetcher_(net::URLFetcher::Create(0,
95 GURL(GetSRTDownloadURL()),
98 url_fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
99 url_fetcher_
->SetMaxRetriesOn5xx(3);
100 url_fetcher_
->SaveResponseToTemporaryFile(
101 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
102 url_fetcher_
->SetRequestContext(
103 g_browser_process
->system_request_context());
104 url_fetcher_
->Start();
107 // net::URLFetcherDelegate:
108 void OnURLFetchComplete(const net::URLFetcher
* source
) override
{
109 // Take ownership of the fetcher in this scope (source == url_fetcher_).
110 DCHECK_EQ(url_fetcher_
.get(), source
);
112 base::FilePath download_path
;
113 if (source
->GetStatus().is_success() &&
114 source
->GetResponseCode() == net::HTTP_OK
) {
115 if (source
->GetResponseAsFilePath(true, &download_path
)) {
116 DCHECK(!download_path
.empty());
120 // As long as the fetch didn't fail due to HTTP_NOT_FOUND, show a prompt
121 // (either offering the tool directly or pointing to the download page).
122 // If the fetch failed to find the file, don't prompt the user since the
123 // tool is not currently available.
124 // TODO(mad): Consider implementing another layer of retries / alternate
125 // fetching mechanisms. http://crbug.com/460293
126 // TODO(mad): In the event the browser is closed before the prompt displays,
127 // we will wait until the next scanner run to re-display it.
128 // Improve this. http://crbug.com/460295
129 if (source
->GetResponseCode() != net::HTTP_NOT_FOUND
)
130 DisplaySRTPrompt(download_path
);
132 RecordSRTPromptHistogram(SRT_PROMPT_DOWNLOAD_UNAVAILABLE
);
134 // Explicitly destroy the url_fetcher_ to avoid destruction races.
135 url_fetcher_
.reset();
137 // At this point, the url_fetcher_ is gone and this SRTFetcher instance is
143 ~SRTFetcher() override
{}
145 // The underlying URL fetcher. The instance is alive from construction through
146 // OnURLFetchComplete.
147 scoped_ptr
<net::URLFetcher
> url_fetcher_
;
149 DISALLOW_COPY_AND_ASSIGN(SRTFetcher
);
152 // This class tries to run the reporter and reacts to its exit code. It
153 // schedules subsequent runs as needed, or retries as soon as a browser is
154 // available when none is on first try.
155 class ReporterRunner
: public chrome::BrowserListObserver
{
157 static ReporterRunner
* GetInstance() {
158 return Singleton
<ReporterRunner
,
159 LeakySingletonTraits
<ReporterRunner
>>::get();
162 // Starts the sequence of attempts to run the reporter.
163 void Run(const base::FilePath
& exe_path
, const std::string
& version
) {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
165 exe_path_
= exe_path
;
171 friend struct DefaultSingletonTraits
<ReporterRunner
>;
173 ~ReporterRunner() override
{}
175 // BrowserListObserver.
176 void OnBrowserSetLastActive(Browser
* browser
) override
{}
177 void OnBrowserRemoved(Browser
* browser
) override
{}
178 void OnBrowserAdded(Browser
* browser
) override
{
180 if (browser
->host_desktop_type() == chrome::GetActiveDesktop()) {
181 MaybeFetchSRT(browser
);
182 BrowserList::RemoveObserver(this);
186 // Identifies that we completed the run of the reporter and schedule the next
187 // run in kDaysBetweenSuccessfulSwReporterRuns.
188 void CompletedRun() {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
190 g_browser_process
->local_state()->SetInt64(
191 prefs::kSwReporterLastTimeTriggered
,
192 base::Time::Now().ToInternalValue());
194 BrowserThread::PostDelayedTask(
195 BrowserThread::UI
, FROM_HERE
,
196 base::Bind(&ReporterRunner::TryToRun
, base::Unretained(this)),
197 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns
));
200 // This method is called on the UI thread when the reporter run has completed.
201 // This is run as a task posted from an interruptible worker thread so should
202 // be resilient to unexpected shutdown.
203 void ReporterDone(int exit_code
) {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
205 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code
);
207 if (g_browser_process
&& g_browser_process
->local_state()) {
208 g_browser_process
->local_state()->SetInteger(
209 prefs::kSwReporterLastExitCode
, exit_code
);
212 // To complete the run if we exit without releasing this scoped closure.
213 base::ScopedClosureRunner
completed(
214 base::Bind(&ReporterRunner::CompletedRun
, base::Unretained(this)));
216 if (!IsInSRTPromptFieldTrialGroups()) {
217 // Knowing about disabled field trial is more important than reporter not
218 // finding anything to remove, so check this case first.
219 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL
);
223 if (exit_code
!= kSwReporterPostRebootCleanupNeeded
&&
224 exit_code
!= kSwReporterCleanupNeeded
) {
225 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_NEEDED
);
229 // Find the last active browser, which may be NULL, in which case we need
230 // to wait for one to be available. We can't use other ways of finding a
231 // browser because we don't have a profile. And we need a browser to get to
232 // a profile, which we need, to tell whether we should prompt or not.
233 // TODO(mad): crbug.com/503269, investigate whether we should change how we
234 // decide when it's time to download the SRT and when to display the prompt.
235 chrome::HostDesktopType desktop_type
= chrome::GetActiveDesktop();
236 Browser
* browser
= chrome::FindLastActiveWithHostDesktopType(desktop_type
);
238 RecordReporterStepHistogram(SW_REPORTER_NO_BROWSER
);
239 BrowserList::AddObserver(GetInstance());
241 MaybeFetchSRT(browser
);
245 static void MaybeFetchSRT(Browser
* browser
) {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
248 Profile
* profile
= browser
->profile();
250 PrefService
* prefs
= profile
->GetPrefs();
253 // Don't show the prompt again if it's been shown before for this profile
254 // and for the current Finch seed.
255 std::string incoming_seed
= GetIncomingSRTSeed();
256 std::string old_seed
= prefs
->GetString(prefs::kSwReporterPromptSeed
);
257 if (!incoming_seed
.empty() && incoming_seed
== old_seed
) {
258 RecordReporterStepHistogram(SW_REPORTER_ALREADY_PROMPTED
);
262 if (!incoming_seed
.empty())
263 prefs
->SetString(prefs::kSwReporterPromptSeed
, incoming_seed
);
264 prefs
->SetString(prefs::kSwReporterPromptVersion
, GetInstance()->version_
);
267 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START
);
269 // All the work happens in the self-deleting class above.
273 // This method is called from a worker thread to launch the SwReporter and
274 // wait for termination to collect its exit code. This task could be
275 // interrupted by a shutdown at anytime, so it shouldn't depend on anything
276 // external that could be shutdown beforehand. It's static to make sure it
277 // won't access data members, except for the need for a this pointer to call
278 // back member functions on the UI thread. We can safely use GetInstance()
279 // here because the singleton is leaky.
280 static void LaunchAndWaitForExit(const base::FilePath
& exe_path
,
281 const std::string
& version
) {
282 const base::CommandLine
reporter_command_line(exe_path
);
283 base::Process reporter_process
=
284 base::LaunchProcess(reporter_command_line
, base::LaunchOptions());
285 if (!reporter_process
.IsValid()) {
286 RecordReporterStepHistogram(SW_REPORTER_FAILED_TO_START
);
287 // Don't call ReporterDone when the reporter process failed to launch, but
288 // try again after the regular delay. It's not worth retrying earlier,
289 // risking running too often if it always fail, since not many users fail
290 // here, less than 1%.
291 BrowserThread::PostDelayedTask(
292 BrowserThread::UI
, FROM_HERE
,
293 base::Bind(&ReporterRunner::TryToRun
,
294 base::Unretained(GetInstance())),
295 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns
));
298 RecordReporterStepHistogram(SW_REPORTER_START_EXECUTION
);
301 bool success
= reporter_process
.WaitForExit(&exit_code
);
303 BrowserThread::PostTask(
304 BrowserThread::UI
, FROM_HERE
,
305 base::Bind(&ReporterRunner::ReporterDone
,
306 base::Unretained(GetInstance()), exit_code
));
310 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
311 if (version_
.empty()) {
312 DCHECK(exe_path_
.empty());
316 // Run the reporter if it hasn't been triggered in the
317 // kDaysBetweenSuccessfulSwReporterRuns days.
318 const base::Time last_time_triggered
= base::Time::FromInternalValue(
319 g_browser_process
->local_state()->GetInt64(
320 prefs::kSwReporterLastTimeTriggered
));
321 base::TimeDelta
next_trigger_delay(
322 last_time_triggered
+
323 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns
) -
325 if (next_trigger_delay
.ToInternalValue() <= 0) {
326 // Use a worker pool because this task is blocking and may take a long
328 base::WorkerPool::PostTask(
329 FROM_HERE
, base::Bind(&ReporterRunner::LaunchAndWaitForExit
,
330 exe_path_
, version_
),
333 BrowserThread::PostDelayedTask(
334 BrowserThread::UI
, FROM_HERE
,
335 base::Bind(&ReporterRunner::TryToRun
,
336 base::Unretained(GetInstance())),
341 base::FilePath exe_path_
;
342 std::string version_
;
344 DISALLOW_COPY_AND_ASSIGN(ReporterRunner
);
349 void RunSwReporter(const base::FilePath
& exe_path
, const std::string
& version
) {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
351 ReporterRunner::GetInstance()->Run(exe_path
, version
);
354 } // namespace safe_browsing