- add sources.
[platform/framework/web/crosswalk.git] / src / cloud_print / gcp20 / prototype / cloud_print_requester.cc
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.
4
5 #include "cloud_print/gcp20/prototype/cloud_print_requester.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/md5.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"
21 #include "url/gurl.h"
22
23 const char kCloudPrintUrl[] = "https://www.google.com/cloudprint";
24
25 namespace {
26
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";
40
41 const char kFirmwareValue[] = "2.0";
42 const char kManufacturerValue[] = "Google";
43 const char kModelValue[] = "GCPPrototype";
44
45 // TODO(maksymb): Replace GCP Version with "2.0" once GCP Server will support it
46 const char kGcpVersion[] = "1.5";
47
48 const int kGaiaMaxRetries = 3;
49
50 GURL CreateRegisterUrl() {
51   return GURL(std::string(kCloudPrintUrl) + "/register");
52 }
53
54 GURL CreateFetchUrl(const std::string& device_id) {
55   GURL url(std::string(kCloudPrintUrl) + "/fetch");
56   url = net::AppendQueryParameter(url, "printerid", device_id);
57   return url;
58 }
59
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);
64   return url;
65 }
66
67 GURL CreatePrinterUrl(const std::string& device_id) {
68   GURL url(std::string(kCloudPrintUrl) + "/printer");
69   url = net::AppendQueryParameter(url, "printerid", device_id);
70   return url;
71 }
72
73 GURL CreateUpdateUrl(const std::string& device_id) {
74   GURL url(std::string(kCloudPrintUrl) + "/update");
75   url = net::AppendQueryParameter(url, "printerid", device_id);
76   return url;
77 }
78
79 std::string LocalSettingsToJson(const LocalSettings& settings) {
80   base::DictionaryValue dictionary;
81   scoped_ptr<base::DictionaryValue> current(new DictionaryValue);
82
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());
90
91   std::string local_settings;
92   base::JSONWriter::Write(&dictionary, &local_settings);
93   return local_settings;
94 }
95
96 }  // namespace
97
98 using cloud_print_response_parser::Job;
99
100 CloudPrintRequester::CloudPrintRequester(
101     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
102     Delegate* delegate)
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";
110 }
111
112 CloudPrintRequester::~CloudPrintRequester() {
113 }
114
115 bool CloudPrintRequester::IsBusy() const {
116   return request_ || gaia_;
117 }
118
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);
129
130   std::string data;
131   std::string data_mimetype;
132   data_mimetype = "multipart/form-data; boundary=" + mime_boundary;
133
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,
154                                   kFirmwareValue,
155                                   mime_boundary, std::string(), &data);
156   net::AddMultipartValueForUpload(kPrinterManufacturer,
157                                   kManufacturerValue,
158                                   mime_boundary, std::string(), &data);
159   net::AddMultipartValueForUpload(kPrinterModel,
160                                   kModelValue,
161                                   mime_boundary, std::string(), &data);
162   net::AddMultipartValueForUpload(kPrinterSetupUrl,
163                                   kCloudPrintUrl,
164                                   mime_boundary, std::string(), &data);
165   net::AddMultipartValueForUpload(kPrinterSupportUrl,
166                                   kCloudPrintUrl,
167                                   mime_boundary, std::string(), &data);
168   net::AddMultipartValueForUpload(kPrinterUpdateUrl,
169                                   kCloudPrintUrl,
170                                   mime_boundary, std::string(), &data);
171   net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data);
172
173   request_ = CreatePost(
174       CreateRegisterUrl(),
175       data,
176       data_mimetype,
177       base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr()));
178   request_->Run(delegate_->GetAccessToken(), context_getter_);
179 }
180
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_);
186 }
187
188 void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) {
189   VLOG(3) << "Function: " << __FUNCTION__;
190   if (IsBusy())
191     return;
192
193   DCHECK(!delegate_->GetAccessToken().empty());
194
195   VLOG(3) << "Function: " << __FUNCTION__ <<
196       ": request created";
197   request_ = CreateGet(
198       CreateFetchUrl(device_id),
199       base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr()));
200   request_->Run(delegate_->GetAccessToken(), context_getter_);
201 }
202
203 void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) {
204   VLOG(3) << "Function: " << __FUNCTION__;
205   DCHECK(!IsBusy());
206   gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
207   gaia_->RefreshToken(oauth_client_info_, refresh_token,
208                       std::vector<std::string>(), kGaiaMaxRetries, this);
209 }
210
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_);
218 }
219
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_);
226 }
227
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_);
234 }
235
236 void CloudPrintRequester::SendLocalSettings(
237     const std::string& device_id,
238     const LocalSettings& settings) {
239   VLOG(3) << "Function: " << __FUNCTION__;
240
241   std::string data_mimetype = "application/x-www-form-urlencoded";
242   std::string data = base::StringPrintf(
243       "%s=%s",
244       kPrinterLocalSettings,
245       net::EscapeUrlEncodedData(LocalSettingsToJson(settings), false).c_str());
246
247   request_ = CreatePost(
248       CreateUpdateUrl(device_id),
249       data, data_mimetype,
250       base::Bind(&CloudPrintRequester::ParseLocalSettingUpdated, AsWeakPtr()));
251   request_->Run(delegate_->GetAccessToken(), context_getter_);
252 }
253
254
255 void CloudPrintRequester::OnFetchComplete(const std::string& response) {
256   VLOG(3) << "Function: " << __FUNCTION__;
257   ParserCallback callback = parser_callback_;
258   EraseRequest();
259   callback.Run(response);
260 }
261
262 void CloudPrintRequester::OnFetchError(const std::string& server_api,
263                                        int server_code,
264                                        int server_http_code) {
265   VLOG(3) << "Function: " << __FUNCTION__;
266   EraseRequest();
267   current_print_job_.reset();
268
269   if (server_http_code == net::HTTP_FORBIDDEN) {
270     delegate_->OnAuthError();
271   } else {
272     delegate_->OnServerError("Fetch error");
273   }
274
275   // TODO(maksymb): Add Privet |server_http_code| and |server_api| support in
276   // case of server errors.
277 }
278
279 void CloudPrintRequester::OnFetchTimeoutReached() {
280   VLOG(3) << "Function: " << __FUNCTION__;
281   EraseRequest();
282   current_print_job_.reset();
283   delegate_->OnNetworkError();
284 }
285
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__;
290   gaia_.reset();
291   delegate_->OnRegistrationFinished(refresh_token,
292                                     access_token, expires_in_seconds);
293 }
294
295 void CloudPrintRequester::OnRefreshTokenResponse(
296     const std::string& access_token,
297     int expires_in_seconds) {
298   VLOG(3) << "Function: " << __FUNCTION__;
299   gaia_.reset();
300   delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds);
301 }
302
303 void CloudPrintRequester::OnOAuthError() {
304   VLOG(3) << "Function: " << __FUNCTION__;
305   gaia_.reset();
306   delegate_->OnAuthError();
307 }
308
309 void CloudPrintRequester::OnNetworkError(int response_code) {
310   VLOG(3) << "Function: " << __FUNCTION__;
311   gaia_.reset();
312
313   if (response_code == net::HTTP_FORBIDDEN) {
314     // TODO(maksymb): delegate_->OnPrinterDeleted();
315   } else {
316     delegate_->OnNetworkError();
317   }
318 }
319
320 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet(
321     const GURL& url,
322     const ParserCallback& parser_callback) {
323   DCHECK(!IsBusy());
324   DCHECK(parser_callback_.is_null());
325   parser_callback_ = parser_callback;
326   return CloudPrintRequest::CreateGet(url, this);
327 }
328
329 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost(
330     const GURL& url,
331     const std::string& content,
332     const std::string& mimetype,
333     const ParserCallback& parser_callback) {
334   DCHECK(!IsBusy());
335   DCHECK(parser_callback_.is_null());
336   parser_callback_ = parser_callback;
337   return CloudPrintRequest::CreatePost(url, content, mimetype, this);
338 }
339
340 void CloudPrintRequester::EraseRequest() {
341   DCHECK(request_);
342   DCHECK(!parser_callback_.is_null());
343   request_.reset();
344   parser_callback_.Reset();
345 }
346
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;
353
354   bool success = cloud_print_response_parser::ParseRegisterStartResponse(
355       response,
356       &error_description,
357       &polling_url,
358       &registration_token,
359       &complete_invite_url,
360       &device_id);
361
362   if (success) {
363     polling_url_ = polling_url;
364     delegate_->OnRegistrationStartResponseParsed(registration_token,
365                                                  complete_invite_url,
366                                                  device_id);
367   } else {
368     delegate_->OnRegistrationError(error_description);
369   }
370 }
371
372 void CloudPrintRequester::ParseRegisterComplete(const std::string& response) {
373   std::string error_description;
374   std::string authorization_code;
375
376   std::string xmpp_jid;
377   bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
378       response,
379       &error_description,
380       &authorization_code,
381       &xmpp_jid);
382
383   if (success) {
384     delegate_->OnXmppJidReceived(xmpp_jid);
385
386     gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
387     gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
388                                  kGaiaMaxRetries, this);
389   } else {
390     delegate_->OnRegistrationError(error_description);
391   }
392 }
393
394 void CloudPrintRequester::ParseFetch(const std::string& response) {
395   VLOG(3) << "Function: " << __FUNCTION__;
396
397   std::string error_description;
398   std::vector<Job> list;
399   bool success = cloud_print_response_parser::ParseFetchResponse(
400       response,
401       &error_description,
402       &list);
403
404   if (success) {
405     delegate_->OnPrintJobsAvailable(list);
406   } else {
407     delegate_->OnServerError(error_description);
408   }
409 }
410
411 void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) {
412   VLOG(3) << "Function: " << __FUNCTION__;
413   current_print_job_->ticket = response;
414
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_);
421 }
422
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_);
428 }
429
430 void CloudPrintRequester::ParsePrintJobDone(const std::string& response) {
431   VLOG(3) << "Function: " << __FUNCTION__;
432   current_print_job_.reset();
433   delegate_->OnPrintJobDone();
434 }
435
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_);
443 }
444
445 void CloudPrintRequester::ParseLocalSettings(const std::string& response) {
446   VLOG(3) << "Function: " << __FUNCTION__;
447
448   std::string error_description;
449   LocalSettings settings;
450   LocalSettings::State state;
451
452   bool success = cloud_print_response_parser::ParseLocalSettingsResponse(
453       response,
454       &error_description,
455       &state,
456       &settings);
457
458   if (success) {
459     delegate_->OnLocalSettingsReceived(state, settings);
460   } else {
461     delegate_->OnServerError(error_description);
462   }
463 }
464
465 void CloudPrintRequester::ParseLocalSettingUpdated(
466     const std::string& response) {
467   delegate_->OnLocalSettingsUpdated();
468 }