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   settings_.InitFrom(service_prefs_);
    147 
    148   // By default we don't poll for jobs when we lose XMPP connection. But this
    149   // behavior can be overridden by a preference.
    150   bool enable_job_poll =
    151     service_prefs_->GetBoolean(prefs::kCloudPrintEnableJobPoll, false);
    152 
    153   gaia::OAuthClientInfo oauth_client_info;
    154   oauth_client_info.client_id =
    155     google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
    156   oauth_client_info.client_secret =
    157     google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
    158   oauth_client_info.redirect_uri = "oob";
    159   backend_.reset(new CloudPrintProxyBackend(this, settings_, oauth_client_info,
    160                                             enable_job_poll));
    161   return true;
    162 }
    163 
    164 void CloudPrintProxy::UnregisterPrintersAndDisableForUser() {
    165   DCHECK(CalledOnValidThread());
    166   if (backend_.get()) {
    167     // Try getting auth and printers info from the backend.
    168     // We'll get notified in this case.
    169     backend_->UnregisterPrinters();
    170   } else {
    171     // If no backend avaialble, disable connector immidiately.
    172     DisableForUser();
    173   }
    174 }
    175 
    176 void CloudPrintProxy::DisableForUser() {
    177   DCHECK(CalledOnValidThread());
    178   user_email_.clear();
    179   enabled_ = false;
    180   if (client_) {
    181     client_->OnCloudPrintProxyDisabled(true);
    182   }
    183   ShutdownBackend();
    184 }
    185 
    186 void CloudPrintProxy::GetProxyInfo(CloudPrintProxyInfo* info) {
    187   info->enabled = enabled_;
    188   info->email.clear();
    189   if (enabled_)
    190     info->email = user_email();
    191   info->proxy_id = settings_.proxy_id();
    192   // If the Cloud Print service is not enabled, we may need to read the old
    193   // value of proxy_id from prefs.
    194   if (info->proxy_id.empty())
    195     info->proxy_id =
    196         service_prefs_->GetString(prefs::kCloudPrintProxyId, std::string());
    197 }
    198 
    199 void CloudPrintProxy::CheckCloudPrintProxyPolicy() {
    200   g_service_process->io_thread()->message_loop_proxy()->PostTask(
    201       FROM_HERE, base::Bind(&CheckCloudPrintProxyPolicyInBrowser));
    202 }
    203 
    204 void CloudPrintProxy::OnAuthenticated(
    205     const std::string& robot_oauth_refresh_token,
    206     const std::string& robot_email,
    207     const std::string& user_email) {
    208   DCHECK(CalledOnValidThread());
    209   service_prefs_->SetString(prefs::kCloudPrintRobotRefreshToken,
    210                             robot_oauth_refresh_token);
    211   service_prefs_->SetString(prefs::kCloudPrintRobotEmail,
    212                             robot_email);
    213   // If authenticating from a robot, the user email will be empty.
    214   if (!user_email.empty()) {
    215     user_email_ = user_email;
    216   }
    217   service_prefs_->SetString(prefs::kCloudPrintEmail, user_email_);
    218   enabled_ = true;
    219   DCHECK(!user_email_.empty());
    220   service_prefs_->WritePrefs();
    221   // When this switch used we don't want connector continue running, we just
    222   // need authentication.
    223   if (CommandLine::ForCurrentProcess()->HasSwitch(
    224           switches::kCloudPrintSetupProxy)) {
    225     ShutdownBackend();
    226     if (client_) {
    227       client_->OnCloudPrintProxyDisabled(false);
    228     }
    229   }
    230 }
    231 
    232 void CloudPrintProxy::OnAuthenticationFailed() {
    233   DCHECK(CalledOnValidThread());
    234   // Don't disable permanently. Could be just connection issue.
    235   ShutdownBackend();
    236   if (client_) {
    237     client_->OnCloudPrintProxyDisabled(false);
    238   }
    239 }
    240 
    241 void CloudPrintProxy::OnPrintSystemUnavailable() {
    242   // If the print system is unavailable, we want to shutdown the proxy and
    243   // disable it non-persistently.
    244   ShutdownBackend();
    245   if (client_) {
    246     client_->OnCloudPrintProxyDisabled(false);
    247   }
    248 }
    249 
    250 void CloudPrintProxy::OnUnregisterPrinters(
    251     const std::string& auth_token,
    252     const std::list<std::string>& printer_ids) {
    253   UMA_HISTOGRAM_COUNTS_10000("CloudPrint.UnregisterPrinters",
    254                              printer_ids.size());
    255   ShutdownBackend();
    256   wipeout_.reset(new CloudPrintWipeout(this, settings_.server_url()));
    257   wipeout_->UnregisterPrinters(auth_token, printer_ids);
    258 }
    259 
    260 void CloudPrintProxy::OnXmppPingUpdated(int ping_timeout) {
    261   DCHECK(CalledOnValidThread());
    262   service_prefs_->SetInt(prefs::kCloudPrintXmppPingTimeout, ping_timeout);
    263   service_prefs_->WritePrefs();
    264 }
    265 
    266 void CloudPrintProxy::OnUnregisterPrintersComplete() {
    267   wipeout_.reset();
    268   // Finish disabling cloud print for this user.
    269   DisableForUser();
    270 }
    271 
    272 void CloudPrintProxy::ShutdownBackend() {
    273   DCHECK(CalledOnValidThread());
    274   if (backend_.get())
    275     backend_->Shutdown();
    276   backend_.reset();
    277 }
    278 
    279 }  // namespace cloud_print
    280