Home | History | Annotate | Download | only in prototype
      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/md5.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/rand_util.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h"
     13 #include "google_apis/google_api_keys.h"
     14 #include "net/base/mime_util.h"
     15 #include "net/base/url_util.h"
     16 #include "net/http/http_status_code.h"
     17 #include "net/proxy/proxy_config_service_fixed.h"
     18 #include "net/url_request/url_request_context.h"
     19 #include "url/gurl.h"
     20 
     21 const char kCloudPrintUrl[] = "https://www.google.com/cloudprint";
     22 
     23 namespace {
     24 
     25 const char kProxyIdValue[] = "proxy";
     26 const char kPrinterNameValue[] = "printer";
     27 const char kPrinterCapsValue[] = "capabilities";
     28 const char kPrinterCapsHashValue[] = "capsHash";
     29 const char kPrinterUserValue[] = "user";
     30 
     31 const int kGaiaMaxRetries = 3;
     32 
     33 GURL CreateRegisterUrl() {
     34   return GURL(std::string(kCloudPrintUrl) + "/register");
     35 }
     36 
     37 GURL CreateFetchUrl(const std::string& device_id) {
     38   GURL url(std::string(kCloudPrintUrl) + "/fetch");
     39   url = net::AppendQueryParameter(url, "printerid", device_id);
     40   return url;
     41 }
     42 
     43 GURL CreateControlUrl(const std::string& job_id, const std::string& status) {
     44   GURL url(std::string(kCloudPrintUrl) + "/control");
     45   url = net::AppendQueryParameter(url, "jobid", job_id);
     46   url = net::AppendQueryParameter(url, "status", status);
     47   return url;
     48 }
     49 
     50 }  // namespace
     51 
     52 using cloud_print_response_parser::Job;
     53 
     54 CloudPrintRequester::CloudPrintRequester(
     55     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     56     Delegate* delegate)
     57     : context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
     58       delegate_(delegate) {
     59   oauth_client_info_.client_id =
     60       google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
     61   oauth_client_info_.client_secret =
     62       google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
     63   oauth_client_info_.redirect_uri = "oob";
     64 }
     65 
     66 CloudPrintRequester::~CloudPrintRequester() {
     67 }
     68 
     69 bool CloudPrintRequester::IsBusy() const {
     70   return request_ || gaia_;
     71 }
     72 
     73 void CloudPrintRequester::StartRegistration(const std::string& proxy_id,
     74                                             const std::string& device_name,
     75                                             const std::string& user,
     76                                             const std::string& cdd) {
     77   std::string mime_boundary;
     78   int r1 = base::RandInt(0, kint32max);
     79   int r2 = base::RandInt(0, kint32max);
     80   base::SStringPrintf(&mime_boundary,
     81                       "---------------------------%08X%08X", r1, r2);
     82 
     83   std::string data;
     84   std::string data_mimetype;
     85   data_mimetype = "multipart/form-data; boundary=" + mime_boundary;
     86 
     87   net::AddMultipartValueForUpload(kProxyIdValue, proxy_id, mime_boundary,
     88                                   std::string(), &data);
     89   net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
     90                                   std::string(), &data);
     91   net::AddMultipartValueForUpload("use_cdd", "true", mime_boundary,
     92                                   std::string(), &data);
     93   net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
     94                                   std::string(), &data);
     95   net::AddMultipartValueForUpload(kPrinterCapsValue, cdd, mime_boundary,
     96                                   "application/json", &data);
     97   net::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(cdd),
     98                                   mime_boundary, std::string(), &data);
     99   net::AddMultipartValueForUpload(kPrinterUserValue, user,
    100                                   mime_boundary, std::string(), &data);
    101   net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data);
    102 
    103   request_ = CreatePost(
    104       CreateRegisterUrl(),
    105       data,
    106       data_mimetype,
    107       base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr()));
    108   request_->Run(delegate_->GetAccessToken(), context_getter_);
    109 }
    110 
    111 void CloudPrintRequester::CompleteRegistration() {
    112   request_ = CreateGet(
    113       GURL(polling_url_ + oauth_client_info_.client_id),
    114       base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr()));
    115   request_->Run(delegate_->GetAccessToken(), context_getter_);
    116 }
    117 
    118 void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) {
    119   VLOG(3) << "Function: " << __FUNCTION__;
    120   if (IsBusy())
    121     return;
    122 
    123   DCHECK(!delegate_->GetAccessToken().empty());
    124 
    125   VLOG(3) << "Function: " << __FUNCTION__ <<
    126       ": request created";
    127   request_ = CreateGet(
    128       CreateFetchUrl(device_id),
    129       base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr()));
    130   request_->Run(delegate_->GetAccessToken(), context_getter_);
    131 }
    132 
    133 void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) {
    134   VLOG(3) << "Function: " << __FUNCTION__;
    135   DCHECK(!IsBusy());
    136   gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
    137   gaia_->RefreshToken(oauth_client_info_, refresh_token,
    138                       std::vector<std::string>(), kGaiaMaxRetries, this);
    139 }
    140 
    141 void CloudPrintRequester::RequestPrintJob(const Job& job) {
    142   VLOG(3) << "Function: " << __FUNCTION__;
    143   current_print_job_.reset(new Job(job));
    144   request_ = CreateGet(
    145       CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"),
    146       base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr()));
    147   request_->Run(delegate_->GetAccessToken(), context_getter_);
    148 }
    149 
    150 void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) {
    151   VLOG(3) << "Function: " << __FUNCTION__;
    152   request_ = CreateGet(
    153       CreateControlUrl(job_id, "DONE"),
    154       base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr()));
    155   request_->Run(delegate_->GetAccessToken(), context_getter_);
    156 }
    157 
    158 void CloudPrintRequester::OnFetchComplete(const std::string& response) {
    159   VLOG(3) << "Function: " << __FUNCTION__;
    160   ParserCallback callback = parser_callback_;
    161   EraseRequest();
    162   callback.Run(response);
    163 }
    164 
    165 void CloudPrintRequester::OnFetchError(const std::string& server_api,
    166                                        int server_code,
    167                                        int server_http_code) {
    168   VLOG(3) << "Function: " << __FUNCTION__;
    169   EraseRequest();
    170   current_print_job_.reset();
    171   delegate_->OnServerError("Fetch error");
    172 
    173   // TODO(maksymb): |server_api| and other
    174   NOTIMPLEMENTED();
    175 }
    176 
    177 void CloudPrintRequester::OnFetchTimeoutReached() {
    178   VLOG(3) << "Function: " << __FUNCTION__;
    179   EraseRequest();
    180   current_print_job_.reset();
    181   delegate_->OnNetworkError();
    182 }
    183 
    184 void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token,
    185                                               const std::string& access_token,
    186                                               int expires_in_seconds) {
    187   VLOG(3) << "Function: " << __FUNCTION__;
    188   gaia_.reset();
    189   delegate_->OnGetAuthCodeResponseParsed(refresh_token,
    190                                          access_token, expires_in_seconds);
    191 }
    192 
    193 void CloudPrintRequester::OnRefreshTokenResponse(
    194     const std::string& access_token,
    195     int expires_in_seconds) {
    196   VLOG(3) << "Function: " << __FUNCTION__;
    197   gaia_.reset();
    198   delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds);
    199 }
    200 
    201 void CloudPrintRequester::OnOAuthError() {
    202   VLOG(3) << "Function: " << __FUNCTION__;
    203   gaia_.reset();
    204   delegate_->OnAuthError();
    205 }
    206 
    207 void CloudPrintRequester::OnNetworkError(int response_code) {
    208   VLOG(3) << "Function: " << __FUNCTION__;
    209   gaia_.reset();
    210 
    211   if (response_code == net::HTTP_FORBIDDEN) {
    212     // TODO(maksymb): delegate_->OnPrinterDeleted();
    213   } else {
    214     delegate_->OnNetworkError();
    215   }
    216 }
    217 
    218 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet(
    219     const GURL& url,
    220     const ParserCallback& parser_callback) {
    221   DCHECK(!IsBusy());
    222   DCHECK(parser_callback_.is_null());
    223   parser_callback_ = parser_callback;
    224   return CloudPrintRequest::CreateGet(url, this);
    225 }
    226 
    227 scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost(
    228     const GURL& url,
    229     const std::string& content,
    230     const std::string& mimetype,
    231     const ParserCallback& parser_callback) {
    232   DCHECK(!IsBusy());
    233   DCHECK(parser_callback_.is_null());
    234   parser_callback_ = parser_callback;
    235   return CloudPrintRequest::CreatePost(url, content, mimetype, this);
    236 }
    237 
    238 void CloudPrintRequester::EraseRequest() {
    239   DCHECK(request_);
    240   DCHECK(!parser_callback_.is_null());
    241   request_.reset();
    242   parser_callback_.Reset();
    243 }
    244 
    245 void CloudPrintRequester::ParseRegisterStart(const std::string& response) {
    246   std::string error_description;
    247   std::string polling_url;
    248   std::string registration_token;
    249   std::string complete_invite_url;
    250   std::string device_id;
    251 
    252   bool success = cloud_print_response_parser::ParseRegisterStartResponse(
    253       response,
    254       &error_description,
    255       &polling_url,
    256       &registration_token,
    257       &complete_invite_url,
    258       &device_id);
    259 
    260   if (success) {
    261     polling_url_ = polling_url;
    262     delegate_->OnRegistrationStartResponseParsed(registration_token,
    263                                                  complete_invite_url,
    264                                                  device_id);
    265   } else {
    266     delegate_->OnRegistrationError(error_description);
    267   }
    268 }
    269 
    270 void CloudPrintRequester::ParseRegisterComplete(const std::string& response) {
    271   std::string error_description;
    272   std::string authorization_code;
    273 
    274   std::string xmpp_jid;
    275   bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
    276       response,
    277       &error_description,
    278       &authorization_code,
    279       &xmpp_jid);
    280 
    281   if (success) {
    282     delegate_->OnXmppJidReceived(xmpp_jid);
    283 
    284     gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
    285     gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
    286                                  kGaiaMaxRetries, this);
    287   } else {
    288     delegate_->OnRegistrationError(error_description);
    289   }
    290 }
    291 
    292 void CloudPrintRequester::ParseFetch(const std::string& response) {
    293   VLOG(3) << "Function: " << __FUNCTION__;
    294 
    295   std::string error_description;
    296   std::vector<Job> list;
    297   bool success = cloud_print_response_parser::ParseFetchResponse(
    298       response,
    299       &error_description,
    300       &list);
    301 
    302   if (success) {
    303     delegate_->OnPrintJobsAvailable(list);
    304   } else {
    305     delegate_->OnServerError(error_description);
    306   }
    307 }
    308 
    309 void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) {
    310   VLOG(3) << "Function: " << __FUNCTION__;
    311   current_print_job_->ticket = response;
    312 
    313   DCHECK(current_print_job_);
    314   request_ = CreateGet(
    315       GURL(current_print_job_->file_url),
    316       base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr()));
    317   request_->AddHeader("Accept: \"application/pdf\"");
    318   request_->Run(delegate_->GetAccessToken(), context_getter_);
    319 }
    320 
    321 void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) {
    322   VLOG(3) << "Function: " << __FUNCTION__;
    323   current_print_job_->file = response;
    324   DCHECK(current_print_job_);
    325   delegate_->OnPrintJobDownloaded(*current_print_job_);
    326 }
    327 
    328 void CloudPrintRequester::ParsePrintJobDone(const std::string& response) {
    329   VLOG(3) << "Function: " << __FUNCTION__;
    330   current_print_job_.reset();
    331   delegate_->OnPrintJobDone();
    332 }
    333 
    334 void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) {
    335   VLOG(3) << "Function: " << __FUNCTION__;
    336   DCHECK(current_print_job_);
    337   request_ = CreateGet(
    338       GURL(current_print_job_->ticket_url),
    339       base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr()));
    340   request_->Run(delegate_->GetAccessToken(), context_getter_);
    341 }
    342 
    343