blob: 42d2bc408b97dacc164615199a6fb44bb7df6712 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2015 The Chromium Authors
jianli7a0c9b62015-05-26 23:24:472// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/gcm_driver/registration_info.h"
6
avi26062922015-12-26 00:14:187#include <stddef.h>
8
Mugdha Lakhani2ade6a02018-01-31 14:31:119#include "base/format_macros.h"
10#include "base/strings/string_number_conversions.h"
brettwb45192d2015-06-29 22:53:2411#include "base/strings/string_split.h"
jianli7a0c9b62015-05-26 23:24:4712#include "base/strings/string_util.h"
Mugdha Lakhani2ade6a02018-01-31 14:31:1113#include "base/strings/stringprintf.h"
jianli7a0c9b62015-05-26 23:24:4714
15namespace gcm {
16
17namespace {
Rayan Kanso3da434f2018-12-19 17:09:3918constexpr char kInstanceIDSerializationPrefix[] = "iid-";
19constexpr char kSerializedValidationTimeSeparator = '#';
20constexpr char kSerializedKeySeparator = ',';
21constexpr int kInstanceIDSerializationPrefixLength =
johnme627dc8c72016-08-19 21:49:3922 sizeof(kInstanceIDSerializationPrefix) / sizeof(char) - 1;
jianli7a0c9b62015-05-26 23:24:4723} // namespace
24
25// static
Rayan Kanso3da434f2018-12-19 17:09:3926scoped_refptr<RegistrationInfo> RegistrationInfo::BuildFromString(
johnmedc5ddc42015-09-17 14:16:4427 const std::string& serialized_key,
28 const std::string& serialized_value,
jianli7a0c9b62015-05-26 23:24:4729 std::string* registration_id) {
Rayan Kanso3da434f2018-12-19 17:09:3930 scoped_refptr<RegistrationInfo> registration;
jianli7a0c9b62015-05-26 23:24:4731
johnme627dc8c72016-08-19 21:49:3932 if (base::StartsWith(serialized_key, kInstanceIDSerializationPrefix,
Rayan Kanso3da434f2018-12-19 17:09:3933 base::CompareCase::SENSITIVE)) {
34 registration = base::MakeRefCounted<InstanceIDTokenInfo>();
35 } else {
36 registration = base::MakeRefCounted<GCMRegistrationInfo>();
37 }
jianli7a0c9b62015-05-26 23:24:4738
Mugdha Lakhani2ade6a02018-01-31 14:31:1139 if (!registration->Deserialize(serialized_key, serialized_value,
jianli7a0c9b62015-05-26 23:24:4740 registration_id)) {
41 registration.reset();
42 }
dcheng51606352015-12-26 21:16:2343 return registration;
jianli7a0c9b62015-05-26 23:24:4744}
45
Rayan Kanso3da434f2018-12-19 17:09:3946RegistrationInfo::RegistrationInfo() = default;
jianli7a0c9b62015-05-26 23:24:4747
Rayan Kanso3da434f2018-12-19 17:09:3948RegistrationInfo::~RegistrationInfo() = default;
jianli7a0c9b62015-05-26 23:24:4749
50// static
51const GCMRegistrationInfo* GCMRegistrationInfo::FromRegistrationInfo(
52 const RegistrationInfo* registration_info) {
53 if (!registration_info || registration_info->GetType() != GCM_REGISTRATION)
Ivan Kotenkov75b1c3a2017-10-24 14:47:2454 return nullptr;
jianli7a0c9b62015-05-26 23:24:4755 return static_cast<const GCMRegistrationInfo*>(registration_info);
56}
57
58// static
59GCMRegistrationInfo* GCMRegistrationInfo::FromRegistrationInfo(
60 RegistrationInfo* registration_info) {
61 if (!registration_info || registration_info->GetType() != GCM_REGISTRATION)
Ivan Kotenkov75b1c3a2017-10-24 14:47:2462 return nullptr;
jianli7a0c9b62015-05-26 23:24:4763 return static_cast<GCMRegistrationInfo*>(registration_info);
64}
65
Rayan Kanso3da434f2018-12-19 17:09:3966GCMRegistrationInfo::GCMRegistrationInfo() = default;
jianli7a0c9b62015-05-26 23:24:4767
Rayan Kanso3da434f2018-12-19 17:09:3968GCMRegistrationInfo::~GCMRegistrationInfo() = default;
jianli7a0c9b62015-05-26 23:24:4769
70RegistrationInfo::RegistrationType GCMRegistrationInfo::GetType() const {
71 return GCM_REGISTRATION;
72}
73
74std::string GCMRegistrationInfo::GetSerializedKey() const {
75 // Multiple registrations are not supported for legacy GCM. So the key is
76 // purely based on the application id.
77 return app_id;
78}
79
80std::string GCMRegistrationInfo::GetSerializedValue(
81 const std::string& registration_id) const {
82 if (sender_ids.empty() || registration_id.empty())
83 return std::string();
84
85 // Serialize as:
Mugdha Lakhani2ade6a02018-01-31 14:31:1186 // sender1,sender2,...=reg_id#time_of_last_validation
jianli7a0c9b62015-05-26 23:24:4787 std::string value;
jdoerrie3feb1852018-10-05 12:16:4488 for (auto iter = sender_ids.begin(); iter != sender_ids.end(); ++iter) {
jianli7a0c9b62015-05-26 23:24:4789 DCHECK(!iter->empty() &&
90 iter->find(',') == std::string::npos &&
91 iter->find('=') == std::string::npos);
92 if (!value.empty())
93 value += ",";
94 value += *iter;
95 }
96
Mugdha Lakhani2ade6a02018-01-31 14:31:1197 return base::StringPrintf("%s=%s%c%" PRId64, value.c_str(),
98 registration_id.c_str(),
99 kSerializedValidationTimeSeparator,
100 last_validated.since_origin().InMicroseconds());
jianli7a0c9b62015-05-26 23:24:47101}
102
Mugdha Lakhani2ade6a02018-01-31 14:31:11103bool GCMRegistrationInfo::Deserialize(const std::string& serialized_key,
104 const std::string& serialized_value,
105 std::string* registration_id) {
johnmedc5ddc42015-09-17 14:16:44106 if (serialized_key.empty() || serialized_value.empty())
jianli7a0c9b62015-05-26 23:24:47107 return false;
108
109 // Application ID is same as the serialized key.
johnmedc5ddc42015-09-17 14:16:44110 app_id = serialized_key;
jianli7a0c9b62015-05-26 23:24:47111
112 // Sender IDs and registration ID are constructed from the serialized value.
Mugdha Lakhani2ade6a02018-01-31 14:31:11113 size_t pos_equals = serialized_value.find('=');
114 if (pos_equals == std::string::npos)
jianli7a0c9b62015-05-26 23:24:47115 return false;
Mugdha Lakhani2ade6a02018-01-31 14:31:11116 // Note that it's valid for pos_hash to be std::string::npos.
117 size_t pos_hash = serialized_value.find(kSerializedValidationTimeSeparator);
Peter Beverlooc894bfb2018-02-27 17:17:29118 bool has_timestamp = pos_hash != std::string::npos;
jianli7a0c9b62015-05-26 23:24:47119
Mugdha Lakhani2ade6a02018-01-31 14:31:11120 std::string senders = serialized_value.substr(0, pos_equals);
121 std::string registration_id_str, last_validated_str;
122 if (has_timestamp) {
123 registration_id_str =
124 serialized_value.substr(pos_equals + 1, pos_hash - pos_equals - 1);
125 last_validated_str = serialized_value.substr(pos_hash + 1);
126 } else {
127 registration_id_str = serialized_value.substr(pos_equals + 1);
128 }
jianli7a0c9b62015-05-26 23:24:47129
brettwb45192d2015-06-29 22:53:24130 sender_ids = base::SplitString(
131 senders, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
jianli7a0c9b62015-05-26 23:24:47132 if (sender_ids.empty() || registration_id_str.empty()) {
133 sender_ids.clear();
134 registration_id_str.clear();
135 return false;
136 }
137
138 if (registration_id)
139 *registration_id = registration_id_str;
Mugdha Lakhani2ade6a02018-01-31 14:31:11140 int64_t last_validated_ms = 0;
141 if (base::StringToInt64(last_validated_str, &last_validated_ms)) {
142 // It's okay for |last_validated| to be the default base::Time() value
143 // when there is no serialized timestamp value available.
Peter Kastinge5a38ed2021-10-02 03:06:35144 last_validated = base::Time() + base::Microseconds(last_validated_ms);
Mugdha Lakhani2ade6a02018-01-31 14:31:11145 }
jianli7a0c9b62015-05-26 23:24:47146
147 return true;
148}
149
150// static
151const InstanceIDTokenInfo* InstanceIDTokenInfo::FromRegistrationInfo(
152 const RegistrationInfo* registration_info) {
153 if (!registration_info || registration_info->GetType() != INSTANCE_ID_TOKEN)
Ivan Kotenkov75b1c3a2017-10-24 14:47:24154 return nullptr;
jianli7a0c9b62015-05-26 23:24:47155 return static_cast<const InstanceIDTokenInfo*>(registration_info);
156}
157
158// static
159InstanceIDTokenInfo* InstanceIDTokenInfo::FromRegistrationInfo(
160 RegistrationInfo* registration_info) {
161 if (!registration_info || registration_info->GetType() != INSTANCE_ID_TOKEN)
Ivan Kotenkov75b1c3a2017-10-24 14:47:24162 return nullptr;
jianli7a0c9b62015-05-26 23:24:47163 return static_cast<InstanceIDTokenInfo*>(registration_info);
164}
165
Rayan Kanso3da434f2018-12-19 17:09:39166InstanceIDTokenInfo::InstanceIDTokenInfo() = default;
jianli7a0c9b62015-05-26 23:24:47167
Rayan Kanso3da434f2018-12-19 17:09:39168InstanceIDTokenInfo::~InstanceIDTokenInfo() = default;
jianli7a0c9b62015-05-26 23:24:47169
170RegistrationInfo::RegistrationType InstanceIDTokenInfo::GetType() const {
171 return INSTANCE_ID_TOKEN;
172}
173
174std::string InstanceIDTokenInfo::GetSerializedKey() const {
johnme627dc8c72016-08-19 21:49:39175 DCHECK(app_id.find(',') == std::string::npos &&
176 authorized_entity.find(',') == std::string::npos &&
jianli7a0c9b62015-05-26 23:24:47177 scope.find(',') == std::string::npos);
178
179 // Multiple registrations are supported for Instance ID. So the key is based
180 // on the combination of (app_id, authorized_entity, scope).
181
182 // Adds a prefix to differentiate easily with GCM registration key.
Mugdha Lakhani2ade6a02018-01-31 14:31:11183 return base::StringPrintf("%s%s%c%s%c%s", kInstanceIDSerializationPrefix,
184 app_id.c_str(), kSerializedKeySeparator,
185 authorized_entity.c_str(), kSerializedKeySeparator,
186 scope.c_str());
jianli7a0c9b62015-05-26 23:24:47187}
188
189std::string InstanceIDTokenInfo::GetSerializedValue(
190 const std::string& registration_id) const {
Mugdha Lakhani2ade6a02018-01-31 14:31:11191 int64_t last_validated_ms = last_validated.since_origin().InMicroseconds();
192 return registration_id + kSerializedValidationTimeSeparator +
Raul Tambref88e5102019-02-06 10:54:03193 base::NumberToString(last_validated_ms);
jianli7a0c9b62015-05-26 23:24:47194}
195
Mugdha Lakhani2ade6a02018-01-31 14:31:11196bool InstanceIDTokenInfo::Deserialize(const std::string& serialized_key,
197 const std::string& serialized_value,
198 std::string* registration_id) {
jianli7a0c9b62015-05-26 23:24:47199 if (serialized_key.empty() || serialized_value.empty())
200 return false;
201
johnme627dc8c72016-08-19 21:49:39202 if (!base::StartsWith(serialized_key, kInstanceIDSerializationPrefix,
brettw95509312015-07-16 23:57:33203 base::CompareCase::SENSITIVE))
jianli7a0c9b62015-05-26 23:24:47204 return false;
205
brettwb45192d2015-06-29 22:53:24206 std::vector<std::string> fields = base::SplitString(
johnme627dc8c72016-08-19 21:49:39207 serialized_key.substr(kInstanceIDSerializationPrefixLength), ",",
208 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
jianli7a0c9b62015-05-26 23:24:47209 if (fields.size() != 3 || fields[0].empty() ||
210 fields[1].empty() || fields[2].empty()) {
211 return false;
212 }
213 app_id = fields[0];
214 authorized_entity = fields[1];
215 scope = fields[2];
216
Mugdha Lakhani2ade6a02018-01-31 14:31:11217 // Get Registration ID and last_validated from serialized value
Peter Beverloo1b76cbea2018-02-23 16:23:02218 size_t pos_hash = serialized_value.find(kSerializedValidationTimeSeparator);
Mugdha Lakhani2ade6a02018-01-31 14:31:11219 bool has_timestamp = (pos_hash != std::string::npos);
Peter Beverloo1b76cbea2018-02-23 16:23:02220
Mugdha Lakhani2ade6a02018-01-31 14:31:11221 std::string registration_id_str, last_validated_str;
222 if (has_timestamp) {
223 registration_id_str = serialized_value.substr(0, pos_hash);
224 last_validated_str = serialized_value.substr(pos_hash + 1);
225 } else {
226 registration_id_str = serialized_value;
227 }
228
jianli7a0c9b62015-05-26 23:24:47229 if (registration_id)
Mugdha Lakhani2ade6a02018-01-31 14:31:11230 *registration_id = registration_id_str;
231
232 int64_t last_validated_ms = 0;
233 if (base::StringToInt64(last_validated_str, &last_validated_ms)) {
234 // It's okay for last_validated to be the default base::Time() value
235 // when there is no serialized timestamp available.
Peter Kastinge5a38ed2021-10-02 03:06:35236 last_validated += base::Microseconds(last_validated_ms);
Mugdha Lakhani2ade6a02018-01-31 14:31:11237 }
jianli7a0c9b62015-05-26 23:24:47238
239 return true;
240}
241
242bool RegistrationInfoComparer::operator()(
Rayan Kanso3da434f2018-12-19 17:09:39243 const scoped_refptr<RegistrationInfo>& a,
244 const scoped_refptr<RegistrationInfo>& b) const {
jianli7a0c9b62015-05-26 23:24:47245 DCHECK(a.get() && b.get());
246
247 // For GCMRegistrationInfo, the comparison is based on app_id only.
johnme627dc8c72016-08-19 21:49:39248 // For InstanceIDTokenInfo, the comparison is based on
jianli7a0c9b62015-05-26 23:24:47249 // <app_id, authorized_entity, scope>.
250 if (a->app_id < b->app_id)
251 return true;
252 if (a->app_id > b->app_id)
253 return false;
254
255 InstanceIDTokenInfo* iid_a =
256 InstanceIDTokenInfo::FromRegistrationInfo(a.get());
257 InstanceIDTokenInfo* iid_b =
258 InstanceIDTokenInfo::FromRegistrationInfo(b.get());
259
260 // !iid_a && !iid_b => false.
261 // !iid_a && iid_b => true.
262 // This makes GCM record is sorted before InstanceID record.
263 if (!iid_a)
Ivan Kotenkov75b1c3a2017-10-24 14:47:24264 return iid_b != nullptr;
jianli7a0c9b62015-05-26 23:24:47265
266 // iid_a && !iid_b => false.
267 if (!iid_b)
268 return false;
269
270 // Otherwise, compare with authorized_entity and scope.
271 if (iid_a->authorized_entity < iid_b->authorized_entity)
272 return true;
273 if (iid_a->authorized_entity > iid_b->authorized_entity)
274 return false;
275 return iid_a->scope < iid_b->scope;
276}
277
278bool ExistsGCMRegistrationInMap(const RegistrationInfoMap& map,
279 const std::string& app_id) {
Rayan Kanso3da434f2018-12-19 17:09:39280 scoped_refptr<RegistrationInfo> gcm_registration =
281 base::MakeRefCounted<GCMRegistrationInfo>();
jianli7a0c9b62015-05-26 23:24:47282 gcm_registration->app_id = app_id;
Rayan Kanso3da434f2018-12-19 17:09:39283 return map.find(gcm_registration) != map.end();
jianli7a0c9b62015-05-26 23:24:47284}
285
286} // namespace gcm