| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <atlbase.h> |
| #include <atlapp.h> // NOLINT |
| |
| #include "base/at_exit.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_pump_dispatcher.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string16.h" |
| #include "base/threading/thread.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "cloud_print/common/win/cloud_print_utils.h" |
| #include "cloud_print/resources.h" |
| #include "cloud_print/service/service_state.h" |
| #include "cloud_print/service/win/chrome_launcher.h" |
| #include "cloud_print/service/win/service_controller.h" |
| #include "cloud_print/service/win/service_utils.h" |
| #include "cloud_print/service/win/setup_listener.h" |
| |
| using cloud_print::LoadLocalString; |
| using cloud_print::GetErrorMessage; |
| |
| class SetupDialog : public base::RefCounted<SetupDialog>, |
| public ATL::CDialogImpl<SetupDialog> { |
| public: |
| // Enables accelerators. |
| class Dispatcher : public base::MessagePumpDispatcher { |
| public: |
| explicit Dispatcher(SetupDialog* dialog) : dialog_(dialog) {} |
| virtual ~Dispatcher() {}; |
| |
| // MessagePumpDispatcher: |
| virtual uint32_t Dispatch(const MSG& msg) OVERRIDE { |
| MSG msg2 = msg; |
| uint32_t action = POST_DISPATCH_NONE; |
| if (!dialog_->IsDialogMessage(&msg2)) |
| action = POST_DISPATCH_PERFORM_DEFAULT; |
| return action; |
| } |
| |
| private: |
| scoped_refptr<SetupDialog> dialog_; |
| }; |
| |
| typedef ATL::CDialogImpl<SetupDialog> Base; |
| enum { IDD = IDD_SETUP_DIALOG }; |
| |
| BEGIN_MSG_MAP(SetupDialog) |
| MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) |
| MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnCtrColor) |
| MESSAGE_HANDLER(WM_DESTROY, OnDestroy) |
| COMMAND_ID_HANDLER(IDCANCEL, OnCancel) |
| COMMAND_ID_HANDLER(IDC_START, OnStart) |
| COMMAND_ID_HANDLER(IDC_INSTALL, OnInstall) |
| COMMAND_ID_HANDLER(IDC_LOGGING, OnLogging) |
| END_MSG_MAP() |
| |
| SetupDialog(); |
| private: |
| // Window Message Handlers |
| LRESULT OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, |
| BOOL& handled); |
| LRESULT OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); |
| LRESULT OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled); |
| LRESULT OnStart(UINT, INT nIdentifier, HWND, BOOL& handled); |
| LRESULT OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled); |
| LRESULT OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled); |
| LRESULT OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); |
| |
| void PostUITask(const base::Closure& task); |
| void PostIOTask(const base::Closure& task); |
| |
| // UI Calls. |
| |
| // Disables all controls after users actions. |
| void DisableControls(); |
| // Updates state of controls after when we received service status. |
| void SetState(ServiceController::State state, const base::string16& user, |
| bool is_logging_enabled); |
| // Show message box with error. |
| void ShowErrorMessageBox(const base::string16& error_message); |
| // Show use message box instructions how to deal with opened Chrome window. |
| void AskToCloseChrome(); |
| base::string16 GetDlgItemText(int id) const; |
| base::string16 GetUser() const; |
| base::string16 GetPassword() const; |
| bool IsLoggingEnabled() const; |
| bool IsInstalled() const { |
| return state_ > ServiceController::STATE_NOT_FOUND; |
| } |
| |
| // IO Calls. |
| // Installs service. |
| void Install(const base::string16& user, const base::string16& password, |
| bool enable_logging); |
| // Starts service. |
| void Start(); |
| // Stops service. |
| void Stop(); |
| // Uninstall service. |
| void Uninstall(); |
| // Update service state. |
| void UpdateState(); |
| // Posts task to UI thread to show error using string id. |
| void ShowError(int string_id); |
| // Posts task to UI thread to show error using string. |
| void ShowError(const base::string16& error_message); |
| // Posts task to UI thread to show error using error code. |
| void ShowError(HRESULT hr); |
| |
| ServiceController::State state_; |
| base::Thread worker_; |
| |
| base::MessageLoop* ui_loop_; |
| base::MessageLoop* io_loop_; |
| |
| ServiceController controller_; |
| }; |
| |
| SetupDialog::SetupDialog() |
| : state_(ServiceController::STATE_NOT_FOUND), |
| worker_("worker") { |
| ui_loop_ = base::MessageLoop::current(); |
| DCHECK(base::MessageLoopForUI::IsCurrent()); |
| |
| worker_.StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
| io_loop_ = worker_.message_loop(); |
| DCHECK(io_loop_->IsType(base::MessageLoop::TYPE_IO)); |
| } |
| |
| void SetupDialog::PostUITask(const base::Closure& task) { |
| ui_loop_->PostTask(FROM_HERE, task); |
| } |
| |
| void SetupDialog::PostIOTask(const base::Closure& task) { |
| io_loop_->PostTask(FROM_HERE, task); |
| } |
| |
| void SetupDialog::ShowErrorMessageBox(const base::string16& error_message) { |
| DCHECK(base::MessageLoopForUI::IsCurrent()); |
| MessageBox(error_message.c_str(), |
| LoadLocalString(IDS_OPERATION_FAILED_TITLE).c_str(), |
| MB_ICONERROR | MB_OK); |
| } |
| |
| void SetupDialog::AskToCloseChrome() { |
| DCHECK(base::MessageLoopForUI::IsCurrent()); |
| MessageBox(LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME).c_str(), |
| LoadLocalString(IDS_CONTINUE_IN_CHROME_TITLE).c_str(), |
| MB_OK); |
| } |
| |
| void SetupDialog::SetState(ServiceController::State status, |
| const base::string16& user, |
| bool is_logging_enabled) { |
| DCHECK(base::MessageLoopForUI::IsCurrent()); |
| state_ = status; |
| |
| DWORD status_string = 0; |
| switch(status) { |
| case ServiceController::STATE_NOT_FOUND: |
| status_string = IDS_SERVICE_NOT_FOUND; |
| break; |
| case ServiceController::STATE_STOPPED: |
| status_string = IDS_SERVICE_STOPPED; |
| break; |
| case ServiceController::STATE_RUNNING: |
| status_string = IDS_SERVICE_RUNNING; |
| break; |
| } |
| SetDlgItemText(IDC_STATUS, |
| status_string ? LoadLocalString(status_string).c_str() : L""); |
| if (IsInstalled()) { |
| SetDlgItemText(IDC_USER, user.c_str()); |
| CheckDlgButton(IDC_LOGGING, |
| is_logging_enabled ? BST_CHECKED : BST_UNCHECKED); |
| } |
| |
| ATL::CWindow start_button = GetDlgItem(IDC_START); |
| DWORD start_string = (status == ServiceController::STATE_STOPPED) ? |
| IDS_SERVICE_START : IDS_SERVICE_STOP; |
| start_button.SetWindowText(LoadLocalString(start_string).c_str()); |
| start_button.ShowWindow(IsInstalled() ? SW_SHOW : SW_HIDE); |
| start_button.EnableWindow(TRUE); |
| |
| ATL::CWindow install_button = GetDlgItem(IDC_INSTALL); |
| DWORD install_string = IsInstalled() ? IDS_SERVICE_UNINSTALL : |
| IDS_SERVICE_INSTALL; |
| install_button.SetWindowText(LoadLocalString(install_string).c_str()); |
| install_button.ShowWindow(SW_SHOW); |
| install_button.EnableWindow(TRUE); |
| |
| if (!IsInstalled()) { |
| GetDlgItem(IDC_USER).EnableWindow(TRUE); |
| GetDlgItem(IDC_PASSWORD).EnableWindow(TRUE); |
| GetDlgItem(IDC_LOGGING).EnableWindow(TRUE); |
| } |
| } |
| |
| LRESULT SetupDialog::OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, |
| BOOL& handled) { |
| ATLVERIFY(CenterWindow()); |
| |
| WTL::CIcon icon; |
| if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON))) { |
| SetIcon(icon); |
| } |
| |
| SetWindowText(LoadLocalString(IDS_SETUP_PROGRAM_NAME).c_str()); |
| SetDlgItemText(IDC_STATE_LABEL, LoadLocalString(IDS_STATE_LABEL).c_str()); |
| SetDlgItemText(IDC_USER_LABEL, LoadLocalString(IDS_USER_LABEL).c_str()); |
| SetDlgItemText(IDC_PASSWORD_LABEL, |
| LoadLocalString(IDS_PASSWORD_LABEL).c_str()); |
| SetDlgItemText(IDC_LOGGING, LoadLocalString(IDS_LOGGING_LABEL).c_str()); |
| SetDlgItemText(IDCANCEL, LoadLocalString(IDS_CLOSE).c_str()); |
| |
| SetState(ServiceController::STATE_UNKNOWN, L"", false); |
| DisableControls(); |
| |
| SetDlgItemText(IDC_USER, GetCurrentUserName().c_str()); |
| |
| PostIOTask(base::Bind(&SetupDialog::UpdateState, this)); |
| |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, |
| BOOL& handled) { |
| HWND window = reinterpret_cast<HWND>(lparam); |
| if (GetDlgItem(IDC_LOGO).m_hWnd == window) { |
| return reinterpret_cast<LRESULT>(::GetStockObject(WHITE_BRUSH)); |
| } |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnStart(UINT, INT nIdentifier, HWND, BOOL& handled) { |
| DisableControls(); |
| DCHECK(IsInstalled()); |
| if (state_ == ServiceController::STATE_RUNNING) |
| PostIOTask(base::Bind(&SetupDialog::Stop, this)); |
| else |
| PostIOTask(base::Bind(&SetupDialog::Start, this)); |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled) { |
| DisableControls(); |
| if (IsInstalled()) { |
| PostIOTask(base::Bind(&SetupDialog::Uninstall, this)); |
| } else { |
| PostIOTask(base::Bind(&SetupDialog::Install, this, GetUser(), |
| GetPassword(), IsLoggingEnabled())); |
| } |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled) { |
| CheckDlgButton(IDC_LOGGING, IsLoggingEnabled()? BST_UNCHECKED : BST_CHECKED); |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled) { |
| DestroyWindow(); |
| return 0; |
| } |
| |
| LRESULT SetupDialog::OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, |
| BOOL& handled) { |
| base::MessageLoop::current()->PostTask(FROM_HERE, |
| base::MessageLoop::QuitClosure()); |
| return 1; |
| } |
| |
| void SetupDialog::DisableControls() { |
| GetDlgItem(IDC_START).EnableWindow(FALSE); |
| GetDlgItem(IDC_INSTALL).EnableWindow(FALSE); |
| GetDlgItem(IDC_USER).EnableWindow(FALSE); |
| GetDlgItem(IDC_PASSWORD).EnableWindow(FALSE); |
| GetDlgItem(IDC_LOGGING).EnableWindow(FALSE); |
| } |
| |
| base::string16 SetupDialog::GetDlgItemText(int id) const { |
| const ATL::CWindow& item = GetDlgItem(id); |
| size_t length = item.GetWindowTextLength(); |
| base::string16 result(length + 1, L'\0'); |
| result.resize(item.GetWindowText(&result[0], result.size())); |
| return result; |
| } |
| |
| base::string16 SetupDialog::GetUser() const { |
| return GetDlgItemText(IDC_USER); |
| } |
| |
| base::string16 SetupDialog::GetPassword() const { |
| return GetDlgItemText(IDC_PASSWORD); |
| } |
| |
| bool SetupDialog::IsLoggingEnabled() const{ |
| return IsDlgButtonChecked(IDC_LOGGING) == BST_CHECKED; |
| } |
| |
| void SetupDialog::UpdateState() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| controller_.UpdateState(); |
| PostUITask(base::Bind(&SetupDialog::SetState, this, controller_.state(), |
| controller_.user(), controller_.is_logging_enabled())); |
| } |
| |
| void SetupDialog::ShowError(const base::string16& error_message) { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| PostUITask(base::Bind(&SetupDialog::SetState, |
| this, |
| ServiceController::STATE_UNKNOWN, |
| L"", |
| false)); |
| PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox, this, |
| error_message)); |
| LOG(ERROR) << error_message; |
| } |
| |
| void SetupDialog::ShowError(int string_id) { |
| ShowError(cloud_print::LoadLocalString(string_id)); |
| } |
| |
| void SetupDialog::ShowError(HRESULT hr) { |
| ShowError(GetErrorMessage(hr)); |
| } |
| |
| void SetupDialog::Install(const base::string16& user, |
| const base::string16& password, |
| bool enable_logging) { |
| // Don't forget to update state on exit. |
| base::ScopedClosureRunner scoped_update_status( |
| base::Bind(&SetupDialog::UpdateState, this)); |
| |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| |
| SetupListener setup(GetUser()); |
| HRESULT hr = controller_.InstallCheckService(user, password, |
| base::FilePath()); |
| if (FAILED(hr)) |
| return ShowError(hr); |
| |
| { |
| // Always uninstall service after requirements check. |
| base::ScopedClosureRunner scoped_uninstall( |
| base::Bind(base::IgnoreResult(&ServiceController::UninstallService), |
| base::Unretained(&controller_))); |
| |
| hr = controller_.StartService(); |
| if (FAILED(hr)) |
| return ShowError(hr); |
| |
| if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) |
| return ShowError(IDS_ERROR_FAILED_START_SERVICE); |
| } |
| |
| if (setup.user_data_dir().empty()) |
| return ShowError(IDS_ERROR_NO_DATA_DIR); |
| |
| if (setup.chrome_path().empty()) |
| return ShowError(IDS_ERROR_NO_CHROME); |
| |
| if (!setup.is_xps_available()) |
| return ShowError(IDS_ERROR_NO_XPS); |
| |
| base::FilePath file = setup.user_data_dir(); |
| file = file.Append(chrome::kServiceStateFileName); |
| |
| std::string proxy_id; |
| std::string contents; |
| |
| if (base::ReadFileToString(file, &contents)) { |
| ServiceState service_state; |
| if (service_state.FromString(contents)) |
| proxy_id = service_state.proxy_id(); |
| } |
| PostUITask(base::Bind(&SetupDialog::AskToCloseChrome, this)); |
| contents = ChromeLauncher::CreateServiceStateFile(proxy_id, setup.printers()); |
| |
| if (contents.empty()) |
| return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); |
| |
| size_t written = base::WriteFile(file, contents.c_str(), |
| contents.size()); |
| if (written != contents.size()) { |
| DWORD last_error = GetLastError(); |
| if (!last_error) |
| return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); |
| return ShowError(HRESULT_FROM_WIN32(last_error)); |
| } |
| |
| hr = controller_.InstallConnectorService(user, password, base::FilePath(), |
| enable_logging); |
| if (FAILED(hr)) |
| return ShowError(hr); |
| |
| hr = controller_.StartService(); |
| if (FAILED(hr)) |
| return ShowError(hr); |
| } |
| |
| void SetupDialog::Start() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| HRESULT hr = controller_.StartService(); |
| if (FAILED(hr)) |
| ShowError(hr); |
| UpdateState(); |
| } |
| |
| void SetupDialog::Stop() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| HRESULT hr = controller_.StopService(); |
| if (FAILED(hr)) |
| ShowError(hr); |
| UpdateState(); |
| } |
| |
| void SetupDialog::Uninstall() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| HRESULT hr = controller_.UninstallService(); |
| if (FAILED(hr)) |
| ShowError(hr); |
| UpdateState(); |
| } |
| |
| class CloudPrintServiceConfigModule |
| : public ATL::CAtlExeModuleT<CloudPrintServiceConfigModule> { |
| }; |
| |
| CloudPrintServiceConfigModule _AtlModule; |
| |
| int WINAPI WinMain(__in HINSTANCE hInstance, |
| __in HINSTANCE hPrevInstance, |
| __in LPSTR lpCmdLine, |
| __in int nCmdShow) { |
| base::AtExitManager at_exit; |
| CommandLine::Init(0, NULL); |
| |
| base::MessageLoopForUI loop; |
| scoped_refptr<SetupDialog> dialog(new SetupDialog()); |
| dialog->Create(NULL); |
| dialog->ShowWindow(SW_SHOW); |
| SetupDialog::Dispatcher dispatcher(dialog); |
| base::RunLoop run_loop(&dispatcher); |
| run_loop.Run(); |
| return 0; |
| } |