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 25namespace { 26 27void 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 57void CheckCloudPrintProxyPolicyInBrowser() { 58 LaunchBrowserProcessWithSwitch(switches::kCheckCloudPrintConnectorPolicy); 59} 60 61} // namespace 62 63namespace cloud_print { 64 65CloudPrintProxy::CloudPrintProxy() 66 : service_prefs_(NULL), 67 client_(NULL), 68 enabled_(false) { 69} 70 71CloudPrintProxy::~CloudPrintProxy() { 72 DCHECK(CalledOnValidThread()); 73 ShutdownBackend(); 74} 75 76void CloudPrintProxy::Initialize(ServiceProcessPrefs* service_prefs, 77 Client* client) { 78 DCHECK(CalledOnValidThread()); 79 service_prefs_ = service_prefs; 80 client_ = client; 81} 82 83void 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 112void 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 141bool 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 164void 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 176void CloudPrintProxy::DisableForUser() { 177 DCHECK(CalledOnValidThread()); 178 user_email_.clear(); 179 enabled_ = false; 180 if (client_) { 181 client_->OnCloudPrintProxyDisabled(true); 182 } 183 ShutdownBackend(); 184} 185 186void 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 199void CloudPrintProxy::CheckCloudPrintProxyPolicy() { 200 g_service_process->io_thread()->message_loop_proxy()->PostTask( 201 FROM_HERE, base::Bind(&CheckCloudPrintProxyPolicyInBrowser)); 202} 203 204void 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 232void 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 241void 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 250void 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 260void CloudPrintProxy::OnXmppPingUpdated(int ping_timeout) { 261 DCHECK(CalledOnValidThread()); 262 service_prefs_->SetInt(prefs::kCloudPrintXmppPingTimeout, ping_timeout); 263 service_prefs_->WritePrefs(); 264} 265 266void CloudPrintProxy::OnUnregisterPrintersComplete() { 267 wipeout_.reset(); 268 // Finish disabling cloud print for this user. 269 DisableForUser(); 270} 271 272void CloudPrintProxy::ShutdownBackend() { 273 DCHECK(CalledOnValidThread()); 274 if (backend_.get()) 275 backend_->Shutdown(); 276 backend_.reset(); 277} 278 279} // namespace cloud_print 280