Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 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 "components/gcm_driver/registration_info.h" |
| 6 | |
avi | 2606292 | 2015-12-26 00:14:18 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 9 | #include "base/format_macros.h" |
| 10 | #include "base/strings/string_number_conversions.h" |
brettw | b45192d | 2015-06-29 22:53:24 | [diff] [blame] | 11 | #include "base/strings/string_split.h" |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 12 | #include "base/strings/string_util.h" |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 13 | #include "base/strings/stringprintf.h" |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 14 | |
| 15 | namespace gcm { |
| 16 | |
| 17 | namespace { |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 18 | constexpr char kInstanceIDSerializationPrefix[] = "iid-"; |
| 19 | constexpr char kSerializedValidationTimeSeparator = '#'; |
| 20 | constexpr char kSerializedKeySeparator = ','; |
| 21 | constexpr int kInstanceIDSerializationPrefixLength = |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 22 | sizeof(kInstanceIDSerializationPrefix) / sizeof(char) - 1; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 23 | } // namespace |
| 24 | |
| 25 | // static |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 26 | scoped_refptr<RegistrationInfo> RegistrationInfo::BuildFromString( |
johnme | dc5ddc4 | 2015-09-17 14:16:44 | [diff] [blame] | 27 | const std::string& serialized_key, |
| 28 | const std::string& serialized_value, |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 29 | std::string* registration_id) { |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 30 | scoped_refptr<RegistrationInfo> registration; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 31 | |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 32 | if (base::StartsWith(serialized_key, kInstanceIDSerializationPrefix, |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 33 | base::CompareCase::SENSITIVE)) { |
| 34 | registration = base::MakeRefCounted<InstanceIDTokenInfo>(); |
| 35 | } else { |
| 36 | registration = base::MakeRefCounted<GCMRegistrationInfo>(); |
| 37 | } |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 38 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 39 | if (!registration->Deserialize(serialized_key, serialized_value, |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 40 | registration_id)) { |
| 41 | registration.reset(); |
| 42 | } |
dcheng | 5160635 | 2015-12-26 21:16:23 | [diff] [blame] | 43 | return registration; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 44 | } |
| 45 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 46 | RegistrationInfo::RegistrationInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 47 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 48 | RegistrationInfo::~RegistrationInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 49 | |
| 50 | // static |
| 51 | const GCMRegistrationInfo* GCMRegistrationInfo::FromRegistrationInfo( |
| 52 | const RegistrationInfo* registration_info) { |
| 53 | if (!registration_info || registration_info->GetType() != GCM_REGISTRATION) |
Ivan Kotenkov | 75b1c3a | 2017-10-24 14:47:24 | [diff] [blame] | 54 | return nullptr; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 55 | return static_cast<const GCMRegistrationInfo*>(registration_info); |
| 56 | } |
| 57 | |
| 58 | // static |
| 59 | GCMRegistrationInfo* GCMRegistrationInfo::FromRegistrationInfo( |
| 60 | RegistrationInfo* registration_info) { |
| 61 | if (!registration_info || registration_info->GetType() != GCM_REGISTRATION) |
Ivan Kotenkov | 75b1c3a | 2017-10-24 14:47:24 | [diff] [blame] | 62 | return nullptr; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 63 | return static_cast<GCMRegistrationInfo*>(registration_info); |
| 64 | } |
| 65 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 66 | GCMRegistrationInfo::GCMRegistrationInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 67 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 68 | GCMRegistrationInfo::~GCMRegistrationInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 69 | |
| 70 | RegistrationInfo::RegistrationType GCMRegistrationInfo::GetType() const { |
| 71 | return GCM_REGISTRATION; |
| 72 | } |
| 73 | |
| 74 | std::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 | |
| 80 | std::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 Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 86 | // sender1,sender2,...=reg_id#time_of_last_validation |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 87 | std::string value; |
jdoerrie | 3feb185 | 2018-10-05 12:16:44 | [diff] [blame] | 88 | for (auto iter = sender_ids.begin(); iter != sender_ids.end(); ++iter) { |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 89 | 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 Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 97 | return base::StringPrintf("%s=%s%c%" PRId64, value.c_str(), |
| 98 | registration_id.c_str(), |
| 99 | kSerializedValidationTimeSeparator, |
| 100 | last_validated.since_origin().InMicroseconds()); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 101 | } |
| 102 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 103 | bool GCMRegistrationInfo::Deserialize(const std::string& serialized_key, |
| 104 | const std::string& serialized_value, |
| 105 | std::string* registration_id) { |
johnme | dc5ddc4 | 2015-09-17 14:16:44 | [diff] [blame] | 106 | if (serialized_key.empty() || serialized_value.empty()) |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 107 | return false; |
| 108 | |
| 109 | // Application ID is same as the serialized key. |
johnme | dc5ddc4 | 2015-09-17 14:16:44 | [diff] [blame] | 110 | app_id = serialized_key; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 111 | |
| 112 | // Sender IDs and registration ID are constructed from the serialized value. |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 113 | size_t pos_equals = serialized_value.find('='); |
| 114 | if (pos_equals == std::string::npos) |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 115 | return false; |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 116 | // Note that it's valid for pos_hash to be std::string::npos. |
| 117 | size_t pos_hash = serialized_value.find(kSerializedValidationTimeSeparator); |
Peter Beverloo | c894bfb | 2018-02-27 17:17:29 | [diff] [blame] | 118 | bool has_timestamp = pos_hash != std::string::npos; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 119 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 120 | 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 | } |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 129 | |
brettw | b45192d | 2015-06-29 22:53:24 | [diff] [blame] | 130 | sender_ids = base::SplitString( |
| 131 | senders, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 132 | 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 Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 140 | 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 Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 144 | last_validated = base::Time() + base::Microseconds(last_validated_ms); |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 145 | } |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 146 | |
| 147 | return true; |
| 148 | } |
| 149 | |
| 150 | // static |
| 151 | const InstanceIDTokenInfo* InstanceIDTokenInfo::FromRegistrationInfo( |
| 152 | const RegistrationInfo* registration_info) { |
| 153 | if (!registration_info || registration_info->GetType() != INSTANCE_ID_TOKEN) |
Ivan Kotenkov | 75b1c3a | 2017-10-24 14:47:24 | [diff] [blame] | 154 | return nullptr; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 155 | return static_cast<const InstanceIDTokenInfo*>(registration_info); |
| 156 | } |
| 157 | |
| 158 | // static |
| 159 | InstanceIDTokenInfo* InstanceIDTokenInfo::FromRegistrationInfo( |
| 160 | RegistrationInfo* registration_info) { |
| 161 | if (!registration_info || registration_info->GetType() != INSTANCE_ID_TOKEN) |
Ivan Kotenkov | 75b1c3a | 2017-10-24 14:47:24 | [diff] [blame] | 162 | return nullptr; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 163 | return static_cast<InstanceIDTokenInfo*>(registration_info); |
| 164 | } |
| 165 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 166 | InstanceIDTokenInfo::InstanceIDTokenInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 167 | |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 168 | InstanceIDTokenInfo::~InstanceIDTokenInfo() = default; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 169 | |
| 170 | RegistrationInfo::RegistrationType InstanceIDTokenInfo::GetType() const { |
| 171 | return INSTANCE_ID_TOKEN; |
| 172 | } |
| 173 | |
| 174 | std::string InstanceIDTokenInfo::GetSerializedKey() const { |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 175 | DCHECK(app_id.find(',') == std::string::npos && |
| 176 | authorized_entity.find(',') == std::string::npos && |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 177 | 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 Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 183 | 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()); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | std::string InstanceIDTokenInfo::GetSerializedValue( |
| 190 | const std::string& registration_id) const { |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 191 | int64_t last_validated_ms = last_validated.since_origin().InMicroseconds(); |
| 192 | return registration_id + kSerializedValidationTimeSeparator + |
Raul Tambre | f88e510 | 2019-02-06 10:54:03 | [diff] [blame] | 193 | base::NumberToString(last_validated_ms); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 194 | } |
| 195 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 196 | bool InstanceIDTokenInfo::Deserialize(const std::string& serialized_key, |
| 197 | const std::string& serialized_value, |
| 198 | std::string* registration_id) { |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 199 | if (serialized_key.empty() || serialized_value.empty()) |
| 200 | return false; |
| 201 | |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 202 | if (!base::StartsWith(serialized_key, kInstanceIDSerializationPrefix, |
brettw | 9550931 | 2015-07-16 23:57:33 | [diff] [blame] | 203 | base::CompareCase::SENSITIVE)) |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 204 | return false; |
| 205 | |
brettw | b45192d | 2015-06-29 22:53:24 | [diff] [blame] | 206 | std::vector<std::string> fields = base::SplitString( |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 207 | serialized_key.substr(kInstanceIDSerializationPrefixLength), ",", |
| 208 | base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 209 | 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 Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 217 | // Get Registration ID and last_validated from serialized value |
Peter Beverloo | 1b76cbea | 2018-02-23 16:23:02 | [diff] [blame] | 218 | size_t pos_hash = serialized_value.find(kSerializedValidationTimeSeparator); |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 219 | bool has_timestamp = (pos_hash != std::string::npos); |
Peter Beverloo | 1b76cbea | 2018-02-23 16:23:02 | [diff] [blame] | 220 | |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 221 | 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 | |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 229 | if (registration_id) |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 230 | *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 Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 236 | last_validated += base::Microseconds(last_validated_ms); |
Mugdha Lakhani | 2ade6a0 | 2018-01-31 14:31:11 | [diff] [blame] | 237 | } |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 238 | |
| 239 | return true; |
| 240 | } |
| 241 | |
| 242 | bool RegistrationInfoComparer::operator()( |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 243 | const scoped_refptr<RegistrationInfo>& a, |
| 244 | const scoped_refptr<RegistrationInfo>& b) const { |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 245 | DCHECK(a.get() && b.get()); |
| 246 | |
| 247 | // For GCMRegistrationInfo, the comparison is based on app_id only. |
johnme | 627dc8c7 | 2016-08-19 21:49:39 | [diff] [blame] | 248 | // For InstanceIDTokenInfo, the comparison is based on |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 249 | // <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 Kotenkov | 75b1c3a | 2017-10-24 14:47:24 | [diff] [blame] | 264 | return iid_b != nullptr; |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 265 | |
| 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 | |
| 278 | bool ExistsGCMRegistrationInMap(const RegistrationInfoMap& map, |
| 279 | const std::string& app_id) { |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 280 | scoped_refptr<RegistrationInfo> gcm_registration = |
| 281 | base::MakeRefCounted<GCMRegistrationInfo>(); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 282 | gcm_registration->app_id = app_id; |
Rayan Kanso | 3da434f | 2018-12-19 17:09:39 | [diff] [blame] | 283 | return map.find(gcm_registration) != map.end(); |
jianli | 7a0c9b6 | 2015-05-26 23:24:47 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | } // namespace gcm |