1 // Copyright 2013 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.
5 #include "cloud_print/gcp20/prototype/cloud_print_requester.h"
8 #include "base/json/json_writer.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/rand_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h"
14 #include "google_apis/google_api_keys.h"
15 #include "net/base/escape.h"
16 #include "net/base/mime_util.h"
17 #include "net/base/url_util.h"
18 #include "net/http/http_status_code.h"
19 #include "net/proxy/proxy_config_service_fixed.h"
20 #include "net/url_request/url_request_context.h"
23 const char kCloudPrintUrl[] = "https://www.google.com/cloudprint";
27 const char kProxyIdValue[] = "proxy";
28 const char kPrinterNameValue[] = "printer";
29 const char kPrinterCapsValue[] = "capabilities";
30 const char kPrinterCapsHashValue[] = "capsHash";
31 const char kPrinterUserValue[] = "user";
32 const char kPrinterGcpVersion[] = "gcp_version";
33 const char kPrinterLocalSettings[] = "local_settings";
34 const char kPrinterFirmware[] = "firmware";
35 const char kPrinterManufacturer[] = "manufacturer";
36 const char kPrinterModel[] = "model";
37 const char kPrinterSetupUrl[] = "setup_url";
38 const char kPrinterSupportUrl[] = "support_url";
39 const char kPrinterUpdateUrl[] = "update_url";
41 const char kFirmwareValue[] = "2.0";
42 const char kManufacturerValue[] = "Google";
43 const char kModelValue[] = "GCPPrototype";
45 // TODO(maksymb): Replace GCP Version with "2.0" once GCP Server will support it
46 const char kGcpVersion[] = "1.5";
48 const int kGaiaMaxRetries = 3;
50 GURL CreateRegisterUrl() {
51 return GURL(std::string(kCloudPrintUrl) + "/register");
54 GURL CreateFetchUrl(const std::string& device_id) {
55 GURL url(std::string(kCloudPrintUrl) + "/fetch");
56 url = net::AppendQueryParameter(url, "printerid", device_id);
60 GURL CreateControlUrl(const std::string& job_id, const std::string& status) {
61 GURL url(std::string(kCloudPrintUrl) + "/control");
62 url = net::AppendQueryParameter(url, "jobid", job_id);
63 url = net::AppendQueryParameter(url, "status", status);
67 GURL CreatePrinterUrl(const std::string& device_id) {
68 GURL url(std::string(kCloudPrintUrl) + "/printer");
69 url = net::AppendQueryParameter(url, "printerid", device_id);
73 GURL CreateUpdateUrl(const std::string& device_id) {
74 GURL url(std::string(kCloudPrintUrl) + "/update");
75 url = net::AppendQueryParameter(url, "printerid", device_id);
79 std::string LocalSettingsToJson(const LocalSettings& settings) {
80 base::DictionaryValue dictionary;
81 scoped_ptr<base::DictionaryValue> current(new DictionaryValue);
83 // TODO(maksymb): Formalize text as constants.
84 current->SetBoolean("local_discovery", settings.local_discovery);
85 current->SetBoolean("access_token_enabled", settings.access_token_enabled);
86 current->SetBoolean("printer/local_printing_enabled",
87 settings.local_printing_enabled);
88 current->SetInteger("xmpp_timeout_value", settings.xmpp_timeout_value);
89 dictionary.Set("current", current.release());
91 std::string local_settings;
92 base::JSONWriter::Write(&dictionary, &local_settings);
93 return local_settings;
98 using cloud_print_response_parser::Job;
100 CloudPrintRequester::CloudPrintRequester(
101 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
103 : context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
104 delegate_(delegate) {
105 oauth_client_info_.client_id =
106 google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
107 oauth_client_info_.client_secret =
108 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
109 oauth_client_info_.redirect_uri = "oob";
112 CloudPrintRequester::~CloudPrintRequester() {
115 bool CloudPrintRequester::IsBusy() const {
116 return request_ || gaia_;
119 void CloudPrintRequester::StartRegistration(const std::string& proxy_id,
120 const std::string& device_name,
121 const std::string& user,
122 const LocalSettings& settings,
123 const std::string& cdd) {
124 std::string mime_boundary;
125 int r1 = base::RandInt(0, kint32max);
126 int r2 = base::RandInt(0, kint32max);
127 base::SStringPrintf(&mime_boundary,
128 "---------------------------%08X%08X", r1, r2);
131 std::string data_mimetype;
132 data_mimetype = "multipart/form-data; boundary=" + mime_boundary;
134 net::AddMultipartValueForUpload(kProxyIdValue, proxy_id, mime_boundary,
135 std::string(), &data);
136 net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
137 std::string(), &data);
138 net::AddMultipartValueForUpload("use_cdd", "true", mime_boundary,
139 std::string(), &data);
140 net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
141 std::string(), &data);
142 net::AddMultipartValueForUpload(kPrinterCapsValue, cdd, mime_boundary,
143 "application/json", &data);
144 net::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(cdd),
145 mime_boundary, std::string(), &data);
146 net::AddMultipartValueForUpload(kPrinterUserValue, user,
147 mime_boundary, std::string(), &data);
148 net::AddMultipartValueForUpload(kPrinterGcpVersion, kGcpVersion,
149 mime_boundary, std::string(), &data);
150 net::AddMultipartValueForUpload(kPrinterLocalSettings,
151 LocalSettingsToJson(settings),
152 mime_boundary, std::string(), &data);
153 net::AddMultipartValueForUpload(kPrinterFirmware,
155 mime_boundary, std::string(), &data);
156 net::AddMultipartValueForUpload(kPrinterManufacturer,
158 mime_boundary, std::string(), &data);
159 net::AddMultipartValueForUpload(kPrinterModel,
161 mime_boundary, std::string(), &data);
162 net::AddMultipartValueForUpload(kPrinterSetupUrl,
164 mime_boundary, std::string(), &data);
165 net::AddMultipartValueForUpload(kPrinterSupportUrl,
167 mime_boundary, std::string(), &data);
168 net::AddMultipartValueForUpload(kPrinterUpdateUrl,
170 mime_boundary, std::string(), &data);
171 net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data);
173 request_ = CreatePost(
177 base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr()));
178 request_->Run(delegate_->GetAccessToken(), context_getter_);
181 void CloudPrintRequester::CompleteRegistration() {
182 request_ = CreateGet(
183 GURL(polling_url_ + oauth_client_info_.client_id),
184 base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr()));
185 request_->Run(delegate_->GetAccessToken(), context_getter_);
188 void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) {
189 VLOG(3) << "Function: " << __FUNCTION__;
193 DCHECK(!delegate_->GetAccessToken().empty());
195 VLOG(3) << "Function: " << __FUNCTION__ <<
197 request_ = CreateGet(
198 CreateFetchUrl(device_id),
199 base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr()));
200 request_->Run(delegate_->GetAccessToken(), context_getter_);
203 void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) {
204 VLOG(3) << "Function: " << __FUNCTION__;
206 gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
207 gaia_->RefreshToken(oauth_client_info_, refresh_token,
208 std::vector<std::string>(), kGaiaMaxRetries, this);
211 void CloudPrintRequester::RequestPrintJob(const Job& job) {
212 VLOG(3) << "Function: " << __FUNCTION__;
213 current_print_job_.reset(new Job(job));
214 request_ = CreateGet(
215 CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"),
216 base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr()));
217 request_->Run(delegate_->GetAccessToken(), context_getter_);
220 void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) {
221 VLOG(3) << "Function: " << __FUNCTION__;
222 request_ = CreateGet(
223 CreateControlUrl(job_id, "DONE"),
224 base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr()));
225 request_->Run(delegate_->GetAccessToken(), context_getter_);
228 void CloudPrintRequester::RequestLocalSettings(const std::string& device_id) {
229 VLOG(3) << "Function: " << __FUNCTION__;
230 request_ = CreateGet(
231 CreatePrinterUrl(device_id),
232 base::Bind(&CloudPrintRequester::ParseLocalSettings, AsWeakPtr()));
233 request_->Run(delegate_->GetAccessToken(), context_getter_);
236 void CloudPrintRequester::SendLocalSettings(
237 const std::string& device_id,
238 const LocalSettings& settings) {
239 VLOG(3) << "Function: " << __FUNCTION__;
241 std::string data_mimetype = "application/x-www-form-urlencoded";
242 std::string data = base::StringPrintf(
244 kPrinterLocalSettings,
245 net::EscapeUrlEncodedData(LocalSettingsToJson(settings), false).c_str());
247 request_ = CreatePost(
248 CreateUpdateUrl(device_id),
250 base::Bind(&CloudPrintRequester::ParseLocalSettingUpdated, AsWeakPtr()));
251 request_->Run(delegate_->GetAccessToken(), context_getter_);
255 void CloudPrintRequester::OnFetchComplete(const std::string& response) {
256 VLOG(3) << "Function: " << __FUNCTION__;
257 ParserCallback callback = parser_callback_;
259 callback.Run(response);
262 void CloudPrintRequester::OnFetchError(const std::string& server_api,
264 int server_http_code) {
265 VLOG(3) << "Function: " << __FUNCTION__;
267 current_print_job_.reset();
269 if (server_http_code == net::HTTP_FORBIDDEN) {
270 delegate_->OnAuthError();
272 delegate_->OnServerError("Fetch error");
275 // TODO(maksymb): Add Privet |server_http_code| and |server_api| support in
276 // case of server errors.
279 void CloudPrintRequester::OnFetchTimeoutReached() {
280 VLOG(3) << "Function: " << __FUNCTION__;
282 current_print_job_.reset();
283 delegate_->OnNetworkError();
286 void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token,
287 const std::string& access_token,
288 int expires_in_seconds) {
289 VLOG(3) << "Function: " << __FUNCTION__;
291 delegate_->OnRegistrationFinished(refresh_token,
292 access_token, expires_in_seconds);
295 void CloudPrintRequester::OnRefreshTokenResponse(
296 const std::string& access_token,
297 int expires_in_seconds) {
298 VLOG(3) << "Function: " << __FUNCTION__;
300 delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds);
303 void CloudPrintRequester::OnOAuthError() {
304 VLOG(3) << "Function: " << __FUNCTION__;
306 delegate_->OnAuthError();
309 void CloudPrintRequester::OnNetworkError(int response_code) {
310 VLOG(3) << "Function: " << __FUNCTION__;
313 if (response_code == net::HTTP_FORBIDDEN) {
314 // TODO(maksymb): delegate_->OnPrinterDeleted();
316 delegate_->OnNetworkError();
320 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet(
322 const ParserCallback& parser_callback) {
324 DCHECK(parser_callback_.is_null());
325 parser_callback_ = parser_callback;
326 return CloudPrintRequest::CreateGet(url, this);
329 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost(
331 const std::string& content,
332 const std::string& mimetype,
333 const ParserCallback& parser_callback) {
335 DCHECK(parser_callback_.is_null());
336 parser_callback_ = parser_callback;
337 return CloudPrintRequest::CreatePost(url, content, mimetype, this);
340 void CloudPrintRequester::EraseRequest() {
342 DCHECK(!parser_callback_.is_null());
344 parser_callback_.Reset();
347 void CloudPrintRequester::ParseRegisterStart(const std::string& response) {
348 std::string error_description;
349 std::string polling_url;
350 std::string registration_token;
351 std::string complete_invite_url;
352 std::string device_id;
354 bool success = cloud_print_response_parser::ParseRegisterStartResponse(
359 &complete_invite_url,
363 polling_url_ = polling_url;
364 delegate_->OnRegistrationStartResponseParsed(registration_token,
368 delegate_->OnRegistrationError(error_description);
372 void CloudPrintRequester::ParseRegisterComplete(const std::string& response) {
373 std::string error_description;
374 std::string authorization_code;
376 std::string xmpp_jid;
377 bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
384 delegate_->OnXmppJidReceived(xmpp_jid);
386 gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
387 gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
388 kGaiaMaxRetries, this);
390 delegate_->OnRegistrationError(error_description);
394 void CloudPrintRequester::ParseFetch(const std::string& response) {
395 VLOG(3) << "Function: " << __FUNCTION__;
397 std::string error_description;
398 std::vector<Job> list;
399 bool success = cloud_print_response_parser::ParseFetchResponse(
405 delegate_->OnPrintJobsAvailable(list);
407 delegate_->OnServerError(error_description);
411 void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) {
412 VLOG(3) << "Function: " << __FUNCTION__;
413 current_print_job_->ticket = response;
415 DCHECK(current_print_job_);
416 request_ = CreateGet(
417 GURL(current_print_job_->file_url),
418 base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr()));
419 request_->AddHeader("Accept: \"application/pdf\"");
420 request_->Run(delegate_->GetAccessToken(), context_getter_);
423 void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) {
424 VLOG(3) << "Function: " << __FUNCTION__;
425 current_print_job_->file = response;
426 DCHECK(current_print_job_);
427 delegate_->OnPrintJobDownloaded(*current_print_job_);
430 void CloudPrintRequester::ParsePrintJobDone(const std::string& response) {
431 VLOG(3) << "Function: " << __FUNCTION__;
432 current_print_job_.reset();
433 delegate_->OnPrintJobDone();
436 void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) {
437 VLOG(3) << "Function: " << __FUNCTION__;
438 DCHECK(current_print_job_);
439 request_ = CreateGet(
440 GURL(current_print_job_->ticket_url),
441 base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr()));
442 request_->Run(delegate_->GetAccessToken(), context_getter_);
445 void CloudPrintRequester::ParseLocalSettings(const std::string& response) {
446 VLOG(3) << "Function: " << __FUNCTION__;
448 std::string error_description;
449 LocalSettings settings;
450 LocalSettings::State state;
452 bool success = cloud_print_response_parser::ParseLocalSettingsResponse(
459 delegate_->OnLocalSettingsReceived(state, settings);
461 delegate_->OnServerError(error_description);
465 void CloudPrintRequester::ParseLocalSettingUpdated(
466 const std::string& response) {
467 delegate_->OnLocalSettingsUpdated();