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 421