blob: d46fdf4acc40028f6c87bd831d963b1998cc9331 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/gcp20/prototype/cloud_print_requester.h"
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/md5.h"
#include "base/message_loop/message_loop.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h"
#include "google_apis/google_api_keys.h"
#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/url_request/url_request_context.h"
#include "url/gurl.h"
const char kCloudPrintUrl[] = "https://www.google.com/cloudprint";
namespace {
const char kProxyIdValue[] = "proxy";
const char kPrinterNameValue[] = "printer";
const char kPrinterCapsValue[] = "capabilities";
const char kPrinterCapsHashValue[] = "capsHash";
const char kPrinterUserValue[] = "user";
const char kPrinterGcpVersion[] = "gcp_version";
const char kPrinterLocalSettings[] = "local_settings";
const char kPrinterFirmware[] = "firmware";
const char kPrinterManufacturer[] = "manufacturer";
const char kPrinterModel[] = "model";
const char kPrinterSetupUrl[] = "setup_url";
const char kPrinterSupportUrl[] = "support_url";
const char kPrinterUpdateUrl[] = "update_url";
const char kFirmwareValue[] = "2.0";
const char kManufacturerValue[] = "Google";
const char kModelValue[] = "GCPPrototype";
// TODO(maksymb): Replace GCP Version with "2.0" once GCP Server will support it
const char kGcpVersion[] = "1.5";
const int kGaiaMaxRetries = 3;
GURL CreateRegisterUrl() {
return GURL(std::string(kCloudPrintUrl) + "/register");
}
GURL CreateFetchUrl(const std::string& device_id) {
GURL url(std::string(kCloudPrintUrl) + "/fetch");
url = net::AppendQueryParameter(url, "printerid", device_id);
return url;
}
GURL CreateControlUrl(const std::string& job_id, const std::string& status) {
GURL url(std::string(kCloudPrintUrl) + "/control");
url = net::AppendQueryParameter(url, "jobid", job_id);
url = net::AppendQueryParameter(url, "status", status);
return url;
}
GURL CreatePrinterUrl(const std::string& device_id) {
GURL url(std::string(kCloudPrintUrl) + "/printer");
url = net::AppendQueryParameter(url, "printerid", device_id);
return url;
}
GURL CreateUpdateUrl(const std::string& device_id) {
GURL url(std::string(kCloudPrintUrl) + "/update");
url = net::AppendQueryParameter(url, "printerid", device_id);
return url;
}
std::string LocalSettingsToJson(const LocalSettings& settings) {
base::DictionaryValue dictionary;
scoped_ptr<base::DictionaryValue> current(new base::DictionaryValue);
// TODO(maksymb): Formalize text as constants.
current->SetBoolean("local_discovery", settings.local_discovery);
current->SetBoolean("access_token_enabled", settings.access_token_enabled);
current->SetBoolean("printer/local_printing_enabled",
settings.local_printing_enabled);
current->SetInteger("xmpp_timeout_value", settings.xmpp_timeout_value);
dictionary.Set("current", current.release());
std::string local_settings;
base::JSONWriter::Write(&dictionary, &local_settings);
return local_settings;
}
} // namespace
using cloud_print_response_parser::Job;
CloudPrintRequester::CloudPrintRequester(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
Delegate* delegate)
: context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
delegate_(delegate) {
oauth_client_info_.client_id =
google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
oauth_client_info_.client_secret =
google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
oauth_client_info_.redirect_uri = "oob";
}
CloudPrintRequester::~CloudPrintRequester() {
}
bool CloudPrintRequester::IsBusy() const {
return request_ || gaia_;
}
void CloudPrintRequester::StartRegistration(const std::string& proxy_id,
const std::string& device_name,
const std::string& user,
const LocalSettings& settings,
const std::string& cdd) {
std::string mime_boundary;
int r1 = base::RandInt(0, kint32max);
int r2 = base::RandInt(0, kint32max);
base::SStringPrintf(&mime_boundary,
"---------------------------%08X%08X", r1, r2);
std::string data;
std::string data_mimetype;
data_mimetype = "multipart/form-data; boundary=" + mime_boundary;
net::AddMultipartValueForUpload(kProxyIdValue, proxy_id, mime_boundary,
std::string(), &data);
net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
std::string(), &data);
net::AddMultipartValueForUpload("use_cdd", "true", mime_boundary,
std::string(), &data);
net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
std::string(), &data);
net::AddMultipartValueForUpload(kPrinterCapsValue, cdd, mime_boundary,
"application/json", &data);
net::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(cdd),
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterUserValue, user,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterGcpVersion, kGcpVersion,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterLocalSettings,
LocalSettingsToJson(settings),
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterFirmware,
kFirmwareValue,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterManufacturer,
kManufacturerValue,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterModel,
kModelValue,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterSetupUrl,
kCloudPrintUrl,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterSupportUrl,
kCloudPrintUrl,
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterUpdateUrl,
kCloudPrintUrl,
mime_boundary, std::string(), &data);
net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data);
request_ = CreatePost(
CreateRegisterUrl(),
data,
data_mimetype,
base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::CompleteRegistration() {
request_ = CreateGet(
GURL(polling_url_ + oauth_client_info_.client_id),
base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) {
VLOG(3) << "Function: " << __FUNCTION__;
if (IsBusy())
return;
DCHECK(!delegate_->GetAccessToken().empty());
VLOG(3) << "Function: " << __FUNCTION__ <<
": request created";
request_ = CreateGet(
CreateFetchUrl(device_id),
base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) {
VLOG(3) << "Function: " << __FUNCTION__;
DCHECK(!IsBusy());
gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
gaia_->RefreshToken(oauth_client_info_, refresh_token,
std::vector<std::string>(), kGaiaMaxRetries, this);
}
void CloudPrintRequester::RequestPrintJob(const Job& job) {
VLOG(3) << "Function: " << __FUNCTION__;
current_print_job_.reset(new Job(job));
request_ = CreateGet(
CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"),
base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) {
VLOG(3) << "Function: " << __FUNCTION__;
request_ = CreateGet(
CreateControlUrl(job_id, "DONE"),
base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::RequestLocalSettings(const std::string& device_id) {
VLOG(3) << "Function: " << __FUNCTION__;
request_ = CreateGet(
CreatePrinterUrl(device_id),
base::Bind(&CloudPrintRequester::ParseLocalSettings, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::SendLocalSettings(
const std::string& device_id,
const LocalSettings& settings) {
VLOG(3) << "Function: " << __FUNCTION__;
std::string data_mimetype = "application/x-www-form-urlencoded";
std::string data = base::StringPrintf(
"%s=%s",
kPrinterLocalSettings,
net::EscapeUrlEncodedData(LocalSettingsToJson(settings), false).c_str());
request_ = CreatePost(
CreateUpdateUrl(device_id),
data, data_mimetype,
base::Bind(&CloudPrintRequester::ParseLocalSettingUpdated, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::OnFetchComplete(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
ParserCallback callback = parser_callback_;
EraseRequest();
callback.Run(response);
}
void CloudPrintRequester::OnFetchError(const std::string& server_api,
int server_code,
int server_http_code) {
VLOG(3) << "Function: " << __FUNCTION__;
EraseRequest();
current_print_job_.reset();
if (server_http_code == net::HTTP_FORBIDDEN) {
delegate_->OnAuthError();
} else {
delegate_->OnServerError("Fetch error");
}
// TODO(maksymb): Add Privet |server_http_code| and |server_api| support in
// case of server errors.
}
void CloudPrintRequester::OnFetchTimeoutReached() {
VLOG(3) << "Function: " << __FUNCTION__;
EraseRequest();
current_print_job_.reset();
delegate_->OnNetworkError();
}
void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token,
const std::string& access_token,
int expires_in_seconds) {
VLOG(3) << "Function: " << __FUNCTION__;
gaia_.reset();
delegate_->OnRegistrationFinished(refresh_token,
access_token, expires_in_seconds);
}
void CloudPrintRequester::OnRefreshTokenResponse(
const std::string& access_token,
int expires_in_seconds) {
VLOG(3) << "Function: " << __FUNCTION__;
gaia_.reset();
delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds);
}
void CloudPrintRequester::OnOAuthError() {
VLOG(3) << "Function: " << __FUNCTION__;
gaia_.reset();
delegate_->OnAuthError();
}
void CloudPrintRequester::OnNetworkError(int response_code) {
VLOG(3) << "Function: " << __FUNCTION__;
gaia_.reset();
if (response_code == net::HTTP_FORBIDDEN) {
// TODO(maksymb): delegate_->OnPrinterDeleted();
} else {
delegate_->OnNetworkError();
}
}
scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet(
const GURL& url,
const ParserCallback& parser_callback) {
DCHECK(!IsBusy());
DCHECK(parser_callback_.is_null());
parser_callback_ = parser_callback;
return CloudPrintRequest::CreateGet(url, this);
}
scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost(
const GURL& url,
const std::string& content,
const std::string& mimetype,
const ParserCallback& parser_callback) {
DCHECK(!IsBusy());
DCHECK(parser_callback_.is_null());
parser_callback_ = parser_callback;
return CloudPrintRequest::CreatePost(url, content, mimetype, this);
}
void CloudPrintRequester::EraseRequest() {
DCHECK(request_);
DCHECK(!parser_callback_.is_null());
request_.reset();
parser_callback_.Reset();
}
void CloudPrintRequester::ParseRegisterStart(const std::string& response) {
std::string error_description;
std::string polling_url;
std::string registration_token;
std::string complete_invite_url;
std::string device_id;
bool success = cloud_print_response_parser::ParseRegisterStartResponse(
response,
&error_description,
&polling_url,
&registration_token,
&complete_invite_url,
&device_id);
if (success) {
polling_url_ = polling_url;
delegate_->OnRegistrationStartResponseParsed(registration_token,
complete_invite_url,
device_id);
} else {
delegate_->OnRegistrationError(error_description);
}
}
void CloudPrintRequester::ParseRegisterComplete(const std::string& response) {
std::string error_description;
std::string authorization_code;
std::string xmpp_jid;
bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
response,
&error_description,
&authorization_code,
&xmpp_jid);
if (success) {
delegate_->OnXmppJidReceived(xmpp_jid);
gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
kGaiaMaxRetries, this);
} else {
delegate_->OnRegistrationError(error_description);
}
}
void CloudPrintRequester::ParseFetch(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
std::string error_description;
std::vector<Job> list;
bool success = cloud_print_response_parser::ParseFetchResponse(
response,
&error_description,
&list);
if (success) {
delegate_->OnPrintJobsAvailable(list);
} else {
delegate_->OnServerError(error_description);
}
}
void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
current_print_job_->ticket = response;
DCHECK(current_print_job_);
request_ = CreateGet(
GURL(current_print_job_->file_url),
base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr()));
request_->AddHeader("Accept: \"application/pdf\"");
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
current_print_job_->file = response;
DCHECK(current_print_job_);
delegate_->OnPrintJobDownloaded(*current_print_job_);
}
void CloudPrintRequester::ParsePrintJobDone(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
current_print_job_.reset();
delegate_->OnPrintJobDone();
}
void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
DCHECK(current_print_job_);
request_ = CreateGet(
GURL(current_print_job_->ticket_url),
base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr()));
request_->Run(delegate_->GetAccessToken(), context_getter_);
}
void CloudPrintRequester::ParseLocalSettings(const std::string& response) {
VLOG(3) << "Function: " << __FUNCTION__;
std::string error_description;
LocalSettings settings;
LocalSettings::State state;
bool success = cloud_print_response_parser::ParseLocalSettingsResponse(
response,
&error_description,
&state,
&settings);
if (success) {
delegate_->OnLocalSettingsReceived(state, settings);
} else {
delegate_->OnServerError(error_description);
}
}
void CloudPrintRequester::ParseLocalSettingUpdated(
const std::string& response) {
delegate_->OnLocalSettingsUpdated();
}