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
LaunchBrowserProcessWithSwitch(const std::string & switch_string)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
CheckCloudPrintProxyPolicyInBrowser()57 void CheckCloudPrintProxyPolicyInBrowser() {
58 LaunchBrowserProcessWithSwitch(switches::kCheckCloudPrintConnectorPolicy);
59 }
60
61 } // namespace
62
63 namespace cloud_print {
64
CloudPrintProxy()65 CloudPrintProxy::CloudPrintProxy()
66 : service_prefs_(NULL),
67 client_(NULL),
68 enabled_(false) {
69 }
70
~CloudPrintProxy()71 CloudPrintProxy::~CloudPrintProxy() {
72 DCHECK(CalledOnValidThread());
73 ShutdownBackend();
74 }
75
Initialize(ServiceProcessPrefs * service_prefs,Client * client)76 void CloudPrintProxy::Initialize(ServiceProcessPrefs* service_prefs,
77 Client* client) {
78 DCHECK(CalledOnValidThread());
79 service_prefs_ = service_prefs;
80 client_ = client;
81 }
82
EnableForUser()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
EnableForUserWithRobot(const std::string & robot_auth_code,const std::string & robot_email,const std::string & user_email,const base::DictionaryValue & user_settings)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
CreateBackend()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
UnregisterPrintersAndDisableForUser()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
DisableForUser()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
GetProxyInfo(CloudPrintProxyInfo * info)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
GetPrinters(std::vector<std::string> * printers)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
CheckCloudPrintProxyPolicy()213 void CloudPrintProxy::CheckCloudPrintProxyPolicy() {
214 g_service_process->io_thread()->message_loop_proxy()->PostTask(
215 FROM_HERE, base::Bind(&CheckCloudPrintProxyPolicyInBrowser));
216 }
217
OnAuthenticated(const std::string & robot_oauth_refresh_token,const std::string & robot_email,const std::string & user_email)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
OnAuthenticationFailed()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
OnPrintSystemUnavailable()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
OnUnregisterPrinters(const std::string & auth_token,const std::list<std::string> & printer_ids)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
OnXmppPingUpdated(int ping_timeout)276 void CloudPrintProxy::OnXmppPingUpdated(int ping_timeout) {
277 DCHECK(CalledOnValidThread());
278 service_prefs_->SetInt(prefs::kCloudPrintXmppPingTimeout, ping_timeout);
279 service_prefs_->WritePrefs();
280 }
281
OnUnregisterPrintersComplete()282 void CloudPrintProxy::OnUnregisterPrintersComplete() {
283 wipeout_.reset();
284 // Finish disabling cloud print for this user.
285 DisableForUser();
286 }
287
ShutdownBackend()288 void CloudPrintProxy::ShutdownBackend() {
289 DCHECK(CalledOnValidThread());
290 if (backend_.get())
291 backend_->Shutdown();
292 backend_.reset();
293 }
294
295 } // namespace cloud_print
296