Add OWNERS for chrome/android/java/res directory.
[chromium-blink-merge.git] / cloud_print / service / win / cloud_print_service.cc
blob55652692f3bd636d8fd5610cc4634ef2343aa4b8
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/files/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,
84 const base::string16& default_option,
85 bool secure) {
86 base::string16 prompt_format = cloud_print::LoadLocalString(string_id);
87 std::vector<base::string16> substitutions(1, default_option);
88 std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL);
89 base::string16 tmp;
90 if (secure) {
91 DWORD saved_mode = 0;
92 // Don't close.
93 HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE);
94 ::GetConsoleMode(stdin_handle, &saved_mode);
95 ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT);
96 std::getline(std::wcin, tmp);
97 ::SetConsoleMode(stdin_handle, saved_mode);
98 std::cout << "\n";
99 } else {
100 std::getline(std::wcin, tmp);
102 if (tmp.empty())
103 return default_option;
104 return tmp;
107 HRESULT ReportError(HRESULT hr, int string_id) {
108 LOG(ERROR) << cloud_print::GetErrorMessage(hr);
109 std::cerr << cloud_print::LoadLocalString(string_id);
110 std::cerr << "\n";
111 return hr;
114 base::string16 StateAsString(ServiceController::State state) {
115 DWORD string_id = 0;
116 switch(state) {
117 case ServiceController::STATE_NOT_FOUND:
118 string_id = IDS_SERVICE_NOT_FOUND;
119 break;
120 case ServiceController::STATE_STOPPED:
121 string_id = IDS_SERVICE_STOPPED;
122 break;
123 case ServiceController::STATE_RUNNING:
124 string_id = IDS_SERVICE_RUNNING;
125 break;
127 return string_id ? cloud_print::LoadLocalString(string_id) : base::string16();
130 } // namespace
133 class CloudPrintServiceModule
134 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
135 IDS_SERVICE_NAME> {
136 public:
137 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
138 IDS_SERVICE_NAME> Base;
140 CloudPrintServiceModule()
141 : check_requirements_(false),
142 controller_(new ServiceController()) {
145 static wchar_t* GetAppIdT() {
146 return ServiceController::GetAppIdT();
149 HRESULT InitializeSecurity() {
150 // TODO(gene): Check if we need to call CoInitializeSecurity and provide
151 // the appropriate security settings for service.
152 return S_OK;
155 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
156 CHECK(pnRetCode);
157 CommandLine command_line(CommandLine::NO_PROGRAM);
158 command_line.ParseFromString(lpCmdLine);
160 LOG(INFO) << command_line.GetCommandLineString();
162 bool is_service = false;
163 *pnRetCode = ParseCommandLine(command_line, &is_service);
164 if (FAILED(*pnRetCode)) {
165 ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
167 if (!is_service) {
168 controller_->UpdateState();
169 std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
170 std::cout << " " << StateAsString(controller_->state());
172 return is_service;
175 HRESULT PreMessageLoop(int nShowCmd) {
176 HRESULT hr = Base::PreMessageLoop(nShowCmd);
177 if (FAILED(hr))
178 return hr;
180 if (check_requirements_) {
181 CheckRequirements();
182 } else {
183 HRESULT hr = StartConnector();
184 if (FAILED(hr))
185 return hr;
188 LogEvent(_T("Service started/resumed"));
189 SetServiceStatus(SERVICE_RUNNING);
191 return hr;
194 HRESULT PostMessageLoop() {
195 StopConnector();
196 setup_listener_.reset();
197 return Base::PostMessageLoop();
200 private:
201 HRESULT ParseCommandLine(const CommandLine& command_line, bool* is_service) {
202 if (!is_service)
203 return E_INVALIDARG;
204 *is_service = false;
206 user_data_dir_switch_ =
207 command_line.GetSwitchValuePath(switches::kUserDataDir);
208 if (!user_data_dir_switch_.empty())
209 user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_);
211 if (command_line.HasSwitch(kStopSwitch))
212 return controller_->StopService();
214 if (command_line.HasSwitch(kUninstallSwitch))
215 return controller_->UninstallService();
217 if (command_line.HasSwitch(kInstallSwitch)) {
218 base::string16 run_as_user;
219 base::string16 run_as_password;
220 base::FilePath user_data_dir;
221 std::vector<std::string> printers;
222 HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password,
223 &user_data_dir, &printers);
224 if (FAILED(hr))
225 return hr;
227 DCHECK(user_data_dir_switch_.empty() ||
228 user_data_dir_switch_ == user_data_dir);
230 hr = SetupServiceState(user_data_dir, printers);
231 if (FAILED(hr))
232 return hr;
234 hr = controller_->InstallConnectorService(
235 run_as_user, run_as_password, user_data_dir_switch_,
236 command_line.HasSwitch(switches::kEnableLogging));
237 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
238 return controller_->StartService();
240 return hr;
243 if (command_line.HasSwitch(kStartSwitch))
244 return controller_->StartService();
246 if (command_line.HasSwitch(kConsoleSwitch)) {
247 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
248 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
249 HRESULT hr = Run();
250 ::SetConsoleCtrlHandler(NULL, FALSE);
251 return hr;
254 if (command_line.HasSwitch(kServiceSwitch) ||
255 command_line.HasSwitch(kRequirementsSwitch)) {
256 *is_service = true;
257 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
258 return S_OK;
262 InvalidUsage();
263 return S_FALSE;
266 HRESULT SelectWindowsAccount(base::string16* run_as_user,
267 base::string16* run_as_password,
268 base::FilePath* user_data_dir,
269 std::vector<std::string>* printers) {
270 *run_as_user = GetCurrentUserName();
271 std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n";
272 *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false);
273 *run_as_user = ReplaceLocalHostInName(*run_as_user);
274 *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true);
275 SetupListener setup(*run_as_user);
276 HRESULT hr = controller_->InstallCheckService(*run_as_user,
277 *run_as_password,
278 user_data_dir_switch_);
279 if (FAILED(hr)) {
280 return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE);
284 // Always uninstall service after requirements check.
285 base::ScopedClosureRunner scoped_uninstall(
286 base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
287 base::Unretained(controller_.get())));
289 hr = controller_->StartService();
290 if (FAILED(hr)) {
291 return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
294 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
295 return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
299 if (setup.user_data_dir().empty()) {
300 return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
303 if (setup.chrome_path().empty()) {
304 return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
307 if (!setup.is_xps_available()) {
308 return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
311 std::cout << "\n";
312 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK);
313 std::cout << "\n";
314 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER);
315 std::cout << "\n " << setup.user_name() << "\n";
316 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME);
317 std::cout << "\n " << setup.chrome_path().value() << "\n";
318 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR);
319 std::cout << "\n " << setup.user_data_dir().value() << "\n";
320 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS);
321 std::cout << "\n ";
322 std::ostream_iterator<std::string> cout_it(std::cout, "\n ");
323 std::copy(setup.printers().begin(), setup.printers().end(), cout_it);
324 std::cout << "\n";
326 *user_data_dir = setup.user_data_dir();
327 *printers = setup.printers();
328 return S_OK;
331 HRESULT SetupServiceState(const base::FilePath& user_data_dir,
332 const std::vector<std::string>& printers) {
333 base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName);
335 std::string contents;
336 ServiceState service_state;
338 bool is_valid = base::ReadFileToString(file, &contents) &&
339 service_state.FromString(contents);
340 std::string proxy_id = service_state.proxy_id();
342 LOG(INFO) << file.value() << ": " << contents;
344 base::string16 message =
345 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME);
346 std::cout << "\n" << message.c_str() << "\n" ;
347 std::string new_contents =
348 ChromeLauncher::CreateServiceStateFile(proxy_id, printers);
350 if (new_contents.empty()) {
351 return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
354 if (new_contents != contents) {
355 size_t written = base::WriteFile(file, new_contents.c_str(),
356 new_contents.size());
357 if (written != new_contents.size()) {
358 return ReportError(cloud_print::GetLastHResult(),
359 IDS_ERROR_FAILED_CREATE_CONFIG);
363 return S_OK;
366 void CheckRequirements() {
367 setup_listener_.reset(new ServiceListener(GetUserDataDir()));
370 HRESULT StartConnector() {
371 chrome_.reset(new ChromeLauncher(GetUserDataDir()));
372 return chrome_->Start() ? S_OK : E_FAIL;
375 void StopConnector() {
376 if (chrome_.get()) {
377 chrome_->Stop();
378 chrome_.reset();
382 base::FilePath GetUserDataDir() const {
383 if (!user_data_dir_switch_.empty())
384 return user_data_dir_switch_;
385 base::FilePath result;
386 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result));
387 return result.Append(kSubDirectory);
390 static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
392 bool check_requirements_;
393 base::FilePath user_data_dir_switch_;
394 scoped_ptr<ChromeLauncher> chrome_;
395 scoped_ptr<ServiceController> controller_;
396 scoped_ptr<ServiceListener> setup_listener_;
399 CloudPrintServiceModule _AtlModule;
401 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
402 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
403 return TRUE;
406 int main(int argc, char** argv) {
407 CommandLine::Init(argc, argv);
408 base::AtExitManager at_exit;
410 logging::LoggingSettings settings;
411 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
412 logging::InitLogging(settings);
414 logging::SetMinLogLevel(
415 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableLogging) ?
416 logging::LOG_INFO : logging::LOG_FATAL);
418 return _AtlModule.WinMain(0);