diff options
author | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-26 11:24:40 +0000 |
---|---|---|
committer | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-26 11:24:40 +0000 |
commit | 12bfcb19c36e0afb3f445de1464b836b549647c6 (patch) | |
tree | 93e7e545c34eb3cd41220ece73e4036f3a06f271 /cloud_print/gcp20 | |
parent | 9e93a9be5f8ef5e623e8d836c00e2cd6216173e5 (diff) | |
download | chromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.zip chromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.tar.gz chromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.tar.bz2 |
GCP2.0 Device: Receiving printjobs.
New request (with timeouts).
BUG=
Review URL: https://chromiumcodereview.appspot.com/19866002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213839 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print/gcp20')
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_request.cc | 118 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_request.h | 79 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.cc | 293 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.h | 117 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_response_parser.cc | 92 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_response_parser.h | 32 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_sd_server.h | 6 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/gcp20_device.gyp | 4 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/print_job_handler.cc | 63 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/print_job_handler.h | 27 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.cc | 145 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.h | 50 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.cc | 3 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.h | 3 |
14 files changed, 880 insertions, 152 deletions
diff --git a/cloud_print/gcp20/prototype/cloud_print_request.cc b/cloud_print/gcp20/prototype/cloud_print_request.cc new file mode 100644 index 0000000..2d36dcc --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_request.cc @@ -0,0 +1,118 @@ +// 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_request.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" + +using net::URLFetcher; +using base::MessageLoop; + +namespace { + +const uint32 kDefaultTimeout = 20; // in seconds + +} // namespace + +CloudPrintRequest::CloudPrintRequest(const GURL& url, + URLFetcher::RequestType method, + Delegate* delegate) + : fetcher_(URLFetcher::Create(url, method, this)), + delegate_(delegate) { + int load_flags = fetcher_->GetLoadFlags(); + load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; + load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; + fetcher_->SetLoadFlags(load_flags); + + fetcher_->AddExtraRequestHeader("X-CloudPrint-Proxy: \"\""); +} + +CloudPrintRequest::~CloudPrintRequest() { +} + +scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreateGet( + const GURL& url, + Delegate* delegate) { + return scoped_ptr<CloudPrintRequest>( + new CloudPrintRequest(url, URLFetcher::GET, delegate)); +} + +scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreatePost( + const GURL& url, + const std::string& content, + const std::string& mimetype, + Delegate* delegate) { + scoped_ptr<CloudPrintRequest> request( + new CloudPrintRequest(url, URLFetcher::POST, delegate)); + request->fetcher_->SetUploadData(mimetype, content); + return request.Pass(); +} + +void CloudPrintRequest::Run( + const std::string& access_token, + scoped_refptr<net::URLRequestContextGetter> context_getter) { + + if (!access_token.empty()) + fetcher_->AddExtraRequestHeader(base::StringPrintf( + "Authorization: Bearer \"%s\"", access_token.c_str())); + + fetcher_->SetRequestContext(context_getter); + fetcher_->Start(); + + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&CloudPrintRequest::OnRequestTimeout, AsWeakPtr()), + base::TimeDelta::FromSeconds(kDefaultTimeout)); +} + +void CloudPrintRequest::AddHeader(const std::string& header) { + fetcher_->AddExtraRequestHeader(header); +} + +void CloudPrintRequest::OnRequestTimeout() { + if (!fetcher_) + return; + fetcher_.reset(); + LOG(WARNING) << "Request timeout reached."; + + DCHECK(delegate_); + delegate_->OnFetchTimeoutReached(); // After this object can be deleted. + // Do *NOT* access members after this + // call. +} + +void CloudPrintRequest::OnURLFetchComplete(const URLFetcher* source) { + DCHECK(source == fetcher_.get()); + std::string response; + source->GetResponseAsString(&response); + + int http_code = fetcher_->GetResponseCode(); + fetcher_.reset(); + VLOG(3) << response; + + DCHECK(delegate_); + if (http_code == net::HTTP_OK) { + delegate_->OnFetchComplete(response); // After this object can be deleted. + // Do *NOT* access members after + // this call. + + } else { + // TODO(maksymb): Add |server_http_code| and |server_api| info for errors. + delegate_->OnFetchError("dummy", -1, http_code); // After this object can + // be deleted. + // Do *NOT* access members + // after this call. + + NOTIMPLEMENTED() << "HTTP code: " << http_code; + } +} + diff --git a/cloud_print/gcp20/prototype/cloud_print_request.h b/cloud_print/gcp20/prototype/cloud_print_request.h new file mode 100644 index 0000000..5adfe35 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_request.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_delegate.h" + +// Request to CloudPrint with timeout control. +// Delegate should delete this object once it is deleting. +class CloudPrintRequest : public net::URLFetcherDelegate, + public base::SupportsWeakPtr<CloudPrintRequest> { + public: + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + + // Invoked when |fetcher_| finished fetching successfully. + // Use for erasing instance of CloudPrintRequest class. + virtual void OnFetchComplete(const std::string& response) = 0; + + // Invoked when |fetcher_| finished fetching successfully. + // Use for erasing instance of CloudPrintRequest class. + virtual void OnFetchError(const std::string& server_api, + int server_code, + int server_http_code) = 0; + + // Invoked when timeout is reached. + // Use for erasing instance of CloudPrintRequest class. + virtual void OnFetchTimeoutReached() = 0; + }; + + virtual ~CloudPrintRequest(); + + // Creates GET request. + static scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url, + Delegate* delegate); + + // Creates POST request. + static scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url, + const std::string& content, + const std::string& mimetype, + Delegate* delegate); + + // Starts request. Once fetch was completed, parser will be called. + void Run(const std::string& access_token, + scoped_refptr<net::URLRequestContextGetter> context_getter); + + // Add header to request. + void AddHeader(const std::string& header); + + private: + CloudPrintRequest(const GURL& url, + net::URLFetcher::RequestType method, + Delegate* delegate); + + // net::URLFetcherDelegate methods: + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + // Method for handling timeout. + void OnRequestTimeout(); + + scoped_ptr<net::URLFetcher> fetcher_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintRequest); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ + diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc index b0c6169..f0d1926 100644 --- a/cloud_print/gcp20/prototype/cloud_print_requester.cc +++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc @@ -4,18 +4,18 @@ #include "cloud_print/gcp20/prototype/cloud_print_requester.h" +#include "base/bind.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_response_parser.h" #include "google_apis/google_api_keys.h" -#include "net/base/load_flags.h" #include "net/base/mime_util.h" #include "net/base/url_util.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" const char kCloudPrintUrl[] = "https://www.google.com/cloudprint"; @@ -34,8 +34,23 @@ 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; +} + } // namespace +using cloud_print_response_parser::Job; + // Used to return a dummy context, which lives on the message loop // given in the constructor. class CloudPrintURLRequestContextGetter : public net::URLRequestContextGetter { @@ -89,7 +104,11 @@ CloudPrintRequester::CloudPrintRequester( CloudPrintRequester::~CloudPrintRequester() { } -bool CloudPrintRequester::StartRegistration(const std::string& proxy_id, +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 std::string& cdd) { @@ -100,65 +119,114 @@ bool CloudPrintRequester::StartRegistration(const std::string& proxy_id, "---------------------------%08X%08X", r1, r2); std::string data; - 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); + 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::AddMultipartFinalDelimiterForUpload(mime_boundary, &data); - std::string mime_type("multipart/form-data; boundary=" + mime_boundary); + request_ = CreatePost( + CreateRegisterUrl(), + data, + data_mimetype, + base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr())); + request_->Run(access_token_, context_getter_); +} + +void CloudPrintRequester::CompleteRegistration() { + request_ = CreateGet( + GURL(polling_url_ + oauth_client_info_.client_id), + base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr())); + request_->Run(access_token_, context_getter_); +} + +void CloudPrintRequester::FetchPrintJobs(const std::string& refresh_token, + const std::string& device_id) { + VLOG(3) << "Function: " << __FUNCTION__; + if (IsBusy()) + return; - if (!CreateRequest( - CreateRegisterUrl(), net::URLFetcher::POST, - base::Bind(&CloudPrintRequester::ParseRegisterStartResponse, - AsWeakPtr()))) { - return false; + if (access_token_.empty()) { + UpdateAccesstoken(refresh_token); + return; } - fetcher_->SetUploadData(mime_type, data); - fetcher_->Start(); - return true; + VLOG(3) << "Function: " << __FUNCTION__ << + ": request created"; + request_ = CreateGet( + CreateFetchUrl(device_id), + base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr())); + request_->Run(access_token_, context_getter_); } -bool CloudPrintRequester::CompleteRegistration() { - if (!CreateRequest( - GURL(polling_url_ + oauth_client_info_.client_id), - net::URLFetcher::GET, - base::Bind(&CloudPrintRequester::ParseRegisterCompleteResponse, - AsWeakPtr()))) { - return false; - } - fetcher_->Start(); +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); + delegate_->OnServerError("Access token requested."); +} - return true; +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(access_token_, context_getter_); } -void CloudPrintRequester::OnURLFetchComplete(const net::URLFetcher* source) { - std::string response; - source->GetResponseAsString(&response); - fetcher_.reset(); - VLOG(1) << response; +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(access_token_, context_getter_); +} - // TODO(maksymb): Add |server_http_code| and |server_api| info for errors. - if (!parse_response_callback_.is_null()) { - parse_response_callback_.Run(response); - parse_response_callback_.Reset(); - } +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(); + delegate_->OnServerError("Fetch error"); + NOTIMPLEMENTED(); +} + +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(); access_token_ = access_token; delegate_->OnGetAuthCodeResponseParsed(refresh_token); } @@ -166,42 +234,53 @@ void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token, void CloudPrintRequester::OnRefreshTokenResponse( const std::string& access_token, int expires_in_seconds) { - NOTIMPLEMENTED(); + VLOG(3) << "Function: " << __FUNCTION__; + gaia_.reset(); + access_token_ = access_token; + LOG(INFO) << "New accesstoken: " << access_token; } void CloudPrintRequester::OnOAuthError() { + VLOG(3) << "Function: " << __FUNCTION__; + gaia_.reset(); NOTIMPLEMENTED(); } void CloudPrintRequester::OnNetworkError(int response_code) { + VLOG(3) << "Function: " << __FUNCTION__; + gaia_.reset(); NOTIMPLEMENTED(); } -bool CloudPrintRequester::CreateRequest(const GURL& url, - net::URLFetcher::RequestType method, - const ParserCallback& callback) { - if (fetcher_) - return false; // Only one query is supported. - - fetcher_.reset(net::URLFetcher::Create(url, method, this)); - fetcher_->SetRequestContext(context_getter_); - - int load_flags = fetcher_->GetLoadFlags(); - load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; - load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; - fetcher_->SetLoadFlags(load_flags); - - fetcher_->AddExtraRequestHeader("X-CloudPrint-Proxy: \"\""); - if (!access_token_.empty()) - fetcher_->AddExtraRequestHeader("Authorization: Bearer " + access_token_); +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); +} - parse_response_callback_ = callback; +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); +} - return true; +void CloudPrintRequester::EraseRequest() { + DCHECK(request_); + DCHECK(!parser_callback_.is_null()); + request_.reset(); + parser_callback_.Reset(); } -void CloudPrintRequester::ParseRegisterStartResponse( - const std::string& response) { +void CloudPrintRequester::ParseRegisterStart(const std::string& response) { + std::string error_description; std::string polling_url; std::string registration_token; std::string complete_invite_url; @@ -209,36 +288,88 @@ void CloudPrintRequester::ParseRegisterStartResponse( bool success = cloud_print_response_parser::ParseRegisterStartResponse( response, - base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError, - base::Unretained(delegate_)), + &error_description, &polling_url, ®istration_token, &complete_invite_url, &device_id); - if (!success) - return; - - polling_url_ = polling_url; - delegate_->OnRegistrationStartResponseParsed(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::ParseRegisterCompleteResponse( - const std::string& response) { +void CloudPrintRequester::ParseRegisterComplete(const std::string& response) { + std::string error_description; std::string authorization_code; bool success = cloud_print_response_parser::ParseRegisterCompleteResponse( response, - base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError, - base::Unretained(delegate_)), + &error_description, &authorization_code); - if (!success) - return; + if (success) { + gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get())); + gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code, + kGaiaMaxRetries, this); + } else { + delegate_->OnRegistrationError(error_description); + } +} - gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get())); - gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code, - kGaiaMaxRetries, this); +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(access_token_, 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(access_token_, context_getter_); } diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h index 663ea7b..49031ad 100644 --- a/cloud_print/gcp20/prototype/cloud_print_requester.h +++ b/cloud_print/gcp20/prototype/cloud_print_requester.h @@ -5,28 +5,26 @@ #ifndef CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ #define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ -#include <map> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/values.h" +#include "cloud_print/gcp20/prototype/cloud_print_request.h" +#include "cloud_print/gcp20/prototype/cloud_print_response_parser.h" #include "google_apis/gaia/gaia_oauth_client.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" -#include "net/url_request/url_request_context_getter.h" - -typedef base::Callback<void(const std::string&)> ParserCallback; class CloudPrintURLRequestContextGetter; +class GURL; extern const char kCloudPrintUrl[]; // Class for requesting CloudPrint server and parsing responses. class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, - public net::URLFetcherDelegate, - public gaia::GaiaOAuthClient::Delegate { + public gaia::GaiaOAuthClient::Delegate, + public CloudPrintRequest::Delegate { public: class Delegate { public: @@ -48,6 +46,23 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // Invoked when server respond with |"success" = false| or we cannot parse // response. virtual void OnRegistrationError(const std::string& description) = 0; + + // Invoked when network connection cannot be established. + virtual void OnNetworkError() = 0; + + // Invoked when server error is received or cannot parse json response. + virtual void OnServerError(const std::string& description) = 0; + + // Invoked when fetch response was received. + virtual void OnPrintJobsAvailable( + const std::vector<cloud_print_response_parser::Job>& jobs) = 0; + + // Invoked when printjob is finally downloaded and available for printing. + virtual void OnPrintJobDownloaded( + const cloud_print_response_parser::Job& job) = 0; + + // Invoked when printjob is marked as done on CloudPrint server. + virtual void OnPrintJobDone() = 0; }; // Creates and initializes objects. @@ -57,20 +72,42 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // Destroys the object. virtual ~CloudPrintRequester(); + // Returns |true| if either |gaia| or |request| is awaiting for response. + bool IsBusy() const; + // Creates query to server for starting registration. - bool StartRegistration(const std::string& proxy_id, + void StartRegistration(const std::string& proxy_id, const std::string& device_name, - const std::string& user, - const std::string& cdd); + const std::string& user, const std::string& cdd); // Creates request for completing registration and receiving refresh token. - bool CompleteRegistration(); + void CompleteRegistration(); + + // Creates request for fetching printjobs. + void FetchPrintJobs(const std::string& refresh_token, + const std::string& device_id); + + // Creates request for updating accesstoken. + // TODO(maksymb): Handle expiration of accesstoken. + void UpdateAccesstoken(const std::string& refresh_token); + + // Creates chain of requests for requesting printjob. + void RequestPrintJob(const cloud_print_response_parser::Job& job); + + // Reports server that printjob has been printed. + void SendPrintJobDone(const std::string& job_id); private: - // net::URLFetcherDelegate - virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + typedef base::Callback<void(const std::string&)> ParserCallback; + + // CloudPrintRequester::Delegate methods: + virtual void OnFetchComplete(const std::string& response) OVERRIDE; + virtual void OnFetchError(const std::string& server_api, + int server_code, + int server_http_code) OVERRIDE; + virtual void OnFetchTimeoutReached() OVERRIDE; - // gaia::GaiaOAuthClient::Delegate + // gaia::GaiaOAuthClient::Delegate methods: virtual void OnGetTokensResponse(const std::string& refresh_token, const std::string& access_token, int expires_in_seconds) OVERRIDE; @@ -79,26 +116,48 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, virtual void OnOAuthError() OVERRIDE; virtual void OnNetworkError(int response_code) OVERRIDE; - // Creates request with given |url| and |method|. When response is received - // callback is called. - // TODO(maksymb): Add timeout control for request. - bool CreateRequest(const GURL& url, - net::URLFetcher::RequestType method, - const ParserCallback& callback) WARN_UNUSED_RESULT; + // Creates GET request. + scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url, + const ParserCallback& callback); + + // Creates POST request. + scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url, + const std::string& content, + const std::string& mimetype, + const ParserCallback& callback); + + // Deletes all info about current request. + void EraseRequest(); // Parses register-start server response. - void ParseRegisterStartResponse(const std::string& response); + void ParseRegisterStart(const std::string& response); // Parses register-complete server response. Initializes gaia (OAuth client) // and receives refresh token. - void ParseRegisterCompleteResponse(const std::string& response); + void ParseRegisterComplete(const std::string& response); + + // Parses fetch printjobs server response. + void ParseFetch(const std::string& response); - // Fetcher contains |NULL| if no server response is awaiting. Otherwise wait - // until URLFetchComplete will be called and close connection. - scoped_ptr<net::URLFetcher> fetcher_; + // Invoked after receiving printjob ticket. + void ParseGetPrintJobTicket(const std::string& response); - // Callback for parsing server response. - ParserCallback parse_response_callback_; + // Invoked after receiving printjob file. + void ParseGetPrintJobData(const std::string& response); + + // Invoked after marking printjob as DONE. + void ParsePrintJobDone(const std::string& response); + + // Invoked after marking printjob as IN_PROGRESS. + void ParsePrintJobInProgress(const std::string& response); + + // |request| contains |NULL| if no server response is awaiting. Otherwise wait + // until callback will be called will be called and close connection. + scoped_ptr<CloudPrintRequest> request_; + + // Contains information about current printjob. Information is filled by + // CloudPrint server responses. + scoped_ptr<cloud_print_response_parser::Job> current_print_job_; // Privet context getter. scoped_refptr<CloudPrintURLRequestContextGetter> context_getter_; @@ -115,6 +174,8 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // OAuth client. scoped_ptr<gaia::GaiaOAuthClient> gaia_; + ParserCallback parser_callback_; + Delegate* delegate_; DISALLOW_COPY_AND_ASSIGN(CloudPrintRequester); diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc index fa2c5f7..d4da3c3 100644 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc @@ -10,28 +10,34 @@ namespace cloud_print_response_parser { +Job::Job() { +} + +Job::~Job() { +} + // Parses json base::Value to base::DictionaryValue and checks |success| status. // Returns |true| on success. bool GetJsonDictinaryAndCheckSuccess(base::Value* json, - const ErrorCallback error_callback, + std::string* error_description, base::DictionaryValue** dictionary) { base::DictionaryValue* response_dictionary = NULL; if (!json || !json->GetAsDictionary(&response_dictionary)) { - error_callback.Run("No JSON dictionary response received."); + *error_description = "No JSON dictionary response received."; return false; } bool success = false; if (!response_dictionary->GetBoolean("success", &success)) { - error_callback.Run("Cannot parse success state."); + *error_description = "Cannot parse success state."; return false; } if (!success) { std::string message; response_dictionary->GetString("message", &message); - error_callback.Run(message); + *error_description = message; return false; } @@ -40,14 +46,14 @@ bool GetJsonDictinaryAndCheckSuccess(base::Value* json, } bool ParseRegisterStartResponse(const std::string& response, - const ErrorCallback error_callback, + std::string* error_description, std::string* polling_url_result, std::string* registration_token_result, std::string* complete_invite_url_result, std::string* device_id_result) { scoped_ptr<base::Value> json(base::JSONReader::Read(response)); base::DictionaryValue* response_dictionary = NULL; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_callback, + if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, &response_dictionary)) { return false; } @@ -55,49 +61,49 @@ bool ParseRegisterStartResponse(const std::string& response, std::string registration_token; if (!response_dictionary->GetString("registration_token", ®istration_token)) { - error_callback.Run("No registration_token specified."); + *error_description = "No registration_token specified."; return false; } std::string complete_invite_url; if (!response_dictionary->GetString("complete_invite_url", &complete_invite_url)) { - error_callback.Run("No complete_invite_url specified."); + *error_description = "No complete_invite_url specified."; return false; } std::string polling_url; if (!response_dictionary->GetString("polling_url", &polling_url)) { - error_callback.Run("No polling_url specified."); + *error_description = "No polling_url specified."; return false; } base::ListValue* list = NULL; if (!response_dictionary->GetList("printers", &list)) { - error_callback.Run("No printers list specified."); + *error_description = "No printers list specified."; return false; } base::Value* printer_value = NULL; if (!list->Get(0, &printer_value)) { - error_callback.Run("Printers list is empty."); + *error_description = "Printers list is empty."; return false; } base::DictionaryValue* printer = NULL; if (!printer_value->GetAsDictionary(&printer)) { - error_callback.Run("Printer is not a json-dictionary."); + *error_description = "Printer is not a json-dictionary."; return false; } std::string device_id; if (!printer->GetString("id", &device_id)) { - error_callback.Run("No id specified."); + *error_description = "No id specified."; return false; } if (device_id.empty()) { - error_callback.Run("id is empty."); + *error_description = "id is empty."; return false; } @@ -109,11 +115,11 @@ bool ParseRegisterStartResponse(const std::string& response, } bool ParseRegisterCompleteResponse(const std::string& response, - const ErrorCallback error_callback, + std::string* error_description, std::string* authorization_code_result) { scoped_ptr<base::Value> json(base::JSONReader::Read(response)); base::DictionaryValue* response_dictionary = NULL; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_callback, + if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, &response_dictionary)) { return false; } @@ -121,7 +127,7 @@ bool ParseRegisterCompleteResponse(const std::string& response, std::string authorization_code; if (!response_dictionary->GetString("authorization_code", &authorization_code)) { - error_callback.Run("Cannot parse authorization_code."); + *error_description = "Cannot parse authorization_code."; return false; } @@ -129,5 +135,57 @@ bool ParseRegisterCompleteResponse(const std::string& response, return true; } +bool ParseFetchResponse(const std::string& response, + std::string* error_description, + std::vector<Job>* list_result) { + scoped_ptr<base::Value> json(base::JSONReader::Read(response)); + base::DictionaryValue* response_dictionary = NULL; + + if (!json || !json->GetAsDictionary(&response_dictionary)) { + *error_description = "No JSON dictionary response received."; + return false; + } + + bool success = false; + if (!response_dictionary->GetBoolean("success", &success)) { + *error_description = "Cannot parse success state."; + return false; + } + + if (!success) { // Let's suppose we have no jobs to proceed. + list_result->clear(); + return true; + } + + if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, + &response_dictionary)) { + return false; + } + + base::ListValue* jobs = NULL; + if (!response_dictionary->GetList("jobs", &jobs)) { + *error_description = "Cannot parse jobs list."; + return false; + } + + std::vector<Job> list(jobs->GetSize()); + for (size_t idx = 0; idx < list.size(); ++idx) { + base::DictionaryValue* job = NULL; + jobs->GetDictionary(idx, &job); + if (!job->GetString("id", &list[idx].job_id) || + !job->GetString("createTime", &list[idx].create_time) || + !job->GetString("fileUrl", &list[idx].file_url) || + !job->GetString("ticketUrl", &list[idx].ticket_url) || + !job->GetString("title", &list[idx].title)) { + *error_description = "Cannot parse job info."; + return false; + } + } + + *list_result = list; + + return true; +} + } // namespace cloud_print_response_parser diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.h b/cloud_print/gcp20/prototype/cloud_print_response_parser.h index 1178301..b691934 100644 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.h +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.h @@ -6,6 +6,7 @@ #define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ #include <string> +#include <vector> #include "base/callback.h" @@ -17,14 +18,26 @@ class DictionaryValue; namespace cloud_print_response_parser { -// Callback for handling parse errors. -typedef base::Callback<void(const std::string&)> ErrorCallback; +struct Job { + Job(); + ~Job(); + + std::string job_id; + std::string create_time; + std::string file_url; + std::string ticket_url; + std::string title; + + // Downloaded data: + std::string file; + std::string ticket; +}; // Parses CloudPrint register start response to out parameters. // Returns |true| on success. Callback is called with description as a parameter -// when parsing failed. +// when parsing is failed. bool ParseRegisterStartResponse(const std::string& response, - const ErrorCallback error_callback, + std::string* error_description, std::string* polling_url_result, std::string* registration_token_result, std::string* complete_invite_url_result, @@ -32,11 +45,18 @@ bool ParseRegisterStartResponse(const std::string& response, // Parses CloudPrint register complete response to out parameters. // Returns |true| on success. Callback is called with description as a parameter -// when parsing failed. +// when parsing is failed. bool ParseRegisterCompleteResponse(const std::string& response, - const ErrorCallback error_callback, + std::string* error_description, std::string* authorization_code_result); +// Parses CloudPrint fetch response to out parameters. +// Returns |true| on success. Callback is called with description as a parameter +// when parsing is failed. +bool ParseFetchResponse(const std::string& response, + std::string* error_description, + std::vector<Job>* list_result); + } // namespace cloud_print_response_parser #endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h index a0d0128..f9aa540 100644 --- a/cloud_print/gcp20/prototype/dns_sd_server.h +++ b/cloud_print/gcp20/prototype/dns_sd_server.h @@ -37,7 +37,8 @@ class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> { // Starts the server. Returns |true| if server works. Also sends // announcement. - bool Start(const ServiceParameters& serv_params, uint32 full_ttl, + bool Start(const ServiceParameters& serv_params, + uint32 full_ttl, const std::vector<std::string>& metadata) WARN_UNUSED_RESULT; // Sends announcement if server works. @@ -57,7 +58,8 @@ class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> { bool CreateSocket(); // Processes single query. - void ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query, + void ProccessQuery(uint32 current_ttl, + const DnsQueryRecord& query, DnsResponseBuilder* builder) const; // Processes DNS message. diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp index bb83e45..e6f1777 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.gyp +++ b/cloud_print/gcp20/prototype/gcp20_device.gyp @@ -29,6 +29,8 @@ 'sources': [ 'cloud_print_response_parser.cc', 'cloud_print_response_parser.h', + 'cloud_print_request.cc', + 'cloud_print_request.h', 'cloud_print_requester.cc', 'cloud_print_requester.h', 'conio_posix.cc', @@ -41,6 +43,8 @@ 'dns_response_builder.h', 'dns_sd_server.cc', 'dns_sd_server.h', + 'print_job_handler.cc', + 'print_job_handler.h', 'printer.cc', 'printer.h', 'privet_http_server.cc', diff --git a/cloud_print/gcp20/prototype/print_job_handler.cc b/cloud_print/gcp20/prototype/print_job_handler.cc new file mode 100644 index 0000000..c7549e4 --- /dev/null +++ b/cloud_print/gcp20/prototype/print_job_handler.cc @@ -0,0 +1,63 @@ +// 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/print_job_handler.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" + +namespace { + +const base::FilePath::CharType kJobsPath[] = FILE_PATH_LITERAL("printjobs"); + +} // namespace + +PrintJobHandler::PrintJobHandler() { +} + +PrintJobHandler::~PrintJobHandler() { +} + +bool PrintJobHandler::SavePrintJob(const std::string& data, + const std::string& ticket, + const std::string& job_name, + const std::string& title) { + VLOG(1) << "Printing printjob: \"" + title + "\""; + base::FilePath directory(kJobsPath); + + using file_util::CreateDirectory; + + if (!base::DirectoryExists(directory) && !CreateDirectory(directory)) { + LOG(WARNING) << "Cannot create directory: " << directory.value(); + return false; + } + + directory = directory.AppendASCII(job_name); + + if (!base::DirectoryExists(directory) && !CreateDirectory(directory)) { + LOG(WARNING) << "Cannot create directory: " << directory.value(); + return false; + } + + int written = file_util::WriteFile(directory.AppendASCII("ticket.xml"), + ticket.data(), + static_cast<int>(ticket.size())); + if (static_cast<size_t>(written) != ticket.size()) { + LOG(WARNING) << "Cannot save ticket."; + return false; + } + + written = file_util::WriteFile(directory.AppendASCII("data.pdf"), + data.data(), + static_cast<int>(data.size())); + if (static_cast<size_t>(written) != data.size()) { + LOG(WARNING) << "Cannot save data."; + return false; + } + + LOG(INFO) << "Saved printjob: " << job_name; + return true; +} + diff --git a/cloud_print/gcp20/prototype/print_job_handler.h b/cloud_print/gcp20/prototype/print_job_handler.h new file mode 100644 index 0000000..594e5a2 --- /dev/null +++ b/cloud_print/gcp20/prototype/print_job_handler.h @@ -0,0 +1,27 @@ +// 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. + +#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ + +#include <string> + +#include "base/basictypes.h" + +class PrintJobHandler { + public: + PrintJobHandler(); + ~PrintJobHandler(); + + bool SavePrintJob(const std::string& data, + const std::string& ticket, + const std::string& job_name, + const std::string& title); + + private: + DISALLOW_COPY_AND_ASSIGN(PrintJobHandler); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ + diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc index 0aaf652..56bcad4 100644 --- a/cloud_print/gcp20/prototype/printer.cc +++ b/cloud_print/gcp20/prototype/printer.cc @@ -8,12 +8,14 @@ #include <string> #include <vector> +#include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/guid.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "cloud_print/gcp20/prototype/command_line_reader.h" #include "cloud_print/gcp20/prototype/service_parameters.h" #include "cloud_print/gcp20/prototype/special_io.h" @@ -36,6 +38,9 @@ const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you " "agree and any other to discard\n"; const int64 kUserConfirmationTimeout = 30; // in seconds +const uint32 kReconnectTimeout = 5; // in seconds +const uint32 kPrintJobsTimeout = 10; // in seconds + const char kCdd[] = "{\n" " 'version': '1.0',\n" @@ -92,6 +97,8 @@ net::IPAddressNumber GetLocalIp(const std::string& interface_name, } // namespace +using cloud_print_response_parser::Job; + Printer::RegistrationInfo::RegistrationInfo() : state(DEV_REG_UNREGISTERED), confirmation_state(CONFIRMATION_PENDING) { @@ -100,7 +107,7 @@ Printer::RegistrationInfo::RegistrationInfo() Printer::RegistrationInfo::~RegistrationInfo() { } -Printer::Printer() : http_server_(this) { +Printer::Printer() : http_server_(this), connection_state_(OFFLINE) { } Printer::~Printer() { @@ -147,6 +154,10 @@ bool Printer::Start() { xtoken_ = XPrivetToken(); starttime_ = base::Time::Now(); + print_job_handler_.reset(new PrintJobHandler); + connection_state_ = CONNECTING; + WakeUp(); + return true; } @@ -154,10 +165,20 @@ bool Printer::IsOnline() const { return requester_; } +void Printer::WakeUp() { + VLOG(3) << "Function: " << __FUNCTION__; + + if (!IsRegistered()) + return; + + FetchPrintJobs(); +} + void Printer::Stop() { dns_server_.Shutdown(); http_server_.Shutdown(); requester_.reset(); + print_job_handler_.reset(); } PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( @@ -185,14 +206,6 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( return PrivetHttpServer::REG_ERROR_OK; } -bool Printer::CheckXPrivetTokenHeader(const std::string& token) const { - return xtoken_.CheckValidXToken(token); -} - -bool Printer::IsRegistered() const { - return reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED; -} - PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken( const std::string& user, std::string* token, @@ -284,7 +297,7 @@ void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { info->url = kCloudPrintUrl; info->id = reg_info_.device_id; info->device_state = "idle"; - info->connection_state = "offline"; + info->connection_state = ConnectionStateToString(connection_state_); info->manufacturer = "Google"; info->model = "Prototype"; info->serial_number = "2.3.5.7.13.17.19.31.61.89.107.127.521.607.1279.2203"; @@ -299,6 +312,14 @@ void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { info->type.push_back("printer"); } +bool Printer::IsRegistered() const { + return reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED; +} + +bool Printer::CheckXPrivetTokenHeader(const std::string& token) const { + return xtoken_.CheckValidXToken(token); +} + void Printer::OnRegistrationStartResponseParsed( const std::string& registration_token, const std::string& complete_invite_url, @@ -313,6 +334,7 @@ void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token) { reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; reg_info_.refresh_token = refresh_token; SaveToFile(base::FilePath(kPrinterStatePath)); + FetchPrintJobs(); } void Printer::OnRegistrationError(const std::string& description) { @@ -323,6 +345,52 @@ void Printer::OnRegistrationError(const std::string& description) { reg_info_.error_description = description; } +void Printer::OnServerError(const std::string& description) { + VLOG(3) << "Function: " << __FUNCTION__; + LOG(ERROR) << "Server error: " << description; + + PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout)); +} + +void Printer::OnNetworkError() { + VLOG(3) << "Function: " << __FUNCTION__; + ChangeState(OFFLINE); + PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout)); +} + +void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) { + VLOG(3) << "Function: " << __FUNCTION__; + ChangeState(ONLINE); + + LOG(INFO) << "Available printjobs: " << jobs.size(); + + if (jobs.empty()) { + PostDelayedWakeUp(base::TimeDelta::FromSeconds(kPrintJobsTimeout)); + return; + } + + // TODO(maksymb): After finishing XMPP add 'Printjobs available' flag. + LOG(INFO) << "Downloading first printjob."; + requester_->RequestPrintJob(jobs[0]); + return; +} + +void Printer::OnPrintJobDownloaded(const Job& job) { + VLOG(3) << "Function: " << __FUNCTION__; + print_job_handler_->SavePrintJob( + job.file, + job.ticket, + base::StringPrintf("%s.%s", job.create_time.c_str(), job.job_id.c_str()), + job.title); + requester_->SendPrintJobDone(job.job_id); +} + +void Printer::OnPrintJobDone() { + VLOG(3) << "Function: " << __FUNCTION__; + // TODO(maksymb): Replace PostTask with with XMPP notifications. + PostWakeUp(); +} + PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors( const std::string& user) const { DCHECK(!IsRegistered()); @@ -375,11 +443,24 @@ std::vector<std::string> Printer::CreateTxt() const { txt.push_back("url=" + std::string(kCloudPrintUrl)); txt.push_back("type=printer"); txt.push_back("id=" + reg_info_.device_id); - txt.push_back("cs=offline"); + txt.push_back("cs=" + ConnectionStateToString(connection_state_)); return txt; } +void Printer::FetchPrintJobs() { + VLOG(3) << "Function: " << __FUNCTION__; + + if (!IsRegistered()) + return; + + if (requester_->IsBusy()) { + PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout)); + } else { + requester_->FetchPrintJobs(reg_info_.refresh_token, reg_info_.device_id); + } +} + void Printer::SaveToFile(const base::FilePath& file_path) const { base::DictionaryValue json; // TODO(maksymb): Get rid of in-place constants. @@ -461,6 +542,21 @@ bool Printer::LoadFromFile(const base::FilePath& file_path) { return true; } +void Printer::PostWakeUp() { + VLOG(3) << "Function: " << __FUNCTION__; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&Printer::WakeUp, AsWeakPtr())); +} + +void Printer::PostDelayedWakeUp(const base::TimeDelta& delay) { + VLOG(3) << "Function: " << __FUNCTION__; + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&Printer::WakeUp, AsWeakPtr()), + delay); +} + PrivetHttpServer::RegistrationErrorStatus Printer::ConfirmationToRegistrationError( RegistrationInfo::ConfirmationState state) { @@ -480,3 +576,30 @@ PrivetHttpServer::RegistrationErrorStatus } } +std::string Printer::ConnectionStateToString(ConnectionState state) const { + switch (state) { + case OFFLINE: + return "offline"; + case ONLINE: + return "online"; + case CONNECTING: + return "connecting"; + case NOT_CONFIGURED: + return "not-configured"; + + default: + NOTREACHED(); + return ""; + } +} + +bool Printer::ChangeState(ConnectionState new_state) { + if (connection_state_ == new_state) + return false; + + VLOG(1) << "Printer is now " << ConnectionStateToString(new_state); + connection_state_ = new_state; + dns_server_.UpdateMetadata(CreateTxt()); + return true; +} + diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h index 9dba787..3f402b9 100644 --- a/cloud_print/gcp20/prototype/printer.h +++ b/cloud_print/gcp20/prototype/printer.h @@ -8,13 +8,17 @@ #include <string> #include <vector> +#include "base/files/file_path.h" #include "base/memory/weak_ptr.h" #include "cloud_print/gcp20/prototype/cloud_print_requester.h" #include "cloud_print/gcp20/prototype/dns_sd_server.h" +#include "cloud_print/gcp20/prototype/print_job_handler.h" #include "cloud_print/gcp20/prototype/privet_http_server.h" #include "cloud_print/gcp20/prototype/x_privet_token.h" -// This class maintain work of DNS-SD server, HTTP server and others. +extern const base::FilePath::CharType kPrinterStatePath[]; + +// This class maintains work of DNS-SD server, HTTP server and others. class Printer : public base::SupportsWeakPtr<Printer>, public PrivetHttpServer::Delegate, public CloudPrintRequester::Delegate { @@ -31,6 +35,9 @@ class Printer : public base::SupportsWeakPtr<Printer>, // Returns true if printer was started. bool IsOnline() const; + // Method for trying to reconnecting to server. + void WakeUp(); + // Stops all servers. void Stop(); @@ -79,6 +86,13 @@ class Printer : public base::SupportsWeakPtr<Printer>, REG_ACTION_CANCEL }; + enum ConnectionState { + NOT_CONFIGURED, + OFFLINE, + ONLINE, + CONNECTING + }; + // PrivetHttpServer::Delegate methods: virtual PrivetHttpServer::RegistrationErrorStatus RegistrationStart( const std::string& user) OVERRIDE; @@ -104,6 +118,13 @@ class Printer : public base::SupportsWeakPtr<Printer>, virtual void OnGetAuthCodeResponseParsed( const std::string& refresh_token) OVERRIDE; virtual void OnRegistrationError(const std::string& description) OVERRIDE; + virtual void OnServerError(const std::string& description) OVERRIDE; + virtual void OnNetworkError() OVERRIDE; + virtual void OnPrintJobsAvailable( + const std::vector<cloud_print_response_parser::Job>& jobs) OVERRIDE; + virtual void OnPrintJobDownloaded( + const cloud_print_response_parser::Job& job) OVERRIDE; + virtual void OnPrintJobDone() OVERRIDE; // Checks if register call is called correctly (|user| is correct, // error is no set etc). Returns |false| if error status is put into |status|. @@ -111,23 +132,37 @@ class Printer : public base::SupportsWeakPtr<Printer>, PrivetHttpServer::RegistrationErrorStatus CheckCommonRegErrors( const std::string& user) const; - // Generates ProxyId for this device. - std::string GenerateProxyId() const; - // Checks if confirmation was received. void WaitUserConfirmation(base::Time valid_until); + // Generates ProxyId for this device. + std::string GenerateProxyId() const; + // Creates data for DNS TXT respond. std::vector<std::string> CreateTxt() const; + // Ask CloudPrint server for printjobs. + void FetchPrintJobs(); + // Saving and loading registration info from file. void SaveToFile(const base::FilePath& file_path) const; bool LoadFromFile(const base::FilePath& file_path); + // Adds |WakeUp| method to the MessageLoop. + void PostWakeUp(); + + // Adds |WakeUp| method to the MessageLoop with certain |delay|. + void PostDelayedWakeUp(const base::TimeDelta& delay); + // Converts errors. PrivetHttpServer::RegistrationErrorStatus ConfirmationToRegistrationError( RegistrationInfo::ConfirmationState state); + std::string ConnectionStateToString(ConnectionState state) const; + + // Changes state and update info in DNS server. + bool ChangeState(ConnectionState new_state); + RegistrationInfo reg_info_; // Contains DNS-SD server. @@ -136,11 +171,16 @@ class Printer : public base::SupportsWeakPtr<Printer>, // Contains Privet HTTP server. PrivetHttpServer http_server_; - // Contains Cloud Print client. + // Connection state of device. + ConnectionState connection_state_; + + // Contains CloudPrint client. scoped_ptr<CloudPrintRequester> requester_; XPrivetToken xtoken_; + scoped_ptr<PrintJobHandler> print_job_handler_; + // Uses for calculating uptime. base::Time starttime_; diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc index 33fd2ab..6c08029 100644 --- a/cloud_print/gcp20/prototype/privet_http_server.cc +++ b/cloud_print/gcp20/prototype/privet_http_server.cc @@ -133,7 +133,8 @@ void PrivetHttpServer::OnHttpRequest(int connection_id, } void PrivetHttpServer::OnWebSocketRequest( - int connection_id, const net::HttpServerRequestInfo& info) { + int connection_id, + const net::HttpServerRequestInfo& info) { } void PrivetHttpServer::OnWebSocketMessage(int connection_id, diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h index 84a464c..7c043b0 100644 --- a/cloud_print/gcp20/prototype/privet_http_server.h +++ b/cloud_print/gcp20/prototype/privet_http_server.h @@ -123,7 +123,8 @@ class PrivetHttpServer: public net::HttpServer::Delegate { // Returns |true| if |request| should be done with correct |method|. // Otherwise sends |Invalid method| error. // Also checks support of |request| by this server. - bool ValidateRequestMethod(int connection_id, const std::string& request, + bool ValidateRequestMethod(int connection_id, + const std::string& request, const std::string& method); // Processes http request after all preparations (XPrivetHeader check, |