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 11 #include "base/at_exit.h" 12 #include "base/bind.h" 13 #include "base/command_line.h" 14 #include "base/file_util.h" 15 #include "base/guid.h" 16 #include "base/logging.h" 17 #include "base/path_service.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/win/scoped_handle.h" 21 #include "chrome/common/chrome_constants.h" 22 #include "chrome/common/chrome_switches.h" 23 #include "cloud_print/common/win/cloud_print_utils.h" 24 #include "cloud_print/service/service_constants.h" 25 #include "cloud_print/service/service_state.h" 26 #include "cloud_print/service/service_switches.h" 27 #include "cloud_print/service/win/chrome_launcher.h" 28 #include "cloud_print/service/win/service_controller.h" 29 #include "cloud_print/service/win/service_listener.h" 30 #include "cloud_print/service/win/service_utils.h" 31 #include "cloud_print/service/win/setup_listener.h" 32 33 namespace { 34 35 void InvalidUsage() { 36 base::FilePath service_path; 37 CHECK(PathService::Get(base::FILE_EXE, &service_path)); 38 39 std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE); 40 std::cout << " " << service_path.BaseName().value(); 41 std::cout << " ["; 42 std::cout << "["; 43 std::cout << "["; 44 std::cout << " -" << kInstallSwitch; 45 std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]"; 46 std::cout << "]"; 47 std::cout << "]"; 48 std::cout << " | -" << kUninstallSwitch; 49 std::cout << " | -" << kStartSwitch; 50 std::cout << " | -" << kStopSwitch; 51 std::cout << " ]\n"; 52 std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION); 53 std::cout << "\n\n"; 54 55 struct { 56 const char* name; 57 int description; 58 } kSwitchHelp[] = {{ 59 kInstallSwitch, IDS_SWITCH_HELP_INSTALL 60 }, { 61 switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR 62 }, { 63 kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL 64 }, { 65 kStartSwitch, IDS_SWITCH_HELP_START 66 }, { 67 kStopSwitch, IDS_SWITCH_HELP_STOP 68 }}; 69 70 for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { 71 std::cout << std::setiosflags(std::ios::left); 72 std::cout << " -" << std::setw(16) << kSwitchHelp[i].name; 73 std::cout << cloud_print::LoadLocalString(kSwitchHelp[i].description); 74 std::cout << "\n"; 75 } 76 std::cout << "\n"; 77 } 78 79 string16 GetOption(int string_id, const string16& default, 80 bool secure) { 81 string16 prompt_format = cloud_print::LoadLocalString(string_id) ; 82 std::vector<string16> substitutions(1, default); 83 std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL); 84 string16 tmp; 85 if (secure) { 86 DWORD saved_mode = 0; 87 // Don't close. 88 HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE); 89 ::GetConsoleMode(stdin_handle, &saved_mode); 90 ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT); 91 std::getline(std::wcin, tmp); 92 ::SetConsoleMode(stdin_handle, saved_mode); 93 std::cout << "\n"; 94 } else { 95 std::getline(std::wcin, tmp); 96 } 97 if (tmp.empty()) 98 return default; 99 return tmp; 100 } 101 102 HRESULT ReportError(HRESULT hr, int string_id) { 103 LOG(ERROR) << cloud_print::GetErrorMessage(hr); 104 std::cerr << cloud_print::LoadLocalString(string_id); 105 std::cerr << "\n"; 106 return hr; 107 } 108 109 string16 StateAsString(ServiceController::State state) { 110 DWORD string_id = 0; 111 switch(state) { 112 case ServiceController::STATE_NOT_FOUND: 113 string_id = IDS_SERVICE_NOT_FOUND; 114 break; 115 case ServiceController::STATE_STOPPED: 116 string_id = IDS_SERVICE_STOPPED; 117 break; 118 case ServiceController::STATE_RUNNING: 119 string_id = IDS_SERVICE_RUNNING; 120 break; 121 } 122 return string_id ? cloud_print::LoadLocalString(string_id) : string16(); 123 } 124 125 } // namespace 126 127 128 class CloudPrintServiceModule 129 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, 130 IDS_SERVICE_NAME> { 131 public: 132 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, 133 IDS_SERVICE_NAME> Base; 134 135 CloudPrintServiceModule() 136 : check_requirements_(false), 137 controller_(new ServiceController()) { 138 } 139 140 static wchar_t* GetAppIdT() { 141 return ServiceController::GetAppIdT(); 142 }; 143 144 HRESULT InitializeSecurity() { 145 // TODO(gene): Check if we need to call CoInitializeSecurity and provide 146 // the appropriate security settings for service. 147 return S_OK; 148 } 149 150 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) { 151 CHECK(pnRetCode); 152 CommandLine command_line(CommandLine::NO_PROGRAM); 153 command_line.ParseFromString(lpCmdLine); 154 155 LOG(INFO) << command_line.GetCommandLineString(); 156 157 bool is_service = false; 158 *pnRetCode = ParseCommandLine(command_line, &is_service); 159 if (FAILED(*pnRetCode)) { 160 ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE); 161 } 162 if (!is_service) { 163 controller_->UpdateState(); 164 std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL); 165 std::cout << " " << StateAsString(controller_->state()); 166 } 167 return is_service; 168 } 169 170 HRESULT PreMessageLoop(int nShowCmd) { 171 HRESULT hr = Base::PreMessageLoop(nShowCmd); 172 if (FAILED(hr)) 173 return hr; 174 175 if (check_requirements_) { 176 CheckRequirements(); 177 } else { 178 HRESULT hr = StartConnector(); 179 if (FAILED(hr)) 180 return hr; 181 } 182 183 LogEvent(_T("Service started/resumed")); 184 SetServiceStatus(SERVICE_RUNNING); 185 186 return hr; 187 } 188 189 HRESULT PostMessageLoop() { 190 StopConnector(); 191 setup_listener_.reset(); 192 return Base::PostMessageLoop(); 193 } 194 195 private: 196 HRESULT ParseCommandLine(const CommandLine& command_line, bool* is_service) { 197 if (!is_service) 198 return E_INVALIDARG; 199 *is_service = false; 200 201 user_data_dir_switch_ = 202 command_line.GetSwitchValuePath(switches::kUserDataDir); 203 if (!user_data_dir_switch_.empty()) 204 user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_); 205 206 if (command_line.HasSwitch(kStopSwitch)) 207 return controller_->StopService(); 208 209 if (command_line.HasSwitch(kUninstallSwitch)) 210 return controller_->UninstallService(); 211 212 if (command_line.HasSwitch(kInstallSwitch)) { 213 string16 run_as_user; 214 string16 run_as_password; 215 base::FilePath user_data_dir; 216 std::vector<std::string> printers; 217 HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password, 218 &user_data_dir, &printers); 219 if (FAILED(hr)) 220 return hr; 221 222 DCHECK(user_data_dir_switch_.empty() || 223 user_data_dir_switch_ == user_data_dir); 224 225 hr = SetupServiceState(user_data_dir, printers); 226 if (FAILED(hr)) 227 return hr; 228 229 hr = controller_->InstallConnectorService( 230 run_as_user, run_as_password, user_data_dir_switch_, 231 command_line.HasSwitch(switches::kEnableLogging)); 232 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) 233 return controller_->StartService(); 234 235 return hr; 236 } 237 238 if (command_line.HasSwitch(kStartSwitch)) 239 return controller_->StartService(); 240 241 if (command_line.HasSwitch(kConsoleSwitch)) { 242 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); 243 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); 244 HRESULT hr = Run(); 245 ::SetConsoleCtrlHandler(NULL, FALSE); 246 return hr; 247 } 248 249 if (command_line.HasSwitch(kServiceSwitch) || 250 command_line.HasSwitch(kRequirementsSwitch)) { 251 *is_service = true; 252 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); 253 return S_OK; 254 } 255 256 257 InvalidUsage(); 258 return S_FALSE; 259 } 260 261 HRESULT SelectWindowsAccount(string16* run_as_user, string16* run_as_password, 262 base::FilePath* user_data_dir, 263 std::vector<std::string>* printers) { 264 *run_as_user = GetCurrentUserName(); 265 std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n"; 266 *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false); 267 *run_as_user = ReplaceLocalHostInName(*run_as_user); 268 *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true); 269 SetupListener setup(*run_as_user); 270 HRESULT hr = controller_->InstallCheckService(*run_as_user, 271 *run_as_password, 272 user_data_dir_switch_); 273 if (FAILED(hr)) { 274 return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE); 275 } 276 277 { 278 // Always uninstall service after requirements check. 279 base::ScopedClosureRunner scoped_uninstall( 280 base::Bind(base::IgnoreResult(&ServiceController::UninstallService), 281 base::Unretained(controller_.get()))); 282 283 hr = controller_->StartService(); 284 if (FAILED(hr)) { 285 return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE); 286 } 287 288 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) { 289 return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE); 290 } 291 } 292 293 if (setup.user_data_dir().empty()) { 294 return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR); 295 } 296 297 if (setup.chrome_path().empty()) { 298 return ReportError(E_FAIL, IDS_ERROR_NO_CHROME); 299 } 300 301 if (!setup.is_xps_available()) { 302 return ReportError(E_FAIL, IDS_ERROR_NO_XPS); 303 } 304 305 std::cout << "\n"; 306 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK); 307 std::cout << "\n"; 308 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER); 309 std::cout << "\n " << setup.user_name() << "\n"; 310 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME); 311 std::cout << "\n " << setup.chrome_path().value() << "\n"; 312 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR); 313 std::cout << "\n " << setup.user_data_dir().value() << "\n"; 314 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS); 315 std::cout << "\n "; 316 std::ostream_iterator<std::string> cout_it(std::cout, "\n "); 317 std::copy(setup.printers().begin(), setup.printers().end(), cout_it); 318 std::cout << "\n"; 319 320 *user_data_dir = setup.user_data_dir(); 321 *printers = setup.printers(); 322 return S_OK; 323 } 324 325 HRESULT SetupServiceState(const base::FilePath& user_data_dir, 326 const std::vector<std::string>& printers) { 327 base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName); 328 329 std::string contents; 330 ServiceState service_state; 331 332 bool is_valid = file_util::ReadFileToString(file, &contents) && 333 service_state.FromString(contents); 334 std::string proxy_id = service_state.proxy_id(); 335 336 LOG(INFO) << file.value() << ": " << contents; 337 338 string16 message = 339 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME); 340 std::cout << "\n" << message.c_str() << "\n" ; 341 std::string new_contents = 342 ChromeLauncher::CreateServiceStateFile(proxy_id, printers); 343 344 if (new_contents.empty()) { 345 return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG); 346 } 347 348 if (new_contents != contents) { 349 size_t written = file_util::WriteFile(file, new_contents.c_str(), 350 new_contents.size()); 351 if (written != new_contents.size()) { 352 return ReportError(cloud_print::GetLastHResult(), 353 IDS_ERROR_FAILED_CREATE_CONFIG); 354 } 355 } 356 357 return S_OK; 358 } 359 360 void CheckRequirements() { 361 setup_listener_.reset(new ServiceListener(GetUserDataDir())); 362 } 363 364 HRESULT StartConnector() { 365 chrome_.reset(new ChromeLauncher(GetUserDataDir())); 366 return chrome_->Start() ? S_OK : E_FAIL; 367 } 368 369 void StopConnector() { 370 if (chrome_.get()) { 371 chrome_->Stop(); 372 chrome_.reset(); 373 } 374 } 375 376 base::FilePath GetUserDataDir() const { 377 if (!user_data_dir_switch_.empty()) 378 return user_data_dir_switch_; 379 base::FilePath result; 380 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result)); 381 return result.Append(kSubDirectory); 382 } 383 384 static BOOL WINAPI ConsoleCtrlHandler(DWORD type); 385 386 bool check_requirements_; 387 base::FilePath user_data_dir_switch_; 388 scoped_ptr<ChromeLauncher> chrome_; 389 scoped_ptr<ServiceController> controller_; 390 scoped_ptr<ServiceListener> setup_listener_; 391 }; 392 393 CloudPrintServiceModule _AtlModule; 394 395 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) { 396 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0); 397 return TRUE; 398 } 399 400 int main(int argc, char** argv) { 401 CommandLine::Init(argc, argv); 402 base::AtExitManager at_exit; 403 404 logging::LoggingSettings settings; 405 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; 406 logging::InitLogging(settings); 407 408 logging::SetMinLogLevel( 409 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableLogging) ? 410 logging::LOG_INFO : logging::LOG_FATAL); 411 412 return _AtlModule.WinMain(0); 413 } 414 415