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 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
UnregisterPrintersAndDisableForUser()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
DisableForUser()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
GetProxyInfo(CloudPrintProxyInfo * info)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
CheckCloudPrintProxyPolicy()199 void CloudPrintProxy::CheckCloudPrintProxyPolicy() {
200 g_service_process->io_thread()->message_loop_proxy()->PostTask(
201 FROM_HERE, base::Bind(&CheckCloudPrintProxyPolicyInBrowser));
202 }
203
OnAuthenticated(const std::string & robot_oauth_refresh_token,const std::string & robot_email,const std::string & user_email)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
OnAuthenticationFailed()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
OnPrintSystemUnavailable()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
OnUnregisterPrinters(const std::string & auth_token,const std::list<std::string> & printer_ids)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
OnXmppPingUpdated(int ping_timeout)260 void CloudPrintProxy::OnXmppPingUpdated(int ping_timeout) {
261 DCHECK(CalledOnValidThread());
262 service_prefs_->SetInt(prefs::kCloudPrintXmppPingTimeout, ping_timeout);
263 service_prefs_->WritePrefs();
264 }
265
OnUnregisterPrintersComplete()266 void CloudPrintProxy::OnUnregisterPrintersComplete() {
267 wipeout_.reset();
268 // Finish disabling cloud print for this user.
269 DisableForUser();
270 }
271
ShutdownBackend()272 void CloudPrintProxy::ShutdownBackend() {
273 DCHECK(CalledOnValidThread());
274 if (backend_.get())
275 backend_->Shutdown();
276 backend_.reset();
277 }
278
279 } // namespace cloud_print
280