// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Work around warning in atlbase.h // https://connect.microsoft.com/VisualStudio/feedback/details/1032199/atlbase-h-gives-warning-c4189-when-compiling-with-atl-no-com-support #pragma warning(push) #pragma warning(disable:4189) #include #pragma warning(pop) #include #include #include #include #include #include #include "base/at_exit.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/files/file_util.h" #include "base/guid.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_handle.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "cloud_print/common/win/cloud_print_utils.h" #include "cloud_print/service/service_constants.h" #include "cloud_print/service/service_state.h" #include "cloud_print/service/service_switches.h" #include "cloud_print/service/win/chrome_launcher.h" #include "cloud_print/service/win/service_controller.h" #include "cloud_print/service/win/service_listener.h" #include "cloud_print/service/win/service_utils.h" #include "cloud_print/service/win/setup_listener.h" namespace { void InvalidUsage() { base::FilePath service_path; CHECK(PathService::Get(base::FILE_EXE, &service_path)); std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE); std::cout << " " << service_path.BaseName().value(); std::cout << " ["; std::cout << "["; std::cout << "["; std::cout << " -" << kInstallSwitch; std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]"; std::cout << "]"; std::cout << "]"; std::cout << " | -" << kUninstallSwitch; std::cout << " | -" << kStartSwitch; std::cout << " | -" << kStopSwitch; std::cout << " ]\n"; std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION); std::cout << "\n\n"; struct { const char* name; int description; } kSwitchHelp[] = {{ kInstallSwitch, IDS_SWITCH_HELP_INSTALL }, { switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR }, { kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL }, { kStartSwitch, IDS_SWITCH_HELP_START }, { kStopSwitch, IDS_SWITCH_HELP_STOP }}; for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { std::cout << std::setiosflags(std::ios::left); std::cout << " -" << std::setw(16) << kSwitchHelp[i].name; std::cout << cloud_print::LoadLocalString(kSwitchHelp[i].description); std::cout << "\n"; } std::cout << "\n"; } base::string16 GetOption(int string_id, const base::string16& default_option, bool secure) { base::string16 prompt_format = cloud_print::LoadLocalString(string_id); std::vector substitutions(1, default_option); std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL); base::string16 tmp; if (secure) { DWORD saved_mode = 0; // Don't close. HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE); ::GetConsoleMode(stdin_handle, &saved_mode); ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT); std::getline(std::wcin, tmp); ::SetConsoleMode(stdin_handle, saved_mode); std::cout << "\n"; } else { std::getline(std::wcin, tmp); } if (tmp.empty()) return default_option; return tmp; } HRESULT ReportError(HRESULT hr, int string_id) { LOG(ERROR) << cloud_print::GetErrorMessage(hr); std::cerr << cloud_print::LoadLocalString(string_id); std::cerr << "\n"; return hr; } base::string16 StateAsString(ServiceController::State state) { DWORD string_id = 0; switch(state) { case ServiceController::STATE_NOT_FOUND: string_id = IDS_SERVICE_NOT_FOUND; break; case ServiceController::STATE_STOPPED: string_id = IDS_SERVICE_STOPPED; break; case ServiceController::STATE_RUNNING: string_id = IDS_SERVICE_RUNNING; break; } return string_id ? cloud_print::LoadLocalString(string_id) : base::string16(); } } // namespace class CloudPrintServiceModule : public ATL::CAtlServiceModuleT { public: typedef ATL::CAtlServiceModuleT Base; CloudPrintServiceModule() : check_requirements_(false), controller_(new ServiceController()) { } static wchar_t* GetAppIdT() { return ServiceController::GetAppIdT(); }; HRESULT InitializeSecurity() { // TODO(gene): Check if we need to call CoInitializeSecurity and provide // the appropriate security settings for service. return S_OK; } bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) { CHECK(pnRetCode); base::CommandLine command_line(base::CommandLine::NO_PROGRAM); command_line.ParseFromString(lpCmdLine); LOG(INFO) << command_line.GetCommandLineString(); bool is_service = false; *pnRetCode = ParseCommandLine(command_line, &is_service); if (FAILED(*pnRetCode)) { ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE); } if (!is_service) { controller_->UpdateState(); std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL); std::cout << " " << StateAsString(controller_->state()); } return is_service; } HRESULT PreMessageLoop(int nShowCmd) { HRESULT hr = Base::PreMessageLoop(nShowCmd); if (FAILED(hr)) return hr; if (check_requirements_) { CheckRequirements(); } else { HRESULT hr = StartConnector(); if (FAILED(hr)) return hr; } LogEvent(_T("Service started/resumed")); SetServiceStatus(SERVICE_RUNNING); return hr; } HRESULT PostMessageLoop() { StopConnector(); setup_listener_.reset(); return Base::PostMessageLoop(); } private: HRESULT ParseCommandLine(const base::CommandLine& command_line, bool* is_service) { if (!is_service) return E_INVALIDARG; *is_service = false; user_data_dir_switch_ = command_line.GetSwitchValuePath(switches::kUserDataDir); if (!user_data_dir_switch_.empty()) user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_); if (command_line.HasSwitch(kStopSwitch)) return controller_->StopService(); if (command_line.HasSwitch(kUninstallSwitch)) return controller_->UninstallService(); if (command_line.HasSwitch(kInstallSwitch)) { base::string16 run_as_user; base::string16 run_as_password; base::FilePath user_data_dir; std::vector printers; HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password, &user_data_dir, &printers); if (FAILED(hr)) return hr; DCHECK(user_data_dir_switch_.empty() || user_data_dir_switch_ == user_data_dir); hr = SetupServiceState(user_data_dir, printers); if (FAILED(hr)) return hr; hr = controller_->InstallConnectorService( run_as_user, run_as_password, user_data_dir_switch_, command_line.HasSwitch(switches::kEnableLogging)); if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) return controller_->StartService(); return hr; } if (command_line.HasSwitch(kStartSwitch)) return controller_->StartService(); if (command_line.HasSwitch(kConsoleSwitch)) { check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); HRESULT hr = Run(); ::SetConsoleCtrlHandler(NULL, FALSE); return hr; } if (command_line.HasSwitch(kServiceSwitch) || command_line.HasSwitch(kRequirementsSwitch)) { *is_service = true; check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); return S_OK; } InvalidUsage(); return S_FALSE; } HRESULT SelectWindowsAccount(base::string16* run_as_user, base::string16* run_as_password, base::FilePath* user_data_dir, std::vector* printers) { *run_as_user = GetCurrentUserName(); std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n"; *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false); *run_as_user = ReplaceLocalHostInName(*run_as_user); *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true); SetupListener setup(*run_as_user); HRESULT hr = controller_->InstallCheckService(*run_as_user, *run_as_password, user_data_dir_switch_); if (FAILED(hr)) { return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE); } { // Always uninstall service after requirements check. base::ScopedClosureRunner scoped_uninstall( base::Bind(base::IgnoreResult(&ServiceController::UninstallService), base::Unretained(controller_.get()))); hr = controller_->StartService(); if (FAILED(hr)) { return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE); } if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) { return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE); } } if (setup.user_data_dir().empty()) { return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR); } if (setup.chrome_path().empty()) { return ReportError(E_FAIL, IDS_ERROR_NO_CHROME); } if (!setup.is_xps_available()) { return ReportError(E_FAIL, IDS_ERROR_NO_XPS); } std::cout << "\n"; std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK); std::cout << "\n"; std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER); std::cout << "\n " << setup.user_name() << "\n"; std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME); std::cout << "\n " << setup.chrome_path().value() << "\n"; std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR); std::cout << "\n " << setup.user_data_dir().value() << "\n"; std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS); std::cout << "\n "; std::ostream_iterator cout_it(std::cout, "\n "); std::copy(setup.printers().begin(), setup.printers().end(), cout_it); std::cout << "\n"; *user_data_dir = setup.user_data_dir(); *printers = setup.printers(); return S_OK; } HRESULT SetupServiceState(const base::FilePath& user_data_dir, const std::vector& printers) { base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName); std::string contents; base::ReadFileToString(file, &contents); ServiceState service_state; service_state.FromString(contents); std::string proxy_id = service_state.proxy_id(); LOG(INFO) << file.value() << ": " << contents; base::string16 message = cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME); std::cout << "\n" << message.c_str() << "\n" ; std::string new_contents = ChromeLauncher::CreateServiceStateFile(proxy_id, printers); if (new_contents.empty()) { return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG); } if (new_contents != contents) { size_t written = base::WriteFile(file, new_contents.c_str(), new_contents.size()); if (written != new_contents.size()) { return ReportError(cloud_print::GetLastHResult(), IDS_ERROR_FAILED_CREATE_CONFIG); } } return S_OK; } void CheckRequirements() { setup_listener_.reset(new ServiceListener(GetUserDataDir())); } HRESULT StartConnector() { chrome_.reset(new ChromeLauncher(GetUserDataDir())); return chrome_->Start() ? S_OK : E_FAIL; } void StopConnector() { if (chrome_.get()) { chrome_->Stop(); chrome_.reset(); } } base::FilePath GetUserDataDir() const { if (!user_data_dir_switch_.empty()) return user_data_dir_switch_; base::FilePath result; CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result)); return result.Append(kSubDirectory); } static BOOL WINAPI ConsoleCtrlHandler(DWORD type); bool check_requirements_; base::FilePath user_data_dir_switch_; scoped_ptr chrome_; scoped_ptr controller_; scoped_ptr setup_listener_; }; CloudPrintServiceModule _AtlModule; BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) { PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0); return TRUE; } int main(int argc, char** argv) { base::CommandLine::Init(argc, argv); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); base::AtExitManager at_exit; logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; logging::InitLogging(settings); logging::SetMinLogLevel( command_line->HasSwitch(switches::kEnableLogging) ? logging::LOG_INFO : logging::LOG_FATAL); return _AtlModule.WinMain(0); }