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/file_util.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string16.h"
15 #include "base/threading/thread.h"
16 #include "chrome/common/chrome_constants.h"
17 #include "cloud_print/common/win/cloud_print_utils.h"
18 #include "cloud_print/resources.h"
19 #include "cloud_print/service/service_state.h"
20 #include "cloud_print/service/win/chrome_launcher.h"
21 #include "cloud_print/service/win/service_controller.h"
22 #include "cloud_print/service/win/service_utils.h"
23 #include "cloud_print/service/win/setup_listener.h"
25 using cloud_print::LoadLocalString
;
26 using cloud_print::GetErrorMessage
;
28 class SetupDialog
: public base::RefCounted
<SetupDialog
>,
29 public ATL::CDialogImpl
<SetupDialog
> {
31 // Enables accelerators.
32 class MessageFilter
: public base::MessageLoopForUI::MessageFilter
{
34 explicit MessageFilter(SetupDialog
* dialog
) : dialog_(dialog
){}
35 virtual ~MessageFilter() {};
37 // MessageLoopForUI::MessageFilter
38 virtual bool ProcessMessage(const MSG
& msg
) OVERRIDE
{
40 return dialog_
->IsDialogMessage(&msg2
) != FALSE
;
44 scoped_refptr
<SetupDialog
> dialog_
;
47 typedef ATL::CDialogImpl
<SetupDialog
> Base
;
48 enum { IDD
= IDD_SETUP_DIALOG
};
50 BEGIN_MSG_MAP(SetupDialog
)
51 MESSAGE_HANDLER(WM_INITDIALOG
, OnInitDialog
)
52 MESSAGE_HANDLER(WM_CTLCOLORSTATIC
, OnCtrColor
)
53 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
54 COMMAND_ID_HANDLER(IDCANCEL
, OnCancel
)
55 COMMAND_ID_HANDLER(IDC_START
, OnStart
)
56 COMMAND_ID_HANDLER(IDC_INSTALL
, OnInstall
)
57 COMMAND_ID_HANDLER(IDC_LOGGING
, OnLogging
)
62 // Window Message Handlers
63 LRESULT
OnInitDialog(UINT message
, WPARAM wparam
, LPARAM lparam
,
65 LRESULT
OnCtrColor(UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
);
66 LRESULT
OnCancel(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
67 LRESULT
OnStart(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
68 LRESULT
OnInstall(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
69 LRESULT
OnLogging(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
);
70 LRESULT
OnDestroy(UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
);
72 void PostUITask(const base::Closure
& task
);
73 void PostIOTask(const base::Closure
& task
);
77 // Disables all controls after users actions.
78 void DisableControls();
79 // Updates state of controls after when we received service status.
80 void SetState(ServiceController::State state
, const string16
& user
,
81 bool is_logging_enabled
);
82 // Show message box with error.
83 void ShowErrorMessageBox(const string16
& error_message
);
84 // Show use message box instructions how to deal with opened Chrome window.
85 void AskToCloseChrome();
86 string16
GetDlgItemText(int id
) const;
87 string16
GetUser() const;
88 string16
GetPassword() const;
89 bool IsLoggingEnabled() const;
90 bool IsInstalled() const {
91 return state_
> ServiceController::STATE_NOT_FOUND
;
96 void Install(const string16
& user
, const string16
& password
,
102 // Uninstall service.
104 // Update service state.
106 // Posts task to UI thread to show error using string id.
107 void ShowError(int string_id
);
108 // Posts task to UI thread to show error using string.
109 void ShowError(const string16
& error_message
);
110 // Posts task to UI thread to show error using error code.
111 void ShowError(HRESULT hr
);
113 ServiceController::State state_
;
114 base::Thread worker_
;
116 base::MessageLoop
* ui_loop_
;
117 base::MessageLoop
* io_loop_
;
119 ServiceController controller_
;
122 SetupDialog::SetupDialog()
123 : state_(ServiceController::STATE_NOT_FOUND
),
125 ui_loop_
= base::MessageLoop::current();
126 DCHECK(ui_loop_
->IsType(base::MessageLoop::TYPE_UI
));
128 worker_
.StartWithOptions(
129 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
130 io_loop_
= worker_
.message_loop();
131 DCHECK(io_loop_
->IsType(base::MessageLoop::TYPE_IO
));
134 void SetupDialog::PostUITask(const base::Closure
& task
) {
135 ui_loop_
->PostTask(FROM_HERE
, task
);
138 void SetupDialog::PostIOTask(const base::Closure
& task
) {
139 io_loop_
->PostTask(FROM_HERE
, task
);
142 void SetupDialog::ShowErrorMessageBox(const string16
& error_message
) {
143 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI
));
144 MessageBox(error_message
.c_str(),
145 LoadLocalString(IDS_OPERATION_FAILED_TITLE
).c_str(),
146 MB_ICONERROR
| MB_OK
);
149 void SetupDialog::AskToCloseChrome() {
150 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI
));
151 MessageBox(LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME
).c_str(),
152 LoadLocalString(IDS_CONTINUE_IN_CHROME_TITLE
).c_str(),
156 void SetupDialog::SetState(ServiceController::State status
,
157 const string16
& user
,
158 bool is_logging_enabled
) {
159 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI
));
162 DWORD status_string
= 0;
164 case ServiceController::STATE_NOT_FOUND
:
165 status_string
= IDS_SERVICE_NOT_FOUND
;
167 case ServiceController::STATE_STOPPED
:
168 status_string
= IDS_SERVICE_STOPPED
;
170 case ServiceController::STATE_RUNNING
:
171 status_string
= IDS_SERVICE_RUNNING
;
174 SetDlgItemText(IDC_STATUS
,
175 status_string
? LoadLocalString(status_string
).c_str() : L
"");
177 SetDlgItemText(IDC_USER
, user
.c_str());
178 CheckDlgButton(IDC_LOGGING
,
179 is_logging_enabled
? BST_CHECKED
: BST_UNCHECKED
);
182 ATL::CWindow start_button
= GetDlgItem(IDC_START
);
183 DWORD start_string
= (status
== ServiceController::STATE_STOPPED
) ?
184 IDS_SERVICE_START
: IDS_SERVICE_STOP
;
185 start_button
.SetWindowText(LoadLocalString(start_string
).c_str());
186 start_button
.ShowWindow(IsInstalled() ? SW_SHOW
: SW_HIDE
);
187 start_button
.EnableWindow(TRUE
);
189 ATL::CWindow install_button
= GetDlgItem(IDC_INSTALL
);
190 DWORD install_string
= IsInstalled() ? IDS_SERVICE_UNINSTALL
:
192 install_button
.SetWindowText(LoadLocalString(install_string
).c_str());
193 install_button
.ShowWindow(SW_SHOW
);
194 install_button
.EnableWindow(TRUE
);
196 if (!IsInstalled()) {
197 GetDlgItem(IDC_USER
).EnableWindow(TRUE
);
198 GetDlgItem(IDC_PASSWORD
).EnableWindow(TRUE
);
199 GetDlgItem(IDC_LOGGING
).EnableWindow(TRUE
);
203 LRESULT
SetupDialog::OnInitDialog(UINT message
, WPARAM wparam
, LPARAM lparam
,
205 ATLVERIFY(CenterWindow());
208 if (icon
.LoadIcon(MAKEINTRESOURCE(IDI_ICON
))) {
212 SetWindowText(LoadLocalString(IDS_SETUP_PROGRAM_NAME
).c_str());
213 SetDlgItemText(IDC_STATE_LABEL
, LoadLocalString(IDS_STATE_LABEL
).c_str());
214 SetDlgItemText(IDC_USER_LABEL
, LoadLocalString(IDS_USER_LABEL
).c_str());
215 SetDlgItemText(IDC_PASSWORD_LABEL
,
216 LoadLocalString(IDS_PASSWORD_LABEL
).c_str());
217 SetDlgItemText(IDC_LOGGING
, LoadLocalString(IDS_LOGGING_LABEL
).c_str());
218 SetDlgItemText(IDCANCEL
, LoadLocalString(IDS_CLOSE
).c_str());
220 SetState(ServiceController::STATE_UNKNOWN
, L
"", false);
223 SetDlgItemText(IDC_USER
, GetCurrentUserName().c_str());
225 PostIOTask(base::Bind(&SetupDialog::UpdateState
, this));
230 LRESULT
SetupDialog::OnCtrColor(UINT message
, WPARAM wparam
, LPARAM lparam
,
232 HWND window
= reinterpret_cast<HWND
>(lparam
);
233 if (GetDlgItem(IDC_LOGO
).m_hWnd
== window
) {
234 return reinterpret_cast<LRESULT
>(::GetStockObject(WHITE_BRUSH
));
239 LRESULT
SetupDialog::OnStart(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
241 DCHECK(IsInstalled());
242 if (state_
== ServiceController::STATE_RUNNING
)
243 PostIOTask(base::Bind(&SetupDialog::Stop
, this));
245 PostIOTask(base::Bind(&SetupDialog::Start
, this));
249 LRESULT
SetupDialog::OnInstall(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
252 PostIOTask(base::Bind(&SetupDialog::Uninstall
, this));
254 PostIOTask(base::Bind(&SetupDialog::Install
, this, GetUser(),
255 GetPassword(), IsLoggingEnabled()));
260 LRESULT
SetupDialog::OnLogging(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
261 CheckDlgButton(IDC_LOGGING
, IsLoggingEnabled()? BST_UNCHECKED
: BST_CHECKED
);
265 LRESULT
SetupDialog::OnCancel(UINT
, INT nIdentifier
, HWND
, BOOL
& handled
) {
270 LRESULT
SetupDialog::OnDestroy(UINT message
, WPARAM wparam
, LPARAM lparam
,
272 base::MessageLoop::current()->PostTask(FROM_HERE
,
273 base::MessageLoop::QuitClosure());
277 void SetupDialog::DisableControls() {
278 GetDlgItem(IDC_START
).EnableWindow(FALSE
);
279 GetDlgItem(IDC_INSTALL
).EnableWindow(FALSE
);
280 GetDlgItem(IDC_USER
).EnableWindow(FALSE
);
281 GetDlgItem(IDC_PASSWORD
).EnableWindow(FALSE
);
282 GetDlgItem(IDC_LOGGING
).EnableWindow(FALSE
);
285 string16
SetupDialog::GetDlgItemText(int id
) const {
286 const ATL::CWindow
& item
= GetDlgItem(id
);
287 size_t length
= item
.GetWindowTextLength();
288 string16
result(length
+ 1, L
'\0');
289 result
.resize(item
.GetWindowText(&result
[0], result
.size()));
293 string16
SetupDialog::GetUser() const {
294 return GetDlgItemText(IDC_USER
);
297 string16
SetupDialog::GetPassword() const{
298 return GetDlgItemText(IDC_PASSWORD
);
301 bool SetupDialog::IsLoggingEnabled() const{
302 return IsDlgButtonChecked(IDC_LOGGING
) == BST_CHECKED
;
305 void SetupDialog::UpdateState() {
306 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
307 controller_
.UpdateState();
308 PostUITask(base::Bind(&SetupDialog::SetState
, this, controller_
.state(),
309 controller_
.user(), controller_
.is_logging_enabled()));
312 void SetupDialog::ShowError(const string16
& error_message
) {
313 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
314 PostUITask(base::Bind(&SetupDialog::SetState
,
316 ServiceController::STATE_UNKNOWN
,
319 PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox
, this,
321 LOG(ERROR
) << error_message
;
324 void SetupDialog::ShowError(int string_id
) {
325 ShowError(cloud_print::LoadLocalString(string_id
));
328 void SetupDialog::ShowError(HRESULT hr
) {
329 ShowError(GetErrorMessage(hr
));
332 void SetupDialog::Install(const string16
& user
, const string16
& password
,
333 bool enable_logging
) {
334 // Don't forget to update state on exit.
335 base::ScopedClosureRunner
scoped_update_status(
336 base::Bind(&SetupDialog::UpdateState
, this));
338 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
340 SetupListener
setup(GetUser());
341 HRESULT hr
= controller_
.InstallCheckService(user
, password
,
344 return ShowError(hr
);
347 // Always uninstall service after requirements check.
348 base::ScopedClosureRunner
scoped_uninstall(
349 base::Bind(base::IgnoreResult(&ServiceController::UninstallService
),
350 base::Unretained(&controller_
)));
352 hr
= controller_
.StartService();
354 return ShowError(hr
);
356 if (!setup
.WaitResponce(base::TimeDelta::FromSeconds(30)))
357 return ShowError(IDS_ERROR_FAILED_START_SERVICE
);
360 if (setup
.user_data_dir().empty())
361 return ShowError(IDS_ERROR_NO_DATA_DIR
);
363 if (setup
.chrome_path().empty())
364 return ShowError(IDS_ERROR_NO_CHROME
);
366 if (!setup
.is_xps_available())
367 return ShowError(IDS_ERROR_NO_XPS
);
369 base::FilePath file
= setup
.user_data_dir();
370 file
= file
.Append(chrome::kServiceStateFileName
);
372 std::string proxy_id
;
373 std::string contents
;
375 if (base::ReadFileToString(file
, &contents
)) {
376 ServiceState service_state
;
377 if (service_state
.FromString(contents
))
378 proxy_id
= service_state
.proxy_id();
380 PostUITask(base::Bind(&SetupDialog::AskToCloseChrome
, this));
381 contents
= ChromeLauncher::CreateServiceStateFile(proxy_id
, setup
.printers());
383 if (contents
.empty())
384 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG
);
386 size_t written
= file_util::WriteFile(file
, contents
.c_str(),
388 if (written
!= contents
.size()) {
389 DWORD last_error
= GetLastError();
391 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG
);
392 return ShowError(HRESULT_FROM_WIN32(last_error
));
395 hr
= controller_
.InstallConnectorService(user
, password
, base::FilePath(),
398 return ShowError(hr
);
400 hr
= controller_
.StartService();
402 return ShowError(hr
);
405 void SetupDialog::Start() {
406 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
407 HRESULT hr
= controller_
.StartService();
413 void SetupDialog::Stop() {
414 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
415 HRESULT hr
= controller_
.StopService();
421 void SetupDialog::Uninstall() {
422 DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO
));
423 HRESULT hr
= controller_
.UninstallService();
429 class CloudPrintServiceConfigModule
430 : public ATL::CAtlExeModuleT
<CloudPrintServiceConfigModule
> {
433 CloudPrintServiceConfigModule _AtlModule
;
435 int WINAPI
WinMain(__in HINSTANCE hInstance
,
436 __in HINSTANCE hPrevInstance
,
437 __in LPSTR lpCmdLine
,
439 base::AtExitManager at_exit
;
440 CommandLine::Init(0, NULL
);
442 base::MessageLoopForUI loop
;
443 scoped_refptr
<SetupDialog
> dialog(new SetupDialog());
444 dialog
->Create(NULL
);
445 dialog
->ShowWindow(SW_SHOW
);
446 scoped_ptr
<SetupDialog::MessageFilter
> filter(
447 new SetupDialog::MessageFilter(dialog
));
448 loop
.SetMessageFilter(filter
.Pass());