summaryrefslogtreecommitdiffstats
path: root/cloud_print
diff options
context:
space:
mode:
authormaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 15:39:15 +0000
committermaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 15:39:15 +0000
commita20b019ab8ee1eeba45257d9c8774e88b1a665e4 (patch)
tree8b62906129a1a89134c3a9447026fcdf885da71b /cloud_print
parent73f6d0a7200a072972d4c8dd774f6f5cd341ff47 (diff)
downloadchromium_src-a20b019ab8ee1eeba45257d9c8774e88b1a665e4.zip
chromium_src-a20b019ab8ee1eeba45257d9c8774e88b1a665e4.tar.gz
chromium_src-a20b019ab8ee1eeba45257d9c8774e88b1a665e4.tar.bz2
XPrivetToken, saving to file, little extend for /privet/info.
Review URL: https://chromiumcodereview.appspot.com/18703004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213129 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print')
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.cc12
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.h6
-rw-r--r--cloud_print/gcp20/prototype/command_line_reader.cc53
-rw-r--r--cloud_print/gcp20/prototype/command_line_reader.h18
-rw-r--r--cloud_print/gcp20/prototype/dns_sd_server.cc11
-rw-r--r--cloud_print/gcp20/prototype/dns_sd_server.h3
-rw-r--r--cloud_print/gcp20/prototype/gcp20_device.gyp33
-rw-r--r--cloud_print/gcp20/prototype/printer.cc228
-rw-r--r--cloud_print/gcp20/prototype/printer.h24
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.cc41
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.h15
-rw-r--r--cloud_print/gcp20/prototype/x_privet_token.cc78
-rw-r--r--cloud_print/gcp20/prototype/x_privet_token.h52
-rw-r--r--cloud_print/gcp20/prototype/x_privet_token_unittest.cc123
14 files changed, 592 insertions, 105 deletions
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc
index 857b3de..ded4b47 100644
--- a/cloud_print/gcp20/prototype/cloud_print_requester.cc
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc
@@ -18,6 +18,8 @@
#include "net/url_request/url_request_context_builder.h"
#include "url/gurl.h"
+const char* kCloudPrintUrl = "https://www.google.com/cloudprint";
+
namespace {
const char kProxyIdValue[] = "proxy";
@@ -28,12 +30,8 @@ const char kPrinterUserValue[] = "user";
const int kGaiaMaxRetries = 3;
-std::string GetCloudPrintUrl() {
- return "https://www.google.com/cloudprint";
-}
-
GURL CreateRegisterUrl() {
- return GURL(GetCloudPrintUrl() + "/register");
+ return GURL(std::string(kCloudPrintUrl) + "/register");
}
} // namespace
@@ -123,7 +121,7 @@ bool CloudPrintRequester::StartRegistration(const std::string& proxy_id,
if (!CreateRequest(
CreateRegisterUrl(), net::URLFetcher::POST,
base::Bind(&CloudPrintRequester::ParseRegisterStartResponse,
- base::Unretained(this)))) {
+ AsWeakPtr()))) {
return false;
}
fetcher_->SetUploadData(mime_type, data);
@@ -137,7 +135,7 @@ bool CloudPrintRequester::CompleteRegistration() {
GURL(polling_url_ + oauth_client_info_.client_id),
net::URLFetcher::GET,
base::Bind(&CloudPrintRequester::ParseRegisterCompleteResponse,
- base::Unretained(this)))) {
+ AsWeakPtr()))) {
return false;
}
fetcher_->Start();
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h
index 345bfce..369cca7 100644
--- a/cloud_print/gcp20/prototype/cloud_print_requester.h
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "net/url_request/url_fetcher.h"
@@ -20,8 +21,11 @@ typedef base::Callback<void(const std::string&)> ParserCallback;
class CloudPrintURLRequestContextGetter;
+extern const char* kCloudPrintUrl;
+
// Class for requesting CloudPrint server and parsing responses.
-class CloudPrintRequester : public net::URLFetcherDelegate,
+class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
+ public net::URLFetcherDelegate,
public gaia::GaiaOAuthClient::Delegate {
public:
class Delegate {
diff --git a/cloud_print/gcp20/prototype/command_line_reader.cc b/cloud_print/gcp20/prototype/command_line_reader.cc
new file mode 100644
index 0000000..a5474e6
--- /dev/null
+++ b/cloud_print/gcp20/prototype/command_line_reader.cc
@@ -0,0 +1,53 @@
+// 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/command_line_reader.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace {
+
+const uint16 kDefaultHttpPort = 10101;
+const uint32 kDefaultTTL = 60*60;
+
+} // namespace
+
+namespace command_line_reader {
+
+uint16 ReadHttpPort() {
+ uint32 http_port = kDefaultHttpPort;
+
+ std::string http_port_string =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII("http-port");
+
+ if (!base::StringToUint(http_port_string, &http_port))
+ http_port = kDefaultHttpPort;
+
+ if (http_port > kuint16max) {
+ LOG(ERROR) << "Port " << http_port << " is too large (maximum is " <<
+ kuint16max << "). Using default port: " << kDefaultHttpPort;
+
+ http_port = kDefaultHttpPort;
+ }
+
+ VLOG(1) << "HTTP port for responses: " << http_port;
+ return static_cast<uint16>(http_port);
+}
+
+uint32 ReadTtl() {
+ uint32 ttl = kDefaultTTL;
+
+ if (!base::StringToUint(
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII("ttl"), &ttl)) {
+ ttl = kDefaultTTL;
+ }
+
+ VLOG(1) << "TTL for announcements: " << ttl;
+ return ttl;
+}
+
+} // namespace command_line_reader
+
diff --git a/cloud_print/gcp20/prototype/command_line_reader.h b/cloud_print/gcp20/prototype/command_line_reader.h
new file mode 100644
index 0000000..1043890
--- /dev/null
+++ b/cloud_print/gcp20/prototype/command_line_reader.h
@@ -0,0 +1,18 @@
+// 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_COMMAND_LINE_READER_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_COMMAND_LINE_READER_H_
+
+#include "base/basictypes.h"
+
+namespace command_line_reader {
+
+uint16 ReadHttpPort();
+uint32 ReadTtl();
+
+} // namespace command_line_reader
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_COMMAND_LINE_READER_H_
+
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.cc b/cloud_print/gcp20/prototype/dns_sd_server.cc
index cecb8d5..38dfb35 100644
--- a/cloud_print/gcp20/prototype/dns_sd_server.cc
+++ b/cloud_print/gcp20/prototype/dns_sd_server.cc
@@ -66,7 +66,7 @@ bool DnsSdServer::Start(const ServiceParameters& serv_params, uint32 full_ttl,
SendAnnouncement(full_ttl_);
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&DnsSdServer::OnDatagramReceived, base::Unretained(this)));
+ base::Bind(&DnsSdServer::OnDatagramReceived, AsWeakPtr()));
return true;
}
@@ -242,11 +242,8 @@ void DnsSdServer::DoLoop(int rv) {
do {
if (rv > 0)
ProcessMessage(rv, recv_buf_.get());
- rv = socket_->RecvFrom(
- recv_buf_.get(),
- recv_buf_->size(),
- &recv_address_,
- base::Bind(&DnsSdServer::DoLoop, base::Unretained(this)));
+ rv = socket_->RecvFrom(recv_buf_.get(), recv_buf_->size(), &recv_address_,
+ base::Bind(&DnsSdServer::DoLoop, AsWeakPtr()));
} while (rv > 0);
// TODO(maksymb): Add handler for errors
@@ -285,7 +282,7 @@ void DnsSdServer::SendAnnouncement(uint32 ttl) {
// Schedule next announcement.
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- base::Bind(&DnsSdServer::Update, base::Unretained(this)),
+ base::Bind(&DnsSdServer::Update, AsWeakPtr()),
base::TimeDelta::FromSeconds(static_cast<int64>(
kTimeToNextAnnouncement*full_ttl_)));
}
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h
index a0dbb64..a0d0128 100644
--- a/cloud_print/gcp20/prototype/dns_sd_server.h
+++ b/cloud_print/gcp20/prototype/dns_sd_server.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "net/base/ip_endpoint.h"
#include "net/udp/udp_socket.h"
@@ -26,7 +27,7 @@ class DnsResponseBuilder;
// Class for sending multicast announcements, receiving queries and answering on
// them.
// TODO(maksymb): Implement probing.
-class DnsSdServer {
+class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> {
public:
// Constructor does not start server.
DnsSdServer();
diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp
index c380d20..795167b 100644
--- a/cloud_print/gcp20/prototype/gcp20_device.gyp
+++ b/cloud_print/gcp20/prototype/gcp20_device.gyp
@@ -20,39 +20,42 @@
'type': 'static_library',
'dependencies': [
'<(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)/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',
+ 'command_line_reader.cc',
+ 'command_line_reader.h',
'dns_packet_parser.cc',
'dns_packet_parser.h',
'dns_response_builder.cc',
'dns_response_builder.h',
'dns_sd_server.cc',
'dns_sd_server.h',
- 'gcp20_device.cc',
'printer.cc',
'printer.h',
'privet_http_server.cc',
'privet_http_server.h',
'service_parameters.cc',
'service_parameters.h',
+ 'x_privet_token.cc',
+ 'x_privet_token.h',
],
},
{
'target_name': 'gcp20_device',
'type': 'executable',
'dependencies': [
- 'gcp20_device.gyp:gcp20_device_lib',
+ 'gcp20_device_lib',
],
- 'sources': [
+ 'sources': [
'gcp20_device.cc',
],
'msvs_settings': {
@@ -67,5 +70,27 @@
},
},
},
+ {
+ 'target_name': 'gcp20_device_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'x_privet_token_unittest.cc',
+ ],
+ 'dependencies': [
+ 'gcp20_device_lib',
+ '<(DEPTH)/base/base.gyp:run_all_unittests',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE
+ 'AdditionalDependencies': [
+ 'secur32.lib',
+ ],
+ },
+ },
+ },
],
}
diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc
index dcbef5a..aa6bcd5 100644
--- a/cloud_print/gcp20/prototype/printer.cc
+++ b/cloud_print/gcp20/prototype/printer.cc
@@ -10,11 +10,17 @@
#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 "cloud_print/gcp20/prototype/command_line_reader.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "net/base/net_util.h"
#include "net/base/url_util.h"
+const base::FilePath::CharType kPrinterStatePath[] =
+ FILE_PATH_LITERAL("printer_state.json");
+
namespace {
const char* kServiceType = "_privet._tcp.local";
@@ -22,9 +28,7 @@ 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* kPrinterDescription = "Printer emulator";
const char* kCdd =
"{\n"
@@ -55,38 +59,6 @@ const char* kCdd =
" }\n"
"}\n";
-std::string GenerateProxyId() {
- return "{" + base::GenerateGUID() + "}";
-}
-
-uint16 ReadHttpPortFromCommandLine() {
- uint32 http_port_tmp = kDefaultHttpPort;
-
- std::string http_port_string_tmp =
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII("http-port");
- base::StringToUint(http_port_string_tmp, &http_port_tmp);
-
- if (http_port_tmp > kuint16max) {
- LOG(ERROR) << "Port " << http_port_tmp << " is too large (maximum is " <<
- kDefaultHttpPort << "). Using default port.";
-
- http_port_tmp = kDefaultHttpPort;
- }
-
- VLOG(1) << "HTTP port for responses: " << http_port_tmp;
- return static_cast<uint16>(http_port_tmp);
-}
-
-uint16 ReadTtlFromCommandLine() {
- uint32 ttl = kDefaultTTL;
-
- base::StringToUint(
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII("ttl"), &ttl);
-
- VLOG(1) << "TTL for announcements: " << ttl;
- return ttl;
-}
-
// Returns local IP address number of first interface found (except loopback).
// Return value is empty if no interface found. Possible interfaces names are
// "eth0", "wlan0" etc. If interface name is empty, function will return IP
@@ -139,27 +111,24 @@ bool Printer::Start() {
}
VLOG(1) << "Local address: " << net::IPAddressToString(ip);
- uint16 port = ReadHttpPortFromCommandLine();
+ uint16 port = command_line_reader::ReadHttpPort();
- // TODO(maksymb): Add loading state from drive.
- reg_info_ = RegistrationInfo();
+ // Starting HTTP server.
+ if (!http_server_.Start(port))
+ return false;
+
+ if (!LoadFromFile(base::FilePath(kPrinterStatePath)))
+ reg_info_ = RegistrationInfo();
// Starting DNS-SD server.
- bool success = dns_server_.Start(
+ if (!dns_server_.Start(
ServiceParameters(kServiceType, kServiceNamePrefix, kServiceDomainName,
ip, port),
- ReadTtlFromCommandLine(),
- CreateTxt());
-
- if (!success)
+ command_line_reader::ReadTtl(),
+ CreateTxt())) {
+ http_server_.Shutdown();
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(
@@ -167,6 +136,9 @@ bool Printer::Start() {
base::MessageLoop::current()->message_loop_proxy(),
this));
+ xtoken_ = XPrivetToken();
+ starttime_ = base::Time::Now();
+
return true;
}
@@ -182,8 +154,8 @@ void Printer::Stop() {
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
const std::string& user) {
- PrivetHttpServer::RegistrationErrorStatus status;
- if (!CheckRegistrationState(user, false, &status))
+ PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
+ if (status != PrivetHttpServer::REG_ERROR_OK)
return status;
if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED)
@@ -198,12 +170,16 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
return PrivetHttpServer::REG_ERROR_OK;
}
+bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
+ return xtoken_.CheckValidXToken(token);
+}
+
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken(
const std::string& user,
std::string* token,
std::string* claim_url) {
- PrivetHttpServer::RegistrationErrorStatus status;
- if (!CheckRegistrationState(user, false, &status))
+ PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
+ if (status != PrivetHttpServer::REG_ERROR_OK)
return status;
// TODO(maksymb): Add user confirmation.
@@ -224,13 +200,14 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken(
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete(
const std::string& user,
std::string* device_id) {
- PrivetHttpServer::RegistrationErrorStatus status;
- if (!CheckRegistrationState(user, false, &status))
+ PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
+ if (status != PrivetHttpServer::REG_ERROR_OK)
return status;
if (reg_info_.state !=
- RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY)
+ RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) {
return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
+ }
reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_COMPLETING;
requester_->CompleteRegistration();
@@ -242,9 +219,11 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete(
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel(
const std::string& user) {
- PrivetHttpServer::RegistrationErrorStatus status;
- if (!CheckRegistrationState(user, true, &status))
+ PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
+ if (status != PrivetHttpServer::REG_ERROR_OK &&
+ status != PrivetHttpServer::REG_ERROR_SERVER_ERROR) {
return status;
+ }
if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED)
return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
@@ -261,11 +240,28 @@ void Printer::GetRegistrationServerError(std::string* description) {
}
void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
+ // TODO(maksymb): Replace "text" with constants.
+
*info = PrivetHttpServer::DeviceInfo();
info->version = "1.0";
+ info->name = kPrinterName;
+ info->description = kPrinterDescription;
+ info->url = kCloudPrintUrl;
+ info->id = reg_info_.device_id;
+ info->device_state = "idle";
+ info->connection_state = "offline";
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";
+ info->firmware = "3.7.31.127.8191.131071.524287.2147483647";
+ info->uptime = static_cast<int>((base::Time::Now() - starttime_).InSeconds());
+
+ info->x_privet_token = xtoken_.GenerateXToken();
+
if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED)
info->api.push_back("/privet/register");
+
+ info->type.push_back("printer");
}
void Printer::OnRegistrationStartResponseParsed(
@@ -281,6 +277,7 @@ void Printer::OnRegistrationStartResponseParsed(
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));
}
void Printer::OnRegistrationError(const std::string& description) {
@@ -291,36 +288,32 @@ void Printer::OnRegistrationError(const std::string& description) {
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;
- }
+PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors(
+ const std::string& user) const {
+ if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED)
+ return PrivetHttpServer::REG_ERROR_REGISTERED;
if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED &&
user != reg_info_.user) {
- *status = PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
- return false;
+ return PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
}
- if (!ignore_error &&
- reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_ERROR) {
- *status = PrivetHttpServer::REG_ERROR_SERVER_ERROR;
- return false;
- }
+ if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_ERROR)
+ return PrivetHttpServer::REG_ERROR_SERVER_ERROR;
- return true;
+ return PrivetHttpServer::REG_ERROR_OK;
+}
+
+std::string Printer::GenerateProxyId() const {
+ return "{" + base::GenerateGUID() +"}";
}
std::vector<std::string> Printer::CreateTxt() const {
std::vector<std::string> txt;
txt.push_back("txtvers=1");
txt.push_back("ty=" + std::string(kPrinterName));
- txt.push_back("note=");
- txt.push_back("url=https://www.google.com/cloudprint");
+ txt.push_back("note=" + std::string(kPrinterDescription));
+ 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");
@@ -328,3 +321,84 @@ std::vector<std::string> Printer::CreateTxt() const {
return txt;
}
+void Printer::SaveToFile(const base::FilePath& file_path) const {
+ base::DictionaryValue json;
+ // TODO(maksymb): Get rid of in-place constants.
+ if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED) {
+ json.SetBoolean("registered", true);
+ json.SetString("user", reg_info_.user);
+ json.SetString("device_id", reg_info_.device_id);
+ json.SetString("refresh_token", reg_info_.refresh_token);
+ } else {
+ json.SetBoolean("registered", false);
+ }
+
+ std::string json_str;
+ base::JSONWriter::WriteWithOptions(&json,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_str);
+ if (!file_util::WriteFile(file_path, json_str.data(),
+ static_cast<int>(json_str.size()))) {
+ LOG(ERROR) << "Cannot write state.";
+ }
+ LOG(INFO) << "State written to file.";
+}
+
+bool Printer::LoadFromFile(const base::FilePath& file_path) {
+ if (!base::PathExists(file_path)) {
+ LOG(INFO) << "Registration info is not found. Printer is unregistered.";
+ return false;
+ }
+
+ LOG(INFO) << "Loading registration info from file.";
+ std::string json_str;
+ if (!file_util::ReadFileToString(file_path, &json_str)) {
+ LOG(ERROR) << "Cannot open file.";
+ return false;
+ }
+
+ scoped_ptr<base::Value> json_val(base::JSONReader::Read(json_str));
+ base::DictionaryValue* json = NULL;
+ if (!json_val || !json_val->GetAsDictionary(&json)) {
+ LOG(ERROR) << "Cannot read JSON dictionary from file.";
+ return false;
+ }
+
+ bool registered = false;
+ if (!json->GetBoolean("registered", &registered)) {
+ LOG(ERROR) << "Cannot parse |registered| state.";
+ return false;
+ }
+
+ if (!registered) {
+ reg_info_ = RegistrationInfo();
+ return true;
+ }
+
+ std::string user;
+ if (!json->GetString("user", &user)) {
+ LOG(ERROR) << "Cannot parse |user|.";
+ return false;
+ }
+
+ std::string device_id;
+ if (!json->GetString("device_id", &device_id)) {
+ LOG(ERROR) << "Cannot parse |device_id|.";
+ return false;
+ }
+
+ std::string refresh_token;
+ if (!json->GetString("refresh_token", &refresh_token)) {
+ LOG(ERROR) << "Cannot parse |refresh_token|.";
+ 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;
+
+ return true;
+}
+
diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h
index 86c58b0..b03c729 100644
--- a/cloud_print/gcp20/prototype/printer.h
+++ b/cloud_print/gcp20/prototype/printer.h
@@ -11,6 +11,7 @@
#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"
+#include "cloud_print/gcp20/prototype/x_privet_token.h"
// This class maintain work of DNS-SD server, HTTP server and others.
class Printer : public PrivetHttpServer::Delegate,
@@ -79,6 +80,8 @@ class Printer : public PrivetHttpServer::Delegate,
virtual void GetRegistrationServerError(std::string* description) OVERRIDE;
virtual void CreateInfo(PrivetHttpServer::DeviceInfo* info) OVERRIDE;
+ virtual bool CheckXPrivetTokenHeader(const std::string& token) const OVERRIDE;
+
// CloudRequester::Delegate methods:
virtual void OnRegistrationStartResponseParsed(
const std::string& registration_token,
@@ -89,17 +92,23 @@ class Printer : public PrivetHttpServer::Delegate,
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;
+ // error is no set etc). Returns |false| if error status is put into |status|.
+ // Otherwise no error was occurred.
+ PrivetHttpServer::RegistrationErrorStatus CheckCommonRegErrors(
+ const std::string& user) const;
+
+ // Generates ProxyId for this device.
+ std::string GenerateProxyId() const;
// Creates data for DNS TXT respond.
std::vector<std::string> CreateTxt() const;
RegistrationInfo reg_info_;
+ // Saving and loading registration info from file.
+ void SaveToFile(const base::FilePath& file_path) const;
+ bool LoadFromFile(const base::FilePath& file_path);
+
// Contains DNS-SD server.
DnsSdServer dns_server_;
@@ -109,6 +118,11 @@ class Printer : public PrivetHttpServer::Delegate,
// Contains Cloud Print client.
scoped_ptr<CloudPrintRequester> requester_;
+ XPrivetToken xtoken_;
+
+ // Uses for calculating uptime.
+ base::Time starttime_;
+
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
index 2114360..55fcefb 100644
--- a/cloud_print/gcp20/prototype/privet_http_server.cc
+++ b/cloud_print/gcp20/prototype/privet_http_server.cc
@@ -4,6 +4,7 @@
#include "cloud_print/gcp20/prototype/privet_http_server.h"
+#include "base/command_line.h"
#include "base/json/json_writer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
@@ -41,7 +42,7 @@ scoped_ptr<base::DictionaryValue> CreateErrorWithTimeout(
} // namespace
-PrivetHttpServer::DeviceInfo::DeviceInfo() {
+PrivetHttpServer::DeviceInfo::DeviceInfo() : uptime(0) {
}
PrivetHttpServer::DeviceInfo::~DeviceInfo() {
@@ -85,11 +86,29 @@ void PrivetHttpServer::OnHttpRequest(int connection_id,
VLOG(1) << "Processing HTTP request: " << info.path;
GURL url("http://host" + info.path);
- // TODO(maksymb): Add checking for X-PrivetToken.
+ if (!CommandLine::ForCurrentProcess()->HasSwitch("disable-x-token")) {
+ net::HttpServerRequestInfo::HeadersMap::const_iterator iter =
+ info.headers.find("X-Privet-Token");
+ if (iter == info.headers.end()) {
+ server_->Send(connection_id, net::HTTP_BAD_REQUEST,
+ "Missing X-Privet-Token header.", "text/plain");
+ return;
+ }
+
+ if (url.path() != "/privet/info" &&
+ !delegate_->CheckXPrivetTokenHeader(iter->second)) {
+ server_->Send(connection_id, net::HTTP_BAD_REQUEST,
+ "{\"error\":\"invalid_x_privet_token\"}",
+ "application/json");
+ return;
+ }
+ }
+
std::string response;
net::HttpStatusCode status_code = ProcessHttpRequest(url, &response);
+ // TODO(maksymb): Add checking for right |info.method| in query.
- server_->Send(connection_id, status_code, response, "text/plain");
+ server_->Send(connection_id, status_code, response, "application/json");
}
void PrivetHttpServer::OnWebSocketRequest(
@@ -139,13 +158,29 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessInfo(
scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue);
response->SetString("version", info.version);
+ response->SetString("name", info.name);
+ response->SetString("description", info.description);
+ response->SetString("url", info.url);
+ response->SetString("id", info.id);
+ response->SetString("device_state", info.device_state);
+ response->SetString("connection_state", info.connection_state);
response->SetString("manufacturer", info.manufacturer);
+ response->SetString("model", info.model);
+ response->SetString("serial_number", info.serial_number);
+ response->SetString("firmware", info.firmware);
+ response->SetInteger("uptime", info.uptime);
+ response->SetString("x-privet-token", info.x_privet_token);
base::ListValue api;
for (size_t i = 0; i < info.api.size(); ++i)
api.AppendString(info.api[i]);
response->Set("api", api.DeepCopy());
+ base::ListValue type;
+ for (size_t i = 0; i < info.type.size(); ++i)
+ type.AppendString(info.type[i]);
+ response->Set("type", type.DeepCopy());
+
*status_code = net::HTTP_OK;
return response.Pass();
}
diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h
index b412bad..580774c 100644
--- a/cloud_print/gcp20/prototype/privet_http_server.h
+++ b/cloud_print/gcp20/prototype/privet_http_server.h
@@ -38,8 +38,21 @@ class PrivetHttpServer: public net::HttpServer::Delegate {
~DeviceInfo();
std::string version;
+ std::string name;
+ std::string description;
+ std::string url;
+ std::string id;
+ std::string device_state;
+ std::string connection_state;
std::string manufacturer;
+ std::string model;
+ std::string serial_number;
+ std::string firmware;
+ int uptime;
+ std::string x_privet_token;
+
std::vector<std::string> api;
+ std::vector<std::string> type;
};
class Delegate {
@@ -72,6 +85,8 @@ class PrivetHttpServer: public net::HttpServer::Delegate {
// Invoked if /privet/info is called.
virtual void CreateInfo(DeviceInfo* info) = 0;
+
+ virtual bool CheckXPrivetTokenHeader(const std::string& token) const = 0;
};
// Constructor doesn't start server.
diff --git a/cloud_print/gcp20/prototype/x_privet_token.cc b/cloud_print/gcp20/prototype/x_privet_token.cc
new file mode 100644
index 0000000..b9afd10
--- /dev/null
+++ b/cloud_print/gcp20/prototype/x_privet_token.cc
@@ -0,0 +1,78 @@
+// 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/x_privet_token.h"
+
+#include "base/base64.h"
+#include "base/format_macros.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+const char kXPrivetTokenDelimeter = ':';
+const uint64 kTimeExpiration = 24*60*60; // in seconds
+
+} // namespace
+
+using base::Time;
+using base::TimeDelta;
+
+XPrivetToken::XPrivetToken() {
+ UpdateSecret();
+}
+
+XPrivetToken::XPrivetToken(const std::string& secret,
+ const base::Time& gen_time)
+ : secret_(secret),
+ last_gen_time_(gen_time) {
+}
+
+std::string XPrivetToken::GenerateXToken() {
+ if (Time::Now() > last_gen_time_ + TimeDelta::FromSeconds(kTimeExpiration))
+ UpdateSecret();
+
+ return GenerateXTokenWithTime(static_cast<uint64>(Time::Now().ToTimeT()));
+}
+
+bool XPrivetToken::CheckValidXToken(const std::string& token_encoded) const {
+ std::string token;
+ if (!base::Base64Decode(token_encoded, &token))
+ return false;
+
+ size_t delimeter_pos = token.find(kXPrivetTokenDelimeter);
+ if (delimeter_pos == std::string::npos)
+ return false;
+
+ std::string issue_time_str = token.substr(delimeter_pos + 1);
+ uint64 issue_time;
+ if (!base::StringToUint64(issue_time_str, &issue_time))
+ return false;
+
+ if (GenerateXTokenWithTime(issue_time) != token_encoded)
+ return false;
+
+ return Time::FromTimeT(issue_time) - last_gen_time_ <
+ TimeDelta::FromSeconds(kTimeExpiration);
+}
+
+std::string XPrivetToken::GenerateXTokenWithTime(uint64 issue_time) const {
+ std::string result;
+ std::string issue_time_str = base::StringPrintf("%"PRIu64, issue_time);
+ std::string hash = base::SHA1HashString(secret_ +
+ kXPrivetTokenDelimeter +
+ issue_time_str);
+ base::Base64Encode(hash + kXPrivetTokenDelimeter + issue_time_str, &result);
+ return result;
+}
+
+void XPrivetToken::UpdateSecret() {
+ secret_ = base::GenerateGUID();
+ last_gen_time_ = base::Time::Now();
+ VLOG(1) << "New Secret is Generated.";
+}
+
diff --git a/cloud_print/gcp20/prototype/x_privet_token.h b/cloud_print/gcp20/prototype/x_privet_token.h
new file mode 100644
index 0000000..edfc283
--- /dev/null
+++ b/cloud_print/gcp20/prototype/x_privet_token.h
@@ -0,0 +1,52 @@
+// 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_X_PRIVET_TOKEN_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_X_PRIVET_TOKEN_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/time/time.h"
+
+// Class for generating and checking X-Privet-Token.
+class XPrivetToken {
+ public:
+ // Initializes the object.
+ XPrivetToken();
+
+ // Destroys the object.
+ ~XPrivetToken() {}
+
+ // Generates X-Privet-Token for /privet/info request. Updates secret
+ // if expired.
+ std::string GenerateXToken();
+
+ // Checks
+ bool CheckValidXToken(const std::string& token_encoded) const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, Generation);
+ FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, CheckingValid);
+ FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, CheckingInvalid);
+
+ // For testing purposes.
+ XPrivetToken(const std::string& secret, const base::Time& gen_time);
+
+ // Generates X-Privet-Token for with certain time of issue.
+ std::string GenerateXTokenWithTime(uint64 issue_time) const;
+
+ // Creates new XPrivetToken secret.
+ void UpdateSecret();
+
+ // X-Privet-Token secret.
+ std::string secret_;
+
+ // Time of last secret generation.
+ base::Time last_gen_time_;
+};
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_X_PRIVET_TOKEN_H_
+
diff --git a/cloud_print/gcp20/prototype/x_privet_token_unittest.cc b/cloud_print/gcp20/prototype/x_privet_token_unittest.cc
new file mode 100644
index 0000000..4317459
--- /dev/null
+++ b/cloud_print/gcp20/prototype/x_privet_token_unittest.cc
@@ -0,0 +1,123 @@
+// 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/x_privet_token.h"
+
+#include <stdio.h>
+
+#include "base/base64.h"
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(XPrivetTokenTest, Generation) {
+ std::string secret = "E3E92296-E290-4E77-B678-6AEF256C30C8";
+ uint64 gen_time = 1372444784;
+ uint64 issue_time = gen_time;
+
+ XPrivetToken xtoken(secret, base::Time::FromTimeT(gen_time));
+
+ std::string issue_time_str = base::StringPrintf("%"PRIu64, issue_time);
+ std::string sha1_val = base::SHA1HashString(secret + ":" + issue_time_str);
+
+ EXPECT_STRCASEEQ("2216828f9eefc3931c1b9a110dcca3dbec23571d",
+ base::HexEncode(sha1_val.data(), sha1_val.size()).c_str());
+
+ std::string base64_val;
+ base::Base64Encode(sha1_val + ":" + issue_time_str, &base64_val);
+
+ EXPECT_EQ(base64_val, xtoken.GenerateXTokenWithTime(issue_time));
+
+ EXPECT_NE(xtoken.GenerateXTokenWithTime(issue_time),
+ xtoken.GenerateXTokenWithTime(issue_time + 1));
+}
+
+TEST(XPrivetTokenTest, CheckingValid) {
+ base::Time gen_time = base::Time::FromTimeT(1372444784);
+ XPrivetToken xtoken("9CEEA1AD-BC24-4D5A-8AB4-A6CE3E0CC4CD", gen_time);
+
+ std::string token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT());
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 1);
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 55);
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 60*60 - 5);
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 60*60 + 10);
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 - 1);
+ EXPECT_TRUE(xtoken.CheckValidXToken(token));
+}
+
+TEST(XPrivetTokenTest, CheckingInvalid) {
+ base::Time gen_time = base::Time::FromTimeT(1372444784);
+ XPrivetToken xtoken("9CEEA1AD-BC24-4D5A-8AB4-A6CE3E0CC4CD", gen_time);
+
+ // Meaningless tokens:
+
+ std::string token = "CEEA1AD9CEEA1AD9CEEA1AD9CEEA1AD";
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA1AD9CEEA1AD9CEEA1AD", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA:1AD9CEEA1AD9CEEA1AD", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA:1AD9CEEA1AD9:CEEA1AD", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA:123456", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA:", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ base::Base64Encode("CEEA1AD9CEEA:1372444784", &token);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ EXPECT_FALSE(xtoken.CheckValidXToken(""));
+
+ // Expired tokens:
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 + 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 7*24*60*60 - 1023);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ // Tokens with different secret:
+
+ XPrivetToken another("6F02AC4E-6F37-4078-AF42-5EE5D8180284", gen_time);
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT() - 24*60*60 - 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT() - 24*60*60 + 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT());
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 - 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+
+ token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 + 1);
+ EXPECT_FALSE(xtoken.CheckValidXToken(token));
+}
+