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 // Work around warning in atlbase.h
6 // https://connect.microsoft.com/VisualStudio/feedback/details/1032199/atlbase-h-gives-warning-c4189-when-compiling-with-atl-no-com-support
8 #pragma warning(disable:4189)
19 #include "base/at_exit.h"
20 #include "base/bind.h"
21 #include "base/callback_helpers.h"
22 #include "base/command_line.h"
23 #include "base/files/file_util.h"
24 #include "base/guid.h"
25 #include "base/logging.h"
26 #include "base/path_service.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/win/scoped_handle.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "cloud_print/common/win/cloud_print_utils.h"
33 #include "cloud_print/service/service_constants.h"
34 #include "cloud_print/service/service_state.h"
35 #include "cloud_print/service/service_switches.h"
36 #include "cloud_print/service/win/chrome_launcher.h"
37 #include "cloud_print/service/win/service_controller.h"
38 #include "cloud_print/service/win/service_listener.h"
39 #include "cloud_print/service/win/service_utils.h"
40 #include "cloud_print/service/win/setup_listener.h"
45 base::FilePath service_path
;
46 CHECK(PathService::Get(base::FILE_EXE
, &service_path
));
48 std::cout
<< cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE
);
49 std::cout
<< " " << service_path
.BaseName().value();
53 std::cout
<< " -" << kInstallSwitch
;
54 std::cout
<< " [ -" << switches::kUserDataDir
<< "=DIRECTORY ]";
57 std::cout
<< " | -" << kUninstallSwitch
;
58 std::cout
<< " | -" << kStartSwitch
;
59 std::cout
<< " | -" << kStopSwitch
;
61 std::cout
<< cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION
);
68 kInstallSwitch
, IDS_SWITCH_HELP_INSTALL
70 switches::kUserDataDir
, IDS_SWITCH_HELP_DATA_DIR
72 kUninstallSwitch
, IDS_SWITCH_HELP_UNINSTALL
74 kStartSwitch
, IDS_SWITCH_HELP_START
76 kStopSwitch
, IDS_SWITCH_HELP_STOP
79 for (size_t i
= 0; i
< arraysize(kSwitchHelp
); ++i
) {
80 std::cout
<< std::setiosflags(std::ios::left
);
81 std::cout
<< " -" << std::setw(16) << kSwitchHelp
[i
].name
;
82 std::cout
<< cloud_print::LoadLocalString(kSwitchHelp
[i
].description
);
88 base::string16
GetOption(int string_id
,
89 const base::string16
& default_option
,
91 base::string16 prompt_format
= cloud_print::LoadLocalString(string_id
);
92 std::vector
<base::string16
> substitutions(1, default_option
);
94 base::ReplaceStringPlaceholders(prompt_format
, substitutions
, NULL
);
99 HANDLE stdin_handle
= ::GetStdHandle(STD_INPUT_HANDLE
);
100 ::GetConsoleMode(stdin_handle
, &saved_mode
);
101 ::SetConsoleMode(stdin_handle
, saved_mode
& ~ENABLE_ECHO_INPUT
);
102 std::getline(std::wcin
, tmp
);
103 ::SetConsoleMode(stdin_handle
, saved_mode
);
106 std::getline(std::wcin
, tmp
);
109 return default_option
;
113 HRESULT
ReportError(HRESULT hr
, int string_id
) {
114 LOG(ERROR
) << cloud_print::GetErrorMessage(hr
);
115 std::cerr
<< cloud_print::LoadLocalString(string_id
);
120 base::string16
StateAsString(ServiceController::State state
) {
123 case ServiceController::STATE_NOT_FOUND
:
124 string_id
= IDS_SERVICE_NOT_FOUND
;
126 case ServiceController::STATE_STOPPED
:
127 string_id
= IDS_SERVICE_STOPPED
;
129 case ServiceController::STATE_RUNNING
:
130 string_id
= IDS_SERVICE_RUNNING
;
132 case ServiceController::STATE_UNKNOWN
:
135 return string_id
? cloud_print::LoadLocalString(string_id
) : base::string16();
141 class CloudPrintServiceModule
142 : public ATL::CAtlServiceModuleT
<CloudPrintServiceModule
,
145 typedef ATL::CAtlServiceModuleT
<CloudPrintServiceModule
,
146 IDS_SERVICE_NAME
> Base
;
148 CloudPrintServiceModule()
149 : check_requirements_(false),
150 controller_(new ServiceController()) {
153 static const wchar_t* GetAppIdT() {
154 return ServiceController::GetAppIdT();
157 HRESULT
InitializeSecurity() {
158 // TODO(gene): Check if we need to call CoInitializeSecurity and provide
159 // the appropriate security settings for service.
163 bool ParseCommandLine(LPCTSTR lpCmdLine
, HRESULT
* pnRetCode
) {
165 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
166 command_line
.ParseFromString(lpCmdLine
);
168 LOG(INFO
) << command_line
.GetCommandLineString();
170 bool is_service
= false;
171 *pnRetCode
= ParseCommandLine(command_line
, &is_service
);
172 if (FAILED(*pnRetCode
)) {
173 ReportError(*pnRetCode
, IDS_OPERATION_FAILED_TITLE
);
176 controller_
->UpdateState();
177 std::cout
<< cloud_print::LoadLocalString(IDS_STATE_LABEL
);
178 std::cout
<< " " << StateAsString(controller_
->state());
183 HRESULT
PreMessageLoop(int nShowCmd
) {
184 HRESULT hr
= Base::PreMessageLoop(nShowCmd
);
188 if (check_requirements_
) {
191 HRESULT hr
= StartConnector();
196 LogEvent(_T("Service started/resumed"));
197 SetServiceStatus(SERVICE_RUNNING
);
202 HRESULT
PostMessageLoop() {
204 setup_listener_
.reset();
205 return Base::PostMessageLoop();
209 HRESULT
ParseCommandLine(const base::CommandLine
& command_line
,
215 user_data_dir_switch_
=
216 command_line
.GetSwitchValuePath(switches::kUserDataDir
);
217 if (!user_data_dir_switch_
.empty())
218 user_data_dir_switch_
= base::MakeAbsoluteFilePath(user_data_dir_switch_
);
220 if (command_line
.HasSwitch(kStopSwitch
))
221 return controller_
->StopService();
223 if (command_line
.HasSwitch(kUninstallSwitch
))
224 return controller_
->UninstallService();
226 if (command_line
.HasSwitch(kInstallSwitch
)) {
227 base::string16 run_as_user
;
228 base::string16 run_as_password
;
229 base::FilePath user_data_dir
;
230 std::vector
<std::string
> printers
;
231 HRESULT hr
= SelectWindowsAccount(&run_as_user
, &run_as_password
,
232 &user_data_dir
, &printers
);
236 DCHECK(user_data_dir_switch_
.empty() ||
237 user_data_dir_switch_
== user_data_dir
);
239 hr
= SetupServiceState(user_data_dir
, printers
);
243 hr
= controller_
->InstallConnectorService(
244 run_as_user
, run_as_password
, user_data_dir_switch_
,
245 command_line
.HasSwitch(switches::kEnableLogging
));
246 if (SUCCEEDED(hr
) && command_line
.HasSwitch(kStartSwitch
))
247 return controller_
->StartService();
252 if (command_line
.HasSwitch(kStartSwitch
))
253 return controller_
->StartService();
255 if (command_line
.HasSwitch(kConsoleSwitch
)) {
256 check_requirements_
= command_line
.HasSwitch(kRequirementsSwitch
);
257 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler
, TRUE
);
259 ::SetConsoleCtrlHandler(NULL
, FALSE
);
263 if (command_line
.HasSwitch(kServiceSwitch
) ||
264 command_line
.HasSwitch(kRequirementsSwitch
)) {
266 check_requirements_
= command_line
.HasSwitch(kRequirementsSwitch
);
275 HRESULT
SelectWindowsAccount(base::string16
* run_as_user
,
276 base::string16
* run_as_password
,
277 base::FilePath
* user_data_dir
,
278 std::vector
<std::string
>* printers
) {
279 *run_as_user
= GetCurrentUserName();
280 std::cout
<< cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1
) << "\n";
281 *run_as_user
= GetOption(IDS_WINDOWS_USER_PROMPT2
, *run_as_user
, false);
282 *run_as_user
= ReplaceLocalHostInName(*run_as_user
);
283 *run_as_password
= GetOption(IDS_WINDOWS_PASSWORD_PROMPT
, L
"", true);
284 SetupListener
setup(*run_as_user
);
285 HRESULT hr
= controller_
->InstallCheckService(*run_as_user
,
287 user_data_dir_switch_
);
289 return ReportError(hr
, IDS_ERROR_FAILED_INSTALL_SERVICE
);
293 // Always uninstall service after requirements check.
294 base::ScopedClosureRunner
scoped_uninstall(
295 base::Bind(base::IgnoreResult(&ServiceController::UninstallService
),
296 base::Unretained(controller_
.get())));
298 hr
= controller_
->StartService();
300 return ReportError(hr
, IDS_ERROR_FAILED_START_SERVICE
);
303 if (!setup
.WaitResponce(base::TimeDelta::FromSeconds(30))) {
304 return ReportError(E_FAIL
, IDS_ERROR_FAILED_START_SERVICE
);
308 if (setup
.user_data_dir().empty()) {
309 return ReportError(E_FAIL
, IDS_ERROR_NO_DATA_DIR
);
312 if (setup
.chrome_path().empty()) {
313 return ReportError(E_FAIL
, IDS_ERROR_NO_CHROME
);
316 if (!setup
.is_xps_available()) {
317 return ReportError(E_FAIL
, IDS_ERROR_NO_XPS
);
321 std::cout
<< cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK
);
323 std::cout
<< cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER
);
324 std::cout
<< "\n " << setup
.user_name() << "\n";
325 std::cout
<< cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME
);
326 std::cout
<< "\n " << setup
.chrome_path().value() << "\n";
327 std::cout
<< cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR
);
328 std::cout
<< "\n " << setup
.user_data_dir().value() << "\n";
329 std::cout
<< cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS
);
331 std::ostream_iterator
<std::string
> cout_it(std::cout
, "\n ");
332 std::copy(setup
.printers().begin(), setup
.printers().end(), cout_it
);
335 *user_data_dir
= setup
.user_data_dir();
336 *printers
= setup
.printers();
340 HRESULT
SetupServiceState(const base::FilePath
& user_data_dir
,
341 const std::vector
<std::string
>& printers
) {
342 base::FilePath file
= user_data_dir
.Append(chrome::kServiceStateFileName
);
344 std::string contents
;
345 base::ReadFileToString(file
, &contents
);
346 ServiceState service_state
;
347 service_state
.FromString(contents
);
348 std::string proxy_id
= service_state
.proxy_id();
350 LOG(INFO
) << file
.value() << ": " << contents
;
352 base::string16 message
=
353 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME
);
354 std::cout
<< "\n" << message
.c_str() << "\n" ;
355 std::string new_contents
=
356 ChromeLauncher::CreateServiceStateFile(proxy_id
, printers
);
358 if (new_contents
.empty()) {
359 return ReportError(E_FAIL
, IDS_ERROR_FAILED_CREATE_CONFIG
);
362 if (new_contents
!= contents
) {
363 size_t written
= base::WriteFile(file
, new_contents
.c_str(),
364 new_contents
.size());
365 if (written
!= new_contents
.size()) {
366 return ReportError(cloud_print::GetLastHResult(),
367 IDS_ERROR_FAILED_CREATE_CONFIG
);
374 void CheckRequirements() {
375 setup_listener_
.reset(new ServiceListener(GetUserDataDir()));
378 HRESULT
StartConnector() {
379 chrome_
.reset(new ChromeLauncher(GetUserDataDir()));
380 return chrome_
->Start() ? S_OK
: E_FAIL
;
383 void StopConnector() {
390 base::FilePath
GetUserDataDir() const {
391 if (!user_data_dir_switch_
.empty())
392 return user_data_dir_switch_
;
393 base::FilePath result
;
394 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA
, &result
));
395 return result
.Append(kSubDirectory
);
398 static BOOL WINAPI
ConsoleCtrlHandler(DWORD type
);
400 bool check_requirements_
;
401 base::FilePath user_data_dir_switch_
;
402 scoped_ptr
<ChromeLauncher
> chrome_
;
403 scoped_ptr
<ServiceController
> controller_
;
404 scoped_ptr
<ServiceListener
> setup_listener_
;
407 CloudPrintServiceModule _AtlModule
;
409 BOOL
CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type
) {
410 PostThreadMessage(_AtlModule
.m_dwThreadID
, WM_QUIT
, 0, 0);
414 int main(int argc
, char** argv
) {
415 base::CommandLine::Init(argc
, argv
);
416 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
417 base::AtExitManager at_exit
;
419 logging::LoggingSettings settings
;
420 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
421 logging::InitLogging(settings
);
423 logging::SetMinLogLevel(
424 command_line
->HasSwitch(switches::kEnableLogging
) ?
425 logging::LOG_INFO
: logging::LOG_FATAL
);
427 return _AtlModule
.WinMain(0);