Home | History | Annotate | Download | only in win
      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/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"
     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