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 ®istration_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