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/service/service_utility_process_host.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/file_util.h"
10#include "base/files/scoped_temp_dir.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop.h"
13#include "base/message_loop/message_loop_proxy.h"
14#include "base/metrics/histogram.h"
15#include "base/process/kill.h"
16#include "base/strings/utf_string_conversions.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/chrome_utility_printing_messages.h"
19#include "content/public/common/child_process_host.h"
20#include "content/public/common/result_codes.h"
21#include "content/public/common/sandbox_init.h"
22#include "ipc/ipc_switches.h"
23#include "printing/page_range.h"
24#include "ui/base/ui_base_switches.h"
25#include "ui/gfx/rect.h"
26
27#if defined(OS_WIN)
28#include "base/files/file_path.h"
29#include "base/memory/scoped_ptr.h"
30#include "base/process/launch.h"
31#include "base/win/scoped_handle.h"
32#include "content/public/common/sandbox_init.h"
33#include "content/public/common/sandboxed_process_launcher_delegate.h"
34#include "printing/emf_win.h"
35
36namespace {
37
38// NOTE: changes to this class need to be reviewed by the security team.
39class ServiceSandboxedProcessLauncherDelegate
40    : public content::SandboxedProcessLauncherDelegate {
41 public:
42  explicit ServiceSandboxedProcessLauncherDelegate(
43      const base::FilePath& exposed_dir)
44    : exposed_dir_(exposed_dir) {
45  }
46
47  virtual void PreSandbox(bool* disable_default_policy,
48                          base::FilePath* exposed_dir) OVERRIDE {
49    *exposed_dir = exposed_dir_;
50  }
51
52 private:
53  base::FilePath exposed_dir_;
54};
55
56}  // namespace
57
58#endif  // OS_WIN
59
60using content::ChildProcessHost;
61
62namespace {
63enum ServiceUtilityProcessHostEvent {
64  SERVICE_UTILITY_STARTED,
65  SERVICE_UTILITY_DISCONNECTED,
66  SERVICE_UTILITY_METAFILE_REQUEST,
67  SERVICE_UTILITY_METAFILE_SUCCEEDED,
68  SERVICE_UTILITY_METAFILE_FAILED,
69  SERVICE_UTILITY_CAPS_REQUEST,
70  SERVICE_UTILITY_CAPS_SUCCEEDED,
71  SERVICE_UTILITY_CAPS_FAILED,
72  SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
73  SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
74  SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
75  SERVICE_UTILITY_EVENT_MAX,
76};
77}  // namespace
78
79ServiceUtilityProcessHost::ServiceUtilityProcessHost(
80    Client* client, base::MessageLoopProxy* client_message_loop_proxy)
81        : handle_(base::kNullProcessHandle),
82          client_(client),
83          client_message_loop_proxy_(client_message_loop_proxy),
84          waiting_for_reply_(false) {
85  child_process_host_.reset(ChildProcessHost::Create(this));
86}
87
88ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
89  // We need to kill the child process when the host dies.
90  base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
91}
92
93bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
94    const base::FilePath& pdf_path,
95    const printing::PdfRenderSettings& render_settings,
96    const std::vector<printing::PageRange>& page_ranges) {
97  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
98                            SERVICE_UTILITY_METAFILE_REQUEST,
99                            SERVICE_UTILITY_EVENT_MAX);
100  start_time_ = base::Time::Now();
101#if !defined(OS_WIN)
102  // This is only implemented on Windows (because currently it is only needed
103  // on Windows). Will add implementations on other platforms when needed.
104  NOTIMPLEMENTED();
105  return false;
106#else  // !defined(OS_WIN)
107  scratch_metafile_dir_.reset(new base::ScopedTempDir);
108  if (!scratch_metafile_dir_->CreateUniqueTempDir())
109    return false;
110  metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf");
111  if (!StartProcess(false, scratch_metafile_dir_->path()))
112    return false;
113
114  base::File pdf_file(
115      pdf_path,
116      base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
117  DCHECK(!waiting_for_reply_);
118  waiting_for_reply_ = true;
119  return child_process_host_->Send(
120      new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
121          IPC::TakeFileHandleForProcess(pdf_file.Pass(), handle()),
122          metafile_path_,
123          render_settings,
124          page_ranges));
125#endif  // !defined(OS_WIN)
126}
127
128bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
129    const std::string& printer_name) {
130  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
131                            SERVICE_UTILITY_CAPS_REQUEST,
132                            SERVICE_UTILITY_EVENT_MAX);
133  start_time_ = base::Time::Now();
134  base::FilePath exposed_path;
135  if (!StartProcess(true, exposed_path))
136    return false;
137  DCHECK(!waiting_for_reply_);
138  waiting_for_reply_ = true;
139  return child_process_host_->Send(
140      new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
141}
142
143bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
144    const std::string& printer_name) {
145  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
146                            SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
147                            SERVICE_UTILITY_EVENT_MAX);
148  start_time_ = base::Time::Now();
149  base::FilePath exposed_path;
150  if (!StartProcess(true, exposed_path))
151    return false;
152  DCHECK(!waiting_for_reply_);
153  waiting_for_reply_ = true;
154  return child_process_host_->Send(
155      new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
156}
157
158bool ServiceUtilityProcessHost::StartProcess(
159    bool no_sandbox,
160    const base::FilePath& exposed_dir) {
161  std::string channel_id = child_process_host_->CreateChannel();
162  if (channel_id.empty())
163    return false;
164
165  base::FilePath exe_path = GetUtilityProcessCmd();
166  if (exe_path.empty()) {
167    NOTREACHED() << "Unable to get utility process binary name.";
168    return false;
169  }
170
171  CommandLine cmd_line(exe_path);
172  cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
173  cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
174  cmd_line.AppendSwitch(switches::kLang);
175
176  if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
177    UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
178                              SERVICE_UTILITY_STARTED,
179                              SERVICE_UTILITY_EVENT_MAX);
180    return true;
181  }
182  return false;
183}
184
185bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
186                                       bool no_sandbox,
187                                       const base::FilePath& exposed_dir) {
188#if !defined(OS_WIN)
189  // TODO(sanjeevr): Implement for non-Windows OSes.
190  NOTIMPLEMENTED();
191  return false;
192#else  // !defined(OS_WIN)
193
194  if (no_sandbox) {
195    base::ProcessHandle process = base::kNullProcessHandle;
196    cmd_line->AppendSwitch(switches::kNoSandbox);
197    base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
198  } else {
199    ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
200    handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
201  }
202  return (handle_ != base::kNullProcessHandle);
203#endif  // !defined(OS_WIN)
204}
205
206base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
207#if defined(OS_LINUX)
208  int flags = ChildProcessHost::CHILD_ALLOW_SELF;
209#else
210  int flags = ChildProcessHost::CHILD_NORMAL;
211#endif
212  return ChildProcessHost::GetChildPath(flags);
213}
214
215void ServiceUtilityProcessHost::OnChildDisconnected() {
216  if (waiting_for_reply_) {
217    // If we are yet to receive a reply then notify the client that the
218    // child died.
219    client_message_loop_proxy_->PostTask(
220        FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
221    UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
222                              SERVICE_UTILITY_DISCONNECTED,
223                              SERVICE_UTILITY_EVENT_MAX);
224    UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
225                        base::Time::Now() - start_time_);
226  }
227  delete this;
228}
229
230bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
231  bool handled = true;
232  IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
233#if defined(WIN_PDF_METAFILE_FOR_PRINTING)
234    IPC_MESSAGE_HANDLER(
235        ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded,
236        OnRenderPDFPagesToMetafilesSucceeded)
237    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
238                        OnRenderPDFPagesToMetafileFailed)
239#endif
240    IPC_MESSAGE_HANDLER(
241        ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
242        OnGetPrinterCapsAndDefaultsSucceeded)
243    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
244                        OnGetPrinterCapsAndDefaultsFailed)
245    IPC_MESSAGE_HANDLER(
246        ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
247        OnGetPrinterSemanticCapsAndDefaultsSucceeded)
248    IPC_MESSAGE_HANDLER(
249        ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
250        OnGetPrinterSemanticCapsAndDefaultsFailed)
251    IPC_MESSAGE_UNHANDLED(handled = false)
252  IPC_END_MESSAGE_MAP()
253  return handled;
254}
255
256base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const {
257  return handle_;
258}
259
260#if defined(WIN_PDF_METAFILE_FOR_PRINTING)
261void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded(
262    const std::vector<printing::PageRange>& page_ranges,
263    double scale_factor) {
264  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
265                            SERVICE_UTILITY_METAFILE_SUCCEEDED,
266                            SERVICE_UTILITY_EVENT_MAX);
267  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
268                      base::Time::Now() - start_time_);
269  DCHECK(waiting_for_reply_);
270  waiting_for_reply_ = false;
271  // If the metafile was successfully created, we need to take our hands off the
272  // scratch metafile directory. The client will delete it when it is done with
273  // metafile.
274  scratch_metafile_dir_->Take();
275
276  // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one
277  // page is printed at a time. This would need to be refactored to change
278  // this.
279  CHECK_EQ(1u, page_ranges.size());
280  CHECK_EQ(page_ranges[0].from, page_ranges[0].to);
281  int page_number = page_ranges[0].from;
282  client_message_loop_proxy_->PostTask(
283      FROM_HERE,
284      base::Bind(&Client::MetafileAvailable,
285                 client_.get(),
286                 metafile_path_.InsertBeforeExtensionASCII(
287                     base::StringPrintf(".%d", page_number)),
288                 page_number,
289                 scale_factor));
290}
291
292void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
293  DCHECK(waiting_for_reply_);
294  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
295                            SERVICE_UTILITY_METAFILE_FAILED,
296                            SERVICE_UTILITY_EVENT_MAX);
297  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
298                      base::Time::Now() - start_time_);
299  waiting_for_reply_ = false;
300  client_message_loop_proxy_->PostTask(
301      FROM_HERE,
302      base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
303}
304#endif  // defined(WIN_PDF_METAFILE_FOR_PRINTING)
305
306void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
307    const std::string& printer_name,
308    const printing::PrinterCapsAndDefaults& caps_and_defaults) {
309  DCHECK(waiting_for_reply_);
310  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
311                            SERVICE_UTILITY_CAPS_SUCCEEDED,
312                            SERVICE_UTILITY_EVENT_MAX);
313  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
314                      base::Time::Now() - start_time_);
315  waiting_for_reply_ = false;
316  client_message_loop_proxy_->PostTask(
317      FROM_HERE,
318      base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
319                 printer_name, caps_and_defaults));
320}
321
322void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
323    const std::string& printer_name,
324    const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
325  DCHECK(waiting_for_reply_);
326  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
327                            SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
328                            SERVICE_UTILITY_EVENT_MAX);
329  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
330                      base::Time::Now() - start_time_);
331  waiting_for_reply_ = false;
332  client_message_loop_proxy_->PostTask(
333      FROM_HERE,
334      base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
335                 true, printer_name, caps_and_defaults));
336}
337
338void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
339    const std::string& printer_name) {
340  DCHECK(waiting_for_reply_);
341  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
342                            SERVICE_UTILITY_CAPS_FAILED,
343                            SERVICE_UTILITY_EVENT_MAX);
344  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
345                      base::Time::Now() - start_time_);
346  waiting_for_reply_ = false;
347  client_message_loop_proxy_->PostTask(
348      FROM_HERE,
349      base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
350                 printer_name, printing::PrinterCapsAndDefaults()));
351}
352
353void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
354    const std::string& printer_name) {
355  DCHECK(waiting_for_reply_);
356  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
357                            SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
358                            SERVICE_UTILITY_EVENT_MAX);
359  UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
360                      base::Time::Now() - start_time_);
361  waiting_for_reply_ = false;
362  client_message_loop_proxy_->PostTask(
363      FROM_HERE,
364      base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
365                 client_.get(), false, printer_name,
366                 printing::PrinterSemanticCapsAndDefaults()));
367}
368
369void ServiceUtilityProcessHost::Client::MetafileAvailable(
370    const base::FilePath& metafile_path,
371    int highest_rendered_page_number,
372    double scale_factor) {
373  // The metafile was created in a temp folder which needs to get deleted after
374  // we have processed it.
375  base::ScopedTempDir scratch_metafile_dir;
376  if (!scratch_metafile_dir.Set(metafile_path.DirName()))
377    LOG(WARNING) << "Unable to set scratch metafile directory";
378#if defined(OS_WIN)
379  // It's important that metafile is declared after scratch_metafile_dir so
380  // that the metafile destructor closes the file before the base::ScopedTempDir
381  // destructor tries to remove the directory.
382  printing::Emf metafile;
383  if (!metafile.InitFromFile(metafile_path)) {
384    OnRenderPDFPagesToMetafileFailed();
385  } else {
386    OnRenderPDFPagesToMetafileSucceeded(metafile,
387                                        highest_rendered_page_number,
388                                        scale_factor);
389  }
390#endif  // defined(OS_WIN)
391}
392
393