Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2012 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 <security.h>
      7 
      8 #include <iomanip>
      9 #include <iostream>
     10 
     11 #include "base/at_exit.h"
     12 #include "base/bind.h"
     13 #include "base/command_line.h"
     14 #include "base/file_util.h"
     15 #include "base/guid.h"
     16 #include "base/logging.h"
     17 #include "base/path_service.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/win/scoped_handle.h"
     21 #include "chrome/common/chrome_constants.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "cloud_print/common/win/cloud_print_utils.h"
     24 #include "cloud_print/service/service_constants.h"
     25 #include "cloud_print/service/service_state.h"
     26 #include "cloud_print/service/service_switches.h"
     27 #include "cloud_print/service/win/chrome_launcher.h"
     28 #include "cloud_print/service/win/service_controller.h"
     29 #include "cloud_print/service/win/service_listener.h"
     30 #include "cloud_print/service/win/service_utils.h"
     31 #include "cloud_print/service/win/setup_listener.h"
     32 
     33 namespace {
     34 
     35 void InvalidUsage() {
     36   base::FilePath service_path;
     37   CHECK(PathService::Get(base::FILE_EXE, &service_path));
     38 
     39   std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE);
     40   std::cout << " " << service_path.BaseName().value();
     41   std::cout << " [";
     42     std::cout << "[";
     43       std::cout << "[";
     44         std::cout << " -" << kInstallSwitch;
     45         std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]";
     46       std::cout << "]";
     47     std::cout << "]";
     48     std::cout << " | -" << kUninstallSwitch;
     49     std::cout << " | -" << kStartSwitch;
     50     std::cout << " | -" << kStopSwitch;
     51   std::cout << " ]\n";
     52   std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION);
     53   std::cout << "\n\n";
     54 
     55   struct {
     56     const char* name;
     57     int description;
     58   } kSwitchHelp[] = {{
     59     kInstallSwitch, IDS_SWITCH_HELP_INSTALL
     60   }, {
     61     switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR
     62   }, {
     63     kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL
     64   }, {
     65     kStartSwitch, IDS_SWITCH_HELP_START
     66   }, {
     67     kStopSwitch, IDS_SWITCH_HELP_STOP
     68   }};
     69 
     70   for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) {
     71     std::cout << std::setiosflags(std::ios::left);
     72     std::cout << "  -" << std::setw(16) << kSwitchHelp[i].name;
     73     std::cout << cloud_print::LoadLocalString(kSwitchHelp[i].description);
     74     std::cout << "\n";
     75   }
     76   std::cout << "\n";
     77 }
     78 
     79 string16 GetOption(int string_id, const string16& default,
     80                    bool secure) {
     81   string16 prompt_format = cloud_print::LoadLocalString(string_id) ;
     82   std::vector<string16> substitutions(1, default);
     83   std::cout << ReplaceStringPlaceholders(prompt_format, substitutions, NULL);
     84   string16 tmp;
     85   if (secure) {
     86     DWORD saved_mode = 0;
     87     // Don't close.
     88     HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE);
     89     ::GetConsoleMode(stdin_handle, &saved_mode);
     90     ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT);
     91     std::getline(std::wcin, tmp);
     92     ::SetConsoleMode(stdin_handle, saved_mode);
     93     std::cout << "\n";
     94   } else {
     95     std::getline(std::wcin, tmp);
     96   }
     97   if (tmp.empty())
     98     return default;
     99   return tmp;
    100 }
    101 
    102 HRESULT ReportError(HRESULT hr, int string_id) {
    103   LOG(ERROR) << cloud_print::GetErrorMessage(hr);
    104   std::cerr << cloud_print::LoadLocalString(string_id);
    105   std::cerr << "\n";
    106   return hr;
    107 }
    108 
    109 string16 StateAsString(ServiceController::State state) {
    110   DWORD string_id = 0;
    111   switch(state) {
    112   case ServiceController::STATE_NOT_FOUND:
    113     string_id = IDS_SERVICE_NOT_FOUND;
    114     break;
    115   case ServiceController::STATE_STOPPED:
    116     string_id = IDS_SERVICE_STOPPED;
    117     break;
    118   case ServiceController::STATE_RUNNING:
    119     string_id = IDS_SERVICE_RUNNING;
    120     break;
    121   }
    122   return string_id ? cloud_print::LoadLocalString(string_id) : string16();
    123 }
    124 
    125 }  // namespace
    126 
    127 
    128 class CloudPrintServiceModule
    129     : public ATL::CAtlServiceModuleT<CloudPrintServiceModule,
    130                                      IDS_SERVICE_NAME> {
    131  public:
    132   typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule,
    133                                   IDS_SERVICE_NAME> Base;
    134 
    135   CloudPrintServiceModule()
    136       : check_requirements_(false),
    137         controller_(new ServiceController()) {
    138   }
    139 
    140   static wchar_t* GetAppIdT() {
    141     return ServiceController::GetAppIdT();
    142   };
    143 
    144   HRESULT InitializeSecurity() {
    145     // TODO(gene): Check if we need to call CoInitializeSecurity and provide
    146     // the appropriate security settings for service.
    147     return S_OK;
    148   }
    149 
    150   bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
    151     CHECK(pnRetCode);
    152     CommandLine command_line(CommandLine::NO_PROGRAM);
    153     command_line.ParseFromString(lpCmdLine);
    154 
    155     LOG(INFO) << command_line.GetCommandLineString();
    156 
    157     bool is_service = false;
    158     *pnRetCode = ParseCommandLine(command_line, &is_service);
    159     if (FAILED(*pnRetCode)) {
    160       ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE);
    161     }
    162     if (!is_service) {
    163       controller_->UpdateState();
    164       std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL);
    165       std::cout << " " << StateAsString(controller_->state());
    166     }
    167     return is_service;
    168   }
    169 
    170   HRESULT PreMessageLoop(int nShowCmd) {
    171     HRESULT hr = Base::PreMessageLoop(nShowCmd);
    172     if (FAILED(hr))
    173       return hr;
    174 
    175     if (check_requirements_) {
    176       CheckRequirements();
    177     } else {
    178       HRESULT hr = StartConnector();
    179       if (FAILED(hr))
    180         return hr;
    181     }
    182 
    183     LogEvent(_T("Service started/resumed"));
    184     SetServiceStatus(SERVICE_RUNNING);
    185 
    186     return hr;
    187   }
    188 
    189   HRESULT PostMessageLoop() {
    190     StopConnector();
    191     setup_listener_.reset();
    192     return Base::PostMessageLoop();
    193   }
    194 
    195  private:
    196   HRESULT ParseCommandLine(const CommandLine& command_line, bool* is_service) {
    197     if (!is_service)
    198       return E_INVALIDARG;
    199     *is_service = false;
    200 
    201     user_data_dir_switch_ =
    202         command_line.GetSwitchValuePath(switches::kUserDataDir);
    203     if (!user_data_dir_switch_.empty())
    204       user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_);
    205 
    206     if (command_line.HasSwitch(kStopSwitch))
    207       return controller_->StopService();
    208 
    209     if (command_line.HasSwitch(kUninstallSwitch))
    210       return controller_->UninstallService();
    211 
    212     if (command_line.HasSwitch(kInstallSwitch)) {
    213       string16 run_as_user;
    214       string16 run_as_password;
    215       base::FilePath user_data_dir;
    216       std::vector<std::string> printers;
    217       HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password,
    218                                         &user_data_dir, &printers);
    219       if (FAILED(hr))
    220         return hr;
    221 
    222       DCHECK(user_data_dir_switch_.empty() ||
    223              user_data_dir_switch_ == user_data_dir);
    224 
    225       hr = SetupServiceState(user_data_dir, printers);
    226       if (FAILED(hr))
    227         return hr;
    228 
    229       hr = controller_->InstallConnectorService(
    230           run_as_user, run_as_password, user_data_dir_switch_,
    231           command_line.HasSwitch(switches::kEnableLogging));
    232       if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
    233         return controller_->StartService();
    234 
    235       return hr;
    236     }
    237 
    238     if (command_line.HasSwitch(kStartSwitch))
    239       return controller_->StartService();
    240 
    241     if (command_line.HasSwitch(kConsoleSwitch)) {
    242       check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
    243       ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
    244       HRESULT hr = Run();
    245       ::SetConsoleCtrlHandler(NULL, FALSE);
    246       return hr;
    247     }
    248 
    249     if (command_line.HasSwitch(kServiceSwitch) ||
    250         command_line.HasSwitch(kRequirementsSwitch)) {
    251       *is_service = true;
    252       check_requirements_ = command_line.HasSwitch(kRequirementsSwitch);
    253       return S_OK;
    254     }
    255 
    256 
    257     InvalidUsage();
    258     return S_FALSE;
    259   }
    260 
    261   HRESULT SelectWindowsAccount(string16* run_as_user, string16* run_as_password,
    262                                base::FilePath* user_data_dir,
    263                                std::vector<std::string>* printers) {
    264     *run_as_user = GetCurrentUserName();
    265     std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n";
    266     *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false);
    267     *run_as_user = ReplaceLocalHostInName(*run_as_user);
    268     *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true);
    269     SetupListener setup(*run_as_user);
    270     HRESULT hr = controller_->InstallCheckService(*run_as_user,
    271                                                   *run_as_password,
    272                                                   user_data_dir_switch_);
    273     if (FAILED(hr)) {
    274       return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE);
    275     }
    276 
    277     {
    278       // Always uninstall service after requirements check.
    279       base::ScopedClosureRunner scoped_uninstall(
    280           base::Bind(base::IgnoreResult(&ServiceController::UninstallService),
    281                      base::Unretained(controller_.get())));
    282 
    283       hr = controller_->StartService();
    284       if (FAILED(hr)) {
    285         return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE);
    286       }
    287 
    288       if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) {
    289         return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE);
    290       }
    291     }
    292 
    293     if (setup.user_data_dir().empty()) {
    294       return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR);
    295     }
    296 
    297     if (setup.chrome_path().empty()) {
    298       return ReportError(E_FAIL, IDS_ERROR_NO_CHROME);
    299     }
    300 
    301     if (!setup.is_xps_available()) {
    302       return ReportError(E_FAIL, IDS_ERROR_NO_XPS);
    303     }
    304 
    305     std::cout << "\n";
    306     std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK);
    307     std::cout << "\n";
    308     std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER);
    309     std::cout << "\n  " << setup.user_name() << "\n";
    310     std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME);
    311     std::cout << "\n  " << setup.chrome_path().value() << "\n";
    312     std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR);
    313     std::cout << "\n  " << setup.user_data_dir().value() << "\n";
    314     std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS);
    315     std::cout << "\n  ";
    316     std::ostream_iterator<std::string> cout_it(std::cout, "\n  ");
    317     std::copy(setup.printers().begin(), setup.printers().end(), cout_it);
    318     std::cout << "\n";
    319 
    320     *user_data_dir = setup.user_data_dir();
    321     *printers = setup.printers();
    322     return S_OK;
    323   }
    324 
    325   HRESULT SetupServiceState(const base::FilePath& user_data_dir,
    326                             const std::vector<std::string>& printers) {
    327     base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName);
    328 
    329     std::string contents;
    330     ServiceState service_state;
    331 
    332     bool is_valid = file_util::ReadFileToString(file, &contents) &&
    333                     service_state.FromString(contents);
    334     std::string proxy_id = service_state.proxy_id();
    335 
    336     LOG(INFO) << file.value() << ": " << contents;
    337 
    338     string16 message =
    339         cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME);
    340     std::cout << "\n" << message.c_str() << "\n" ;
    341     std::string new_contents =
    342         ChromeLauncher::CreateServiceStateFile(proxy_id, printers);
    343 
    344     if (new_contents.empty()) {
    345       return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG);
    346     }
    347 
    348     if (new_contents != contents) {
    349       size_t  written = file_util::WriteFile(file, new_contents.c_str(),
    350                                               new_contents.size());
    351       if (written != new_contents.size()) {
    352         return ReportError(cloud_print::GetLastHResult(),
    353                            IDS_ERROR_FAILED_CREATE_CONFIG);
    354       }
    355     }
    356 
    357     return S_OK;
    358   }
    359 
    360   void CheckRequirements() {
    361     setup_listener_.reset(new ServiceListener(GetUserDataDir()));
    362   }
    363 
    364   HRESULT StartConnector() {
    365     chrome_.reset(new ChromeLauncher(GetUserDataDir()));
    366     return chrome_->Start() ? S_OK : E_FAIL;
    367   }
    368 
    369   void StopConnector() {
    370     if (chrome_.get()) {
    371       chrome_->Stop();
    372       chrome_.reset();
    373     }
    374   }
    375 
    376   base::FilePath GetUserDataDir() const {
    377     if (!user_data_dir_switch_.empty())
    378       return user_data_dir_switch_;
    379     base::FilePath result;
    380     CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result));
    381     return result.Append(kSubDirectory);
    382   }
    383 
    384   static BOOL WINAPI ConsoleCtrlHandler(DWORD type);
    385 
    386   bool check_requirements_;
    387   base::FilePath user_data_dir_switch_;
    388   scoped_ptr<ChromeLauncher> chrome_;
    389   scoped_ptr<ServiceController> controller_;
    390   scoped_ptr<ServiceListener> setup_listener_;
    391 };
    392 
    393 CloudPrintServiceModule _AtlModule;
    394 
    395 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) {
    396   PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0);
    397   return TRUE;
    398 }
    399 
    400 int main(int argc, char** argv) {
    401   CommandLine::Init(argc, argv);
    402   base::AtExitManager at_exit;
    403 
    404   logging::LoggingSettings settings;
    405   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
    406   logging::InitLogging(settings);
    407 
    408   logging::SetMinLogLevel(
    409       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableLogging) ?
    410       logging::LOG_INFO : logging::LOG_FATAL);
    411 
    412   return _AtlModule.WinMain(0);
    413 }
    414 
    415