1// Copyright (c) 2012 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/sandboxed_zip_analyzer.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/location.h"
10#include "base/logging.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "chrome/common/chrome_utility_messages.h"
13#include "chrome/common/safe_browsing/zip_analyzer.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/browser/child_process_data.h"
16#include "content/public/browser/render_process_host.h"
17#include "content/public/common/content_switches.h"
18#include "ipc/ipc_message_macros.h"
19#include "ipc/ipc_platform_file.h"
20
21using content::BrowserThread;
22
23namespace safe_browsing {
24
25SandboxedZipAnalyzer::SandboxedZipAnalyzer(
26    const base::FilePath& zip_file,
27    const ResultCallback& result_callback)
28    : zip_file_name_(zip_file),
29      callback_(result_callback),
30      callback_called_(false) {
31}
32
33void SandboxedZipAnalyzer::Start() {
34  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
35  // Starting the analyzer will block on opening the zip file, so run this
36  // on a worker thread.  The task does not need to block shutdown.
37  if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
38          FROM_HERE,
39          base::Bind(&SandboxedZipAnalyzer::AnalyzeInSandbox, this),
40          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)) {
41    NOTREACHED();
42  }
43}
44
45SandboxedZipAnalyzer::~SandboxedZipAnalyzer() {
46  // If we're using UtilityProcessHost, we may not be destroyed on
47  // the UI or IO thread.
48}
49
50void SandboxedZipAnalyzer::AnalyzeInSandbox() {
51  zip_file_.Initialize(zip_file_name_,
52                       base::File::FLAG_OPEN | base::File::FLAG_READ);
53  if (!zip_file_.IsValid()) {
54    VLOG(1) << "Could not open zip file: " << zip_file_name_.value();
55    if (!BrowserThread::PostTask(
56            BrowserThread::IO, FROM_HERE,
57            base::Bind(&SandboxedZipAnalyzer::OnAnalyzeZipFileFinished, this,
58                       zip_analyzer::Results()))) {
59      NOTREACHED();
60    }
61    return;
62  }
63
64  BrowserThread::PostTask(
65      BrowserThread::IO, FROM_HERE,
66      base::Bind(&SandboxedZipAnalyzer::StartProcessOnIOThread, this));
67  // The file will be closed on the IO thread once it has been handed
68  // off to the child process.
69}
70
71bool SandboxedZipAnalyzer::OnMessageReceived(const IPC::Message& message) {
72  bool handled = true;
73  IPC_BEGIN_MESSAGE_MAP(SandboxedZipAnalyzer, message)
74    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
75                        OnUtilityProcessStarted)
76    IPC_MESSAGE_HANDLER(
77        ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished,
78        OnAnalyzeZipFileFinished)
79    IPC_MESSAGE_UNHANDLED(handled = false)
80  IPC_END_MESSAGE_MAP()
81  return handled;
82}
83
84void SandboxedZipAnalyzer::OnAnalyzeZipFileFinished(
85    const zip_analyzer::Results& results) {
86  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
87  if (callback_called_)
88    return;
89  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
90                          base::Bind(callback_, results));
91  callback_called_ = true;
92}
93
94void SandboxedZipAnalyzer::StartProcessOnIOThread() {
95  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96  utility_process_host_ = content::UtilityProcessHost::Create(
97      this,
98      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get())
99      ->AsWeakPtr();
100  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
101  // Wait for the startup notification before sending the main IPC to the
102  // utility process, so that we can dup the file handle.
103}
104
105void SandboxedZipAnalyzer::OnUtilityProcessStarted() {
106  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
107  base::ProcessHandle utility_process =
108      content::RenderProcessHost::run_renderer_in_process() ?
109          base::GetCurrentProcessHandle() :
110          utility_process_host_->GetData().handle;
111
112  if (utility_process == base::kNullProcessHandle) {
113    DLOG(ERROR) << "Child process handle is null";
114  }
115  utility_process_host_->Send(
116      new ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection(
117          IPC::TakeFileHandleForProcess(zip_file_.Pass(), utility_process)));
118}
119
120}  // namespace safe_browsing
121