Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / cloud_print / service / win / cloud_print_service.cc
blob6f580951584057c1a475264446ca7ac0bd05d808
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.
5 #include <atlbase.h>
6 #include <security.h>
8 #include <iomanip>
9 #include <iostream>
10 #include <iterator>
11 #include <string>
12 #include <vector>
14 #include "base/at_exit.h"
15 #include "base/bind.h"
16 #include "base/callback_helpers.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/guid.h"
20 #include "base/logging.h"
21 #include "base/path_service.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/win/scoped_handle.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "cloud_print/common/win/cloud_print_utils.h"
28 #include "cloud_print/service/service_constants.h"
29 #include "cloud_print/service/service_state.h"
30 #include "cloud_print/service/service_switches.h"
31 #include "cloud_print/service/win/chrome_launcher.h"
32 #include "cloud_print/service/win/service_controller.h"
33 #include "cloud_print/service/win/service_listener.h"
34 #include "cloud_print/service/win/service_utils.h"
35 #include "cloud_print/service/win/setup_listener.h"
37 namespace {
39 void InvalidUsage() {
40 base::FilePath service_path;
41 CHECK(PathService::Get(base::FILE_EXE, &service_path));
43 std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE);
44 std::cout << " " << service_path.BaseName().value();
45 std::cout << " [";
46 std::cout << "[";
47 std::cout << "[";
48 std::cout << " -" << kInstallSwitch;
49 std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]";
50 std::cout << "]";
51 std::cout << "]";
52 std::cout << " | -" << kUninstallSwitch;
53 std::cout << " | -" << kStartSwitch;
54 std::cout << " | -" << kStopSwitch;
55 std::cout << " ]\n";
56 std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION);
57 std::cout << "\n\n";
59 struct {
60 const char* name;
61 int description;
62 } kSwitchHelp[] = {{
63 kInstallSwitch, IDS_SWITCH_HELP_INSTALL
64 }, {
65 switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR
66 }, {
67 kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL
68 }, {
69 kStartSwitch, IDS_SWITCH_HELP_START
70 }, {
71 kStopSwitch, IDS_SWITCH_HELP_STOP
72 }};
74 for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) {
75 std::cout << std::setiosflags(std::ios::left);
76 std::cout << " -" << std::setw(16) << kSwitchHelp[i].name;
77 std::cout << cloud_print::LoadLocalString(kSwitchHelp[i].description);
78 std::cout << "\n";
80 std::cout << "\n";
83 base::string16 GetOption(int string_id, const base::string16& default,
84 bool secure) {
85 base::string16 prompt_format = cloud_print::LoadLocalString(string_id);
86 std::vector<base::string16> substitutions(1, default);
87 std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL);
88 base::string16 tmp;
89 if (secure) {
90 DWORD saved_mode = 0;
91 // Don't close.
92 HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE);
93 ::GetConsoleMode(stdin_handle, &saved_mode);
94 ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT);
95 std::getline(std::wcin, tmp);
96 ::SetConsoleMode(stdin_handle, saved_mode);
97 std::cout << "\n";
98 } else {
99 std::getline(std::wcin, tmp);
101 if (tmp.empty())
102 return default;
103 return tmp;
106 HRESULT ReportError(HRESULT hr, int string_id) {
107 LOG(ERROR) << cloud_print::GetErrorMessage(hr);
108 std::cerr << cloud_print::LoadLocalString(string_id);
109 std::cerr << "\n";
110 return hr;
113 base::string16 StateAsString(ServiceController::State state) {
114 DWORD string_id = 0;
115 switch(state) {
116 case ServiceController::STATE_NOT_FOUND:
117 string_id = IDS_SERVICE_NOT_FOUND;
118 break;
119 case ServiceController::STATE_STOPPED:
120 string_id = IDS_SERVICE_STOPPED;
121 break;
122 case ServiceController::STATE_RUNNING:
123 string_id = IDS_SERVICE_RUNNING;
124 break;
126 return string_id ? cloud_print::LoadLocalString(string_id) : base::string16();
129 } // namespace
132 class CloudPrintServiceModule
133 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
134 IDS_SERVICE_NAME> {
135 public:
136 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
137 IDS_SERVICE_NAME> Base;
139 CloudPrintServiceModule()
140 : check_requirements_(false),
141 controller_(new ServiceController()) {
144 static wchar_t* GetAppIdT() {
145 return ServiceController::GetAppIdT();
148 HRESULT InitializeSecurity() {
149 // TODO(gene): Check if we need to call CoInitializeSecurity and provide
150 // the appropriate security settings for service.
151 return S_OK;
154 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
155 CHECK(pnRetCode);
156 CommandLine command_line(CommandLine::NO_PROGRAM);
157 command_line.ParseFromString(lpCmdLine);
159 LOG(INFO) << command_line.GetCommandLineString();
161 bool is_service = false;
162 *pnRetCode = ParseCommandLine(command_line, &is_service);
163 if (FAILED(*pnRetCode)) {
164 ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
166 if (!is_service) {
167 controller_->UpdateState();
168 std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
169 std::cout << " " << StateAsString(controller_->state());
171 return is_service;
174 HRESULT PreMessageLoop(int nShowCmd) {
175 HRESULT hr = Base::PreMessageLoop(nShowCmd);
176 if (FAILED(hr))
177 return hr;
179 if (check_requirements_) {
180 CheckRequirements();
181 } else {
182 HRESULT hr = StartConnector();
183 if (FAILED(hr))
184 return hr;
187 LogEvent(_T("Service started/resumed"));
188 SetServiceStatus(SERVICE_RUNNING);
190 return hr;
193 HRESULT PostMessageLoop() {
194 StopConnector();
195 setup_listener_.reset();
196 return Base::PostMessageLoop();
199 private:
200 HRESULT ParseCommandLine(const CommandLine& command_line, bool* is_service) {
201 if (!is_service)
202 return E_INVALIDARG;
203 *is_service = false;
205 user_data_dir_switch_ =
206 command_line.GetSwitchValuePath(switches::kUserDataDir);
207 if (!user_data_dir_switch_.empty())
208 user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_);
210 if (command_line.HasSwitch(kStopSwitch))
211 return controller_->StopService();
213 if (command_line.HasSwitch(kUninstallSwitch))
214 return controller_->UninstallService();
216 if (command_line.HasSwitch(kInstallSwitch)) {
217 base::string16 run_as_user;
218 base::string16 run_as_password;
219 base::FilePath user_data_dir;
220 std::vector<std::string> printers;
221 HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password,
222 &user_data_dir, &printers);
223 if (FAILED(hr))
224 return hr;
226 DCHECK(user_data_dir_switch_.empty() ||
227 user_data_dir_switch_ == user_data_dir);
229 hr = SetupServiceState(user_data_dir, printers);
230 if (FAILED(hr))
231 return hr;
233 hr = controller_->InstallConnectorService(
234 run_as_user, run_as_password, user_data_dir_switch_,
235 command_line.HasSwitch(switches::kEnableLogging));
236 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
237 return controller_->StartService();
239 return hr;
242 if (command_line.HasSwitch(kStartSwitch))
243 return controller_->StartService();
245 if (command_line.HasSwitch(kConsoleSwitch)) {
246 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
247 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
248 HRESULT hr = Run();
249 ::SetConsoleCtrlHandler(NULL, FALSE);
250 return hr;
253 if (command_line.HasSwitch(kServiceSwitch) ||
254 command_line.HasSwitch(kRequirementsSwitch)) {
255 *is_service = true;
256 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
257 return S_OK;
261 InvalidUsage();
262 return S_FALSE;
265 HRESULT SelectWindowsAccount(base::string16* run_as_user,
266 base::string16* run_as_password,
267 base::FilePath* user_data_dir,
268 std::vector<std::string>* printers) {
269 *run_as_user = GetCurrentUserName();
270 std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n";
271 *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false);
272 *run_as_user = ReplaceLocalHostInName(*run_as_user);
273 *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true);
274 SetupListener setup(*run_as_user);
275 HRESULT hr = controller_->InstallCheckService(*run_as_user,
276 *run_as_password,
277 user_data_dir_switch_);
278 if (FAILED(hr)) {
279 return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE);
283 // Always uninstall service after requirements check.
284 base::ScopedClosureRunner scoped_uninstall(
285 base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
286 base::Unretained(controller_.get())));
288 hr = controller_->StartService();
289 if (FAILED(hr)) {
290 return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
293 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
294 return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
298 if (setup.user_data_dir().empty()) {
299 return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
302 if (setup.chrome_path().empty()) {
303 return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
306 if (!setup.is_xps_available()) {
307 return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
310 std::cout << "\n";
311 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK);
312 std::cout << "\n";
313 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER);
314 std::cout << "\n " << setup.user_name() << "\n";
315 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME);
316 std::cout << "\n " << setup.chrome_path().value() << "\n";
317 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR);
318 std::cout << "\n " << setup.user_data_dir().value() << "\n";
319 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS);
320 std::cout << "\n ";
321 std::ostream_iterator<std::string> cout_it(std::cout, "\n ");
322 std::copy(setup.printers().begin(), setup.printers().end(), cout_it);
323 std::cout << "\n";
325 *user_data_dir = setup.user_data_dir();
326 *printers = setup.printers();
327 return S_OK;
330 HRESULT SetupServiceState(const base::FilePath& user_data_dir,
331 const std::vector<std::string>& printers) {
332 base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName);
334 std::string contents;
335 ServiceState service_state;
337 bool is_valid = base::ReadFileToString(file, &contents) &&
338 service_state.FromString(contents);
339 std::string proxy_id = service_state.proxy_id();
341 LOG(INFO) << file.value() << ": " << contents;
343 base::string16 message =
344 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME);
345 std::cout << "\n" << message.c_str() << "\n" ;
346 std::string new_contents =
347 ChromeLauncher::CreateServiceStateFile(proxy_id, printers);
349 if (new_contents.empty()) {
350 return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
353 if (new_contents != contents) {
354 size_t written = base::WriteFile(file, new_contents.c_str(),
355 new_contents.size());
356 if (written != new_contents.size()) {
357 return ReportError(cloud_print::GetLastHResult(),
358 IDS_ERROR_FAILED_CREATE_CONFIG);
362 return S_OK;
365 void CheckRequirements() {
366 setup_listener_.reset(new ServiceListener(GetUserDataDir()));
369 HRESULT StartConnector() {
370 chrome_.reset(new ChromeLauncher(GetUserDataDir()));
371 return chrome_->Start() ? S_OK : E_FAIL;
374 void StopConnector() {
375 if (chrome_.get()) {
376 chrome_->Stop();
377 chrome_.reset();
381 base::FilePath GetUserDataDir() const {
382 if (!user_data_dir_switch_.empty())
383 return user_data_dir_switch_;
384 base::FilePath result;
385 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result));
386 return result.Append(kSubDirectory);
389 static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
391 bool check_requirements_;
392 base::FilePath user_data_dir_switch_;
393 scoped_ptr<ChromeLauncher> chrome_;
394 scoped_ptr<ServiceController> controller_;
395 scoped_ptr<ServiceListener> setup_listener_;
398 CloudPrintServiceModule _AtlModule;
400 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
401 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
402 return TRUE;
405 int main(int argc, char** argv) {
406 CommandLine::Init(argc, argv);
407 base::AtExitManager at_exit;
409 logging::LoggingSettings settings;
410 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
411 logging::InitLogging(settings);
413 logging::SetMinLogLevel(
414 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableLogging) ?
415 logging::LOG_INFO : logging::LOG_FATAL);
417 return _AtlModule.WinMain(0);