diff options
author | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 15:39:15 +0000 |
---|---|---|
committer | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 15:39:15 +0000 |
commit | a20b019ab8ee1eeba45257d9c8774e88b1a665e4 (patch) | |
tree | 8b62906129a1a89134c3a9447026fcdf885da71b | |
parent | 73f6d0a7200a072972d4c8dd774f6f5cd341ff47 (diff) | |
download | chromium_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
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.cc | 12 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.h | 6 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/command_line_reader.cc | 53 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/command_line_reader.h | 18 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_sd_server.cc | 11 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_sd_server.h | 3 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/gcp20_device.gyp | 33 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.cc | 228 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.h | 24 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.cc | 41 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.h | 15 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/x_privet_token.cc | 78 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/x_privet_token.h | 52 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/x_privet_token_unittest.cc | 123 |
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", ®istered)) { + 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)); +} + |