// 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 "remoting/protocol/pairing_registry.h" #include "base/base64.h" #include "base/bind.h" #include "base/guid.h" #include "base/json/json_string_value_serializer.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/thread_task_runner_handle.h" #include "base/values.h" #include "crypto/random.h" namespace remoting { namespace protocol { // How many bytes of random data to use for the shared secret. const int kKeySize = 16; const char PairingRegistry::kCreatedTimeKey[] = "createdTime"; const char PairingRegistry::kClientIdKey[] = "clientId"; const char PairingRegistry::kClientNameKey[] = "clientName"; const char PairingRegistry::kSharedSecretKey[] = "sharedSecret"; PairingRegistry::Pairing::Pairing() { } PairingRegistry::Pairing::Pairing(const base::Time& created_time, const std::string& client_name, const std::string& client_id, const std::string& shared_secret) : created_time_(created_time), client_name_(client_name), client_id_(client_id), shared_secret_(shared_secret) { } PairingRegistry::Pairing::~Pairing() { } PairingRegistry::Pairing PairingRegistry::Pairing::Create( const std::string& client_name) { base::Time created_time = base::Time::Now(); std::string client_id = base::GenerateGUID(); std::string shared_secret; char buffer[kKeySize]; crypto::RandBytes(buffer, arraysize(buffer)); base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)), &shared_secret); return Pairing(created_time, client_name, client_id, shared_secret); } PairingRegistry::Pairing PairingRegistry::Pairing::CreateFromValue( const base::DictionaryValue& pairing) { std::string client_name, client_id; double created_time_value; if (pairing.GetDouble(kCreatedTimeKey, &created_time_value) && pairing.GetString(kClientNameKey, &client_name) && pairing.GetString(kClientIdKey, &client_id)) { // The shared secret is optional. std::string shared_secret; pairing.GetString(kSharedSecretKey, &shared_secret); base::Time created_time = base::Time::FromJsTime(created_time_value); return Pairing(created_time, client_name, client_id, shared_secret); } LOG(ERROR) << "Failed to load pairing information: unexpected format."; return Pairing(); } scoped_ptr<base::DictionaryValue> PairingRegistry::Pairing::ToValue() const { scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue()); pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime()); pairing->SetString(kClientNameKey, client_name()); pairing->SetString(kClientIdKey, client_id()); if (!shared_secret().empty()) pairing->SetString(kSharedSecretKey, shared_secret()); return pairing.Pass(); } bool PairingRegistry::Pairing::operator==(const Pairing& other) const { return created_time_ == other.created_time_ && client_id_ == other.client_id_ && client_name_ == other.client_name_ && shared_secret_ == other.shared_secret_; } bool PairingRegistry::Pairing::is_valid() const { return !client_id_.empty() && !shared_secret_.empty(); } PairingRegistry::PairingRegistry( scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner, scoped_ptr<Delegate> delegate) : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()), delegate_task_runner_(delegate_task_runner), delegate_(delegate.Pass()) { DCHECK(delegate_); } PairingRegistry::Pairing PairingRegistry::CreatePairing( const std::string& client_name) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); Pairing result = Pairing::Create(client_name); AddPairing(result); return result; } void PairingRegistry::GetPairing(const std::string& client_id, const GetPairingCallback& callback) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); GetPairingCallback wrapped_callback = base::Bind( &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext, this, callback); base::Closure request = base::Bind( &PairingRegistry::DoLoad, this, client_id, wrapped_callback); ServiceOrQueueRequest(request); } void PairingRegistry::GetAllPairings( const GetAllPairingsCallback& callback) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); GetAllPairingsCallback wrapped_callback = base::Bind( &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext, this, callback); GetAllPairingsCallback sanitize_callback = base::Bind( &PairingRegistry::SanitizePairings, this, wrapped_callback); base::Closure request = base::Bind( &PairingRegistry::DoLoadAll, this, sanitize_callback); ServiceOrQueueRequest(request); } void PairingRegistry::DeletePairing( const std::string& client_id, const DoneCallback& callback) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); DoneCallback wrapped_callback = base::Bind( &PairingRegistry::InvokeDoneCallbackAndScheduleNext, this, callback); base::Closure request = base::Bind( &PairingRegistry::DoDelete, this, client_id, wrapped_callback); ServiceOrQueueRequest(request); } void PairingRegistry::ClearAllPairings( const DoneCallback& callback) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); DoneCallback wrapped_callback = base::Bind( &PairingRegistry::InvokeDoneCallbackAndScheduleNext, this, callback); base::Closure request = base::Bind( &PairingRegistry::DoDeleteAll, this, wrapped_callback); ServiceOrQueueRequest(request); } PairingRegistry::~PairingRegistry() { } void PairingRegistry::PostTask( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const tracked_objects::Location& from_here, const base::Closure& task) { task_runner->PostTask(from_here, task); } void PairingRegistry::AddPairing(const Pairing& pairing) { DoneCallback wrapped_callback = base::Bind( &PairingRegistry::InvokeDoneCallbackAndScheduleNext, this, DoneCallback()); base::Closure request = base::Bind( &PairingRegistry::DoSave, this, pairing, wrapped_callback); ServiceOrQueueRequest(request); } void PairingRegistry::DoLoadAll( const protocol::PairingRegistry::GetAllPairingsCallback& callback) { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); scoped_ptr<base::ListValue> pairings = delegate_->LoadAll(); PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, base::Passed(&pairings))); } void PairingRegistry::DoDeleteAll( const protocol::PairingRegistry::DoneCallback& callback) { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); bool success = delegate_->DeleteAll(); PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); } void PairingRegistry::DoLoad( const std::string& client_id, const protocol::PairingRegistry::GetPairingCallback& callback) { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); Pairing pairing = delegate_->Load(client_id); PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing)); } void PairingRegistry::DoSave( const protocol::PairingRegistry::Pairing& pairing, const protocol::PairingRegistry::DoneCallback& callback) { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); bool success = delegate_->Save(pairing); PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); } void PairingRegistry::DoDelete( const std::string& client_id, const protocol::PairingRegistry::DoneCallback& callback) { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); bool success = delegate_->Delete(client_id); PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); } void PairingRegistry::InvokeDoneCallbackAndScheduleNext( const DoneCallback& callback, bool success) { // CreatePairing doesn't have a callback, so the callback can be null. if (!callback.is_null()) callback.Run(success); pending_requests_.pop(); ServiceNextRequest(); } void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext( const GetPairingCallback& callback, Pairing pairing) { callback.Run(pairing); pending_requests_.pop(); ServiceNextRequest(); } void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext( const GetAllPairingsCallback& callback, scoped_ptr<base::ListValue> pairings) { callback.Run(pairings.Pass()); pending_requests_.pop(); ServiceNextRequest(); } void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback, scoped_ptr<base::ListValue> pairings) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue()); for (size_t i = 0; i < pairings->GetSize(); ++i) { DictionaryValue* pairing_json; if (!pairings->GetDictionary(i, &pairing_json)) { LOG(WARNING) << "A pairing entry is not a dictionary."; continue; } // Parse the pairing data. Pairing pairing = Pairing::CreateFromValue(*pairing_json); if (!pairing.is_valid()) { LOG(WARNING) << "Could not parse a pairing entry."; continue; } // Clear the shared secrect and append the pairing data to the list. Pairing sanitized_pairing( pairing.created_time(), pairing.client_name(), pairing.client_id(), ""); sanitized_pairings->Append(sanitized_pairing.ToValue().release()); } callback.Run(sanitized_pairings.Pass()); } void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) { bool servicing_request = !pending_requests_.empty(); pending_requests_.push(request); if (!servicing_request) { ServiceNextRequest(); } } void PairingRegistry::ServiceNextRequest() { if (pending_requests_.empty()) return; PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front()); } } // namespace protocol } // namespace remoting