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