diff options
author | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 18:58:41 +0000 |
---|---|---|
committer | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 18:58:41 +0000 |
commit | aa138039b70d0cdc8f039f0e1c683d98d1681665 (patch) | |
tree | 979d5a54053d327b1432bb7a35252566d06ae284 /cloud_print | |
parent | 55a3456c3b3b53f3908bd30aad776afc97e057b9 (diff) | |
download | chromium_src-aa138039b70d0cdc8f039f0e1c683d98d1681665.zip chromium_src-aa138039b70d0cdc8f039f0e1c683d98d1681665.tar.gz chromium_src-aa138039b70d0cdc8f039f0e1c683d98d1681665.tar.bz2 |
GCP2.0 Device: XMPP (without pings).
BUG=
Review URL: https://chromiumcodereview.appspot.com/20238002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215334 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print')
12 files changed, 659 insertions, 161 deletions
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc index f0d1926..1154d62 100644 --- a/cloud_print/gcp20/prototype/cloud_print_requester.cc +++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc @@ -9,13 +9,13 @@ #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/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 "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"; @@ -51,44 +51,6 @@ GURL CreateControlUrl(const std::string& job_id, const std::string& status) { 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 { - public: - // |task_runner| must not be NULL. - explicit CloudPrintURLRequestContextGetter( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - DCHECK(task_runner); - network_task_runner_ = task_runner; - } - - // URLRequestContextGetter implementation. - virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { - if (!context_) { - net::URLRequestContextBuilder builder; -#if defined(OS_LINUX) || defined(OS_ANDROID) - builder.set_proxy_config_service( - new net::ProxyConfigServiceFixed(net::ProxyConfig())); -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - context_.reset(builder.Build()); - } - return context_.get(); - } - - virtual scoped_refptr<base::SingleThreadTaskRunner> - GetNetworkTaskRunner() const OVERRIDE { - return network_task_runner_; - } - - protected: - virtual ~CloudPrintURLRequestContextGetter() {} - - scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; - scoped_ptr<net::URLRequestContext> context_; - - DISALLOW_COPY_AND_ASSIGN(CloudPrintURLRequestContextGetter); -}; - CloudPrintRequester::CloudPrintRequester( scoped_refptr<base::SingleThreadTaskRunner> task_runner, Delegate* delegate) @@ -143,33 +105,29 @@ void CloudPrintRequester::StartRegistration(const std::string& proxy_id, data, data_mimetype, base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr())); - request_->Run(access_token_, context_getter_); + 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(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } -void CloudPrintRequester::FetchPrintJobs(const std::string& refresh_token, - const std::string& device_id) { +void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) { VLOG(3) << "Function: " << __FUNCTION__; if (IsBusy()) return; - if (access_token_.empty()) { - UpdateAccesstoken(refresh_token); - return; - } + DCHECK(!delegate_->GetAccessToken().empty()); VLOG(3) << "Function: " << __FUNCTION__ << ": request created"; request_ = CreateGet( CreateFetchUrl(device_id), base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr())); - request_->Run(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) { @@ -178,7 +136,6 @@ void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) { 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."); } void CloudPrintRequester::RequestPrintJob(const Job& job) { @@ -187,7 +144,7 @@ void CloudPrintRequester::RequestPrintJob(const Job& job) { request_ = CreateGet( CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"), base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr())); - request_->Run(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) { @@ -195,7 +152,7 @@ void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) { request_ = CreateGet( CreateControlUrl(job_id, "DONE"), base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr())); - request_->Run(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } void CloudPrintRequester::OnFetchComplete(const std::string& response) { @@ -212,6 +169,8 @@ void CloudPrintRequester::OnFetchError(const std::string& server_api, EraseRequest(); current_print_job_.reset(); delegate_->OnServerError("Fetch error"); + + // TODO(maksymb): |server_api| and other NOTIMPLEMENTED(); } @@ -227,8 +186,8 @@ void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token, int expires_in_seconds) { VLOG(3) << "Function: " << __FUNCTION__; gaia_.reset(); - access_token_ = access_token; - delegate_->OnGetAuthCodeResponseParsed(refresh_token); + delegate_->OnGetAuthCodeResponseParsed(refresh_token, + access_token, expires_in_seconds); } void CloudPrintRequester::OnRefreshTokenResponse( @@ -236,20 +195,24 @@ void CloudPrintRequester::OnRefreshTokenResponse( int expires_in_seconds) { VLOG(3) << "Function: " << __FUNCTION__; gaia_.reset(); - access_token_ = access_token; - LOG(INFO) << "New accesstoken: " << access_token; + delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds); } void CloudPrintRequester::OnOAuthError() { VLOG(3) << "Function: " << __FUNCTION__; gaia_.reset(); - NOTIMPLEMENTED(); + delegate_->OnAuthError(); } void CloudPrintRequester::OnNetworkError(int response_code) { VLOG(3) << "Function: " << __FUNCTION__; gaia_.reset(); - NOTIMPLEMENTED(); + + if (response_code == net::HTTP_FORBIDDEN) { + // TODO(maksymb): delegate_->OnPrinterDeleted(); + } else { + delegate_->OnNetworkError(); + } } scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet( @@ -308,12 +271,16 @@ 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); + &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); @@ -348,7 +315,7 @@ void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) { GURL(current_print_job_->file_url), base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr())); request_->AddHeader("Accept: \"application/pdf\""); - request_->Run(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) { @@ -370,6 +337,6 @@ void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) { request_ = CreateGet( GURL(current_print_job_->ticket_url), base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr())); - request_->Run(access_token_, context_getter_); + request_->Run(delegate_->GetAccessToken(), context_getter_); } diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h index 49031ad..f9c2487 100644 --- a/cloud_print/gcp20/prototype/cloud_print_requester.h +++ b/cloud_print/gcp20/prototype/cloud_print_requester.h @@ -18,6 +18,7 @@ class CloudPrintURLRequestContextGetter; class GURL; +class URLRequestContextGetter; extern const char kCloudPrintUrl[]; @@ -41,7 +42,16 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // Invoked when server respond for registration-getAuthCode query and // response is successfully parsed. virtual void OnGetAuthCodeResponseParsed( - const std::string& refresh_token) = 0; + const std::string& refresh_token, + const std::string& access_token, + int access_token_expires_in_seconds) = 0; + + // Invoked when XMPP JID was received and it has to be saved. + virtual void OnXmppJidReceived(const std::string& xmpp_jid) = 0; + + // Invoked when access_token was received after UpdateAccesstoken() call. + virtual void OnAccesstokenReceviced(const std::string& access_token, + int expires_in_seconds) = 0; // Invoked when server respond with |"success" = false| or we cannot parse // response. @@ -53,6 +63,12 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // Invoked when server error is received or cannot parse json response. virtual void OnServerError(const std::string& description) = 0; + // Invoked when authorization failed. + virtual void OnAuthError() = 0; + + // Invoked when access_token is needed. + virtual std::string GetAccessToken() = 0; + // Invoked when fetch response was received. virtual void OnPrintJobsAvailable( const std::vector<cloud_print_response_parser::Job>& jobs) = 0; @@ -65,7 +81,7 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, virtual void OnPrintJobDone() = 0; }; - // Creates and initializes objects. + // Creates and initializes object. CloudPrintRequester(scoped_refptr<base::SingleThreadTaskRunner> task_runner, Delegate* delegate); @@ -84,8 +100,7 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, void CompleteRegistration(); // Creates request for fetching printjobs. - void FetchPrintJobs(const std::string& refresh_token, - const std::string& device_id); + void FetchPrintJobs(const std::string& device_id); // Creates request for updating accesstoken. // TODO(maksymb): Handle expiration of accesstoken. @@ -159,15 +174,12 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, // CloudPrint server responses. scoped_ptr<cloud_print_response_parser::Job> current_print_job_; - // Privet context getter. - scoped_refptr<CloudPrintURLRequestContextGetter> context_getter_; + // CloudPrint context getter. + scoped_refptr<net::URLRequestContextGetter> context_getter_; // URL for completing registration and receiving OAuth account. std::string polling_url_; - // Last valid access_token. - std::string access_token_; - // OAuth client information (client_id, client_secret, etc). gaia::OAuthClientInfo oauth_client_info_; diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc index d4da3c3..367a994 100644 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc @@ -116,7 +116,8 @@ bool ParseRegisterStartResponse(const std::string& response, bool ParseRegisterCompleteResponse(const std::string& response, std::string* error_description, - std::string* authorization_code_result) { + std::string* authorization_code_result, + std::string* xmpp_jid_result) { scoped_ptr<base::Value> json(base::JSONReader::Read(response)); base::DictionaryValue* response_dictionary = NULL; if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, @@ -131,7 +132,14 @@ bool ParseRegisterCompleteResponse(const std::string& response, return false; } + std::string xmpp_jid; + if (!response_dictionary->GetString("xmpp_jid", &xmpp_jid)) { + *error_description = "Cannot parse xmpp jid."; + return false; + } + *authorization_code_result = authorization_code; + *xmpp_jid_result = xmpp_jid; return true; } diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.h b/cloud_print/gcp20/prototype/cloud_print_response_parser.h index b691934..5c7254f 100644 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.h +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.h @@ -48,7 +48,8 @@ bool ParseRegisterStartResponse(const std::string& response, // when parsing is failed. bool ParseRegisterCompleteResponse(const std::string& response, std::string* error_description, - std::string* authorization_code_result); + std::string* authorization_code_result, + std::string* xmpp_jid_result); // Parses CloudPrint fetch response to out parameters. // Returns |true| on success. Callback is called with description as a parameter diff --git a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc new file mode 100644 index 0000000..b6bfae9 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc @@ -0,0 +1,37 @@ +// 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_url_request_context_getter.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" + +CloudPrintURLRequestContextGetter::CloudPrintURLRequestContextGetter( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + DCHECK(task_runner); + network_task_runner_ = task_runner; +} + +CloudPrintURLRequestContextGetter::~CloudPrintURLRequestContextGetter() { +} + +net::URLRequestContext* +CloudPrintURLRequestContextGetter::GetURLRequestContext() { + if (!context_) { + net::URLRequestContextBuilder builder; +#if defined(OS_LINUX) || defined(OS_ANDROID) + builder.set_proxy_config_service( + new net::ProxyConfigServiceFixed(net::ProxyConfig())); +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + context_.reset(builder.Build()); + } + return context_.get(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +CloudPrintURLRequestContextGetter::GetNetworkTaskRunner() const { + return network_task_runner_; +} + diff --git a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h new file mode 100644 index 0000000..efe6719 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h @@ -0,0 +1,36 @@ +// 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_URL_REQUEST_CONTEXT_GETTER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_URL_REQUEST_CONTEXT_GETTER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/url_request/url_request_context_getter.h" + +// Used to return a dummy context, which lives on the message loop +// given in the constructor. +class CloudPrintURLRequestContextGetter : public net::URLRequestContextGetter { + public: + // |task_runner| must not be NULL. + explicit CloudPrintURLRequestContextGetter( + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + + // URLRequestContextGetter implementation. + virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; + + virtual scoped_refptr<base::SingleThreadTaskRunner> + GetNetworkTaskRunner() const OVERRIDE; + + private: + virtual ~CloudPrintURLRequestContextGetter(); + + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; + scoped_ptr<net::URLRequestContext> context_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintURLRequestContextGetter); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_URL_REQUEST_CONTEXT_GETTER_H_ + diff --git a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc new file mode 100644 index 0000000..bcf7dcc --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc @@ -0,0 +1,108 @@ +// 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_xmpp_listener.h" + +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h" +#include "jingle/notifier/base/notifier_options.h" +#include "jingle/notifier/listener/push_client.h" + +namespace { + +const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com"; + +// Splits message into two parts (e.g. '<device_id>/delete' will be splitted +// into '<device_id>' and '/delete'). +void TokenizeXmppMessage(const std::string& message, std::string* device_id, + std::string* path) { + std::string::size_type pos = message.find('/'); + if (pos != std::string::npos) { + *device_id = message.substr(0, pos); + *path = message.substr(pos); + } else { + *device_id = message; + path->clear(); + } +} + +} // namespace + +CloudPrintXmppListener::CloudPrintXmppListener( + const std::string& robot_email, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + Delegate* delegate) + : robot_email_(robot_email), + context_getter_(new CloudPrintURLRequestContextGetter(task_runner)), + delegate_(delegate) { +} + +CloudPrintXmppListener::~CloudPrintXmppListener() { + push_client_->RemoveObserver(this); + push_client_.reset(); +} + +void CloudPrintXmppListener::Connect(const std::string& access_token) { + push_client_.reset(); + access_token_ = access_token; + + notifier::NotifierOptions options; + options.request_context_getter = context_getter_; + options.auth_mechanism = "X-OAUTH2"; + options.try_ssltcp_first = true; + push_client_ = notifier::PushClient::CreateDefault(options); + + notifier::Subscription subscription; + subscription.channel = kCloudPrintPushNotificationsSource; + subscription.from = kCloudPrintPushNotificationsSource; + + notifier::SubscriptionList list; + list.push_back(subscription); + + push_client_->UpdateSubscriptions(list); + push_client_->AddObserver(this); + push_client_->UpdateCredentials(robot_email_, access_token_); +} + +void CloudPrintXmppListener::OnNotificationsEnabled() { + delegate_->OnXmppConnected(); +} + +void CloudPrintXmppListener::OnNotificationsDisabled( + notifier::NotificationsDisabledReason reason) { + switch (reason) { + case notifier::NOTIFICATION_CREDENTIALS_REJECTED: + delegate_->OnXmppAuthError(); + break; + case notifier::TRANSIENT_NOTIFICATION_ERROR: + delegate_->OnXmppNetworkError(); + break; + default: + NOTREACHED() << "XMPP failed with unexpected reason code: " << reason; + } +} + +void CloudPrintXmppListener::OnIncomingNotification( + const notifier::Notification& notification) { + std::string device_id; + std::string path; + TokenizeXmppMessage(notification.data, &device_id, &path); + + if (path.empty() || path == "/") { + delegate_->OnXmppNewPrintJob(device_id); + } else if (path == "/update_settings") { + delegate_->OnXmppNewLocalSettings(device_id); + } else if (path == "/delete") { + delegate_->OnXmppDeleteNotification(device_id); + } else { + LOG(ERROR) << "Cannot parse XMPP notification: " << notification.data; + } +} + +void CloudPrintXmppListener::OnPingResponse() { + // TODO(maksymb): Implement pings + NOTIMPLEMENTED(); +} + diff --git a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h new file mode 100644 index 0000000..843ba89 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h @@ -0,0 +1,96 @@ +// 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_XMPP_LISTENER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_XMPP_LISTENER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "jingle/notifier/listener/push_client_observer.h" + +namespace base { + +class SingleThreadTaskRunner; + +} // namespace base + +namespace net { + +class URLRequestContextGetter; + +} // namespace net + +namespace notifier { + +class PushClient; + +} // namespace notifier + +class CloudPrintXmppListener: public notifier::PushClientObserver { + public: + class Delegate { + public: + virtual ~Delegate() {} + + // Invoked when XMPP connection was established. + virtual void OnXmppConnected() = 0; + + // Invoked when server rejected our credentials. + virtual void OnXmppAuthError() = 0; + + // Invoked when server is unavailable. + virtual void OnXmppNetworkError() = 0; + + // Invoked when new printjob was received. + virtual void OnXmppNewPrintJob(const std::string& device_id) = 0; + + // Invoked when local settings was updated. + virtual void OnXmppNewLocalSettings(const std::string& device_id) = 0; + + // Invoked when printer was deleted from server. + virtual void OnXmppDeleteNotification(const std::string& device_id) = 0; + }; + + CloudPrintXmppListener( + const std::string& robot_email, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + Delegate* delegate); + + virtual ~CloudPrintXmppListener(); + + // Connects to the server. + void Connect(const std::string& access_token); + + private: + // notifier::PushClientObserver methods: + virtual void OnNotificationsEnabled() OVERRIDE; + virtual void OnNotificationsDisabled( + notifier::NotificationsDisabledReason reason) OVERRIDE; + virtual void OnIncomingNotification( + const notifier::Notification& notification) OVERRIDE; + virtual void OnPingResponse() OVERRIDE; + + // Is used for reconnection when number of retries is now exhausted. + void ReconnectInternal(); + + // Credentials: + std::string robot_email_; + std::string access_token_; + + scoped_refptr<net::URLRequestContextGetter> context_getter_; + + // Internal listener. + scoped_ptr<notifier::PushClient> push_client_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintXmppListener); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_XMPP_LISTENER_H_ + diff --git a/cloud_print/gcp20/prototype/gcp20_device.cc b/cloud_print/gcp20/prototype/gcp20_device.cc index d58adc5..31025ab 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.cc +++ b/cloud_print/gcp20/prototype/gcp20_device.cc @@ -22,10 +22,19 @@ void StartPrinter(Printer* printer) { } base::RunLoop* g_runner = NULL; +Printer* g_printer = NULL; void OnAbort(int val) { - if (g_runner) - g_runner->Quit(); + if (g_runner) { + g_printer->Stop(); // TODO(maksymb): Make this call in safe place call: + // |OnAbort| is called from different thread. + g_printer = NULL; + + g_runner->Quit(); // Always do after printer.Stop() to make sure XMPP will + // be disabled fully before |Quit| will be called + // (XMPP disables itself via MessageLoop call). + g_runner = NULL; + } } } // namespace @@ -45,10 +54,9 @@ int main(int argc, char* argv[]) { base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&StartPrinter, &printer)); base::RunLoop runner; + g_printer = &printer; g_runner = &runner; runner.Run(); - g_runner = NULL; - printer.Stop(); return 0; } diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp index e6f1777..b2e526f 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.gyp +++ b/cloud_print/gcp20/prototype/gcp20_device.gyp @@ -22,17 +22,22 @@ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '<(DEPTH)/google_apis/google_apis.gyp:google_apis', + '<(DEPTH)/jingle/jingle.gyp:notifier', '<(DEPTH)/net/net.gyp:http_server', '<(DEPTH)/net/net.gyp:net', '<(DEPTH)/url/url.gyp:url_lib', ], 'sources': [ - 'cloud_print_response_parser.cc', + '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', + 'cloud_print_url_request_context_getter.cc', + 'cloud_print_url_request_context_getter.h', + 'cloud_print_xmpp_listener.cc', + 'cloud_print_xmpp_listener.h', 'conio_posix.cc', 'conio_posix.h', 'command_line_reader.cc', diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc index 2a0086b..b6ff2c41 100644 --- a/cloud_print/gcp20/prototype/printer.cc +++ b/cloud_print/gcp20/prototype/printer.cc @@ -42,7 +42,8 @@ const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you " const int64 kUserConfirmationTimeout = 30; // in seconds const uint32 kReconnectTimeout = 5; // in seconds -const uint32 kPrintJobsTimeout = 10; // in seconds + +const double kTimeToNextAccessTokenUpdate = 0.8; // relatively to living time. const char kCdd[] = "{\n" @@ -98,6 +99,10 @@ net::IPAddressNumber GetLocalIp(const std::string& interface_name, return net::IPAddressNumber(); } +scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() { + return base::MessageLoop::current()->message_loop_proxy(); +} + } // namespace using cloud_print_response_parser::Job; @@ -110,7 +115,12 @@ Printer::RegistrationInfo::RegistrationInfo() Printer::RegistrationInfo::~RegistrationInfo() { } -Printer::Printer() : http_server_(this), connection_state_(OFFLINE) { +Printer::Printer() + : http_server_(this), + connection_state_(OFFLINE), + on_idle_posted_(false), + pending_local_settings_check_(false), + pending_print_jobs_check_(false) { } Printer::~Printer() { @@ -118,7 +128,7 @@ Printer::~Printer() { } bool Printer::Start() { - if (IsOnline()) + if (IsRunning()) return true; // TODO(maksymb): Add switch for command line to control interface name. @@ -152,33 +162,16 @@ bool Printer::Start() { return false; } - // Creating Cloud Requester. - requester_.reset( - new CloudPrintRequester( - base::MessageLoop::current()->message_loop_proxy(), - this)); - + print_job_handler_.reset(new PrintJobHandler); xtoken_ = XPrivetToken(); starttime_ = base::Time::Now(); - print_job_handler_.reset(new PrintJobHandler); - connection_state_ = CONNECTING; - WakeUp(); - + TryConnect(); return true; } -bool Printer::IsOnline() const { - return requester_; -} - -void Printer::WakeUp() { - VLOG(3) << "Function: " << __FUNCTION__; - - if (!IsRegistered()) - return; - - FetchPrintJobs(); +bool Printer::IsRunning() const { + return print_job_handler_; } void Printer::Stop() { @@ -186,6 +179,17 @@ void Printer::Stop() { http_server_.Shutdown(); requester_.reset(); print_job_handler_.reset(); + xmpp_listener_.reset(); +} + +void Printer::OnAuthError() { + access_token_update_ = base::Time::Now(); + ChangeState(OFFLINE); + // TODO(maksymb): Implement *instant* updating of access_token. +} + +std::string Printer::GetAccessToken() { + return access_token_; } PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( @@ -285,9 +289,7 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel( return PrivetHttpServer::REG_ERROR_INVALID_ACTION; reg_info_ = RegistrationInfo(); - requester_.reset(new CloudPrintRequester( - base::MessageLoop::current()->message_loop_proxy(), - this)); // Forget all old queries. + requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); return PrivetHttpServer::REG_ERROR_OK; } @@ -342,11 +344,36 @@ void Printer::OnRegistrationStartResponseParsed( reg_info_.complete_invite_url = complete_invite_url; } -void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token) { +void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token, + const std::string& access_token, + int access_token_expires_in_seconds) { reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; reg_info_.refresh_token = refresh_token; - SaveToFile(base::FilePath(kPrinterStatePath)); - FetchPrintJobs(); + RememberAccessToken(access_token, access_token_expires_in_seconds); + + ConnectXmpp(); +} + +void Printer::OnAccesstokenReceviced(const std::string& access_token, + int expires_in_seconds) { + VLOG(3) << "Function: " << __FUNCTION__; + RememberAccessToken(access_token, expires_in_seconds); + switch (connection_state_) { + case ONLINE: + PostOnIdle(); + break; + + case CONNECTING: + TryConnect(); + break; + + default: + NOTREACHED(); + } +} + +void Printer::OnXmppJidReceived(const std::string& xmpp_jid) { + reg_info_.xmpp_jid = xmpp_jid; } void Printer::OnRegistrationError(const std::string& description) { @@ -357,32 +384,30 @@ void Printer::OnRegistrationError(const std::string& description) { reg_info_.error_description = description; } -void Printer::OnServerError(const std::string& description) { +void Printer::OnNetworkError() { VLOG(3) << "Function: " << __FUNCTION__; - LOG(ERROR) << "Server error: " << description; - - PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout)); + ChangeState(OFFLINE); } -void Printer::OnNetworkError() { +void Printer::OnServerError(const std::string& description) { VLOG(3) << "Function: " << __FUNCTION__; + LOG(ERROR) << "Server error: " << description; + 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)); + pending_print_jobs_check_ = false; + PostOnIdle(); return; } - // TODO(maksymb): After finishing XMPP add 'Printjobs available' flag. - LOG(INFO) << "Downloading first printjob."; + LOG(INFO) << "Downloading printjob."; requester_->RequestPrintJob(jobs[0]); return; } @@ -399,8 +424,121 @@ void Printer::OnPrintJobDownloaded(const Job& job) { void Printer::OnPrintJobDone() { VLOG(3) << "Function: " << __FUNCTION__; - // TODO(maksymb): Replace PostTask with with XMPP notifications. - PostWakeUp(); + PostOnIdle(); +} + +void Printer::OnXmppConnected() { + pending_local_settings_check_ = true; + pending_print_jobs_check_ = true; + ChangeState(ONLINE); + PostOnIdle(); +} + +void Printer::OnXmppAuthError() { + OnAuthError(); +} + +void Printer::OnXmppNetworkError() { + ChangeState(OFFLINE); +} + +void Printer::OnXmppNewPrintJob(const std::string& device_id) { + DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; + pending_print_jobs_check_ = true; +} + +void Printer::OnXmppNewLocalSettings(const std::string& device_id) { + DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; + NOTIMPLEMENTED(); +} + +void Printer::OnXmppDeleteNotification(const std::string& device_id) { + DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; + NOTIMPLEMENTED(); +} + +void Printer::TryConnect() { + VLOG(3) << "Function: " << __FUNCTION__; + + ChangeState(CONNECTING); + if (!requester_) + requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); + + if (IsRegistered()) { + if (access_token_update_ < base::Time::Now()) { + requester_->UpdateAccesstoken(reg_info_.refresh_token); + } else { + ConnectXmpp(); + } + } else { + // TODO(maksymb): Ping google.com to check connection state. + ChangeState(ONLINE); + } +} + +void Printer::ConnectXmpp() { + xmpp_listener_.reset( + new CloudPrintXmppListener(reg_info_.xmpp_jid, GetTaskRunner(), this)); + xmpp_listener_->Connect(access_token_); +} + +void Printer::OnIdle() { + DCHECK(IsRegistered()); + DCHECK(on_idle_posted_) << "Instant call is not allowed"; + on_idle_posted_ = false; + + if (connection_state_ != ONLINE) + return; + + if (access_token_update_ < base::Time::Now()) { + requester_->UpdateAccesstoken(reg_info_.refresh_token); + return; + } + + // TODO(maksymb): Check if privet-accesstoken was requested. + + // TODO(maksymb): Check if local-printing was requested. + + if (pending_local_settings_check_) { + GetLocalSettings(); + return; + } + + if (pending_print_jobs_check_) { + FetchPrintJobs(); + return; + } + + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&Printer::PostOnIdle, AsWeakPtr()), + base::TimeDelta::FromMilliseconds(1000)); +} + +void Printer::GetLocalSettings() { + DCHECK(IsRegistered()); + + pending_local_settings_check_ = false; + PostOnIdle(); +} + +void Printer::FetchPrintJobs() { + VLOG(3) << "Function: " << __FUNCTION__; + + DCHECK(IsRegistered()); + requester_->FetchPrintJobs(reg_info_.device_id); +} + +void Printer::RememberAccessToken(const std::string& access_token, + int expires_in_seconds) { + using base::Time; + using base::TimeDelta; + access_token_ = access_token; + int64 time_to_update = static_cast<int64>(expires_in_seconds * + kTimeToNextAccessTokenUpdate); + access_token_update_ = Time::Now() + TimeDelta::FromSeconds(time_to_update); + VLOG(1) << "Current access_token: " << access_token; + SaveToFile(base::FilePath(kPrinterStatePath)); } PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors( @@ -460,19 +598,6 @@ std::vector<std::string> Printer::CreateTxt() const { 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. @@ -481,6 +606,10 @@ void Printer::SaveToFile(const base::FilePath& file_path) const { json.SetString("user", reg_info_.user); json.SetString("device_id", reg_info_.device_id); json.SetString("refresh_token", reg_info_.refresh_token); + json.SetString("xmpp_jid", reg_info_.xmpp_jid); + json.SetString("access_token", access_token_); + json.SetInteger("access_token_update", + static_cast<int>(access_token_update_.ToTimeT())); } else { json.SetBoolean("registered", false); } @@ -497,10 +626,8 @@ void Printer::SaveToFile(const base::FilePath& file_path) const { } bool Printer::LoadFromFile(const base::FilePath& file_path) { - if (!base::PathExists(file_path)) { - LOG(INFO) << "Registration info is not found. Printer is unregistered."; + if (!base::PathExists(file_path)) return false; - } LOG(INFO) << "Loading registration info from file."; std::string json_str; @@ -545,28 +672,45 @@ bool Printer::LoadFromFile(const base::FilePath& file_path) { return false; } + std::string xmpp_jid; + if (!json->GetString("xmpp_jid", &xmpp_jid)) { + LOG(ERROR) << "Cannot parse |xmpp_jid|."; + return false; + } + + std::string access_token; + if (!json->GetString("access_token", &access_token)) { + LOG(ERROR) << "Cannot parse |access_token|."; + return false; + } + + int access_token_update; + if (!json->GetInteger("access_token_update", &access_token_update)) { + LOG(ERROR) << "Cannot parse |access_token_update|."; + return false; + } + reg_info_ = RegistrationInfo(); reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; reg_info_.user = user; reg_info_.device_id = device_id; reg_info_.refresh_token = refresh_token; + reg_info_.xmpp_jid = xmpp_jid; + using base::Time; + access_token_ = access_token; + access_token_update_ = Time::FromTimeT(access_token_update); return true; } -void Printer::PostWakeUp() { +void Printer::PostOnIdle() { VLOG(3) << "Function: " << __FUNCTION__; - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&Printer::WakeUp, AsWeakPtr())); -} + DCHECK(!on_idle_posted_) << "Only one instance can be posted."; + on_idle_posted_ = true; -void Printer::PostDelayedWakeUp(const base::TimeDelta& delay) { - VLOG(3) << "Function: " << __FUNCTION__; - base::MessageLoop::current()->PostDelayedTask( + base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&Printer::WakeUp, AsWeakPtr()), - delay); + base::Bind(&Printer::OnIdle, AsWeakPtr())); } PrivetHttpServer::RegistrationErrorStatus @@ -609,9 +753,36 @@ 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; + LOG(INFO) << base::StringPrintf( + "Printer is now %s (%s)", + ConnectionStateToString(connection_state_).c_str(), + IsRegistered() ? "registered" : "unregistered"); + dns_server_.UpdateMetadata(CreateTxt()); + + switch (connection_state_) { + case CONNECTING: + break; + + case ONLINE: + break; + + case OFFLINE: + requester_.reset(); + xmpp_listener_.reset(); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&Printer::TryConnect, AsWeakPtr()), + base::TimeDelta::FromSeconds(kReconnectTimeout)); + + case NOT_CONFIGURED: + break; + + default: + NOTREACHED(); + } + return true; } diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h index 3f402b9..7b1ba50 100644 --- a/cloud_print/gcp20/prototype/printer.h +++ b/cloud_print/gcp20/prototype/printer.h @@ -11,6 +11,7 @@ #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/cloud_print_xmpp_listener.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" @@ -21,7 +22,8 @@ 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 { + public CloudPrintRequester::Delegate, + public CloudPrintXmppListener::Delegate { public: // Constructs uninitialized object. Printer(); @@ -33,10 +35,7 @@ class Printer : public base::SupportsWeakPtr<Printer>, bool Start(); // Returns true if printer was started. - bool IsOnline() const; - - // Method for trying to reconnecting to server. - void WakeUp(); + bool IsRunning() const; // Stops all servers. void Stop(); @@ -69,6 +68,7 @@ class Printer : public base::SupportsWeakPtr<Printer>, std::string user; std::string refresh_token; std::string device_id; + std::string xmpp_jid; RegistrationState state; ConfirmationState confirmation_state; @@ -116,16 +116,55 @@ class Printer : public base::SupportsWeakPtr<Printer>, const std::string& complete_invite_url, const std::string& device_id) OVERRIDE; virtual void OnGetAuthCodeResponseParsed( - const std::string& refresh_token) OVERRIDE; + const std::string& refresh_token, + const std::string& access_token, + int access_token_expires_in_seconds) OVERRIDE; + virtual void OnXmppJidReceived(const std::string& xmpp_jid) OVERRIDE; + virtual void OnAccesstokenReceviced(const std::string& access_token, + int expires_in_seconds) OVERRIDE; virtual void OnRegistrationError(const std::string& description) OVERRIDE; - virtual void OnServerError(const std::string& description) OVERRIDE; virtual void OnNetworkError() OVERRIDE; + virtual void OnServerError(const std::string& description) OVERRIDE; + virtual void OnAuthError() OVERRIDE; + virtual std::string GetAccessToken() 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; + // CloudPrintXmppListener::Delegate methods: + virtual void OnXmppConnected() OVERRIDE; + virtual void OnXmppAuthError() OVERRIDE; + virtual void OnXmppNetworkError() OVERRIDE; + virtual void OnXmppNewPrintJob(const std::string& device_id) OVERRIDE; + virtual void OnXmppNewLocalSettings(const std::string& device_id) OVERRIDE; + virtual void OnXmppDeleteNotification(const std::string& device_id) OVERRIDE; + + // Method for trying to reconnecting to server on start or after network fail. + void TryConnect(); + + // Connects XMPP. + void ConnectXmpp(); + + // Method to handle pending events. + // Do *NOT* call this method instantly. Only with |PostOnIdle|. + void OnIdle(); + + // Method for checking printer status. + // (e.g. printjobs, local settings, deleted status). + void CheckPendingUpdates(); + + // Ask CloudPrint server for new local sendings. + void GetLocalSettings(); + + // Ask CloudPrint server for printjobs. + void FetchPrintJobs(); + + // Saves |access_token| and calculates time for next update. + void RememberAccessToken(const std::string& access_token, + int expires_in_seconds); + // Checks if register call is called correctly (|user| is correct, // error is no set etc). Returns |false| if error status is put into |status|. // Otherwise no error was occurred. @@ -141,18 +180,12 @@ class Printer : public base::SupportsWeakPtr<Printer>, // 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); + // Adds |OnIdle| method to the MessageLoop. + void PostOnIdle(); // Converts errors. PrivetHttpServer::RegistrationErrorStatus ConfirmationToRegistrationError( @@ -177,13 +210,29 @@ class Printer : public base::SupportsWeakPtr<Printer>, // Contains CloudPrint client. scoped_ptr<CloudPrintRequester> requester_; + // XMPP Listener. + scoped_ptr<CloudPrintXmppListener> xmpp_listener_; + XPrivetToken xtoken_; scoped_ptr<PrintJobHandler> print_job_handler_; + // Last valid |access_token|. + std::string access_token_; + base::Time access_token_update_; + // Uses for calculating uptime. base::Time starttime_; + // Used for preventing two and more OnIdle posted in message loop. + bool on_idle_posted_; + + // Contains |true| if Printer has to check pending local settings. + bool pending_local_settings_check_; + + // Contains |true| if Printer has to check available printjobs. + bool pending_print_jobs_check_; + DISALLOW_COPY_AND_ASSIGN(Printer); }; |