pairing_registry.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/protocol/pairing_registry.h"
6
7#include "base/base64.h"
8#include "base/bind.h"
9#include "base/guid.h"
10#include "base/json/json_string_value_serializer.h"
11#include "base/location.h"
12#include "base/single_thread_task_runner.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/thread_task_runner_handle.h"
15#include "base/values.h"
16#include "crypto/random.h"
17
18namespace remoting {
19namespace protocol {
20
21// How many bytes of random data to use for the shared secret.
22const int kKeySize = 16;
23
24const char PairingRegistry::kCreatedTimeKey[] = "createdTime";
25const char PairingRegistry::kClientIdKey[] = "clientId";
26const char PairingRegistry::kClientNameKey[] = "clientName";
27const char PairingRegistry::kSharedSecretKey[] = "sharedSecret";
28
29PairingRegistry::Pairing::Pairing() {
30}
31
32PairingRegistry::Pairing::Pairing(const base::Time& created_time,
33                                  const std::string& client_name,
34                                  const std::string& client_id,
35                                  const std::string& shared_secret)
36    : created_time_(created_time),
37      client_name_(client_name),
38      client_id_(client_id),
39      shared_secret_(shared_secret) {
40}
41
42PairingRegistry::Pairing::~Pairing() {
43}
44
45PairingRegistry::Pairing PairingRegistry::Pairing::Create(
46    const std::string& client_name) {
47  base::Time created_time = base::Time::Now();
48  std::string client_id = base::GenerateGUID();
49  std::string shared_secret;
50  char buffer[kKeySize];
51  crypto::RandBytes(buffer, arraysize(buffer));
52  base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)),
53                     &shared_secret);
54  return Pairing(created_time, client_name, client_id, shared_secret);
55}
56
57PairingRegistry::Pairing PairingRegistry::Pairing::CreateFromValue(
58    const base::DictionaryValue& pairing) {
59  std::string client_name, client_id;
60  double created_time_value;
61  if (pairing.GetDouble(kCreatedTimeKey, &created_time_value) &&
62      pairing.GetString(kClientNameKey, &client_name) &&
63      pairing.GetString(kClientIdKey, &client_id)) {
64    // The shared secret is optional.
65    std::string shared_secret;
66    pairing.GetString(kSharedSecretKey, &shared_secret);
67    base::Time created_time = base::Time::FromJsTime(created_time_value);
68    return Pairing(created_time, client_name, client_id, shared_secret);
69  }
70
71  LOG(ERROR) << "Failed to load pairing information: unexpected format.";
72  return Pairing();
73}
74
75scoped_ptr<base::DictionaryValue> PairingRegistry::Pairing::ToValue() const {
76  scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue());
77  pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime());
78  pairing->SetString(kClientNameKey, client_name());
79  pairing->SetString(kClientIdKey, client_id());
80  if (!shared_secret().empty())
81    pairing->SetString(kSharedSecretKey, shared_secret());
82  return pairing.Pass();
83}
84
85bool PairingRegistry::Pairing::operator==(const Pairing& other) const {
86  return created_time_ == other.created_time_ &&
87         client_id_ == other.client_id_ &&
88         client_name_ == other.client_name_ &&
89         shared_secret_ == other.shared_secret_;
90}
91
92bool PairingRegistry::Pairing::is_valid() const {
93  return !client_id_.empty() && !shared_secret_.empty();
94}
95
96PairingRegistry::PairingRegistry(
97    scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner,
98    scoped_ptr<Delegate> delegate)
99    : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
100      delegate_task_runner_(delegate_task_runner),
101      delegate_(delegate.Pass()) {
102  DCHECK(delegate_);
103}
104
105PairingRegistry::Pairing PairingRegistry::CreatePairing(
106    const std::string& client_name) {
107  DCHECK(caller_task_runner_->BelongsToCurrentThread());
108
109  Pairing result = Pairing::Create(client_name);
110  AddPairing(result);
111  return result;
112}
113
114void PairingRegistry::GetPairing(const std::string& client_id,
115                                 const GetPairingCallback& callback) {
116  DCHECK(caller_task_runner_->BelongsToCurrentThread());
117
118  GetPairingCallback wrapped_callback = base::Bind(
119      &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext,
120      this, callback);
121  base::Closure request = base::Bind(
122      &PairingRegistry::DoLoad, this, client_id, wrapped_callback);
123  ServiceOrQueueRequest(request);
124}
125
126void PairingRegistry::GetAllPairings(
127    const GetAllPairingsCallback& callback) {
128  DCHECK(caller_task_runner_->BelongsToCurrentThread());
129
130  GetAllPairingsCallback wrapped_callback = base::Bind(
131      &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext,
132      this, callback);
133  GetAllPairingsCallback sanitize_callback = base::Bind(
134      &PairingRegistry::SanitizePairings,
135      this, wrapped_callback);
136  base::Closure request = base::Bind(
137      &PairingRegistry::DoLoadAll, this, sanitize_callback);
138  ServiceOrQueueRequest(request);
139}
140
141void PairingRegistry::DeletePairing(
142    const std::string& client_id, const DoneCallback& callback) {
143  DCHECK(caller_task_runner_->BelongsToCurrentThread());
144
145  DoneCallback wrapped_callback = base::Bind(
146      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
147      this, callback);
148  base::Closure request = base::Bind(
149      &PairingRegistry::DoDelete, this, client_id, wrapped_callback);
150  ServiceOrQueueRequest(request);
151}
152
153void PairingRegistry::ClearAllPairings(
154    const DoneCallback& callback) {
155  DCHECK(caller_task_runner_->BelongsToCurrentThread());
156
157  DoneCallback wrapped_callback = base::Bind(
158      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
159      this, callback);
160  base::Closure request = base::Bind(
161      &PairingRegistry::DoDeleteAll, this, wrapped_callback);
162  ServiceOrQueueRequest(request);
163}
164
165PairingRegistry::~PairingRegistry() {
166}
167
168void PairingRegistry::PostTask(
169    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
170    const tracked_objects::Location& from_here,
171    const base::Closure& task) {
172  task_runner->PostTask(from_here, task);
173}
174
175void PairingRegistry::AddPairing(const Pairing& pairing) {
176  DoneCallback wrapped_callback = base::Bind(
177      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
178      this, DoneCallback());
179  base::Closure request = base::Bind(
180      &PairingRegistry::DoSave, this, pairing, wrapped_callback);
181  ServiceOrQueueRequest(request);
182}
183
184void PairingRegistry::DoLoadAll(
185    const protocol::PairingRegistry::GetAllPairingsCallback& callback) {
186  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
187
188  scoped_ptr<base::ListValue> pairings = delegate_->LoadAll();
189  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback,
190                                                      base::Passed(&pairings)));
191}
192
193void PairingRegistry::DoDeleteAll(
194    const protocol::PairingRegistry::DoneCallback& callback) {
195  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
196
197  bool success = delegate_->DeleteAll();
198  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
199}
200
201void PairingRegistry::DoLoad(
202    const std::string& client_id,
203    const protocol::PairingRegistry::GetPairingCallback& callback) {
204  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
205
206  Pairing pairing = delegate_->Load(client_id);
207  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing));
208}
209
210void PairingRegistry::DoSave(
211    const protocol::PairingRegistry::Pairing& pairing,
212    const protocol::PairingRegistry::DoneCallback& callback) {
213  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
214
215  bool success = delegate_->Save(pairing);
216  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
217}
218
219void PairingRegistry::DoDelete(
220    const std::string& client_id,
221    const protocol::PairingRegistry::DoneCallback& callback) {
222  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
223
224  bool success = delegate_->Delete(client_id);
225  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
226}
227
228void PairingRegistry::InvokeDoneCallbackAndScheduleNext(
229    const DoneCallback& callback, bool success) {
230  // CreatePairing doesn't have a callback, so the callback can be null.
231  if (!callback.is_null())
232    callback.Run(success);
233
234  pending_requests_.pop();
235  ServiceNextRequest();
236}
237
238void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext(
239    const GetPairingCallback& callback, Pairing pairing) {
240  callback.Run(pairing);
241  pending_requests_.pop();
242  ServiceNextRequest();
243}
244
245void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext(
246    const GetAllPairingsCallback& callback,
247    scoped_ptr<base::ListValue> pairings) {
248  callback.Run(pairings.Pass());
249  pending_requests_.pop();
250  ServiceNextRequest();
251}
252
253void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback,
254                                       scoped_ptr<base::ListValue> pairings) {
255  DCHECK(caller_task_runner_->BelongsToCurrentThread());
256
257  scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue());
258  for (size_t i = 0; i < pairings->GetSize(); ++i) {
259    DictionaryValue* pairing_json;
260    if (!pairings->GetDictionary(i, &pairing_json)) {
261      LOG(WARNING) << "A pairing entry is not a dictionary.";
262      continue;
263    }
264
265    // Parse the pairing data.
266    Pairing pairing = Pairing::CreateFromValue(*pairing_json);
267    if (!pairing.is_valid()) {
268      LOG(WARNING) << "Could not parse a pairing entry.";
269      continue;
270    }
271
272    // Clear the shared secrect and append the pairing data to the list.
273    Pairing sanitized_pairing(
274        pairing.created_time(),
275        pairing.client_name(),
276        pairing.client_id(),
277        "");
278    sanitized_pairings->Append(sanitized_pairing.ToValue().release());
279  }
280
281  callback.Run(sanitized_pairings.Pass());
282}
283
284void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) {
285  bool servicing_request = !pending_requests_.empty();
286  pending_requests_.push(request);
287  if (!servicing_request) {
288    ServiceNextRequest();
289  }
290}
291
292void PairingRegistry::ServiceNextRequest() {
293  if (pending_requests_.empty())
294    return;
295
296  PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front());
297}
298
299}  // namespace protocol
300}  // namespace remoting
301