| 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 |