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