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 21 const char SetupListener::kXpsAvailableJsonValueName[] = "xps_available"; 22 const char SetupListener::kChromePathJsonValueName[] = "chrome_path"; 23 const char SetupListener::kPrintersJsonValueName[] = "printers"; 24 const char SetupListener::kUserDataDirJsonValueName[] = "user_data_dir"; 25 const char SetupListener::kUserNameJsonValueName[] = "user_name"; 26 const wchar_t SetupListener::kSetupPipeName[] = 27 L"\\\\.\\pipe\\CloudPrintServiceSetup"; 28 29 SetupListener::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 41 SetupListener::~SetupListener() { 42 ipc_thread_->message_loop()->PostTask(FROM_HERE, 43 base::Bind(&SetupListener::Disconnect, 44 base::Unretained(this))); 45 ipc_thread_->Stop(); 46 } 47 48 bool 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 84 void SetupListener::OnChannelError() { 85 done_event_->Signal(); 86 } 87 88 bool SetupListener::WaitResponce(const base::TimeDelta& delta) { 89 return done_event_->TimedWait(delta) && succeded_; 90 } 91 92 void SetupListener::Disconnect() { 93 channel_.reset(); 94 ipc_thread_->message_loop()->QuitWhenIdle(); 95 } 96 97 void 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_.reset(new IPC::Channel(IPC::ChannelHandle(pipe), 121 IPC::Channel::MODE_SERVER, this)); 122 channel_->Connect(); 123 } 124 } 125 126