Home | History | Annotate | Download | only in cloud_print
      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 "chrome/service/cloud_print/cloud_print_proxy.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/path_service.h"
     11 #include "base/process/kill.h"
     12 #include "base/process/launch.h"
     13 #include "base/values.h"
     14 #include "chrome/common/chrome_switches.h"
     15 #include "chrome/common/cloud_print/cloud_print_constants.h"
     16 #include "chrome/common/cloud_print/cloud_print_proxy_info.h"
     17 #include "chrome/common/pref_names.h"
     18 #include "chrome/service/cloud_print/print_system.h"
     19 #include "chrome/service/service_process.h"
     20 #include "chrome/service/service_process_prefs.h"
     21 #include "google_apis/gaia/gaia_oauth_client.h"
     22 #include "google_apis/google_api_keys.h"
     23 #include "url/gurl.h"
     24 
     25 namespace {
     26 
     27 void LaunchBrowserProcessWithSwitch(const std::string& switch_string) {
     28   DCHECK(g_service_process->io_thread()->message_loop_proxy()->
     29       BelongsToCurrentThread());
     30   base::FilePath exe_path;
     31   PathService::Get(base::FILE_EXE, &exe_path);
     32   if (exe_path.empty()) {
     33     NOTREACHED() << "Unable to get browser process binary name.";
     34   }
     35   CommandLine cmd_line(exe_path);
     36 
     37   const CommandLine& process_command_line = *CommandLine::ForCurrentProcess();
     38   base::FilePath user_data_dir =
     39       process_command_line.GetSwitchValuePath(switches::kUserDataDir);
     40   if (!user_data_dir.empty())
     41     cmd_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir);
     42   cmd_line.AppendSwitch(switch_string);
     43 
     44 #if defined(OS_POSIX) && !defined(OS_MACOSX)
     45   base::ProcessHandle pid = 0;
     46   base::LaunchProcess(cmd_line, base::LaunchOptions(), &pid);
     47   base::EnsureProcessGetsReaped(pid);
     48 #else
     49   base::LaunchOptions launch_options;
     50 #if defined(OS_WIN)
     51   launch_options.force_breakaway_from_job_ = true;
     52 #endif  // OS_WIN
     53   base::LaunchProcess(cmd_line, launch_options, NULL);
     54 #endif
     55 }
     56 
     57 void CheckCloudPrintProxyPolicyInBrowser() {
     58   LaunchBrowserProcessWithSwitch(switches::kCheckCloudPrintConnectorPolicy);
     59 }
     60 
     61 }  // namespace
     62 
     63 namespace cloud_print {
     64 
     65 CloudPrintProxy::CloudPrintProxy()
     66     : service_prefs_(NULL),
     67       client_(NULL),
     68       enabled_(false) {
     69 }
     70 
     71 CloudPrintProxy::~CloudPrintProxy() {
     72   DCHECK(CalledOnValidThread());
     73   ShutdownBackend();
     74 }
     75 
     76 void CloudPrintProxy::Initialize(ServiceProcessPrefs* service_prefs,
     77                                  Client* client) {
     78   DCHECK(CalledOnValidThread());
     79   service_prefs_ = service_prefs;
     80   client_ = client;
     81 }
     82 
     83 void CloudPrintProxy::EnableForUser() {
     84   DCHECK(CalledOnValidThread());
     85   if (!CreateBackend())
     86     return;
     87   DCHECK(backend_.get());
     88   // Read persisted robot credentials because we may decide to reuse it if the
     89   // passed in LSID belongs the same user.
     90   std::string robot_refresh_token = service_prefs_->GetString(
     91       prefs::kCloudPrintRobotRefreshToken, std::string());
     92   std::string robot_email =
     93       service_prefs_->GetString(prefs::kCloudPrintRobotEmail, std::string());
     94   user_email_ = service_prefs_->GetString(prefs::kCloudPrintEmail, user_email_);
     95 
     96   // See if we have persisted robot credentials.
     97   if (!robot_refresh_token.empty()) {
     98     DCHECK(!robot_email.empty());
     99     backend_->InitializeWithRobotToken(robot_refresh_token, robot_email);
    100   } else {
    101     // Finally see if we have persisted user credentials (legacy case).
    102     std::string cloud_print_token =
    103         service_prefs_->GetString(prefs::kCloudPrintAuthToken, std::string());
    104     DCHECK(!cloud_print_token.empty());
    105     backend_->InitializeWithToken(cloud_print_token);
    106   }
    107   if (client_) {
    108     client_->OnCloudPrintProxyEnabled(true);
    109   }
    110 }
    111 
    112 void CloudPrintProxy::EnableForUserWithRobot(
    113     const std::string& robot_auth_code,
    114     const std::string& robot_email,
    115     const std::string& user_email,
    116     const base::DictionaryValue& user_settings) {
    117   DCHECK(CalledOnValidThread());
    118 
    119   ShutdownBackend();
    120   std::string proxy_id(
    121       service_prefs_->GetString(prefs::kCloudPrintProxyId, std::string()));
    122   service_prefs_->RemovePref(prefs::kCloudPrintRoot);
    123   if (!proxy_id.empty()) {
    124     // Keep only proxy id;
    125     service_prefs_->SetString(prefs::kCloudPrintProxyId, proxy_id);
    126   }
    127   service_prefs_->SetValue(prefs::kCloudPrintUserSettings,
    128                            user_settings.DeepCopy());
    129   service_prefs_->WritePrefs();
    130 
    131   if (!CreateBackend())
    132     return;
    133   DCHECK(backend_.get());
    134   user_email_ = user_email;
    135   backend_->InitializeWithRobotAuthCode(robot_auth_code, robot_email);
    136   if (client_) {
    137     client_->OnCloudPrintProxyEnabled(true);
    138   }
    139 }
    140 
    141 bool CloudPrintProxy::CreateBackend() {
    142   DCHECK(CalledOnValidThread());
    143   if (backend_.get())
    144     return false;
    145 
    146   ConnectorSettings settings;
    147   settings.InitFrom(service_prefs_);
    148 
    149   // By default we don't poll for jobs when we lose XMPP connection. But this
    150   // behavior can be overridden by a preference.
    151   bool enable_job_poll =
    152     service_prefs_->GetBoolean(prefs::kCloudPrintEnableJobPoll, false);
    153 
    154   gaia::OAuthClientInfo oauth_client_info;
    155   oauth_client_info.client_id =
    156     google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
    157   oauth_client_info.client_secret =
    158     google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
    159   oauth_client_info.redirect_uri = "oob";
    160   backend_.reset(new CloudPrintProxyBackend(
    161       this, settings, oauth_client_info, enable_job_poll));
    162   return true;
    163 }
    164 
    165 void CloudPrintProxy::UnregisterPrintersAndDisableForUser() {
    166   DCHECK(CalledOnValidThread());
    167   if (backend_.get()) {
    168     // Try getting auth and printers info from the backend.
    169     // We'll get notified in this case.
    170     backend_->UnregisterPrinters();
    171   } else {
    172     // If no backend avaialble, disable connector immidiately.
    173     DisableForUser();
    174   }
    175 }
    176 
    177 void CloudPrintProxy::DisableForUser() {
    178   DCHECK(CalledOnValidThread());
    179   user_email_.clear();
    180   enabled_ = false;
    181   if (client_) {
    182     client_->OnCloudPrintProxyDisabled(true);
    183   }
    184   ShutdownBackend();
    185 }
    186 
    187 void CloudPrintProxy::GetProxyInfo(CloudPrintProxyInfo* info) {
    188   info->enabled = enabled_;
    189   info->email.clear();
    190   if (enabled_)
    191     info->email = user_email();
    192   ConnectorSettings settings;
    193   settings.InitFrom(service_prefs_);
    194   info->proxy_id = settings.proxy_id();
    195 }
    196 
    197 void CloudPrintProxy::GetPrinters(std::vector<std::string>* printers) {
    198   ConnectorSettings settings;
    199   settings.InitFrom(service_prefs_);
    200   scoped_refptr<PrintSystem> print_system =
    201       PrintSystem::CreateInstance(settings.print_system_settings());
    202   if (!print_system)
    203     return;
    204   PrintSystem::PrintSystemResult result = print_system->Init();
    205   if (!result.succeeded())
    206     return;
    207   printing::PrinterList printer_list;
    208   print_system->EnumeratePrinters(&printer_list);
    209   for (size_t i = 0; i < printer_list.size(); ++i)
    210     printers->push_back(printer_list[i].printer_name);
    211 }
    212 
    213 void CloudPrintProxy::CheckCloudPrintProxyPolicy() {
    214   g_service_process->io_thread()->message_loop_proxy()->PostTask(
    215       FROM_HERE, base::Bind(&CheckCloudPrintProxyPolicyInBrowser));
    216 }
    217 
    218 void CloudPrintProxy::OnAuthenticated(
    219     const std::string& robot_oauth_refresh_token,
    220     const std::string& robot_email,
    221     const std::string& user_email) {
    222   DCHECK(CalledOnValidThread());
    223   service_prefs_->SetString(prefs::kCloudPrintRobotRefreshToken,
    224                             robot_oauth_refresh_token);
    225   service_prefs_->SetString(prefs::kCloudPrintRobotEmail,
    226                             robot_email);
    227   // If authenticating from a robot, the user email will be empty.
    228   if (!user_email.empty()) {
    229     user_email_ = user_email;
    230   }
    231   service_prefs_->SetString(prefs::kCloudPrintEmail, user_email_);
    232   enabled_ = true;
    233   DCHECK(!user_email_.empty());
    234   service_prefs_->WritePrefs();
    235   // When this switch used we don't want connector continue running, we just
    236   // need authentication.
    237   if (CommandLine::ForCurrentProcess()->HasSwitch(
    238           switches::kCloudPrintSetupProxy)) {
    239     ShutdownBackend();
    240     if (client_) {
    241       client_->OnCloudPrintProxyDisabled(false);
    242     }
    243   }
    244 }
    245 
    246 void CloudPrintProxy::OnAuthenticationFailed() {
    247   DCHECK(CalledOnValidThread());
    248   // Don't disable permanently. Could be just connection issue.
    249   ShutdownBackend();
    250   if (client_) {
    251     client_->OnCloudPrintProxyDisabled(false);
    252   }
    253 }
    254 
    255 void CloudPrintProxy::OnPrintSystemUnavailable() {
    256   // If the print system is unavailable, we want to shutdown the proxy and
    257   // disable it non-persistently.
    258   ShutdownBackend();
    259   if (client_) {
    260     client_->OnCloudPrintProxyDisabled(false);
    261   }
    262 }
    263 
    264 void CloudPrintProxy::OnUnregisterPrinters(
    265     const std::string& auth_token,
    266     const std::list<std::string>& printer_ids) {
    267   UMA_HISTOGRAM_COUNTS_10000("CloudPrint.UnregisterPrinters",
    268                              printer_ids.size());
    269   ShutdownBackend();
    270   ConnectorSettings settings;
    271   settings.InitFrom(service_prefs_);
    272   wipeout_.reset(new CloudPrintWipeout(this, settings.server_url()));
    273   wipeout_->UnregisterPrinters(auth_token, printer_ids);
    274 }
    275 
    276 void CloudPrintProxy::OnXmppPingUpdated(int ping_timeout) {
    277   DCHECK(CalledOnValidThread());
    278   service_prefs_->SetInt(prefs::kCloudPrintXmppPingTimeout, ping_timeout);
    279   service_prefs_->WritePrefs();
    280 }
    281 
    282 void CloudPrintProxy::OnUnregisterPrintersComplete() {
    283   wipeout_.reset();
    284   // Finish disabling cloud print for this user.
    285   DisableForUser();
    286 }
    287 
    288 void CloudPrintProxy::ShutdownBackend() {
    289   DCHECK(CalledOnValidThread());
    290   if (backend_.get())
    291     backend_->Shutdown();
    292   backend_.reset();
    293 }
    294 
    295 }  // namespace cloud_print
    296