Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / cloud_print / service / win / cloud_print_service.cc
blob760672fcc7207c76fa22490ac311c8d749cf57d8
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 <<
94 base::ReplaceStringPlaceholders(prompt_format, substitutions, NULL);
95 base::string16 tmp;
96 if (secure) {
97 DWORD saved_mode = 0;
98 // Don't close.
99 HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE);
100 ::GetConsoleMode(stdin_handle, &saved_mode);
101 ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT);
102 std::getline(std::wcin, tmp);
103 ::SetConsoleMode(stdin_handle, saved_mode);
104 std::cout << "\n";
105 } else {
106 std::getline(std::wcin, tmp);
108 if (tmp.empty())
109 return default_option;
110 return tmp;
113 HRESULT ReportError(HRESULT hr, int string_id) {
114 LOG(ERROR) << cloud_print::GetErrorMessage(hr);
115 std::cerr << cloud_print::LoadLocalString(string_id);
116 std::cerr << "\n";
117 return hr;
120 base::string16 StateAsString(ServiceController::State state) {
121 DWORD string_id = 0;
122 switch(state) {
123 case ServiceController::STATE_NOT_FOUND:
124 string_id = IDS_SERVICE_NOT_FOUND;
125 break;
126 case ServiceController::STATE_STOPPED:
127 string_id = IDS_SERVICE_STOPPED;
128 break;
129 case ServiceController::STATE_RUNNING:
130 string_id = IDS_SERVICE_RUNNING;
131 break;
132 case ServiceController::STATE_UNKNOWN:
133 break;
135 return string_id ? cloud_print::LoadLocalString(string_id) : base::string16();
138 } // namespace
141 class CloudPrintServiceModule
142 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
143 IDS_SERVICE_NAME> {
144 public:
145 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
146 IDS_SERVICE_NAME> Base;
148 CloudPrintServiceModule()
149 : check_requirements_(false),
150 controller_(new ServiceController()) {
153 static const wchar_t* GetAppIdT() {
154 return ServiceController::GetAppIdT();
157 HRESULT InitializeSecurity() {
158 // TODO(gene): Check if we need to call CoInitializeSecurity and provide
159 // the appropriate security settings for service.
160 return S_OK;
163 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
164 CHECK(pnRetCode);
165 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
166 command_line.ParseFromString(lpCmdLine);
168 LOG(INFO) << command_line.GetCommandLineString();
170 bool is_service = false;
171 *pnRetCode = ParseCommandLine(command_line, &is_service);
172 if (FAILED(*pnRetCode)) {
173 ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
175 if (!is_service) {
176 controller_->UpdateState();
177 std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
178 std::cout << " " << StateAsString(controller_->state());
180 return is_service;
183 HRESULT PreMessageLoop(int nShowCmd) {
184 HRESULT hr = Base::PreMessageLoop(nShowCmd);
185 if (FAILED(hr))
186 return hr;
188 if (check_requirements_) {
189 CheckRequirements();
190 } else {
191 HRESULT hr = StartConnector();
192 if (FAILED(hr))
193 return hr;
196 LogEvent(_T("Service started/resumed"));
197 SetServiceStatus(SERVICE_RUNNING);
199 return hr;
202 HRESULT PostMessageLoop() {
203 StopConnector();
204 setup_listener_.reset();
205 return Base::PostMessageLoop();
208 private:
209 HRESULT ParseCommandLine(const base::CommandLine& command_line,
210 bool* is_service) {
211 if (!is_service)
212 return E_INVALIDARG;
213 *is_service = false;
215 user_data_dir_switch_ =
216 command_line.GetSwitchValuePath(switches::kUserDataDir);
217 if (!user_data_dir_switch_.empty())
218 user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_);
220 if (command_line.HasSwitch(kStopSwitch))
221 return controller_->StopService();
223 if (command_line.HasSwitch(kUninstallSwitch))
224 return controller_->UninstallService();
226 if (command_line.HasSwitch(kInstallSwitch)) {
227 base::string16 run_as_user;
228 base::string16 run_as_password;
229 base::FilePath user_data_dir;
230 std::vector<std::string> printers;
231 HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password,
232 &user_data_dir, &printers);
233 if (FAILED(hr))
234 return hr;
236 DCHECK(user_data_dir_switch_.empty() ||
237 user_data_dir_switch_ == user_data_dir);
239 hr = SetupServiceState(user_data_dir, printers);
240 if (FAILED(hr))
241 return hr;
243 hr = controller_->InstallConnectorService(
244 run_as_user, run_as_password, user_data_dir_switch_,
245 command_line.HasSwitch(switches::kEnableLogging));
246 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
247 return controller_->StartService();
249 return hr;
252 if (command_line.HasSwitch(kStartSwitch))
253 return controller_->StartService();
255 if (command_line.HasSwitch(kConsoleSwitch)) {
256 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
257 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
258 HRESULT hr = Run();
259 ::SetConsoleCtrlHandler(NULL, FALSE);
260 return hr;
263 if (command_line.HasSwitch(kServiceSwitch) ||
264 command_line.HasSwitch(kRequirementsSwitch)) {
265 *is_service = true;
266 check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
267 return S_OK;
271 InvalidUsage();
272 return S_FALSE;
275 HRESULT SelectWindowsAccount(base::string16* run_as_user,
276 base::string16* run_as_password,
277 base::FilePath* user_data_dir,
278 std::vector<std::string>* printers) {
279 *run_as_user = GetCurrentUserName();
280 std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n";
281 *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false);
282 *run_as_user = ReplaceLocalHostInName(*run_as_user);
283 *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true);
284 SetupListener setup(*run_as_user);
285 HRESULT hr = controller_->InstallCheckService(*run_as_user,
286 *run_as_password,
287 user_data_dir_switch_);
288 if (FAILED(hr)) {
289 return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE);
293 // Always uninstall service after requirements check.
294 base::ScopedClosureRunner scoped_uninstall(
295 base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
296 base::Unretained(controller_.get())));
298 hr = controller_->StartService();
299 if (FAILED(hr)) {
300 return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
303 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
304 return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
308 if (setup.user_data_dir().empty()) {
309 return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
312 if (setup.chrome_path().empty()) {
313 return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
316 if (!setup.is_xps_available()) {
317 return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
320 std::cout << "\n";
321 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK);
322 std::cout << "\n";
323 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER);
324 std::cout << "\n " << setup.user_name() << "\n";
325 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME);
326 std::cout << "\n " << setup.chrome_path().value() << "\n";
327 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR);
328 std::cout << "\n " << setup.user_data_dir().value() << "\n";
329 std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS);
330 std::cout << "\n ";
331 std::ostream_iterator<std::string> cout_it(std::cout, "\n ");
332 std::copy(setup.printers().begin(), setup.printers().end(), cout_it);
333 std::cout << "\n";
335 *user_data_dir = setup.user_data_dir();
336 *printers = setup.printers();
337 return S_OK;
340 HRESULT SetupServiceState(const base::FilePath& user_data_dir,
341 const std::vector<std::string>& printers) {
342 base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName);
344 std::string contents;
345 base::ReadFileToString(file, &contents);
346 ServiceState service_state;
347 service_state.FromString(contents);
348 std::string proxy_id = service_state.proxy_id();
350 LOG(INFO) << file.value() << ": " << contents;
352 base::string16 message =
353 cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME);
354 std::cout << "\n" << message.c_str() << "\n" ;
355 std::string new_contents =
356 ChromeLauncher::CreateServiceStateFile(proxy_id, printers);
358 if (new_contents.empty()) {
359 return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
362 if (new_contents != contents) {
363 size_t written = base::WriteFile(file, new_contents.c_str(),
364 new_contents.size());
365 if (written != new_contents.size()) {
366 return ReportError(cloud_print::GetLastHResult(),
367 IDS_ERROR_FAILED_CREATE_CONFIG);
371 return S_OK;
374 void CheckRequirements() {
375 setup_listener_.reset(new ServiceListener(GetUserDataDir()));
378 HRESULT StartConnector() {
379 chrome_.reset(new ChromeLauncher(GetUserDataDir()));
380 return chrome_->Start() ? S_OK : E_FAIL;
383 void StopConnector() {
384 if (chrome_.get()) {
385 chrome_->Stop();
386 chrome_.reset();
390 base::FilePath GetUserDataDir() const {
391 if (!user_data_dir_switch_.empty())
392 return user_data_dir_switch_;
393 base::FilePath result;
394 CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result));
395 return result.Append(kSubDirectory);
398 static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
400 bool check_requirements_;
401 base::FilePath user_data_dir_switch_;
402 scoped_ptr<ChromeLauncher> chrome_;
403 scoped_ptr<ServiceController> controller_;
404 scoped_ptr<ServiceListener> setup_listener_;
407 CloudPrintServiceModule _AtlModule;
409 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
410 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
411 return TRUE;
414 int main(int argc, char** argv) {
415 base::CommandLine::Init(argc, argv);
416 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
417 base::AtExitManager at_exit;
419 logging::LoggingSettings settings;
420 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
421 logging::InitLogging(settings);
423 logging::SetMinLogLevel(
424 command_line->HasSwitch(switches::kEnableLogging) ?
425 logging::LOG_INFO : logging::LOG_FATAL);
427 return _AtlModule.WinMain(0);