summaryrefslogtreecommitdiffstats
path: root/cloud_print
diff options
context:
space:
mode:
authormaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-19 22:14:14 +0000
committermaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-19 22:14:14 +0000
commit6676bf444b999c661a5289f1b33c0bcc04712d5c (patch)
tree1217411def2d28478d7789441e957744a78fb872 /cloud_print
parent6d2143e075b4026b12d16288a3b3732ff73cc602 (diff)
downloadchromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.zip
chromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.tar.gz
chromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.tar.bz2
Finished registration to Google Cloud Print.
No saving state. No network error handling. No /privet/reset. Fixed bug with empty command line arguments. BUG= Review URL: https://chromiumcodereview.appspot.com/17921002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212667 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print')
-rw-r--r--cloud_print/DEPS1
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.cc246
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.h120
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_response_parser.cc133
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_response_parser.h43
-rw-r--r--cloud_print/gcp20/prototype/dns_response_builder.cc3
-rw-r--r--cloud_print/gcp20/prototype/dns_response_builder.h8
-rw-r--r--cloud_print/gcp20/prototype/dns_sd_server.h6
-rw-r--r--cloud_print/gcp20/prototype/gcp20_device.cc28
-rw-r--r--cloud_print/gcp20/prototype/gcp20_device.gyp17
-rw-r--r--cloud_print/gcp20/prototype/printer.cc231
-rw-r--r--cloud_print/gcp20/prototype/printer.h84
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.cc255
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.h135
-rw-r--r--cloud_print/gcp20/prototype/service_parameters.cc3
-rw-r--r--cloud_print/gcp20/prototype/service_parameters.h2
16 files changed, 1271 insertions, 44 deletions
diff --git a/cloud_print/DEPS b/cloud_print/DEPS
index f7eacc3..1df0543 100644
--- a/cloud_print/DEPS
+++ b/cloud_print/DEPS
@@ -5,4 +5,5 @@ include_rules = [
"+grit",
"+net",
"+printing",
+ "+url",
]
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc
new file mode 100644
index 0000000..857b3de
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc
@@ -0,0 +1,246 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cloud_print/gcp20/prototype/cloud_print_requester.h"
+
+#include "base/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 "url/gurl.h"
+
+namespace {
+
+const char kProxyIdValue[] = "proxy";
+const char kPrinterNameValue[] = "printer";
+const char kPrinterCapsValue[] = "capabilities";
+const char kPrinterCapsHashValue[] = "capsHash";
+const char kPrinterUserValue[] = "user";
+
+const int kGaiaMaxRetries = 3;
+
+std::string GetCloudPrintUrl() {
+ return "https://www.google.com/cloudprint";
+}
+
+GURL CreateRegisterUrl() {
+ return GURL(GetCloudPrintUrl() + "/register");
+}
+
+} // namespace
+
+// 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)
+ : context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
+ delegate_(delegate) {
+ oauth_client_info_.client_id =
+ google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT);
+ oauth_client_info_.client_secret =
+ google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT);
+ oauth_client_info_.redirect_uri = "oob";
+}
+
+CloudPrintRequester::~CloudPrintRequester() {
+}
+
+bool CloudPrintRequester::StartRegistration(const std::string& proxy_id,
+ const std::string& device_name,
+ const std::string& user,
+ const std::string& cdd) {
+ std::string mime_boundary;
+ int r1 = base::RandInt(0, kint32max);
+ int r2 = base::RandInt(0, kint32max);
+ base::SStringPrintf(&mime_boundary,
+ "---------------------------%08X%08X", r1, r2);
+
+ std::string data;
+ 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);
+
+ if (!CreateRequest(
+ CreateRegisterUrl(), net::URLFetcher::POST,
+ base::Bind(&CloudPrintRequester::ParseRegisterStartResponse,
+ base::Unretained(this)))) {
+ return false;
+ }
+ fetcher_->SetUploadData(mime_type, data);
+ fetcher_->Start();
+
+ return true;
+}
+
+bool CloudPrintRequester::CompleteRegistration() {
+ if (!CreateRequest(
+ GURL(polling_url_ + oauth_client_info_.client_id),
+ net::URLFetcher::GET,
+ base::Bind(&CloudPrintRequester::ParseRegisterCompleteResponse,
+ base::Unretained(this)))) {
+ return false;
+ }
+ fetcher_->Start();
+
+ return true;
+}
+
+void CloudPrintRequester::OnURLFetchComplete(const net::URLFetcher* source) {
+ std::string response;
+ source->GetResponseAsString(&response);
+ fetcher_.reset();
+ VLOG(1) << response;
+
+ // 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::OnGetTokensResponse(const std::string& refresh_token,
+ const std::string& access_token,
+ int expires_in_seconds) {
+ access_token_ = access_token;
+ delegate_->OnGetAuthCodeResponseParsed(refresh_token);
+}
+
+void CloudPrintRequester::OnRefreshTokenResponse(
+ const std::string& access_token,
+ int expires_in_seconds) {
+ NOTIMPLEMENTED();
+}
+
+void CloudPrintRequester::OnOAuthError() {
+ NOTIMPLEMENTED();
+}
+
+void CloudPrintRequester::OnNetworkError(int response_code) {
+ 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_);
+
+ parse_response_callback_ = callback;
+
+ return true;
+}
+
+void CloudPrintRequester::ParseRegisterStartResponse(
+ const std::string& response) {
+ std::string polling_url;
+ std::string registration_token;
+ std::string complete_invite_url;
+ std::string device_id;
+
+ bool success = cloud_print_response_parser::ParseRegisterStartResponse(
+ response,
+ base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError,
+ base::Unretained(delegate_)),
+ &polling_url,
+ &registration_token,
+ &complete_invite_url,
+ &device_id);
+
+ if (!success)
+ return;
+
+ polling_url_ = polling_url;
+ delegate_->OnRegistrationStartResponseParsed(registration_token,
+ complete_invite_url, device_id);
+}
+
+void CloudPrintRequester::ParseRegisterCompleteResponse(
+ const std::string& response) {
+ std::string authorization_code;
+
+ bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
+ response,
+ base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError,
+ base::Unretained(delegate_)),
+ &authorization_code);
+
+ if (!success)
+ return;
+
+ gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
+ gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
+ kGaiaMaxRetries, this);
+}
+
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h
new file mode 100644
index 0000000..345bfce
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.h
@@ -0,0 +1,120 @@
+// 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_REQUESTER_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/values.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 for requesting CloudPrint server and parsing responses.
+class CloudPrintRequester : public net::URLFetcherDelegate,
+ public gaia::GaiaOAuthClient::Delegate {
+ public:
+ class Delegate {
+ public:
+ Delegate() {}
+ virtual ~Delegate() {}
+
+ // Invoked when server respond for registration-start query and response is
+ // successfully parsed.
+ virtual void OnRegistrationStartResponseParsed(
+ const std::string& registration_token,
+ const std::string& complete_invite_url,
+ const std::string& device_id) = 0;
+
+ // Invoked when server respond for registration-getAuthCode query and
+ // response is successfully parsed.
+ virtual void OnGetAuthCodeResponseParsed(
+ const std::string& refresh_token) = 0;
+
+ // Invoked when server respond with |"success" = false| or we cannot parse
+ // response.
+ virtual void OnRegistrationError(const std::string& description) = 0;
+ };
+
+ // Creates and initializes objects.
+ CloudPrintRequester(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ Delegate* delegate);
+
+ // Destroys the object.
+ virtual ~CloudPrintRequester();
+
+ // Creates query to server for starting registration.
+ bool StartRegistration(const std::string& proxy_id,
+ const std::string& device_name,
+ const std::string& user,
+ const std::string& cdd);
+
+ // Creates request for completing registration and receiving refresh token.
+ bool CompleteRegistration();
+
+ private:
+ // net::URLFetcherDelegate
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // gaia::GaiaOAuthClient::Delegate
+ virtual void OnGetTokensResponse(const std::string& refresh_token,
+ const std::string& access_token,
+ int expires_in_seconds) OVERRIDE;
+ virtual void OnRefreshTokenResponse(const std::string& access_token,
+ int expires_in_seconds) OVERRIDE;
+ 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;
+
+ // Parses register-start server response.
+ void ParseRegisterStartResponse(const std::string& response);
+
+ // Parses register-complete server response. Initializes gaia (OAuth client)
+ // and receives refresh token.
+ void ParseRegisterCompleteResponse(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_;
+
+ // Callback for parsing server response.
+ ParserCallback parse_response_callback_;
+
+ // Privet context getter.
+ scoped_refptr<CloudPrintURLRequestContextGetter> 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_;
+
+ // OAuth client.
+ scoped_ptr<gaia::GaiaOAuthClient> gaia_;
+
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintRequester);
+};
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_
+
diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc
new file mode 100644
index 0000000..fa2c5f7
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc
@@ -0,0 +1,133 @@
+// 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_response_parser.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/values.h"
+
+namespace cloud_print_response_parser {
+
+// Parses json base::Value to base::DictionaryValue and checks |success| status.
+// Returns |true| on success.
+bool GetJsonDictinaryAndCheckSuccess(base::Value* json,
+ const ErrorCallback error_callback,
+ base::DictionaryValue** dictionary) {
+ base::DictionaryValue* response_dictionary = NULL;
+
+ if (!json || !json->GetAsDictionary(&response_dictionary)) {
+ error_callback.Run("No JSON dictionary response received.");
+ return false;
+ }
+
+ bool success = false;
+ if (!response_dictionary->GetBoolean("success", &success)) {
+ error_callback.Run("Cannot parse success state.");
+ return false;
+ }
+
+ if (!success) {
+ std::string message;
+ response_dictionary->GetString("message", &message);
+ error_callback.Run(message);
+ return false;
+ }
+
+ *dictionary = response_dictionary;
+ return true;
+}
+
+bool ParseRegisterStartResponse(const std::string& response,
+ const ErrorCallback error_callback,
+ 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,
+ &response_dictionary)) {
+ return false;
+ }
+
+ std::string registration_token;
+ if (!response_dictionary->GetString("registration_token",
+ &registration_token)) {
+ error_callback.Run("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.");
+ return false;
+ }
+
+ std::string polling_url;
+ if (!response_dictionary->GetString("polling_url", &polling_url)) {
+ error_callback.Run("No polling_url specified.");
+ return false;
+ }
+
+ base::ListValue* list = NULL;
+ if (!response_dictionary->GetList("printers", &list)) {
+ error_callback.Run("No printers list specified.");
+ return false;
+ }
+
+ base::Value* printer_value = NULL;
+ if (!list->Get(0, &printer_value)) {
+ error_callback.Run("Printers list is empty.");
+ return false;
+ }
+
+ base::DictionaryValue* printer = NULL;
+ if (!printer_value->GetAsDictionary(&printer)) {
+ error_callback.Run("Printer is not a json-dictionary.");
+ return false;
+ }
+
+ std::string device_id;
+ if (!printer->GetString("id", &device_id)) {
+ error_callback.Run("No id specified.");
+ return false;
+ }
+
+ if (device_id.empty()) {
+ error_callback.Run("id is empty.");
+ return false;
+ }
+
+ *polling_url_result = polling_url;
+ *registration_token_result = registration_token;
+ *complete_invite_url_result = complete_invite_url;
+ *device_id_result = device_id;
+ return true;
+}
+
+bool ParseRegisterCompleteResponse(const std::string& response,
+ const ErrorCallback error_callback,
+ 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,
+ &response_dictionary)) {
+ return false;
+ }
+
+ std::string authorization_code;
+ if (!response_dictionary->GetString("authorization_code",
+ &authorization_code)) {
+ error_callback.Run("Cannot parse authorization_code.");
+ return false;
+ }
+
+ *authorization_code_result = authorization_code;
+ 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
new file mode 100644
index 0000000..1178301
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.h
@@ -0,0 +1,43 @@
+// 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_RESPONSE_PARSER_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace base {
+
+class DictionaryValue;
+
+} // namespace base
+
+namespace cloud_print_response_parser {
+
+// Callback for handling parse errors.
+typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+// Parses CloudPrint register start response to out parameters.
+// Returns |true| on success. Callback is called with description as a parameter
+// when parsing failed.
+bool ParseRegisterStartResponse(const std::string& response,
+ const ErrorCallback error_callback,
+ std::string* polling_url_result,
+ std::string* registration_token_result,
+ std::string* complete_invite_url_result,
+ std::string* device_id_result);
+
+// Parses CloudPrint register complete response to out parameters.
+// Returns |true| on success. Callback is called with description as a parameter
+// when parsing failed.
+bool ParseRegisterCompleteResponse(const std::string& response,
+ const ErrorCallback error_callback,
+ std::string* authorization_code_result);
+
+} // namespace cloud_print_response_parser
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_
+
diff --git a/cloud_print/gcp20/prototype/dns_response_builder.cc b/cloud_print/gcp20/prototype/dns_response_builder.cc
index 2bcd43f..c5ad602 100644
--- a/cloud_print/gcp20/prototype/dns_response_builder.cc
+++ b/cloud_print/gcp20/prototype/dns_response_builder.cc
@@ -34,6 +34,9 @@ DnsResponseBuilder::DnsResponseBuilder(uint16 id) {
header_.arcount = 0;
}
+DnsResponseBuilder::~DnsResponseBuilder() {
+}
+
void DnsResponseBuilder::AppendPtr(const std::string& service_type, uint32 ttl,
const std::string& service_name) {
std::string rdata;
diff --git a/cloud_print/gcp20/prototype/dns_response_builder.h b/cloud_print/gcp20/prototype/dns_response_builder.h
index 430a7e1..0d4a3eb 100644
--- a/cloud_print/gcp20/prototype/dns_response_builder.h
+++ b/cloud_print/gcp20/prototype/dns_response_builder.h
@@ -13,8 +13,10 @@
#include "net/dns/dns_protocol.h"
namespace net {
- class IOBufferWithSize;
-}
+
+class IOBufferWithSize;
+
+} // namespace net
// Record for storing response data.
struct DnsResponseRecord {
@@ -35,7 +37,7 @@ class DnsResponseBuilder {
explicit DnsResponseBuilder(uint16 id);
// Destroys the object.
- ~DnsResponseBuilder() {}
+ ~DnsResponseBuilder();
// Methods for appending different types of responses to packet.
void AppendPtr(const std::string& service_type, uint32 ttl,
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h
index d5ef793..a0dbb64 100644
--- a/cloud_print/gcp20/prototype/dns_sd_server.h
+++ b/cloud_print/gcp20/prototype/dns_sd_server.h
@@ -15,8 +15,10 @@
#include "net/udp/udp_socket.h"
namespace net {
- class IOBufferWithSize;
-}
+
+class IOBufferWithSize;
+
+} // namespace net
struct DnsQueryRecord;
class DnsResponseBuilder;
diff --git a/cloud_print/gcp20/prototype/gcp20_device.cc b/cloud_print/gcp20/prototype/gcp20_device.cc
index cbd0c86..d58adc5 100644
--- a/cloud_print/gcp20/prototype/gcp20_device.cc
+++ b/cloud_print/gcp20/prototype/gcp20_device.cc
@@ -16,28 +16,23 @@
namespace {
-Printer printer;
-base::AtExitManager at_exit;
-
-void StopPrinter() {
- printer.Stop();
-}
-
-void StartPrinter() {
- bool success = printer.Start();
+void StartPrinter(Printer* printer) {
+ bool success = printer->Start();
DCHECK(success);
-
- at_exit.RegisterTask(base::Bind(&StopPrinter));
}
+base::RunLoop* g_runner = NULL;
+
void OnAbort(int val) {
- StopPrinter();
- base::MessageLoop::current()->Quit();
+ if (g_runner)
+ g_runner->Quit();
}
} // namespace
int main(int argc, char* argv[]) {
+ base::AtExitManager at_exit;
+ Printer printer;
CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
@@ -47,9 +42,14 @@ int main(int argc, char* argv[]) {
signal(SIGINT, OnAbort); // Handle Ctrl+C signal.
base::MessageLoop loop(base::MessageLoop::TYPE_IO);
- base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&StartPrinter));
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&StartPrinter, &printer));
base::RunLoop runner;
+ 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 8324b8b..c380d20 100644
--- a/cloud_print/gcp20/prototype/gcp20_device.gyp
+++ b/cloud_print/gcp20/prototype/gcp20_device.gyp
@@ -20,9 +20,17 @@
'type': 'static_library',
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/google_apis/google_apis.gyp:google_apis',
+ '<(DEPTH)/net/net.gyp:http_server',
'<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/url/url.gyp:url_lib',
+ '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
],
'sources': [
+ 'cloud_print_response_parser.cc',
+ 'cloud_print_response_parser.h',
+ 'cloud_print_requester.cc',
+ 'cloud_print_requester.h',
'dns_packet_parser.cc',
'dns_packet_parser.h',
'dns_response_builder.cc',
@@ -32,6 +40,8 @@
'gcp20_device.cc',
'printer.cc',
'printer.h',
+ 'privet_http_server.cc',
+ 'privet_http_server.h',
'service_parameters.cc',
'service_parameters.h',
],
@@ -49,9 +59,10 @@
'VCLinkerTool': {
'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE
'AdditionalDependencies': [
- 'secur32.lib',
- 'httpapi.lib',
- 'Ws2_32.lib',
+# TODO(maksymb): Check which of whis libs is needed.
+ 'secur32.lib',
+ 'httpapi.lib',
+ 'Ws2_32.lib',
],
},
},
diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc
index 4d39fe8..dcbef5a 100644
--- a/cloud_print/gcp20/prototype/printer.cc
+++ b/cloud_print/gcp20/prototype/printer.cc
@@ -8,9 +8,12 @@
#include <vector>
#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/guid.h"
#include "base/strings/string_number_conversions.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "net/base/net_util.h"
+#include "net/base/url_util.h"
namespace {
@@ -18,9 +21,44 @@ const char* kServiceType = "_privet._tcp.local";
const char* kServiceNamePrefix = "first_gcp20_device";
const char* kServiceDomainName = "my-privet-device.local";
+const char* kPrinterName = "Google GCP2.0 Prototype";
+
const uint16 kDefaultTTL = 60*60;
const uint16 kDefaultHttpPort = 10101;
+const char* kCdd =
+"{\n"
+" 'version': '1.0',\n"
+" 'printer': {\n"
+" 'vendor_capability': [\n"
+" {\n"
+" 'id': 'psk:MediaType',\n"
+" 'display_name': 'Media Type',\n"
+" 'type': 'SELECT',\n"
+" 'select_cap': {\n"
+" 'option': [\n"
+" {\n"
+" 'value': 'psk:Plain',\n"
+" 'display_name': 'Plain Paper',\n"
+" 'is_default': true\n"
+" },\n"
+" {\n"
+" 'value': 'ns0000:Glossy',\n"
+" 'display_name': 'Glossy Photo',\n"
+" 'is_default': false\n"
+" }\n"
+" ]\n"
+" }\n"
+" }\n"
+" ],\n"
+" 'reverse_order': { 'default': false }\n"
+" }\n"
+"}\n";
+
+std::string GenerateProxyId() {
+ return "{" + base::GenerateGUID() + "}";
+}
+
uint16 ReadHttpPortFromCommandLine() {
uint32 http_port_tmp = kDefaultHttpPort;
@@ -76,7 +114,13 @@ net::IPAddressNumber GetLocalIp(const std::string& interface_name,
} // namespace
-Printer::Printer() : initialized_(false) {
+Printer::RegistrationInfo::RegistrationInfo() : state(DEV_REG_UNREGISTERED) {
+}
+
+Printer::RegistrationInfo::~RegistrationInfo() {
+}
+
+Printer::Printer() : http_server_(this) {
}
Printer::~Printer() {
@@ -84,7 +128,7 @@ Printer::~Printer() {
}
bool Printer::Start() {
- if (initialized_)
+ if (IsOnline())
return true;
// TODO(maksymb): Add switch for command line to control interface name.
@@ -95,35 +139,190 @@ bool Printer::Start() {
}
VLOG(1) << "Local address: " << net::IPAddressToString(ip);
+ uint16 port = ReadHttpPortFromCommandLine();
+
+ // TODO(maksymb): Add loading state from drive.
+ reg_info_ = RegistrationInfo();
+
// Starting DNS-SD server.
- initialized_ = dns_server_.Start(
- ServiceParameters(kServiceType,
- kServiceNamePrefix,
- kServiceDomainName,
- ip,
- ReadHttpPortFromCommandLine()),
+ bool success = dns_server_.Start(
+ ServiceParameters(kServiceType, kServiceNamePrefix, kServiceDomainName,
+ ip, port),
ReadTtlFromCommandLine(),
CreateTxt());
- return initialized_;
+ if (!success)
+ return false;
+
+ // Starting HTTP server.
+ success = http_server_.Start(port);
+ if (!success)
+ return false;
+ // TODO(maksymb): Check what happened if DNS server will start but HTTP
+ // server won't.
+
+ // Creating Cloud Requester.
+ requester_.reset(
+ new CloudPrintRequester(
+ base::MessageLoop::current()->message_loop_proxy(),
+ this));
+
+ return true;
}
-void Printer::Stop() {
- if (!initialized_)
- return;
+bool Printer::IsOnline() const {
+ return requester_;
+}
+void Printer::Stop() {
dns_server_.Shutdown();
+ http_server_.Shutdown();
+ requester_.reset();
+}
+
+PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
+ const std::string& user) {
+ PrivetHttpServer::RegistrationErrorStatus status;
+ if (!CheckRegistrationState(user, false, &status))
+ return status;
+
+ if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED)
+ return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
+
+ reg_info_ = RegistrationInfo();
+ reg_info_.user = user;
+ reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_STARTED;
+
+ requester_->StartRegistration(GenerateProxyId(), kPrinterName, user, kCdd);
+
+ return PrivetHttpServer::REG_ERROR_OK;
+}
+
+PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken(
+ const std::string& user,
+ std::string* token,
+ std::string* claim_url) {
+ PrivetHttpServer::RegistrationErrorStatus status;
+ if (!CheckRegistrationState(user, false, &status))
+ return status;
+
+ // TODO(maksymb): Add user confirmation.
+
+ if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_STARTED)
+ return PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
+
+ if (reg_info_.state ==
+ RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) {
+ *token = reg_info_.registration_token;
+ *claim_url = reg_info_.complete_invite_url;
+ return PrivetHttpServer::REG_ERROR_OK;
+ }
+
+ return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
+}
+
+PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete(
+ const std::string& user,
+ std::string* device_id) {
+ PrivetHttpServer::RegistrationErrorStatus status;
+ if (!CheckRegistrationState(user, false, &status))
+ return status;
+
+ if (reg_info_.state !=
+ RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY)
+ return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
+
+ reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_COMPLETING;
+ requester_->CompleteRegistration();
+
+ *device_id = reg_info_.device_id;
+
+ return PrivetHttpServer::REG_ERROR_OK;
+}
+
+PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel(
+ const std::string& user) {
+ PrivetHttpServer::RegistrationErrorStatus status;
+ if (!CheckRegistrationState(user, true, &status))
+ return status;
+
+ if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED)
+ return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
+
+ reg_info_ = RegistrationInfo();
+ return PrivetHttpServer::REG_ERROR_OK;
+}
+
+void Printer::GetRegistrationServerError(std::string* description) {
+ DCHECK_EQ(reg_info_.state, RegistrationInfo::DEV_REG_REGISTRATION_ERROR) <<
+ "Method shouldn't be called when not needed.";
+
+ *description = reg_info_.error_description;
+}
+
+void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
+ *info = PrivetHttpServer::DeviceInfo();
+ info->version = "1.0";
+ info->manufacturer = "Google";
+ if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED)
+ info->api.push_back("/privet/register");
+}
+
+void Printer::OnRegistrationStartResponseParsed(
+ const std::string& registration_token,
+ const std::string& complete_invite_url,
+ const std::string& device_id) {
+ reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY;
+ reg_info_.device_id = device_id;
+ reg_info_.registration_token = registration_token;
+ reg_info_.complete_invite_url = complete_invite_url;
+}
+
+void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token) {
+ reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED;
+ reg_info_.refresh_token = refresh_token;
+}
+
+void Printer::OnRegistrationError(const std::string& description) {
+ LOG(ERROR) << "server_error: " << description;
+
+ // TODO(maksymb): Implement waiting after error and timeout of registration.
+ reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_ERROR;
+ reg_info_.error_description = description;
+}
+
+bool Printer::CheckRegistrationState(
+ const std::string& user,
+ bool ignore_error,
+ PrivetHttpServer::RegistrationErrorStatus* status) const {
+ if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED) {
+ *status = PrivetHttpServer::REG_ERROR_REGISTERED;
+ return false;
+ }
+
+ if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED &&
+ user != reg_info_.user) {
+ *status = PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
+ return false;
+ }
+
+ if (!ignore_error &&
+ reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_ERROR) {
+ *status = PrivetHttpServer::REG_ERROR_SERVER_ERROR;
+ return false;
+ }
+
+ return true;
}
std::vector<std::string> Printer::CreateTxt() const {
std::vector<std::string> txt;
-
txt.push_back("txtvers=1");
- txt.push_back("ty=Google Cloud Ready Printer GCP2.0 Prototype");
- txt.push_back("note=Virtual printer");
+ txt.push_back("ty=" + std::string(kPrinterName));
+ txt.push_back("note=");
txt.push_back("url=https://www.google.com/cloudprint");
txt.push_back("type=printer");
- txt.push_back("id=");
+ txt.push_back("id=" + reg_info_.device_id);
txt.push_back("cs=offline");
return txt;
diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h
index d2d72b6..86c58b0 100644
--- a/cloud_print/gcp20/prototype/printer.h
+++ b/cloud_print/gcp20/prototype/printer.h
@@ -8,33 +8,107 @@
#include <string>
#include <vector>
+#include "cloud_print/gcp20/prototype/cloud_print_requester.h"
#include "cloud_print/gcp20/prototype/dns_sd_server.h"
+#include "cloud_print/gcp20/prototype/privet_http_server.h"
-// This class will maintain work of DNS-SD server, HTTP server and others.
-class Printer {
+// This class maintain work of DNS-SD server, HTTP server and others.
+class Printer : public PrivetHttpServer::Delegate,
+ public CloudPrintRequester::Delegate {
public:
// Constructs uninitialized object.
Printer();
// Destroys the object.
- ~Printer();
+ virtual ~Printer();
// Starts all servers.
bool Start();
+ // Returns true if printer was started.
+ bool IsOnline() const;
+
// Stops all servers.
void Stop();
private:
+ struct RegistrationInfo {
+ enum RegistrationState {
+ DEV_REG_UNREGISTERED = 0,
+ DEV_REG_REGISTRATION_STARTED,
+ DEV_REG_REGISTRATION_CLAIM_TOKEN_READY,
+ DEV_REG_REGISTRATION_COMPLETING,
+ DEV_REG_REGISTRATION_ERROR,
+ DEV_REG_REGISTERED,
+ };
+
+ RegistrationInfo();
+ ~RegistrationInfo();
+
+ std::string user;
+ std::string refresh_token;
+ std::string device_id;
+ RegistrationState state;
+
+ std::string registration_token;
+ std::string complete_invite_url;
+
+ // Contains error response if |DEV_REG_REGISTRATION_ERROR| is set.
+ std::string error_description;
+ };
+
+ enum RegistrationAction {
+ REG_ACTION_START,
+ REG_ACTION_GET_CLAIM_TOKEN,
+ REG_ACTION_COMPLETE,
+ REG_ACTION_CANCEL
+ };
+
+ // PrivetHttpServer::Delegate methods:
+ virtual PrivetHttpServer::RegistrationErrorStatus RegistrationStart(
+ const std::string& user) OVERRIDE;
+ virtual PrivetHttpServer::RegistrationErrorStatus RegistrationGetClaimToken(
+ const std::string& user,
+ std::string* token,
+ std::string* claim_url) OVERRIDE;
+ virtual PrivetHttpServer::RegistrationErrorStatus RegistrationComplete(
+ const std::string& user,
+ std::string* device_id) OVERRIDE;
+ virtual PrivetHttpServer::RegistrationErrorStatus RegistrationCancel(
+ const std::string& user) OVERRIDE;
+ virtual void GetRegistrationServerError(std::string* description) OVERRIDE;
+ virtual void CreateInfo(PrivetHttpServer::DeviceInfo* info) OVERRIDE;
+
+ // CloudRequester::Delegate methods:
+ virtual void OnRegistrationStartResponseParsed(
+ const std::string& registration_token,
+ const std::string& complete_invite_url,
+ const std::string& device_id) OVERRIDE;
+ virtual void OnGetAuthCodeResponseParsed(
+ const std::string& refresh_token) OVERRIDE;
+ virtual void OnRegistrationError(const std::string& description) OVERRIDE;
+
+ // Checks if register call is called correctly (|user| is correct,
+ // error is no set etc).
+ bool CheckRegistrationState(
+ const std::string& user,
+ bool ignore_error,
+ PrivetHttpServer::RegistrationErrorStatus* status) const;
+
// Creates data for DNS TXT respond.
std::vector<std::string> CreateTxt() const;
- // Contains |true| if initialized.
- bool initialized_;
+ RegistrationInfo reg_info_;
// Contains DNS-SD server.
DnsSdServer dns_server_;
+ // Contains Privet HTTP server.
+ PrivetHttpServer http_server_;
+
+ // Contains Cloud Print client.
+ scoped_ptr<CloudPrintRequester> requester_;
+
DISALLOW_COPY_AND_ASSIGN(Printer);
};
diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc
new file mode 100644
index 0000000..2114360
--- /dev/null
+++ b/cloud_print/gcp20/prototype/privet_http_server.cc
@@ -0,0 +1,255 @@
+// 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/privet_http_server.h"
+
+#include "base/json/json_writer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/url_util.h"
+#include "net/socket/tcp_listen_socket.h"
+#include "url/gurl.h"
+
+namespace {
+
+// {"error":|error_type|}
+scoped_ptr<base::DictionaryValue> CreateError(const std::string& error_type) {
+ scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue);
+ error->SetString("error", error_type);
+
+ return error.Pass();
+}
+
+// {"error":|error_type|, "description":|description|}
+scoped_ptr<base::DictionaryValue> CreateErrorWithDescription(
+ const std::string& error_type,
+ const std::string& description) {
+ scoped_ptr<base::DictionaryValue> error(CreateError(error_type));
+ error->SetString("description", description);
+ return error.Pass();
+}
+
+// {"error":|error_type|, "timeout":|timout|}
+scoped_ptr<base::DictionaryValue> CreateErrorWithTimeout(
+ const std::string& error_type,
+ int timeout) {
+ scoped_ptr<base::DictionaryValue> error(CreateError(error_type));
+ error->SetInteger("timeout", timeout);
+ return error.Pass();
+}
+
+} // namespace
+
+PrivetHttpServer::DeviceInfo::DeviceInfo() {
+}
+
+PrivetHttpServer::DeviceInfo::~DeviceInfo() {
+}
+
+PrivetHttpServer::PrivetHttpServer(Delegate* delegate)
+ : port_(0),
+ delegate_(delegate) {
+}
+
+PrivetHttpServer::~PrivetHttpServer() {
+ Shutdown();
+}
+
+bool PrivetHttpServer::Start(uint16 port) {
+ if (server_)
+ return true;
+
+ net::TCPListenSocketFactory factory("0.0.0.0", port);
+ server_ = new net::HttpServer(factory, this);
+ net::IPEndPoint address;
+
+ if (server_->GetLocalAddress(&address) != net::OK) {
+ NOTREACHED() << "Cannot start HTTP server";
+ } else {
+ VLOG(1) << "Address of HTTP server: " << address.ToString();
+ }
+
+ return true;
+}
+
+void PrivetHttpServer::Shutdown() {
+ if (!server_)
+ return;
+
+ server_ = NULL;
+}
+
+void PrivetHttpServer::OnHttpRequest(int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ VLOG(1) << "Processing HTTP request: " << info.path;
+ GURL url("http://host" + info.path);
+
+ // TODO(maksymb): Add checking for X-PrivetToken.
+ std::string response;
+ net::HttpStatusCode status_code = ProcessHttpRequest(url, &response);
+
+ server_->Send(connection_id, status_code, response, "text/plain");
+}
+
+void PrivetHttpServer::OnWebSocketRequest(
+ int connection_id, const net::HttpServerRequestInfo& info) {
+}
+
+void PrivetHttpServer::OnWebSocketMessage(int connection_id,
+ const std::string& data) {
+}
+
+void PrivetHttpServer::OnClose(int connection_id) {
+}
+
+net::HttpStatusCode PrivetHttpServer::ProcessHttpRequest(
+ const GURL& url,
+ std::string* response) {
+ net::HttpStatusCode status_code = net::HTTP_OK;
+ scoped_ptr<base::DictionaryValue> json_response;
+
+ if (url.path() == "/privet/info") {
+ json_response = ProcessInfo(&status_code);
+ } else if (url.path() == "/privet/register") {
+ json_response = ProcessRegister(url, &status_code);
+ } else {
+ response->clear();
+ return net::HTTP_NOT_FOUND;
+ }
+
+ if (!json_response) {
+ response->clear();
+ return status_code;
+ }
+
+ base::JSONWriter::WriteWithOptions(json_response.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ response);
+ return status_code;
+}
+
+// Privet API methods:
+
+scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessInfo(
+ net::HttpStatusCode* status_code) const {
+
+ DeviceInfo info;
+ delegate_->CreateInfo(&info);
+
+ scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue);
+ response->SetString("version", info.version);
+ response->SetString("manufacturer", info.manufacturer);
+
+ base::ListValue api;
+ for (size_t i = 0; i < info.api.size(); ++i)
+ api.AppendString(info.api[i]);
+ response->Set("api", api.DeepCopy());
+
+ *status_code = net::HTTP_OK;
+ return response.Pass();
+}
+
+scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessRegister(
+ const GURL& url,
+ net::HttpStatusCode* status_code) {
+ // TODO(maksymb): Add saving state to drive.
+
+ std::string action;
+ if (!net::GetValueForKeyInQuery(url, "action", &action)) {
+ *status_code = net::HTTP_BAD_REQUEST;
+ return scoped_ptr<base::DictionaryValue>();
+ }
+
+ // TODO(maksymb): Is there a possibility |user| to be empty?
+ std::string user;
+ if (!net::GetValueForKeyInQuery(url, "user", &user) || user.empty()) {
+ *status_code = net::HTTP_BAD_REQUEST;
+ return scoped_ptr<base::DictionaryValue>();
+ }
+
+ RegistrationErrorStatus status = REG_ERROR_NO_RESULT;
+ scoped_ptr<base::DictionaryValue> response(new DictionaryValue);
+ response->SetString("action", action);
+ response->SetString("user", user);
+
+ if (action == "start")
+ status = delegate_->RegistrationStart(user);
+
+ if (action == "getClaimToken") {
+ std::string token;
+ std::string claim_url;
+ status = delegate_->RegistrationGetClaimToken(user, &token, &claim_url);
+ response->SetString("token", token);
+ response->SetString("claim_url", claim_url);
+ }
+
+ if (action == "complete") {
+ std::string device_id;
+ status = delegate_->RegistrationComplete(user, &device_id);
+ response->SetString("device_id", device_id);
+ }
+
+ if (action == "cancel")
+ status = delegate_->RegistrationCancel(user);
+
+ if (status != REG_ERROR_OK)
+ response.reset();
+
+ ProcessRegistrationStatus(status, status_code, &response);
+ return response.Pass();
+}
+
+void PrivetHttpServer::ProcessRegistrationStatus(
+ RegistrationErrorStatus status,
+ net::HttpStatusCode *status_code,
+ scoped_ptr<base::DictionaryValue>* current_response) const {
+ switch (status) {
+ case REG_ERROR_OK:
+ *status_code = net::HTTP_OK;
+ DCHECK(*current_response) << "Response shouldn't be empty.";
+ break;
+ case REG_ERROR_NO_RESULT:
+ *status_code = net::HTTP_BAD_REQUEST;
+ current_response->reset();
+ break;
+ case REG_ERROR_REGISTERED:
+ *status_code = net::HTTP_NOT_FOUND;
+ current_response->reset();
+ break;
+
+ case REG_ERROR_DEVICE_BUSY:
+ *status_code = net::HTTP_OK;
+ *current_response = CreateErrorWithTimeout("device_busy", 30);
+ break;
+ case REG_ERROR_PENDING_USER_ACTION:
+ *status_code = net::HTTP_OK;
+ *current_response = CreateErrorWithTimeout("pending_user_action", 30);
+ break;
+ case REG_ERROR_USER_CANCEL:
+ *status_code = net::HTTP_OK;
+ *current_response = CreateError("user_cancel");
+ break;
+ case REG_ERROR_CONFIRMATION_TIMEOUT:
+ *status_code = net::HTTP_OK;
+ *current_response = CreateError("confirmation_timeout");
+ break;
+ case REG_ERROR_INVALID_ACTION:
+ *status_code = net::HTTP_OK;
+ *current_response = CreateError("invalid_action");
+ break;
+ case REG_ERROR_SERVER_ERROR: {
+ *status_code = net::HTTP_OK;
+ std::string description;
+ delegate_->GetRegistrationServerError(&description);
+ *current_response = CreateErrorWithDescription("server_error",
+ description);
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ };
+}
+
+
diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h
new file mode 100644
index 0000000..b412bad
--- /dev/null
+++ b/cloud_print/gcp20/prototype/privet_http_server.h
@@ -0,0 +1,135 @@
+// 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_PRIVET_HTTP_SERVER_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/values.h"
+#include "net/server/http_server.h"
+#include "net/server/http_server_request_info.h"
+
+class GURL;
+
+// HTTP server for receiving .
+class PrivetHttpServer: public net::HttpServer::Delegate {
+ public:
+ // TODO(maksymb): Move this enum to some namespace instead of this class.
+ enum RegistrationErrorStatus {
+ REG_ERROR_OK,
+ REG_ERROR_NO_RESULT, // default value, never set.
+ REG_ERROR_REGISTERED,
+
+ REG_ERROR_DEVICE_BUSY,
+ REG_ERROR_PENDING_USER_ACTION,
+ REG_ERROR_USER_CANCEL,
+ REG_ERROR_CONFIRMATION_TIMEOUT,
+ REG_ERROR_INVALID_ACTION,
+ REG_ERROR_SERVER_ERROR
+ };
+
+ // TODO(maksymb): Move this struct to some namespace instead of this class.
+ struct DeviceInfo {
+ DeviceInfo();
+ ~DeviceInfo();
+
+ std::string version;
+ std::string manufacturer;
+ std::vector<std::string> api;
+ };
+
+ class Delegate {
+ public:
+ Delegate() {}
+
+ virtual ~Delegate() {}
+
+ // Invoked when registration is starting.
+ virtual RegistrationErrorStatus RegistrationStart(
+ const std::string& user) = 0;
+
+ // Invoked when claimtoken is needed.
+ virtual RegistrationErrorStatus RegistrationGetClaimToken(
+ const std::string& user,
+ std::string* token,
+ std::string* claim_url) = 0;
+
+ // Invoked when registration is going to be completed.
+ virtual RegistrationErrorStatus RegistrationComplete(
+ const std::string& user,
+ std::string* device_id) = 0;
+
+ // Invoked when client asked for cancelling the registration.
+ virtual RegistrationErrorStatus RegistrationCancel(
+ const std::string& user) = 0;
+
+ // Invoked for receiving server error details.
+ virtual void GetRegistrationServerError(std::string* description) = 0;
+
+ // Invoked if /privet/info is called.
+ virtual void CreateInfo(DeviceInfo* info) = 0;
+ };
+
+ // Constructor doesn't start server.
+ explicit PrivetHttpServer(Delegate* delegate);
+
+ // Destroys the object.
+ virtual ~PrivetHttpServer();
+
+ // Starts HTTP server: start listening port |port| for HTTP requests.
+ bool Start(uint16 port);
+
+ // Stops HTTP server.
+ void Shutdown();
+
+ private:
+ // net::HttpServer::Delegate methods:
+ virtual void OnHttpRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketMessage(int connection_id,
+ const std::string& data) OVERRIDE;
+ virtual void OnClose(int connection_id) OVERRIDE;
+
+ // Processes http request after all preparations (XPrivetHeader check,
+ // data handling etc.)
+ net::HttpStatusCode ProcessHttpRequest(const GURL& url,
+ std::string* response);
+
+ // Pivet API methods. Return reference to NULL if output should be empty.
+ scoped_ptr<base::DictionaryValue> ProcessInfo(
+ net::HttpStatusCode* status_code) const;
+ scoped_ptr<base::DictionaryValue> ProcessReset(
+ net::HttpStatusCode* status_code);
+ scoped_ptr<base::DictionaryValue> ProcessRegister(
+ const GURL& url,
+ net::HttpStatusCode* status_code);
+
+ // Proccesses current status and depending on it replaces (or not)
+ // |current_response| with error or empty response.
+ void ProcessRegistrationStatus(
+ RegistrationErrorStatus status,
+ net::HttpStatusCode *status_code,
+ scoped_ptr<base::DictionaryValue>* current_response) const;
+
+ // Port for listening.
+
+ uint16 port_;
+
+ // Contains encapsulated object for listening for requests.
+ scoped_refptr<net::HttpServer> server_;
+
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrivetHttpServer);
+};
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
+
diff --git a/cloud_print/gcp20/prototype/service_parameters.cc b/cloud_print/gcp20/prototype/service_parameters.cc
index e32581e..7433a89 100644
--- a/cloud_print/gcp20/prototype/service_parameters.cc
+++ b/cloud_print/gcp20/prototype/service_parameters.cc
@@ -7,6 +7,9 @@
ServiceParameters::ServiceParameters() : http_port_(0) {
}
+ServiceParameters::~ServiceParameters() {
+}
+
ServiceParameters::ServiceParameters(const std::string& service_type,
const std::string& service_name_prefix,
const std::string& service_domain_name,
diff --git a/cloud_print/gcp20/prototype/service_parameters.h b/cloud_print/gcp20/prototype/service_parameters.h
index 79582ad..f86f870 100644
--- a/cloud_print/gcp20/prototype/service_parameters.h
+++ b/cloud_print/gcp20/prototype/service_parameters.h
@@ -13,7 +13,7 @@
struct ServiceParameters {
ServiceParameters();
- ~ServiceParameters() {}
+ ~ServiceParameters();
ServiceParameters(const std::string& service_type,
const std::string& service_name_prefix,