Change next_proto member type.
[chromium-blink-merge.git] / cloud_print / service / win / cloud_print_service.cc
blobc1b9601bada8c0411dd399b89fcefeb05fe31d68
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
7 #pragma warning(push)
8 #pragma warning(disable:4189)
9 #include <atlbase.h>
10 #pragma warning(pop)
11 #include <security.h>
13 #include <iomanip>
14 #include <iostream>
15 #include <iterator>
16 #include <string>
17 #include <vector>
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"
42 namespace {
44 void InvalidUsage() {
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();
50 std::cout << " [";
51 std::cout << "[";
52 std::cout << "[";
53 std::cout << " -" << kInstallSwitch;
54 std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]";
55 std::cout << "]";
56 std::cout << "]";
57 std::cout << " | -" << kUninstallSwitch;
58 std::cout << " | -" << kStartSwitch;
59 std::cout << " | -" << kStopSwitch;
60 std::cout << " ]\n";
61 std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION);
62 std::cout << "\n\n";
64 struct {
65 const char* name;
66 int description;
67 } kSwitchHelp[] = {{
68 kInstallSwitch, IDS_SWITCH_HELP_INSTALL
69 }, {
70 switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR
71 }, {
72 kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL
73 }, {
74 kStartSwitch, IDS_SWITCH_HELP_START
75 }, {
76 kStopSwitch, IDS_SWITCH_HELP_STOP
77 }};
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);
83 std::cout << "\n";
85 std::cout << "\n";
88 base::string16 GetOption(int string_id,
89 const base::string16& default_option,
90 bool secure) {
91 base::string16 prompt_format = cloud_print::LoadLocalString(string_id);
92 std::vector<base::string16> substitutions(1, default_option);
93 std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL);
94 base::string16 tmp;
95 if (secure) {
96 DWORD saved_mode = 0;
97 // Don't close.
98 HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE);
99 ::GetConsoleMode(stdin_handle, &saved_mode);
100 ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT);
101 std::getline(std::wcin, tmp);
102 ::SetConsoleMode(stdin_handle, saved_mode);
103 std::cout << "\n";
104 } else {
105 std::getline(std::wcin, tmp);
107 if (tmp.empty())
108 return default_option;
109 return tmp;
112 HRESULT ReportError(HRESULT hr, int string_id) {
113 LOG(ERROR) << cloud_print::GetErrorMessage(hr);
114 std::cerr << cloud_print::LoadLocalString(string_id);
115 std::cerr << "\n";
116 return hr;
119 base::string16 StateAsString(ServiceController::State state) {
120 DWORD string_id = 0;
121 switch(state) {
122 case ServiceController::STATE_NOT_FOUND:
123 string_id = IDS_SERVICE_NOT_FOUND;
124 break;
125 case ServiceController::STATE_STOPPED:
126 string_id = IDS_SERVICE_STOPPED;
127 break;
128 case ServiceController::STATE_RUNNING:
129 string_id = IDS_SERVICE_RUNNING;
130 break;
132 return string_id ? cloud_print::LoadLocalString(string_id) : base::string16();
135 } // namespace
138 class CloudPrintServiceModule
139 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
140 IDS_SERVICE_NAME> {
141 public:
142 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
143 IDS_SERVICE_NAME> Base;
145 CloudPrintServiceModule()
146 : check_requirements_(false),
147 controller_(new ServiceController()) {
150 static wchar_t* GetAppIdT() {
151 return ServiceController::GetAppIdT();
154 HRESULT InitializeSecurity() {
155 // TODO(gene): Check if we need to call CoInitializeSecurity and provide
156 // the appropriate security settings for service.
157 return S_OK;
160 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
161 CHECK(pnRetCode);
162 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
163 command_line.ParseFromString(lpCmdLine);
165 LOG(INFO) << command_line.GetCommandLineString();
167 bool is_service = false;
168 *pnRetCode = ParseCommandLine(command_line, &is_service);
169 if (FAILED(*pnRetCode)) {
170 ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
172 if (!is_service) {
173 controller_->UpdateState();
174 std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
175 std::cout << " " << StateAsString(controller_->state());
177 return is_service;
180 HRESULT PreMessageLoop(int nShowCmd) {
181 HRESULT hr = Base::PreMessageLoop(nShowCmd);
182 if (FAILED(hr))
183 return hr;
185 if (check_requirements_) {
186 CheckRequirements();
187 } else {
188 HRESULT hr = StartConnector();
189 if (FAILED(hr))
190 return hr;
193 LogEvent(_T("Service started/resumed"));
194 SetServiceStatus(SERVICE_RUNNING);
196 return hr;
199 HRESULT PostMessageLoop() {
200 StopConnector();
201 setup_listener_.reset();
202 return Base::PostMessageLoop();
205 private:
206 HRESULT ParseCommandLine(const base::CommandLine& command_line,
207 bool* is_service) {
208 if (!is_service)
209 return E_INVALIDARG;
210 *is_service = false;
212 user_data_dir_switch_ =
213 command_line.GetSwitchValuePath(switches::kUserDataDir);
214 if (!user_data_dir_switch_.empty())
215 user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_);
217 if (command_line.HasSwitch(kStopSwitch))
218 return controller_->StopService();
220 if (command_line.HasSwitch(kUninstallSwitch))
221 return controller_->UninstallService();
223 if (command_line.HasSwitch(kInstallSwitch)) {
224 base::string16 run_as_user;
225 base::string16 run_as_password;
226 base::FilePath user_data_dir;
227 std::vector<std::string> printers;
228 HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password,
229 &user_data_dir, &printers);
230 if (FAILED(hr))
231 return hr;
233 DCHECK(user_data_dir_switch_.empty() ||
234 user_data_dir_switch_ == user_data_dir);
236 hr = SetupServiceState(user_data_dir, printers);
237 if (FAILED(hr))
238 return hr;
240 hr = controller_->InstallConnectorService(
241 run_as_user, run_as_password, user_data_dir_switch_,
242 command_line.HasSwitch(switches::kEnableLogging));
243 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
244 return controller_->StartService();
246 return hr;
249 if (command_line.HasSwitch(kStartSwitch))
250 return controller_->StartService();
252 if (command_line.HasSwitch(kConsoleSwitch)) {
253 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
254 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
255 HRESULT hr = Run();
256 ::SetConsoleCtrlHandler(NULL, FALSE);
257 return hr;
260 if (command_line.HasSwitch(kServiceSwitch) ||
261 command_line.HasSwitch(kRequirementsSwitch)) {
262 *is_service = true;
263 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
264 return S_OK;
268 InvalidUsage();
269 return S_FALSE;
272 HRESULT SelectWindowsAccount(base::string16* run_as_user,
273 base::string16* run_as_password,
274 base::FilePath* user_data_dir,
275 std::vector<std::string>* printers) {
276 *run_as_user = GetCurrentUserName();
277 std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n";
278 *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false);
279 *run_as_user = ReplaceLocalHostInName(*run_as_user);
280 *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true);
281 SetupListener setup(*run_as_user);
282 HRESULT hr = controller_->InstallCheckService(*run_as_user,
283 *run_as_password,
284 user_data_dir_switch_);
285 if (FAILED(hr)) {
286 return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE);
290 // Always uninstall service after requirements check.
291 base::ScopedClosureRunner scoped_uninstall(
292 base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
293 base::Unretained(controller_.get())));
295 hr = controller_->StartService();
296 if (FAILED(hr)) {
297 return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
300 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
301 return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
305 if (setup.user_data_dir().empty()) {
306 return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
309 if (setup.chrome_path().empty()) {
310 return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
313 if (!setup.is_xps_available()) {
314 return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
317 std::cout << "\n";
318 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK);
319 std::cout << "\n";
320 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER);
321 std::cout << "\n " << setup.user_name() << "\n";
322 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME);
323 std::cout << "\n " << setup.chrome_path().value() << "\n";
324 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR);
325 std::cout << "\n " << setup.user_data_dir().value() << "\n";
326 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS);
327 std::cout << "\n ";
328 std::ostream_iterator<std::string> cout_it(std::cout, "\n ");
329 std::copy(setup.printers().begin(), setup.printers().end(), cout_it);
330 std::cout << "\n";
332 *user_data_dir = setup.user_data_dir();
333 *printers = setup.printers();
334 return S_OK;
337 HRESULT SetupServiceState(const base::FilePath& user_data_dir,
338 const std::vector<std::string>& printers) {
339 base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName);
341 std::string contents;
342 base::ReadFileToString(file, &contents);
343 ServiceState service_state;
344 service_state.FromString(contents);
345 std::string proxy_id = service_state.proxy_id();
347 LOG(INFO) << file.value() << ": " << contents;
349 base::string16 message =
350 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME);
351 std::cout << "\n" << message.c_str() << "\n" ;
352 std::string new_contents =
353 ChromeLauncher::CreateServiceStateFile(proxy_id, printers);
355 if (new_contents.empty()) {
356 return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
359 if (new_contents != contents) {
360 size_t written = base::WriteFile(file, new_contents.c_str(),
361 new_contents.size());
362 if (written != new_contents.size()) {
363 return ReportError(cloud_print::GetLastHResult(),
364 IDS_ERROR_FAILED_CREATE_CONFIG);
368 return S_OK;
371 void CheckRequirements() {
372 setup_listener_.reset(new ServiceListener(GetUserDataDir()));
375 HRESULT StartConnector() {
376 chrome_.reset(new ChromeLauncher(GetUserDataDir()));
377 return chrome_->Start() ? S_OK : E_FAIL;
380 void StopConnector() {
381 if (chrome_.get()) {
382 chrome_->Stop();
383 chrome_.reset();
387 base::FilePath GetUserDataDir() const {
388 if (!user_data_dir_switch_.empty())
389 return user_data_dir_switch_;
390 base::FilePath result;
391 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result));
392 return result.Append(kSubDirectory);
395 static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
397 bool check_requirements_;
398 base::FilePath user_data_dir_switch_;
399 scoped_ptr<ChromeLauncher> chrome_;
400 scoped_ptr<ServiceController> controller_;
401 scoped_ptr<ServiceListener> setup_listener_;
404 CloudPrintServiceModule _AtlModule;
406 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
407 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
408 return TRUE;
411 int main(int argc, char** argv) {
412 base::CommandLine::Init(argc, argv);
413 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
414 base::AtExitManager at_exit;
416 logging::LoggingSettings settings;
417 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
418 logging::InitLogging(settings);
420 logging::SetMinLogLevel(
421 command_line->HasSwitch(switches::kEnableLogging) ?
422 logging::LOG_INFO : logging::LOG_FATAL);
424 return _AtlModule.WinMain(0);