LEFT | RIGHT |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 #include <cwctype> | 18 #include <cwctype> |
19 #include <limits> | 19 #include <limits> |
20 | 20 |
21 #include "DownloadableSubscription.h" | 21 #include "DownloadableSubscription.h" |
22 #include "../Base64.h" | |
23 #include "../FilterNotifier.h" | 22 #include "../FilterNotifier.h" |
24 #include "../StringScanner.h" | 23 #include "../StringScanner.h" |
25 #include "../filter/CommentFilter.h" | 24 #include "../filter/CommentFilter.h" |
| 25 |
| 26 ABP_NS_USING |
26 | 27 |
27 namespace { | 28 namespace { |
28 constexpr int MILLIS_IN_HOUR = 60 * 60 * 1000; | 29 constexpr int MILLIS_IN_HOUR = 60 * 60 * 1000; |
29 constexpr int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; | 30 constexpr int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; |
30 // limits | 31 // limits |
31 constexpr int64_t MAX_HOUR = std::numeric_limits<int64_t>::max() / MILLIS_IN_H
OUR; | 32 constexpr int64_t MAX_HOUR = std::numeric_limits<int64_t>::max() / MILLIS_IN_H
OUR; |
32 constexpr int64_t MAX_DAY = std::numeric_limits<int64_t>::max() / MILLIS_IN_DA
Y; | 33 constexpr int64_t MAX_DAY = std::numeric_limits<int64_t>::max() / MILLIS_IN_DA
Y; |
33 | 34 |
34 typedef std::pair<DependentString, DependentString> Param; | 35 typedef std::pair<DependentString, DependentString> Param; |
35 | 36 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 DownloadableSubscription_Parser::DownloadableSubscription_Parser() | 89 DownloadableSubscription_Parser::DownloadableSubscription_Parser() |
89 { | 90 { |
90 annotate_address(this, "DownloadableSubscription_Parser"); | 91 annotate_address(this, "DownloadableSubscription_Parser"); |
91 } | 92 } |
92 | 93 |
93 namespace { | 94 namespace { |
94 const DependentString ADBLOCK_HEADER(u"[Adblock"_str); | 95 const DependentString ADBLOCK_HEADER(u"[Adblock"_str); |
95 const DependentString ADBLOCK_PLUS_EXTRA_HEADER(u"Plus"_str); | 96 const DependentString ADBLOCK_PLUS_EXTRA_HEADER(u"Plus"_str); |
96 | 97 |
97 const DependentString ERROR_INVALID_DATA(u"synchronize_invalid_data"_str); | 98 const DependentString ERROR_INVALID_DATA(u"synchronize_invalid_data"_str); |
98 | |
99 // Only check for trailing base64 padding. There should be at most 2 '='. | |
100 // In that case return a truncated string. | |
101 DependentString CleanUpChecksum(const String& checksum) | |
102 { | |
103 const auto len = checksum.length(); | |
104 if ((len > 22 && len <= 24) && | |
105 (checksum[22] == u'=' && (len == 23 || checksum[23] == u'='))) | |
106 return DependentString(checksum, 0, 22); | |
107 return DependentString(checksum); | |
108 } | |
109 } | 99 } |
110 | 100 |
111 /// Return true if more line expected. | 101 /// Return true if more line expected. |
112 bool DownloadableSubscription_Parser::GetNextLine(DependentString& buffer, Depen
dentString& line) | 102 bool DownloadableSubscription_Parser::GetNextLine(DependentString& buffer, Depen
dentString& line) |
113 { | 103 { |
114 StringScanner scanner(buffer); | 104 StringScanner scanner(buffer); |
115 String::value_type ch = 0; | 105 String::value_type ch = 0; |
116 while (ch != u'\r' && ch != u'\n') | 106 while (ch != u'\r' && ch != u'\n') |
117 { | 107 { |
118 ch = scanner.next(); | 108 ch = scanner.next(); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 ; | 163 ; |
174 if (ch) | 164 if (ch) |
175 scanner.back(); | 165 scanner.back(); |
176 if (scanner.position() + 1 > index) | 166 if (scanner.position() + 1 > index) |
177 minVersion.reset(current, index, scanner.position() + 1 - index); | 167 minVersion.reset(current, index, scanner.position() + 1 - index); |
178 | 168 |
179 if (ch != u']') | 169 if (ch != u']') |
180 return false; | 170 return false; |
181 | 171 |
182 mRequiredVersion = minVersion; | 172 mRequiredVersion = minVersion; |
183 mChecksum.Update(line); | |
184 return true; | 173 return true; |
185 } | 174 } |
186 | 175 |
187 void DownloadableSubscription_Parser::ProcessLine(const String& line) | 176 void DownloadableSubscription_Parser::ProcessLine(const String& line) |
188 { | 177 { |
189 auto param = ParseParam(line); | 178 auto param = ParseParam(line); |
190 if (param.first.is_invalid()) | 179 if (param.first.is_invalid()) |
191 { | 180 { |
192 if (!line.empty()) | 181 if (!line.empty()) |
193 mFiltersText.emplace_back(line); | 182 mFiltersText.emplace_back(line); |
194 } | 183 } |
195 else if (param.first == u"checksum"_str) | |
196 { | |
197 mParams[param.first] = CleanUpChecksum(param.second); | |
198 return; | |
199 } | |
200 else | 184 else |
201 mParams[param.first] = param.second; | 185 mParams[param.first] = param.second; |
202 | |
203 // Checksum is an MD5 checksum (base64-encoded without the trailing "=") of | |
204 // all lines in UTF-8 without the checksum line, joined with "\n". | |
205 mChecksum.Update((const uint8_t*)"\n", 1); | |
206 mChecksum.Update(line); | |
207 } | 186 } |
208 | 187 |
209 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) | 188 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) |
210 { | 189 { |
211 bool isHour = false; | 190 bool isHour = false; |
212 StringScanner scanner(expires); | 191 StringScanner scanner(expires); |
213 String::size_type numStart = 0; | 192 String::size_type numStart = 0; |
214 String::size_type numLen = 0; | 193 String::size_type numLen = 0; |
215 while(!scanner.done()) | 194 while(!scanner.done()) |
216 { | 195 { |
(...skipping 11 matching lines...) Expand all Loading... |
228 } | 207 } |
229 else | 208 else |
230 { | 209 { |
231 if (numLen) | 210 if (numLen) |
232 scanner.back(); | 211 scanner.back(); |
233 break; | 212 break; |
234 } | 213 } |
235 } | 214 } |
236 | 215 |
237 DependentString numStr(expires, numStart, numLen); | 216 DependentString numStr(expires, numStart, numLen); |
238 int64_t num = numStr.toInt<int64_t>(); | 217 int64_t num = lexical_cast<int64_t>(numStr); |
239 if (num == 0) | 218 if (num == 0) |
240 return 0; | 219 return 0; |
241 | 220 |
242 while (!scanner.done()) | 221 while (!scanner.done()) |
243 { | 222 { |
244 auto ch = scanner.next(); | 223 auto ch = scanner.next(); |
245 if (std::iswspace(ch)) | 224 if (std::iswspace(ch)) |
246 continue; | 225 continue; |
247 | 226 |
248 if (ch == u'h') | 227 if (ch == u'h') |
249 isHour = true; | 228 isHour = true; |
250 | 229 |
251 // assume we are done here. The rest is ignored. | 230 // assume we are done here. The rest is ignored. |
252 break; | 231 break; |
253 } | 232 } |
254 // check for overflow. | 233 // check for overflow. |
255 if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY)) | 234 if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY)) |
256 return 0; | 235 return 0; |
257 | 236 |
258 num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY; | 237 num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY; |
259 return num; | 238 return num; |
260 } | 239 } |
261 | 240 |
262 bool DownloadableSubscription_Parser::VerifyChecksum() | |
263 { | |
264 if (!mParams.find(u"checksum"_str)) | |
265 return true; | |
266 | |
267 if (mB64Checksum.is_invalid()) | |
268 { | |
269 uint8_t checksum[MD5::CHECKSUM_LENGTH]; | |
270 mChecksum.Final(checksum); | |
271 mB64Checksum = ToBase64(checksum, MD5::CHECKSUM_LENGTH); | |
272 } | |
273 return (mParams[u"checksum"_str] == mB64Checksum); | |
274 } | |
275 | |
276 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs
cription) | 241 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs
cription) |
277 { | 242 { |
278 if (mB64Checksum.is_invalid()) | |
279 VerifyChecksum(); // here we ignore the checksum, but we calculate it. | |
280 | |
281 FilterNotifier::SubscriptionChange( | 243 FilterNotifier::SubscriptionChange( |
282 FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, | 244 FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, |
283 subscription); | 245 subscription); |
284 | 246 |
285 if (!mRequiredVersion.empty()) | 247 if (!mRequiredVersion.empty()) |
286 subscription.SetRequiredVersion(mRequiredVersion); | 248 subscription.SetRequiredVersion(mRequiredVersion); |
287 | 249 |
288 auto entry = mParams.find(u"title"_str); | 250 auto entry = mParams.find(u"title"_str); |
289 if (entry) | 251 if (entry) |
290 { | 252 { |
291 subscription.SetTitle(entry->second); | 253 subscription.SetTitle(entry->second); |
292 subscription.SetFixedTitle(true); | 254 subscription.SetFixedTitle(true); |
293 } | 255 } |
294 else | 256 else |
295 subscription.SetFixedTitle(false); | 257 subscription.SetFixedTitle(false); |
296 | 258 |
297 int32_t version = 0; | 259 int32_t version = 0; |
298 entry = mParams.find(u"version"_str); | 260 entry = mParams.find(u"version"_str); |
299 if (entry) | 261 if (entry) |
300 version = entry->second.toInt<int32_t>(); | 262 version = lexical_cast<int32_t>(entry->second); |
301 subscription.SetDataRevision(version); | 263 subscription.SetDataRevision(version); |
302 | 264 |
303 int64_t expires = 0; | 265 int64_t expires = 0; |
304 entry = mParams.find(u"expires"_str); | 266 entry = mParams.find(u"expires"_str); |
305 if (entry) | 267 if (entry) |
306 expires = ParseExpires(entry->second); | 268 expires = ParseExpires(entry->second); |
307 | 269 |
308 Subscription::Filters filters; | 270 Subscription::Filters filters; |
309 filters.reserve(mFiltersText.size()); | 271 filters.reserve(mFiltersText.size()); |
310 for (auto text : mFiltersText) | 272 for (auto text : mFiltersText) |
(...skipping 21 matching lines...) Expand all Loading... |
332 return emptyString; | 294 return emptyString; |
333 } | 295 } |
334 | 296 |
335 const String& DownloadableSubscription_Parser::GetHomepage() const | 297 const String& DownloadableSubscription_Parser::GetHomepage() const |
336 { | 298 { |
337 auto entry = mParams.find(u"homepage"_str); | 299 auto entry = mParams.find(u"homepage"_str); |
338 if (entry) | 300 if (entry) |
339 return entry->second; | 301 return entry->second; |
340 return emptyString; | 302 return emptyString; |
341 } | 303 } |
| 304 |
| 305 ABP_NS_USING |
342 | 306 |
343 DownloadableSubscription::DownloadableSubscription(const String& id) | 307 DownloadableSubscription::DownloadableSubscription(const String& id) |
344 : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), | 308 : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), |
345 mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), | 309 mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), |
346 mErrorCount(0), mDataRevision(0), mDownloadCount(0) | 310 mErrorCount(0), mDataRevision(0), mDownloadCount(0) |
347 { | 311 { |
348 SetTitle(id); | 312 SetTitle(id); |
349 } | 313 } |
350 | 314 |
351 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload() | 315 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload() |
352 { | 316 { |
353 return new DownloadableSubscription_Parser(); | 317 return new DownloadableSubscription_Parser(); |
354 } | 318 } |
355 | 319 |
356 OwnedString DownloadableSubscription::Serialize() const | 320 OwnedString DownloadableSubscription::Serialize() const |
357 { | 321 { |
358 OwnedString result(Subscription::Serialize()); | 322 OwnedString result(Subscription::Serialize()); |
359 if (mFixedTitle) | 323 if (mFixedTitle) |
360 result.append(u"fixedTitle=true\n"_str); | 324 result.append(ABP_TEXT("fixedTitle=true\n"_str)); |
361 if (!mHomepage.empty()) | 325 if (!mHomepage.empty()) |
362 { | 326 { |
363 result.append(u"homepage="_str); | 327 result.append(ABP_TEXT("homepage="_str)); |
364 result.append(mHomepage); | 328 result.append(mHomepage); |
365 result.append(u'\n'); | 329 result.append(ABP_TEXT('\n')); |
366 } | 330 } |
367 if (mLastCheck) | 331 if (mLastCheck) |
368 { | 332 { |
369 result.append(u"lastCheck="_str); | 333 result.append(ABP_TEXT("lastCheck="_str)); |
370 result.append(mLastCheck); | 334 result.append(mLastCheck); |
371 result.append(u'\n'); | 335 result.append(ABP_TEXT('\n')); |
372 } | 336 } |
373 if (mHardExpiration) | 337 if (mHardExpiration) |
374 { | 338 { |
375 result.append(u"expires="_str); | 339 result.append(ABP_TEXT("expires="_str)); |
376 result.append(mHardExpiration); | 340 result.append(mHardExpiration); |
377 result.append(u'\n'); | 341 result.append(ABP_TEXT('\n')); |
378 } | 342 } |
379 if (mSoftExpiration) | 343 if (mSoftExpiration) |
380 { | 344 { |
381 result.append(u"softExpiration="_str); | 345 result.append(ABP_TEXT("softExpiration="_str)); |
382 result.append(mSoftExpiration); | 346 result.append(mSoftExpiration); |
383 result.append(u'\n'); | 347 result.append(ABP_TEXT('\n')); |
384 } | 348 } |
385 if (mLastDownload) | 349 if (mLastDownload) |
386 { | 350 { |
387 result.append(u"lastDownload="_str); | 351 result.append(ABP_TEXT("lastDownload="_str)); |
388 result.append(mLastDownload); | 352 result.append(mLastDownload); |
389 result.append(u'\n'); | 353 result.append(ABP_TEXT('\n')); |
390 } | 354 } |
391 if (!mDownloadStatus.empty()) | 355 if (!mDownloadStatus.empty()) |
392 { | 356 { |
393 result.append(u"downloadStatus="_str); | 357 result.append(ABP_TEXT("downloadStatus="_str)); |
394 result.append(mDownloadStatus); | 358 result.append(mDownloadStatus); |
395 result.append(u'\n'); | 359 result.append(ABP_TEXT('\n')); |
396 } | 360 } |
397 if (mLastSuccess) | 361 if (mLastSuccess) |
398 { | 362 { |
399 result.append(u"lastSuccess="_str); | 363 result.append(ABP_TEXT("lastSuccess="_str)); |
400 result.append(mLastSuccess); | 364 result.append(mLastSuccess); |
401 result.append(u'\n'); | 365 result.append(ABP_TEXT('\n')); |
402 } | 366 } |
403 if (mErrorCount) | 367 if (mErrorCount) |
404 { | 368 { |
405 result.append(u"errors="_str); | 369 result.append(ABP_TEXT("errors="_str)); |
406 result.append(mErrorCount); | 370 result.append(mErrorCount); |
407 result.append(u'\n'); | 371 result.append(ABP_TEXT('\n')); |
408 } | 372 } |
409 if (mDataRevision) | 373 if (mDataRevision) |
410 { | 374 { |
411 result.append(u"version="_str); | 375 result.append(ABP_TEXT("version="_str)); |
412 result.append(mDataRevision); | 376 result.append(mDataRevision); |
413 result.append(u'\n'); | 377 result.append(ABP_TEXT('\n')); |
414 } | 378 } |
415 if (!mRequiredVersion.empty()) | 379 if (!mRequiredVersion.empty()) |
416 { | 380 { |
417 result.append(u"requiredVersion="_str); | 381 result.append(ABP_TEXT("requiredVersion="_str)); |
418 result.append(mRequiredVersion); | 382 result.append(mRequiredVersion); |
419 result.append(u'\n'); | 383 result.append(ABP_TEXT('\n')); |
420 } | 384 } |
421 if (mDownloadCount) | 385 if (mDownloadCount) |
422 { | 386 { |
423 result.append(u"downloadCount="_str); | 387 result.append(ABP_TEXT("downloadCount="_str)); |
424 result.append(mDownloadCount); | 388 result.append(mDownloadCount); |
425 result.append(u'\n'); | 389 result.append(ABP_TEXT('\n')); |
426 } | 390 } |
427 return result; | 391 return result; |
428 } | 392 } |
LEFT | RIGHT |