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.
5 #include "remoting/protocol/pairing_registry.h"
7 #include "base/base64.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"
21 // How many bytes of random data to use for the shared secret.
22 const int kKeySize
= 16;
24 const char PairingRegistry::kCreatedTimeKey
[] = "createdTime";
25 const char PairingRegistry::kClientIdKey
[] = "clientId";
26 const char PairingRegistry::kClientNameKey
[] = "clientName";
27 const char PairingRegistry::kSharedSecretKey
[] = "sharedSecret";
29 PairingRegistry::Pairing::Pairing() {
32 PairingRegistry::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
) {
42 PairingRegistry::Pairing::~Pairing() {
45 PairingRegistry::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
)),
54 return Pairing(created_time
, client_name
, client_id
, shared_secret
);
57 PairingRegistry::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
);
71 LOG(ERROR
) << "Failed to load pairing information: unexpected format.";
75 scoped_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();
85 bool 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_
;
92 bool PairingRegistry::Pairing::is_valid() const {
93 return !client_id_
.empty() && !shared_secret_
.empty();
96 PairingRegistry::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()) {
105 PairingRegistry::Pairing
PairingRegistry::CreatePairing(
106 const std::string
& client_name
) {
107 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
109 Pairing result
= Pairing::Create(client_name
);
114 void PairingRegistry::GetPairing(const std::string
& client_id
,
115 const GetPairingCallback
& callback
) {
116 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
118 GetPairingCallback wrapped_callback
= base::Bind(
119 &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext
,
121 base::Closure request
= base::Bind(
122 &PairingRegistry::DoLoad
, this, client_id
, wrapped_callback
);
123 ServiceOrQueueRequest(request
);
126 void PairingRegistry::GetAllPairings(
127 const GetAllPairingsCallback
& callback
) {
128 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
130 GetAllPairingsCallback wrapped_callback
= base::Bind(
131 &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext
,
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
);
141 void PairingRegistry::DeletePairing(
142 const std::string
& client_id
, const DoneCallback
& callback
) {
143 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
145 DoneCallback wrapped_callback
= base::Bind(
146 &PairingRegistry::InvokeDoneCallbackAndScheduleNext
,
148 base::Closure request
= base::Bind(
149 &PairingRegistry::DoDelete
, this, client_id
, wrapped_callback
);
150 ServiceOrQueueRequest(request
);
153 void PairingRegistry::ClearAllPairings(
154 const DoneCallback
& callback
) {
155 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
157 DoneCallback wrapped_callback
= base::Bind(
158 &PairingRegistry::InvokeDoneCallbackAndScheduleNext
,
160 base::Closure request
= base::Bind(
161 &PairingRegistry::DoDeleteAll
, this, wrapped_callback
);
162 ServiceOrQueueRequest(request
);
165 PairingRegistry::~PairingRegistry() {
168 void 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
);
175 void 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
);
184 void PairingRegistry::DoLoadAll(
185 const protocol::PairingRegistry::GetAllPairingsCallback
& callback
) {
186 DCHECK(delegate_task_runner_
->BelongsToCurrentThread());
188 scoped_ptr
<base::ListValue
> pairings
= delegate_
->LoadAll();
189 PostTask(caller_task_runner_
, FROM_HERE
, base::Bind(callback
,
190 base::Passed(&pairings
)));
193 void PairingRegistry::DoDeleteAll(
194 const protocol::PairingRegistry::DoneCallback
& callback
) {
195 DCHECK(delegate_task_runner_
->BelongsToCurrentThread());
197 bool success
= delegate_
->DeleteAll();
198 PostTask(caller_task_runner_
, FROM_HERE
, base::Bind(callback
, success
));
201 void PairingRegistry::DoLoad(
202 const std::string
& client_id
,
203 const protocol::PairingRegistry::GetPairingCallback
& callback
) {
204 DCHECK(delegate_task_runner_
->BelongsToCurrentThread());
206 Pairing pairing
= delegate_
->Load(client_id
);
207 PostTask(caller_task_runner_
, FROM_HERE
, base::Bind(callback
, pairing
));
210 void PairingRegistry::DoSave(
211 const protocol::PairingRegistry::Pairing
& pairing
,
212 const protocol::PairingRegistry::DoneCallback
& callback
) {
213 DCHECK(delegate_task_runner_
->BelongsToCurrentThread());
215 bool success
= delegate_
->Save(pairing
);
216 PostTask(caller_task_runner_
, FROM_HERE
, base::Bind(callback
, success
));
219 void PairingRegistry::DoDelete(
220 const std::string
& client_id
,
221 const protocol::PairingRegistry::DoneCallback
& callback
) {
222 DCHECK(delegate_task_runner_
->BelongsToCurrentThread());
224 bool success
= delegate_
->Delete(client_id
);
225 PostTask(caller_task_runner_
, FROM_HERE
, base::Bind(callback
, success
));
228 void 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
);
234 pending_requests_
.pop();
235 ServiceNextRequest();
238 void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext(
239 const GetPairingCallback
& callback
, Pairing pairing
) {
240 callback
.Run(pairing
);
241 pending_requests_
.pop();
242 ServiceNextRequest();
245 void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext(
246 const GetAllPairingsCallback
& callback
,
247 scoped_ptr
<base::ListValue
> pairings
) {
248 callback
.Run(pairings
.Pass());
249 pending_requests_
.pop();
250 ServiceNextRequest();
253 void PairingRegistry::SanitizePairings(const GetAllPairingsCallback
& callback
,
254 scoped_ptr
<base::ListValue
> pairings
) {
255 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
257 scoped_ptr
<base::ListValue
> sanitized_pairings(new base::ListValue());
258 for (size_t i
= 0; i
< pairings
->GetSize(); ++i
) {
259 base::DictionaryValue
* pairing_json
;
260 if (!pairings
->GetDictionary(i
, &pairing_json
)) {
261 LOG(WARNING
) << "A pairing entry is not a dictionary.";
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.";
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(),
278 sanitized_pairings
->Append(sanitized_pairing
.ToValue().release());
281 callback
.Run(sanitized_pairings
.Pass());
284 void PairingRegistry::ServiceOrQueueRequest(const base::Closure
& request
) {
285 bool servicing_request
= !pending_requests_
.empty();
286 pending_requests_
.push(request
);
287 if (!servicing_request
) {
288 ServiceNextRequest();
292 void PairingRegistry::ServiceNextRequest() {
293 if (pending_requests_
.empty())
296 PostTask(delegate_task_runner_
, FROM_HERE
, pending_requests_
.front());
299 } // namespace protocol
300 } // namespace remoting