Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / cloud_print / service / win / cloud_print_service.cc
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 <atlbase.h>
6 #include <security.h>
7
8 #include <iomanip>
9 #include <iostream>
10 #include <iterator>
11 #include <string>
12 #include <vector>
13
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"
36
37 namespace {
38
39 void InvalidUsage() {
40   base::FilePath service_path;
41   CHECK(PathService::Get(base::FILE_EXE, &service_path));
42
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";
58
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   }};
73
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";
79   }
80   std::cout << "\n";
81 }
82
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);
101   }
102   if (tmp.empty())
103     return default_option;
104   return tmp;
105 }
106
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;
112 }
113
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;
126   }
127   return string_id ? cloud_print::LoadLocalString(string_id) : base::string16();
128 }
129
130 }  // namespace
131
132
133 class CloudPrintServiceModule
134     : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
135                                      IDS_SERVICE_NAME> {
136  public:
137   typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
138                                   IDS_SERVICE_NAME> Base;
139
140   CloudPrintServiceModule()
141       : check_requirements_(false),
142         controller_(new ServiceController()) {
143   }
144
145   static wchar_t* GetAppIdT() {
146     return ServiceController::GetAppIdT();
147   };
148
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;
153   }
154
155   bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
156     CHECK(pnRetCode);
157     CommandLine command_line(CommandLine::NO_PROGRAM);
158     command_line.ParseFromString(lpCmdLine);
159
160     LOG(INFO) << command_line.GetCommandLineString();
161
162     bool is_service = false;
163     *pnRetCode = ParseCommandLine(command_line, &is_service);
164     if (FAILED(*pnRetCode)) {
165       ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
166     }
167     if (!is_service) {
168       controller_->UpdateState();
169       std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
170       std::cout << " " << StateAsString(controller_->state());
171     }
172     return is_service;
173   }
174
175   HRESULT PreMessageLoop(int nShowCmd) {
176     HRESULT hr = Base::PreMessageLoop(nShowCmd);
177     if (FAILED(hr))
178       return hr;
179
180     if (check_requirements_) {
181       CheckRequirements();
182     } else {
183       HRESULT hr = StartConnector();
184       if (FAILED(hr))
185         return hr;
186     }
187
188     LogEvent(_T("Service started/resumed"));
189     SetServiceStatus(SERVICE_RUNNING);
190
191     return hr;
192   }
193
194   HRESULT PostMessageLoop() {
195     StopConnector();
196     setup_listener_.reset();
197     return Base::PostMessageLoop();
198   }
199
200  private:
201   HRESULT ParseCommandLine(const CommandLine& command_line, bool* is_service) {
202     if (!is_service)
203       return E_INVALIDARG;
204     *is_service = false;
205
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_);
210
211     if (command_line.HasSwitch(kStopSwitch))
212       return controller_->StopService();
213
214     if (command_line.HasSwitch(kUninstallSwitch))
215       return controller_->UninstallService();
216
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;
226
227       DCHECK(user_data_dir_switch_.empty() ||
228              user_data_dir_switch_ == user_data_dir);
229
230       hr = SetupServiceState(user_data_dir, printers);
231       if (FAILED(hr))
232         return hr;
233
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();
239
240       return hr;
241     }
242
243     if (command_line.HasSwitch(kStartSwitch))
244       return controller_->StartService();
245
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;
252     }
253
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;
259     }
260
261
262     InvalidUsage();
263     return S_FALSE;
264   }
265
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);
281     }
282
283     {
284       // Always uninstall service after requirements check.
285       base::ScopedClosureRunner scoped_uninstall(
286           base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
287                      base::Unretained(controller_.get())));
288
289       hr = controller_->StartService();
290       if (FAILED(hr)) {
291         return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
292       }
293
294       if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
295         return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
296       }
297     }
298
299     if (setup.user_data_dir().empty()) {
300       return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
301     }
302
303     if (setup.chrome_path().empty()) {
304       return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
305     }
306
307     if (!setup.is_xps_available()) {
308       return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
309     }
310
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";
325
326     *user_data_dir = setup.user_data_dir();
327     *printers = setup.printers();
328     return S_OK;
329   }
330
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);
334
335     std::string contents;
336     ServiceState service_state;
337
338     bool is_valid = base::ReadFileToString(file, &contents) &&
339                     service_state.FromString(contents);
340     std::string proxy_id = service_state.proxy_id();
341
342     LOG(INFO) << file.value() << ": " << contents;
343
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);
349
350     if (new_contents.empty()) {
351       return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
352     }
353
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);
360       }
361     }
362
363     return S_OK;
364   }
365
366   void CheckRequirements() {
367     setup_listener_.reset(new ServiceListener(GetUserDataDir()));
368   }
369
370   HRESULT StartConnector() {
371     chrome_.reset(new ChromeLauncher(GetUserDataDir()));
372     return chrome_->Start() ? S_OK : E_FAIL;
373   }
374
375   void StopConnector() {
376     if (chrome_.get()) {
377       chrome_->Stop();
378       chrome_.reset();
379     }
380   }
381
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);
388   }
389
390   static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
391
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_;
397 };
398
399 CloudPrintServiceModule _AtlModule;
400
401 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
402   PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
403   return TRUE;
404 }
405
406 int main(int argc, char** argv) {
407   CommandLine::Init(argc, argv);
408   base::AtExitManager at_exit;
409
410   logging::LoggingSettings settings;
411   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
412   logging::InitLogging(settings);
413
414   logging::SetMinLogLevel(
415       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableLogging) ?
416       logging::LOG_INFO : logging::LOG_FATAL);
417
418   return _AtlModule.WinMain(0);
419 }
420