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 "cloud_print/service/win/setup_listener.h"
6
7#include <atlbase.h>
8#include <atlsecurity.h>
9
10#include "base/bind.h"
11#include "base/guid.h"
12#include "base/json/json_reader.h"
13#include "base/strings/utf_string_conversions.h"
14#include "base/synchronization/waitable_event.h"
15#include "base/threading/platform_thread.h"
16#include "base/threading/thread.h"
17#include "base/values.h"
18#include "cloud_print/service/win/service_utils.h"
19#include "ipc/ipc_channel.h"
20
21const char SetupListener::kXpsAvailableJsonValueName[] = "xps_available";
22const char SetupListener::kChromePathJsonValueName[] = "chrome_path";
23const char SetupListener::kPrintersJsonValueName[] = "printers";
24const char SetupListener::kUserDataDirJsonValueName[] = "user_data_dir";
25const char SetupListener::kUserNameJsonValueName[] = "user_name";
26const wchar_t SetupListener::kSetupPipeName[] =
27    L"\\\\.\\pipe\\CloudPrintServiceSetup";
28
29SetupListener::SetupListener(const base::string16& user)
30    : done_event_(new base::WaitableEvent(true, false)),
31      ipc_thread_(new base::Thread("ipc_thread")),
32      succeded_(false),
33      is_xps_available_(false) {
34  ipc_thread_->StartWithOptions(
35      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
36  ipc_thread_->message_loop()->PostTask(
37      FROM_HERE,
38      base::Bind(&SetupListener::Connect, base::Unretained(this), user));
39}
40
41SetupListener::~SetupListener() {
42  ipc_thread_->message_loop()->PostTask(FROM_HERE,
43                                        base::Bind(&SetupListener::Disconnect,
44                                                   base::Unretained(this)));
45  ipc_thread_->Stop();
46}
47
48bool SetupListener::OnMessageReceived(const IPC::Message& msg) {
49  PickleIterator iter(msg);
50  std::string json_string;
51  if (!iter.ReadString(&json_string))
52    return false;
53  scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
54  const base::DictionaryValue* dictionary = NULL;
55  if (!value || !value->GetAsDictionary(&dictionary) || !dictionary) {
56    LOG(ERROR) << "Invalid response from service";
57    return false;
58  }
59
60  const base::ListValue* printers = NULL;
61  if (dictionary->GetList(kPrintersJsonValueName, &printers) && printers) {
62    for (size_t i = 0; i < printers->GetSize(); ++i) {
63      std::string printer;
64      if (printers->GetString(i, &printer) && !printer.empty())
65        printers_.push_back(printer);
66    }
67  }
68  dictionary->GetBoolean(kXpsAvailableJsonValueName, &is_xps_available_);
69  dictionary->GetString(kUserNameJsonValueName, &user_name_);
70
71  base::string16 chrome_path;
72  dictionary->GetString(kChromePathJsonValueName, &chrome_path);
73  chrome_path_ = base::FilePath(chrome_path);
74
75  base::string16 user_data_dir;
76  dictionary->GetString(kUserDataDirJsonValueName, &user_data_dir);
77  user_data_dir_ = base::FilePath(user_data_dir);
78
79  succeded_ = true;
80  done_event_->Signal();
81  return true;
82}
83
84void SetupListener::OnChannelError() {
85  done_event_->Signal();
86}
87
88bool SetupListener::WaitResponce(const base::TimeDelta& delta) {
89  return done_event_->TimedWait(delta) && succeded_;
90}
91
92void SetupListener::Disconnect() {
93  channel_.reset();
94  ipc_thread_->message_loop()->QuitWhenIdle();
95}
96
97void SetupListener::Connect(const base::string16& user) {
98  ATL::CDacl dacl;
99
100  ATL::CSid user_sid;
101  if (!user_sid.LoadAccount(ReplaceLocalHostInName(user).c_str())) {
102    LOG(ERROR) << "Unable to load Sid for" << user;
103  } else {
104    dacl.AddAllowedAce(user_sid, GENERIC_READ | GENERIC_WRITE);
105  }
106
107  ATL::CSecurityDesc desk;
108  desk.SetDacl(dacl);
109
110  ATL::CSecurityAttributes attribs(desk);
111
112  base::win::ScopedHandle pipe(
113      CreateNamedPipe(kSetupPipeName,
114                      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
115                      FILE_FLAG_FIRST_PIPE_INSTANCE,
116                      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,
117                      IPC::Channel::kReadBufferSize,
118                      IPC::Channel::kReadBufferSize, 5000, &attribs));
119  if (pipe.IsValid()) {
120    channel_ = IPC::Channel::CreateServer(IPC::ChannelHandle(pipe.Get()),
121                                          this);
122    channel_->Connect();
123  }
124}
125
126