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