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. 4 5 #include <atlbase.h> 6 #include <atlapp.h> // NOLINT 7 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/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" 26 27 using cloud_print::LoadLocalString; 28 using cloud_print::GetErrorMessage; 29 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 virtual ~Dispatcher() {}; 38 39 // MessagePumpDispatcher: 40 virtual 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; 46 } 47 48 private: 49 scoped_refptr<SetupDialog> dialog_; 50 }; 51 52 typedef ATL::CDialogImpl<SetupDialog> Base; 53 enum { IDD = IDD_SETUP_DIALOG }; 54 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() 64 65 SetupDialog(); 66 private: 67 // Window Message Handlers 68 LRESULT OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, 69 BOOL& handled); 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); 76 77 void PostUITask(const base::Closure& task); 78 void PostIOTask(const base::Closure& task); 79 80 // UI Calls. 81 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; 97 } 98 99 // IO Calls. 100 // Installs service. 101 void Install(const base::string16& user, const base::string16& password, 102 bool enable_logging); 103 // Starts service. 104 void Start(); 105 // Stops service. 106 void Stop(); 107 // Uninstall service. 108 void Uninstall(); 109 // Update service state. 110 void UpdateState(); 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); 117 118 ServiceController::State state_; 119 base::Thread worker_; 120 121 base::MessageLoop* ui_loop_; 122 base::MessageLoop* io_loop_; 123 124 ServiceController controller_; 125 }; 126 127 SetupDialog::SetupDialog() 128 : state_(ServiceController::STATE_NOT_FOUND), 129 worker_("worker") { 130 ui_loop_ = base::MessageLoop::current(); 131 DCHECK(base::MessageLoopForUI::IsCurrent()); 132 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)); 137 } 138 139 void SetupDialog::PostUITask(const base::Closure& task) { 140 ui_loop_->PostTask(FROM_HERE, task); 141 } 142 143 void SetupDialog::PostIOTask(const base::Closure& task) { 144 io_loop_->PostTask(FROM_HERE, task); 145 } 146 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); 152 } 153 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(), 158 MB_OK); 159 } 160 161 void SetupDialog::SetState(ServiceController::State status, 162 const base::string16& user, 163 bool is_logging_enabled) { 164 DCHECK(base::MessageLoopForUI::IsCurrent()); 165 state_ = status; 166 167 DWORD status_string = 0; 168 switch(status) { 169 case ServiceController::STATE_NOT_FOUND: 170 status_string = IDS_SERVICE_NOT_FOUND; 171 break; 172 case ServiceController::STATE_STOPPED: 173 status_string = IDS_SERVICE_STOPPED; 174 break; 175 case ServiceController::STATE_RUNNING: 176 status_string = IDS_SERVICE_RUNNING; 177 break; 178 } 179 SetDlgItemText(IDC_STATUS, 180 status_string ? LoadLocalString(status_string).c_str() : L""); 181 if (IsInstalled()) { 182 SetDlgItemText(IDC_USER, user.c_str()); 183 CheckDlgButton(IDC_LOGGING, 184 is_logging_enabled ? BST_CHECKED : BST_UNCHECKED); 185 } 186 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); 193 194 ATL::CWindow install_button = GetDlgItem(IDC_INSTALL); 195 DWORD install_string = IsInstalled() ? IDS_SERVICE_UNINSTALL : 196 IDS_SERVICE_INSTALL; 197 install_button.SetWindowText(LoadLocalString(install_string).c_str()); 198 install_button.ShowWindow(SW_SHOW); 199 install_button.EnableWindow(TRUE); 200 201 if (!IsInstalled()) { 202 GetDlgItem(IDC_USER).EnableWindow(TRUE); 203 GetDlgItem(IDC_PASSWORD).EnableWindow(TRUE); 204 GetDlgItem(IDC_LOGGING).EnableWindow(TRUE); 205 } 206 } 207 208 LRESULT SetupDialog::OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, 209 BOOL& handled) { 210 ATLVERIFY(CenterWindow()); 211 212 WTL::CIcon icon; 213 if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON))) { 214 SetIcon(icon); 215 } 216 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()); 224 225 SetState(ServiceController::STATE_UNKNOWN, L"", false); 226 DisableControls(); 227 228 SetDlgItemText(IDC_USER, GetCurrentUserName().c_str()); 229 230 PostIOTask(base::Bind(&SetupDialog::UpdateState, this)); 231 232 return 0; 233 } 234 235 LRESULT SetupDialog::OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, 236 BOOL& handled) { 237 HWND window = reinterpret_cast<HWND>(lparam); 238 if (GetDlgItem(IDC_LOGO).m_hWnd == window) { 239 return reinterpret_cast<LRESULT>(::GetStockObject(WHITE_BRUSH)); 240 } 241 return 0; 242 } 243 244 LRESULT SetupDialog::OnStart(UINT, INT nIdentifier, HWND, BOOL& handled) { 245 DisableControls(); 246 DCHECK(IsInstalled()); 247 if (state_ == ServiceController::STATE_RUNNING) 248 PostIOTask(base::Bind(&SetupDialog::Stop, this)); 249 else 250 PostIOTask(base::Bind(&SetupDialog::Start, this)); 251 return 0; 252 } 253 254 LRESULT SetupDialog::OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled) { 255 DisableControls(); 256 if (IsInstalled()) { 257 PostIOTask(base::Bind(&SetupDialog::Uninstall, this)); 258 } else { 259 PostIOTask(base::Bind(&SetupDialog::Install, this, GetUser(), 260 GetPassword(), IsLoggingEnabled())); 261 } 262 return 0; 263 } 264 265 LRESULT SetupDialog::OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled) { 266 CheckDlgButton(IDC_LOGGING, IsLoggingEnabled()? BST_UNCHECKED : BST_CHECKED); 267 return 0; 268 } 269 270 LRESULT SetupDialog::OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled) { 271 DestroyWindow(); 272 return 0; 273 } 274 275 LRESULT SetupDialog::OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, 276 BOOL& handled) { 277 base::MessageLoop::current()->PostTask(FROM_HERE, 278 base::MessageLoop::QuitClosure()); 279 return 1; 280 } 281 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); 288 } 289 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())); 295 return result; 296 } 297 298 base::string16 SetupDialog::GetUser() const { 299 return GetDlgItemText(IDC_USER); 300 } 301 302 base::string16 SetupDialog::GetPassword() const { 303 return GetDlgItemText(IDC_PASSWORD); 304 } 305 306 bool SetupDialog::IsLoggingEnabled() const{ 307 return IsDlgButtonChecked(IDC_LOGGING) == BST_CHECKED; 308 } 309 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())); 315 } 316 317 void SetupDialog::ShowError(const base::string16& error_message) { 318 DCHECK(base::MessageLoopForIO::IsCurrent()); 319 PostUITask(base::Bind(&SetupDialog::SetState, 320 this, 321 ServiceController::STATE_UNKNOWN, 322 L"", 323 false)); 324 PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox, this, 325 error_message)); 326 LOG(ERROR) << error_message; 327 } 328 329 void SetupDialog::ShowError(int string_id) { 330 ShowError(cloud_print::LoadLocalString(string_id)); 331 } 332 333 void SetupDialog::ShowError(HRESULT hr) { 334 ShowError(GetErrorMessage(hr)); 335 } 336 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)); 343 344 DCHECK(base::MessageLoopForIO::IsCurrent()); 345 346 SetupListener setup(GetUser()); 347 HRESULT hr = controller_.InstallCheckService(user, password, 348 base::FilePath()); 349 if (FAILED(hr)) 350 return ShowError(hr); 351 352 { 353 // Always uninstall service after requirements check. 354 base::ScopedClosureRunner scoped_uninstall( 355 base::Bind(base::IgnoreResult(&ServiceController::UninstallService), 356 base::Unretained(&controller_))); 357 358 hr = controller_.StartService(); 359 if (FAILED(hr)) 360 return ShowError(hr); 361 362 if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) 363 return ShowError(IDS_ERROR_FAILED_START_SERVICE); 364 } 365 366 if (setup.user_data_dir().empty()) 367 return ShowError(IDS_ERROR_NO_DATA_DIR); 368 369 if (setup.chrome_path().empty()) 370 return ShowError(IDS_ERROR_NO_CHROME); 371 372 if (!setup.is_xps_available()) 373 return ShowError(IDS_ERROR_NO_XPS); 374 375 base::FilePath file = setup.user_data_dir(); 376 file = file.Append(chrome::kServiceStateFileName); 377 378 std::string proxy_id; 379 std::string contents; 380 381 if (base::ReadFileToString(file, &contents)) { 382 ServiceState service_state; 383 if (service_state.FromString(contents)) 384 proxy_id = service_state.proxy_id(); 385 } 386 PostUITask(base::Bind(&SetupDialog::AskToCloseChrome, this)); 387 contents = ChromeLauncher::CreateServiceStateFile(proxy_id, setup.printers()); 388 389 if (contents.empty()) 390 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); 391 392 size_t written = base::WriteFile(file, contents.c_str(), 393 contents.size()); 394 if (written != contents.size()) { 395 DWORD last_error = GetLastError(); 396 if (!last_error) 397 return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); 398 return ShowError(HRESULT_FROM_WIN32(last_error)); 399 } 400 401 hr = controller_.InstallConnectorService(user, password, base::FilePath(), 402 enable_logging); 403 if (FAILED(hr)) 404 return ShowError(hr); 405 406 hr = controller_.StartService(); 407 if (FAILED(hr)) 408 return ShowError(hr); 409 } 410 411 void SetupDialog::Start() { 412 DCHECK(base::MessageLoopForIO::IsCurrent()); 413 HRESULT hr = controller_.StartService(); 414 if (FAILED(hr)) 415 ShowError(hr); 416 UpdateState(); 417 } 418 419 void SetupDialog::Stop() { 420 DCHECK(base::MessageLoopForIO::IsCurrent()); 421 HRESULT hr = controller_.StopService(); 422 if (FAILED(hr)) 423 ShowError(hr); 424 UpdateState(); 425 } 426 427 void SetupDialog::Uninstall() { 428 DCHECK(base::MessageLoopForIO::IsCurrent()); 429 HRESULT hr = controller_.UninstallService(); 430 if (FAILED(hr)) 431 ShowError(hr); 432 UpdateState(); 433 } 434 435 class CloudPrintServiceConfigModule 436 : public ATL::CAtlExeModuleT<CloudPrintServiceConfigModule> { 437 }; 438 439 CloudPrintServiceConfigModule _AtlModule; 440 441 int WINAPI WinMain(__in HINSTANCE hInstance, 442 __in HINSTANCE hPrevInstance, 443 __in LPSTR lpCmdLine, 444 __in int nCmdShow) { 445 base::AtExitManager at_exit; 446 CommandLine::Init(0, NULL); 447 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); 454 run_loop.Run(); 455 return 0; 456 } 457