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_config.cc
blob278c0dcf42033d12299f4a72622a7825ab69db9c
1 // Copyright 2013 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 #include <atlbase.h>
6 #include <atlapp.h> // NOLINT
8 #include "base/at_exit.h"
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/message_loop/message_pump_dispatcher.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string16.h"
17 #include "base/threading/thread.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "cloud_print/common/win/cloud_print_utils.h"
20 #include "cloud_print/resources.h"
21 #include "cloud_print/service/service_state.h"
22 #include "cloud_print/service/win/chrome_launcher.h"
23 #include "cloud_print/service/win/service_controller.h"
24 #include "cloud_print/service/win/service_utils.h"
25 #include "cloud_print/service/win/setup_listener.h"
27 using cloud_print::LoadLocalString;
28 using cloud_print::GetErrorMessage;
30 class SetupDialog : public base::RefCounted<SetupDialog>,
31 public ATL::CDialogImpl<SetupDialog> {
32 public:
33 // Enables accelerators.
34 class Dispatcher : public base::MessagePumpDispatcher {
35 public:
36 explicit Dispatcher(SetupDialog* dialog) : dialog_(dialog) {}
37 ~Dispatcher() override {}
39 // MessagePumpDispatcher:
40 uint32_t Dispatch(const MSG& msg) override {
41 MSG msg2 = msg;
42 uint32_t action = POST_DISPATCH_NONE;
43 if (!dialog_->IsDialogMessage(&msg2))
44 action = POST_DISPATCH_PERFORM_DEFAULT;
45 return action;
48 private:
49 scoped_refptr<SetupDialog> dialog_;
52 typedef ATL::CDialogImpl<SetupDialog> Base;
53 enum { IDD = IDD_SETUP_DIALOG };
55 BEGIN_MSG_MAP(SetupDialog)
56 MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
57 MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnCtrColor)
58 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
59 COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
60 COMMAND_ID_HANDLER(IDC_START, OnStart)
61 COMMAND_ID_HANDLER(IDC_INSTALL, OnInstall)
62 COMMAND_ID_HANDLER(IDC_LOGGING, OnLogging)
63 END_MSG_MAP()
65 SetupDialog();
67 private:
68 friend class base::RefCounted<SetupDialog>;
69 ~SetupDialog() override {}
71 // Window Message Handlers
72 LRESULT OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam,
73 BOOL& handled);
74 LRESULT OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
75 LRESULT OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled);
76 LRESULT OnStart(UINT, INT nIdentifier, HWND, BOOL& handled);
77 LRESULT OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled);
78 LRESULT OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled);
79 LRESULT OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
81 void PostUITask(const base::Closure& task);
82 void PostIOTask(const base::Closure& task);
84 // UI Calls.
86 // Disables all controls after users actions.
87 void DisableControls();
88 // Updates state of controls after when we received service status.
89 void SetState(ServiceController::State state, const base::string16& user,
90 bool is_logging_enabled);
91 // Show message box with error.
92 void ShowErrorMessageBox(const base::string16& error_message);
93 // Show use message box instructions how to deal with opened Chrome window.
94 void AskToCloseChrome();
95 base::string16 GetDlgItemText(int id) const;
96 base::string16 GetUser() const;
97 base::string16 GetPassword() const;
98 bool IsLoggingEnabled() const;
99 bool IsInstalled() const {
100 return state_ > ServiceController::STATE_NOT_FOUND;
103 // IO Calls.
104 // Installs service.
105 void Install(const base::string16& user, const base::string16& password,
106 bool enable_logging);
107 // Starts service.
108 void Start();
109 // Stops service.
110 void Stop();
111 // Uninstall service.
112 void Uninstall();
113 // Update service state.
114 void UpdateState();
115 // Posts task to UI thread to show error using string id.
116 void ShowError(int string_id);
117 // Posts task to UI thread to show error using string.
118 void ShowError(const base::string16& error_message);
119 // Posts task to UI thread to show error using error code.
120 void ShowError(HRESULT hr);
122 ServiceController::State state_;
123 base::Thread worker_;
125 base::MessageLoop* ui_loop_;
126 base::MessageLoop* io_loop_;
128 ServiceController controller_;
131 SetupDialog::SetupDialog()
132 : state_(ServiceController::STATE_NOT_FOUND),
133 worker_("worker") {
134 ui_loop_ = base::MessageLoop::current();
135 DCHECK(base::MessageLoopForUI::IsCurrent());
137 worker_.StartWithOptions(
138 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
139 io_loop_ = worker_.message_loop();
140 DCHECK(io_loop_->IsType(base::MessageLoop::TYPE_IO));
143 void SetupDialog::PostUITask(const base::Closure& task) {
144 ui_loop_->PostTask(FROM_HERE, task);
147 void SetupDialog::PostIOTask(const base::Closure& task) {
148 io_loop_->PostTask(FROM_HERE, task);
151 void SetupDialog::ShowErrorMessageBox(const base::string16& error_message) {
152 DCHECK(base::MessageLoopForUI::IsCurrent());
153 MessageBox(error_message.c_str(),
154 LoadLocalString(IDS_OPERATION_FAILED_TITLE).c_str(),
155 MB_ICONERROR | MB_OK);
158 void SetupDialog::AskToCloseChrome() {
159 DCHECK(base::MessageLoopForUI::IsCurrent());
160 MessageBox(LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME).c_str(),
161 LoadLocalString(IDS_CONTINUE_IN_CHROME_TITLE).c_str(),
162 MB_OK);
165 void SetupDialog::SetState(ServiceController::State status,
166 const base::string16& user,
167 bool is_logging_enabled) {
168 DCHECK(base::MessageLoopForUI::IsCurrent());
169 state_ = status;
171 DWORD status_string = 0;
172 switch(status) {
173 case ServiceController::STATE_NOT_FOUND:
174 status_string = IDS_SERVICE_NOT_FOUND;
175 break;
176 case ServiceController::STATE_STOPPED:
177 status_string = IDS_SERVICE_STOPPED;
178 break;
179 case ServiceController::STATE_RUNNING:
180 status_string = IDS_SERVICE_RUNNING;
181 break;
182 case ServiceController::STATE_UNKNOWN:
183 break;
185 SetDlgItemText(IDC_STATUS,
186 status_string ? LoadLocalString(status_string).c_str() : L"");
187 if (IsInstalled()) {
188 SetDlgItemText(IDC_USER, user.c_str());
189 CheckDlgButton(IDC_LOGGING,
190 is_logging_enabled ? BST_CHECKED : BST_UNCHECKED);
193 ATL::CWindow start_button = GetDlgItem(IDC_START);
194 DWORD start_string = (status == ServiceController::STATE_STOPPED) ?
195 IDS_SERVICE_START : IDS_SERVICE_STOP;
196 start_button.SetWindowText(LoadLocalString(start_string).c_str());
197 start_button.ShowWindow(IsInstalled() ? SW_SHOW : SW_HIDE);
198 start_button.EnableWindow(TRUE);
200 ATL::CWindow install_button = GetDlgItem(IDC_INSTALL);
201 DWORD install_string = IsInstalled() ? IDS_SERVICE_UNINSTALL :
202 IDS_SERVICE_INSTALL;
203 install_button.SetWindowText(LoadLocalString(install_string).c_str());
204 install_button.ShowWindow(SW_SHOW);
205 install_button.EnableWindow(TRUE);
207 if (!IsInstalled()) {
208 GetDlgItem(IDC_USER).EnableWindow(TRUE);
209 GetDlgItem(IDC_PASSWORD).EnableWindow(TRUE);
210 GetDlgItem(IDC_LOGGING).EnableWindow(TRUE);
214 LRESULT SetupDialog::OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam,
215 BOOL& handled) {
216 ATLVERIFY(CenterWindow());
218 WTL::CIcon icon;
219 if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON))) {
220 SetIcon(icon);
223 SetWindowText(LoadLocalString(IDS_SETUP_PROGRAM_NAME).c_str());
224 SetDlgItemText(IDC_STATE_LABEL, LoadLocalString(IDS_STATE_LABEL).c_str());
225 SetDlgItemText(IDC_USER_LABEL, LoadLocalString(IDS_USER_LABEL).c_str());
226 SetDlgItemText(IDC_PASSWORD_LABEL,
227 LoadLocalString(IDS_PASSWORD_LABEL).c_str());
228 SetDlgItemText(IDC_LOGGING, LoadLocalString(IDS_LOGGING_LABEL).c_str());
229 SetDlgItemText(IDCANCEL, LoadLocalString(IDS_CLOSE).c_str());
231 SetState(ServiceController::STATE_UNKNOWN, L"", false);
232 DisableControls();
234 SetDlgItemText(IDC_USER, GetCurrentUserName().c_str());
236 PostIOTask(base::Bind(&SetupDialog::UpdateState, this));
238 return 0;
241 LRESULT SetupDialog::OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam,
242 BOOL& handled) {
243 HWND window = reinterpret_cast<HWND>(lparam);
244 if (GetDlgItem(IDC_LOGO).m_hWnd == window) {
245 return reinterpret_cast<LRESULT>(::GetStockObject(WHITE_BRUSH));
247 return 0;
250 LRESULT SetupDialog::OnStart(UINT, INT nIdentifier, HWND, BOOL& handled) {
251 DisableControls();
252 DCHECK(IsInstalled());
253 if (state_ == ServiceController::STATE_RUNNING)
254 PostIOTask(base::Bind(&SetupDialog::Stop, this));
255 else
256 PostIOTask(base::Bind(&SetupDialog::Start, this));
257 return 0;
260 LRESULT SetupDialog::OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled) {
261 DisableControls();
262 if (IsInstalled()) {
263 PostIOTask(base::Bind(&SetupDialog::Uninstall, this));
264 } else {
265 PostIOTask(base::Bind(&SetupDialog::Install, this, GetUser(),
266 GetPassword(), IsLoggingEnabled()));
268 return 0;
271 LRESULT SetupDialog::OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled) {
272 CheckDlgButton(IDC_LOGGING, IsLoggingEnabled()? BST_UNCHECKED : BST_CHECKED);
273 return 0;
276 LRESULT SetupDialog::OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled) {
277 DestroyWindow();
278 return 0;
281 LRESULT SetupDialog::OnDestroy(UINT message, WPARAM wparam, LPARAM lparam,
282 BOOL& handled) {
283 base::MessageLoop::current()->PostTask(FROM_HERE,
284 base::MessageLoop::QuitClosure());
285 return 1;
288 void SetupDialog::DisableControls() {
289 GetDlgItem(IDC_START).EnableWindow(FALSE);
290 GetDlgItem(IDC_INSTALL).EnableWindow(FALSE);
291 GetDlgItem(IDC_USER).EnableWindow(FALSE);
292 GetDlgItem(IDC_PASSWORD).EnableWindow(FALSE);
293 GetDlgItem(IDC_LOGGING).EnableWindow(FALSE);
296 base::string16 SetupDialog::GetDlgItemText(int id) const {
297 const ATL::CWindow& item = GetDlgItem(id);
298 size_t length = item.GetWindowTextLength();
299 base::string16 result(length + 1, L'\0');
300 result.resize(item.GetWindowText(&result[0], result.size()));
301 return result;
304 base::string16 SetupDialog::GetUser() const {
305 return GetDlgItemText(IDC_USER);
308 base::string16 SetupDialog::GetPassword() const {
309 return GetDlgItemText(IDC_PASSWORD);
312 bool SetupDialog::IsLoggingEnabled() const{
313 return IsDlgButtonChecked(IDC_LOGGING) == BST_CHECKED;
316 void SetupDialog::UpdateState() {
317 DCHECK(base::MessageLoopForIO::IsCurrent());
318 controller_.UpdateState();
319 PostUITask(base::Bind(&SetupDialog::SetState, this, controller_.state(),
320 controller_.user(), controller_.is_logging_enabled()));
323 void SetupDialog::ShowError(const base::string16& error_message) {
324 DCHECK(base::MessageLoopForIO::IsCurrent());
325 PostUITask(base::Bind(&SetupDialog::SetState,
326 this,
327 ServiceController::STATE_UNKNOWN,
328 L"",
329 false));
330 PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox, this,
331 error_message));
332 LOG(ERROR) << error_message;
335 void SetupDialog::ShowError(int string_id) {
336 ShowError(cloud_print::LoadLocalString(string_id));
339 void SetupDialog::ShowError(HRESULT hr) {
340 ShowError(GetErrorMessage(hr));
343 void SetupDialog::Install(const base::string16& user,
344 const base::string16& password,
345 bool enable_logging) {
346 // Don't forget to update state on exit.
347 base::ScopedClosureRunner scoped_update_status(
348 base::Bind(&SetupDialog::UpdateState, this));
350 DCHECK(base::MessageLoopForIO::IsCurrent());
352 SetupListener setup(GetUser());
353 HRESULT hr = controller_.InstallCheckService(user, password,
354 base::FilePath());
355 if (FAILED(hr))
356 return ShowError(hr);
359 // Always uninstall service after requirements check.
360 base::ScopedClosureRunner scoped_uninstall(
361 base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
362 base::Unretained(&controller_)));
364 hr = controller_.StartService();
365 if (FAILED(hr))
366 return ShowError(hr);
368 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30)))
369 return ShowError(IDS_ERROR_FAILED_START_SERVICE);
372 if (setup.user_data_dir().empty())
373 return ShowError(IDS_ERROR_NO_DATA_DIR);
375 if (setup.chrome_path().empty())
376 return ShowError(IDS_ERROR_NO_CHROME);
378 if (!setup.is_xps_available())
379 return ShowError(IDS_ERROR_NO_XPS);
381 base::FilePath file = setup.user_data_dir();
382 file = file.Append(chrome::kServiceStateFileName);
384 std::string proxy_id;
385 std::string contents;
387 if (base::ReadFileToString(file, &contents)) {
388 ServiceState service_state;
389 if (service_state.FromString(contents))
390 proxy_id = service_state.proxy_id();
392 PostUITask(base::Bind(&SetupDialog::AskToCloseChrome, this));
393 contents = ChromeLauncher::CreateServiceStateFile(proxy_id, setup.printers());
395 if (contents.empty())
396 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG);
398 size_t written = base::WriteFile(file, contents.c_str(),
399 contents.size());
400 if (written != contents.size()) {
401 DWORD last_error = GetLastError();
402 if (!last_error)
403 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG);
404 return ShowError(HRESULT_FROM_WIN32(last_error));
407 hr = controller_.InstallConnectorService(user, password, base::FilePath(),
408 enable_logging);
409 if (FAILED(hr))
410 return ShowError(hr);
412 hr = controller_.StartService();
413 if (FAILED(hr))
414 return ShowError(hr);
417 void SetupDialog::Start() {
418 DCHECK(base::MessageLoopForIO::IsCurrent());
419 HRESULT hr = controller_.StartService();
420 if (FAILED(hr))
421 ShowError(hr);
422 UpdateState();
425 void SetupDialog::Stop() {
426 DCHECK(base::MessageLoopForIO::IsCurrent());
427 HRESULT hr = controller_.StopService();
428 if (FAILED(hr))
429 ShowError(hr);
430 UpdateState();
433 void SetupDialog::Uninstall() {
434 DCHECK(base::MessageLoopForIO::IsCurrent());
435 HRESULT hr = controller_.UninstallService();
436 if (FAILED(hr))
437 ShowError(hr);
438 UpdateState();
441 class CloudPrintServiceConfigModule
442 : public ATL::CAtlExeModuleT<CloudPrintServiceConfigModule> {
445 CloudPrintServiceConfigModule _AtlModule;
447 int WINAPI WinMain(__in HINSTANCE hInstance,
448 __in HINSTANCE hPrevInstance,
449 __in LPSTR lpCmdLine,
450 __in int nCmdShow) {
451 base::AtExitManager at_exit;
452 base::CommandLine::Init(0, NULL);
454 base::MessageLoopForUI loop;
455 scoped_refptr<SetupDialog> dialog(new SetupDialog());
456 dialog->Create(NULL);
457 dialog->ShowWindow(SW_SHOW);
458 SetupDialog::Dispatcher dispatcher(dialog.get());
459 base::RunLoop run_loop(&dispatcher);
460 run_loop.Run();
461 return 0;