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.
6 #include <atlapp.h> // NOLINT
8 #include "base/at_exit.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
> {
33 // Enables accelerators.
34 class Dispatcher
: public base::MessagePumpDispatcher
{
36 explicit Dispatcher(SetupDialog
* dialog
) : dialog_(dialog
) {}
37 virtual ~Dispatcher() {};
39 // MessagePumpDispatcher:
40 virtual uint32_t Dispatch(const MSG
& msg
) override
{
42 uint32_t action
= POST_DISPATCH_NONE
;
43 if (!dialog_
->IsDialogMessage(&msg2
))
44 action
= POST_DISPATCH_PERFORM_DEFAULT
;
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
)
67 // Window Message Handlers
68 LRESULT
OnInitDialog(UINT message
, WPARAM wparam
, LPARAM lparam
,
70 LRESULT
OnCtrColor(UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
);
71 LRESULT
OnCancel(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
72 LRESULT
OnStart(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
73 LRESULT
OnInstall(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
74 LRESULT
OnLogging(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
75 LRESULT
OnDestroy(UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
);
77 void PostUITask(const base::Closure
& task
);
78 void PostIOTask(const base::Closure
& task
);
82 // Disables all controls after users actions.
83 void DisableControls();
84 // Updates state of controls after when we received service status.
85 void SetState(ServiceController::State state
, const base::string16
& user
,
86 bool is_logging_enabled
);
87 // Show message box with error.
88 void ShowErrorMessageBox(const base::string16
& error_message
);
89 // Show use message box instructions how to deal with opened Chrome window.
90 void AskToCloseChrome();
91 base::string16
GetDlgItemText(int id
) const;
92 base::string16
GetUser() const;
93 base::string16
GetPassword() const;
94 bool IsLoggingEnabled() const;
95 bool IsInstalled() const {
96 return state_
> ServiceController::STATE_NOT_FOUND
;
101 void Install(const base::string16
& user
, const base::string16
& password
,
102 bool enable_logging
);
107 // Uninstall service.
109 // Update service state.
111 // Posts task to UI thread to show error using string id.
112 void ShowError(int string_id
);
113 // Posts task to UI thread to show error using string.
114 void ShowError(const base::string16
& error_message
);
115 // Posts task to UI thread to show error using error code.
116 void ShowError(HRESULT hr
);
118 ServiceController::State state_
;
119 base::Thread worker_
;
121 base::MessageLoop
* ui_loop_
;
122 base::MessageLoop
* io_loop_
;
124 ServiceController controller_
;
127 SetupDialog::SetupDialog()
128 : state_(ServiceController::STATE_NOT_FOUND
),
130 ui_loop_
= base::MessageLoop::current();
131 DCHECK(base::MessageLoopForUI::IsCurrent());
133 worker_
.StartWithOptions(
134 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
135 io_loop_
= worker_
.message_loop();
136 DCHECK(io_loop_
->IsType(base::MessageLoop::TYPE_IO
));
139 void SetupDialog::PostUITask(const base::Closure
& task
) {
140 ui_loop_
->PostTask(FROM_HERE
, task
);
143 void SetupDialog::PostIOTask(const base::Closure
& task
) {
144 io_loop_
->PostTask(FROM_HERE
, task
);
147 void SetupDialog::ShowErrorMessageBox(const base::string16
& error_message
) {
148 DCHECK(base::MessageLoopForUI::IsCurrent());
149 MessageBox(error_message
.c_str(),
150 LoadLocalString(IDS_OPERATION_FAILED_TITLE
).c_str(),
151 MB_ICONERROR
| MB_OK
);
154 void SetupDialog::AskToCloseChrome() {
155 DCHECK(base::MessageLoopForUI::IsCurrent());
156 MessageBox(LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME
).c_str(),
157 LoadLocalString(IDS_CONTINUE_IN_CHROME_TITLE
).c_str(),
161 void SetupDialog::SetState(ServiceController::State status
,
162 const base::string16
& user
,
163 bool is_logging_enabled
) {
164 DCHECK(base::MessageLoopForUI::IsCurrent());
167 DWORD status_string
= 0;
169 case ServiceController::STATE_NOT_FOUND
:
170 status_string
= IDS_SERVICE_NOT_FOUND
;
172 case ServiceController::STATE_STOPPED
:
173 status_string
= IDS_SERVICE_STOPPED
;
175 case ServiceController::STATE_RUNNING
:
176 status_string
= IDS_SERVICE_RUNNING
;
179 SetDlgItemText(IDC_STATUS
,
180 status_string
? LoadLocalString(status_string
).c_str() : L
"");
182 SetDlgItemText(IDC_USER
, user
.c_str());
183 CheckDlgButton(IDC_LOGGING
,
184 is_logging_enabled
? BST_CHECKED
: BST_UNCHECKED
);
187 ATL::CWindow start_button
= GetDlgItem(IDC_START
);
188 DWORD start_string
= (status
== ServiceController::STATE_STOPPED
) ?
189 IDS_SERVICE_START
: IDS_SERVICE_STOP
;
190 start_button
.SetWindowText(LoadLocalString(start_string
).c_str());
191 start_button
.ShowWindow(IsInstalled() ? SW_SHOW
: SW_HIDE
);
192 start_button
.EnableWindow(TRUE
);
194 ATL::CWindow install_button
= GetDlgItem(IDC_INSTALL
);
195 DWORD install_string
= IsInstalled() ? IDS_SERVICE_UNINSTALL
:
197 install_button
.SetWindowText(LoadLocalString(install_string
).c_str());
198 install_button
.ShowWindow(SW_SHOW
);
199 install_button
.EnableWindow(TRUE
);
201 if (!IsInstalled()) {
202 GetDlgItem(IDC_USER
).EnableWindow(TRUE
);
203 GetDlgItem(IDC_PASSWORD
).EnableWindow(TRUE
);
204 GetDlgItem(IDC_LOGGING
).EnableWindow(TRUE
);
208 LRESULT
SetupDialog::OnInitDialog(UINT message
, WPARAM wparam
, LPARAM lparam
,
210 ATLVERIFY(CenterWindow());
213 if (icon
.LoadIcon(MAKEINTRESOURCE(IDI_ICON
))) {
217 SetWindowText(LoadLocalString(IDS_SETUP_PROGRAM_NAME
).c_str());
218 SetDlgItemText(IDC_STATE_LABEL
, LoadLocalString(IDS_STATE_LABEL
).c_str());
219 SetDlgItemText(IDC_USER_LABEL
, LoadLocalString(IDS_USER_LABEL
).c_str());
220 SetDlgItemText(IDC_PASSWORD_LABEL
,
221 LoadLocalString(IDS_PASSWORD_LABEL
).c_str());
222 SetDlgItemText(IDC_LOGGING
, LoadLocalString(IDS_LOGGING_LABEL
).c_str());
223 SetDlgItemText(IDCANCEL
, LoadLocalString(IDS_CLOSE
).c_str());
225 SetState(ServiceController::STATE_UNKNOWN
, L
"", false);
228 SetDlgItemText(IDC_USER
, GetCurrentUserName().c_str());
230 PostIOTask(base::Bind(&SetupDialog::UpdateState
, this));
235 LRESULT
SetupDialog::OnCtrColor(UINT message
, WPARAM wparam
, LPARAM lparam
,
237 HWND window
= reinterpret_cast<HWND
>(lparam
);
238 if (GetDlgItem(IDC_LOGO
).m_hWnd
== window
) {
239 return reinterpret_cast<LRESULT
>(::GetStockObject(WHITE_BRUSH
));
244 LRESULT
SetupDialog::OnStart(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
246 DCHECK(IsInstalled());
247 if (state_
== ServiceController::STATE_RUNNING
)
248 PostIOTask(base::Bind(&SetupDialog::Stop
, this));
250 PostIOTask(base::Bind(&SetupDialog::Start
, this));
254 LRESULT
SetupDialog::OnInstall(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
257 PostIOTask(base::Bind(&SetupDialog::Uninstall
, this));
259 PostIOTask(base::Bind(&SetupDialog::Install
, this, GetUser(),
260 GetPassword(), IsLoggingEnabled()));
265 LRESULT
SetupDialog::OnLogging(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
266 CheckDlgButton(IDC_LOGGING
, IsLoggingEnabled()? BST_UNCHECKED
: BST_CHECKED
);
270 LRESULT
SetupDialog::OnCancel(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
275 LRESULT
SetupDialog::OnDestroy(UINT message
, WPARAM wparam
, LPARAM lparam
,
277 base::MessageLoop::current()->PostTask(FROM_HERE
,
278 base::MessageLoop::QuitClosure());
282 void SetupDialog::DisableControls() {
283 GetDlgItem(IDC_START
).EnableWindow(FALSE
);
284 GetDlgItem(IDC_INSTALL
).EnableWindow(FALSE
);
285 GetDlgItem(IDC_USER
).EnableWindow(FALSE
);
286 GetDlgItem(IDC_PASSWORD
).EnableWindow(FALSE
);
287 GetDlgItem(IDC_LOGGING
).EnableWindow(FALSE
);
290 base::string16
SetupDialog::GetDlgItemText(int id
) const {
291 const ATL::CWindow
& item
= GetDlgItem(id
);
292 size_t length
= item
.GetWindowTextLength();
293 base::string16
result(length
+ 1, L
'\0');
294 result
.resize(item
.GetWindowText(&result
[0], result
.size()));
298 base::string16
SetupDialog::GetUser() const {
299 return GetDlgItemText(IDC_USER
);
302 base::string16
SetupDialog::GetPassword() const {
303 return GetDlgItemText(IDC_PASSWORD
);
306 bool SetupDialog::IsLoggingEnabled() const{
307 return IsDlgButtonChecked(IDC_LOGGING
) == BST_CHECKED
;
310 void SetupDialog::UpdateState() {
311 DCHECK(base::MessageLoopForIO::IsCurrent());
312 controller_
.UpdateState();
313 PostUITask(base::Bind(&SetupDialog::SetState
, this, controller_
.state(),
314 controller_
.user(), controller_
.is_logging_enabled()));
317 void SetupDialog::ShowError(const base::string16
& error_message
) {
318 DCHECK(base::MessageLoopForIO::IsCurrent());
319 PostUITask(base::Bind(&SetupDialog::SetState
,
321 ServiceController::STATE_UNKNOWN
,
324 PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox
, this,
326 LOG(ERROR
) << error_message
;
329 void SetupDialog::ShowError(int string_id
) {
330 ShowError(cloud_print::LoadLocalString(string_id
));
333 void SetupDialog::ShowError(HRESULT hr
) {
334 ShowError(GetErrorMessage(hr
));
337 void SetupDialog::Install(const base::string16
& user
,
338 const base::string16
& password
,
339 bool enable_logging
) {
340 // Don't forget to update state on exit.
341 base::ScopedClosureRunner
scoped_update_status(
342 base::Bind(&SetupDialog::UpdateState
, this));
344 DCHECK(base::MessageLoopForIO::IsCurrent());
346 SetupListener
setup(GetUser());
347 HRESULT hr
= controller_
.InstallCheckService(user
, password
,
350 return ShowError(hr
);
353 // Always uninstall service after requirements check.
354 base::ScopedClosureRunner
scoped_uninstall(
355 base::Bind(base::IgnoreResult(&ServiceController::UninstallService
),
356 base::Unretained(&controller_
)));
358 hr
= controller_
.StartService();
360 return ShowError(hr
);
362 if (!setup
.WaitResponce(base::TimeDelta::FromSeconds(30)))
363 return ShowError(IDS_ERROR_FAILED_START_SERVICE
);
366 if (setup
.user_data_dir().empty())
367 return ShowError(IDS_ERROR_NO_DATA_DIR
);
369 if (setup
.chrome_path().empty())
370 return ShowError(IDS_ERROR_NO_CHROME
);
372 if (!setup
.is_xps_available())
373 return ShowError(IDS_ERROR_NO_XPS
);
375 base::FilePath file
= setup
.user_data_dir();
376 file
= file
.Append(chrome::kServiceStateFileName
);
378 std::string proxy_id
;
379 std::string contents
;
381 if (base::ReadFileToString(file
, &contents
)) {
382 ServiceState service_state
;
383 if (service_state
.FromString(contents
))
384 proxy_id
= service_state
.proxy_id();
386 PostUITask(base::Bind(&SetupDialog::AskToCloseChrome
, this));
387 contents
= ChromeLauncher::CreateServiceStateFile(proxy_id
, setup
.printers());
389 if (contents
.empty())
390 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG
);
392 size_t written
= base::WriteFile(file
, contents
.c_str(),
394 if (written
!= contents
.size()) {
395 DWORD last_error
= GetLastError();
397 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG
);
398 return ShowError(HRESULT_FROM_WIN32(last_error
));
401 hr
= controller_
.InstallConnectorService(user
, password
, base::FilePath(),
404 return ShowError(hr
);
406 hr
= controller_
.StartService();
408 return ShowError(hr
);
411 void SetupDialog::Start() {
412 DCHECK(base::MessageLoopForIO::IsCurrent());
413 HRESULT hr
= controller_
.StartService();
419 void SetupDialog::Stop() {
420 DCHECK(base::MessageLoopForIO::IsCurrent());
421 HRESULT hr
= controller_
.StopService();
427 void SetupDialog::Uninstall() {
428 DCHECK(base::MessageLoopForIO::IsCurrent());
429 HRESULT hr
= controller_
.UninstallService();
435 class CloudPrintServiceConfigModule
436 : public ATL::CAtlExeModuleT
<CloudPrintServiceConfigModule
> {
439 CloudPrintServiceConfigModule _AtlModule
;
441 int WINAPI
WinMain(__in HINSTANCE hInstance
,
442 __in HINSTANCE hPrevInstance
,
443 __in LPSTR lpCmdLine
,
445 base::AtExitManager at_exit
;
446 CommandLine::Init(0, NULL
);
448 base::MessageLoopForUI loop
;
449 scoped_refptr
<SetupDialog
> dialog(new SetupDialog());
450 dialog
->Create(NULL
);
451 dialog
->ShowWindow(SW_SHOW
);
452 SetupDialog::Dispatcher
dispatcher(dialog
);
453 base::RunLoop
run_loop(&dispatcher
);