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/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