| 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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 86 } | 87 } | 
| 87 | 88 | 
| 88 DownloadableSubscription_Parser::DownloadableSubscription_Parser() | 89 DownloadableSubscription_Parser::DownloadableSubscription_Parser() | 
| 89   : mFirstLine(true) | 90   : mFirstLine(true) | 
| 90 { | 91 { | 
| 91   annotate_address(this, "DownloadableSubscription_Parser"); | 92   annotate_address(this, "DownloadableSubscription_Parser"); | 
| 92 } | 93 } | 
| 93 | 94 | 
| 94 namespace { | 95 namespace { | 
| 95   const DependentString ADBLOCK_HEADER(u"[Adblock"_str); | 96   const DependentString ADBLOCK_HEADER(u"[Adblock"_str); | 
| 96 |  | 
| 97   // Only check for trailing base64 padding. There should be at most 2 '='. |  | 
| 98   // In that case return a truncated string. |  | 
| 99   DependentString CleanUpChecksum(const String& checksum) |  | 
| 100   { |  | 
| 101     const auto len = checksum.length(); |  | 
| 102     if ((len > 22 && len <= 24) && |  | 
| 103         (checksum[22] == u'=' && (len == 23 || checksum[23] == u'='))) |  | 
| 104       return DependentString(checksum, 0, 22); |  | 
| 105     return DependentString(checksum); |  | 
| 106   } |  | 
| 107 } | 97 } | 
| 108 | 98 | 
| 109 void DownloadableSubscription_Parser::Process(const String& line) | 99 void DownloadableSubscription_Parser::Process(const String& line) | 
| 110 { | 100 { | 
| 111   bool isHeader = false; | 101   bool isHeader = false; | 
| 112   bool doChecksum = true; |  | 
| 113   isHeader = line.find(ADBLOCK_HEADER) != String::npos; | 102   isHeader = line.find(ADBLOCK_HEADER) != String::npos; | 
| 114   auto param = ParseParam(line); | 103   if (!isHeader) | 
| 115   if (!param.first.is_invalid()) | 104   { | 
| 116   { | 105     auto param = ParseParam(line); | 
| 117     if (param.first == u"checksum"_str) | 106     if (param.first.is_invalid()) | 
| 118     { | 107       mFiltersText.emplace_back(line); | 
| 119       mParams[param.first] = CleanUpChecksum(param.second); |  | 
| 120       doChecksum = false; |  | 
| 121     } |  | 
| 122     else | 108     else | 
| 123       mParams[param.first] = param.second; | 109       mParams[param.first] = param.second; | 
| 124   } | 110   } | 
| 125   // Checksum is an MD5 checksum (base64-encoded without the trailing "=") of |  | 
| 126   // all lines in UTF-8 without the checksum line, joined with "\n". |  | 
| 127   if (doChecksum) |  | 
| 128   { |  | 
| 129     if (!mFirstLine) |  | 
| 130       mChecksum.Update((const uint8_t*)"\n", 1); |  | 
| 131     else |  | 
| 132       mFirstLine = false; |  | 
| 133     mChecksum.Update(line); |  | 
| 134   } |  | 
| 135   if (param.first.is_invalid() && !isHeader) |  | 
| 136     mFiltersText.emplace_back(line); |  | 
| 137 } | 111 } | 
| 138 | 112 | 
| 139 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) | 113 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) | 
| 140 { | 114 { | 
| 141   bool isHour = false; | 115   bool isHour = false; | 
| 142   StringScanner scanner(expires); | 116   StringScanner scanner(expires); | 
| 143   String::size_type numStart = 0; | 117   String::size_type numStart = 0; | 
| 144   String::size_type numLen = 0; | 118   String::size_type numLen = 0; | 
| 145   while(!scanner.done()) | 119   while(!scanner.done()) | 
| 146   { | 120   { | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 158     } | 132     } | 
| 159     else | 133     else | 
| 160     { | 134     { | 
| 161       if (numLen) | 135       if (numLen) | 
| 162         scanner.back(); | 136         scanner.back(); | 
| 163       break; | 137       break; | 
| 164     } | 138     } | 
| 165   } | 139   } | 
| 166 | 140 | 
| 167   DependentString numStr(expires, numStart, numLen); | 141   DependentString numStr(expires, numStart, numLen); | 
| 168   int64_t num = numStr.toInt<int64_t>(); | 142   int64_t num = lexical_cast<int64_t>(numStr); | 
| 169   if (num == 0) | 143   if (num == 0) | 
| 170     return 0; | 144     return 0; | 
| 171 | 145 | 
| 172   while (!scanner.done()) | 146   while (!scanner.done()) | 
| 173   { | 147   { | 
| 174     auto ch = scanner.next(); | 148     auto ch = scanner.next(); | 
| 175     if (std::iswspace(ch)) | 149     if (std::iswspace(ch)) | 
| 176       continue; | 150       continue; | 
| 177 | 151 | 
| 178     if (ch == u'h') | 152     if (ch == u'h') | 
| 179       isHour = true; | 153       isHour = true; | 
| 180 | 154 | 
| 181     // assume we are done here. The rest is ignored. | 155     // assume we are done here. The rest is ignored. | 
| 182     break; | 156     break; | 
| 183   } | 157   } | 
| 184   // check for overflow. | 158   // check for overflow. | 
| 185   if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY)) | 159   if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY)) | 
| 186     return 0; | 160     return 0; | 
| 187 | 161 | 
| 188   num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY; | 162   num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY; | 
| 189   return num; | 163   return num; | 
| 190 } | 164 } | 
| 191 | 165 | 
| 192 bool DownloadableSubscription_Parser::VerifyChecksum() |  | 
| 193 { |  | 
| 194   if (!mParams.find(u"checksum"_str)) |  | 
| 195     return true; |  | 
| 196 |  | 
| 197   if (mB64Checksum.is_invalid()) |  | 
| 198   { |  | 
| 199     uint8_t checksum[MD5::CHECKSUM_LENGTH]; |  | 
| 200     mChecksum.Final(checksum); |  | 
| 201     mB64Checksum = ToBase64(checksum, MD5::CHECKSUM_LENGTH); |  | 
| 202   } |  | 
| 203   return (mParams[u"checksum"_str] == mB64Checksum); |  | 
| 204 } |  | 
| 205 |  | 
| 206 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs
     cription) | 166 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs
     cription) | 
| 207 { | 167 { | 
| 208   if (mB64Checksum.is_invalid()) |  | 
| 209     VerifyChecksum(); // here we ignore the checksum, but we calculate it. |  | 
| 210 |  | 
| 211   auto entry = mParams.find(u"title"_str); | 168   auto entry = mParams.find(u"title"_str); | 
| 212   if (entry) | 169   if (entry) | 
| 213   { | 170   { | 
| 214     subscription.SetTitle(entry->second); | 171     subscription.SetTitle(entry->second); | 
| 215     subscription.SetFixedTitle(true); | 172     subscription.SetFixedTitle(true); | 
| 216   } | 173   } | 
| 217   else | 174   else | 
| 218     subscription.SetFixedTitle(false); | 175     subscription.SetFixedTitle(false); | 
| 219 | 176 | 
| 220   int32_t version = 0; | 177   int32_t version = 0; | 
| 221   entry = mParams.find(u"version"_str); | 178   entry = mParams.find(u"version"_str); | 
| 222   if (entry) | 179   if (entry) | 
| 223     version = entry->second.toInt<int32_t>(); | 180     version = lexical_cast<int32_t>(entry->second); | 
| 224   subscription.SetDataRevision(version); | 181   subscription.SetDataRevision(version); | 
| 225 | 182 | 
| 226   int64_t expires = 0; | 183   int64_t expires = 0; | 
| 227   entry = mParams.find(u"expires"_str); | 184   entry = mParams.find(u"expires"_str); | 
| 228   if (entry) | 185   if (entry) | 
| 229     expires = ParseExpires(entry->second); | 186     expires = ParseExpires(entry->second); | 
| 230 | 187 | 
| 231   FilterNotifier::SubscriptionChange( | 188   FilterNotifier::SubscriptionChange( | 
| 232     FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, | 189     FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, | 
| 233     subscription); | 190     subscription); | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 259   return emptyString; | 216   return emptyString; | 
| 260 } | 217 } | 
| 261 | 218 | 
| 262 const String& DownloadableSubscription_Parser::GetHomepage() const | 219 const String& DownloadableSubscription_Parser::GetHomepage() const | 
| 263 { | 220 { | 
| 264   auto entry = mParams.find(u"homepage"_str); | 221   auto entry = mParams.find(u"homepage"_str); | 
| 265   if (entry) | 222   if (entry) | 
| 266     return entry->second; | 223     return entry->second; | 
| 267   return emptyString; | 224   return emptyString; | 
| 268 } | 225 } | 
|  | 226 | 
|  | 227 ABP_NS_USING | 
| 269 | 228 | 
| 270 DownloadableSubscription::DownloadableSubscription(const String& id) | 229 DownloadableSubscription::DownloadableSubscription(const String& id) | 
| 271     : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), | 230     : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), | 
| 272       mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), | 231       mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), | 
| 273       mErrorCount(0), mDataRevision(0), mDownloadCount(0) | 232       mErrorCount(0), mDataRevision(0), mDownloadCount(0) | 
| 274 { | 233 { | 
| 275   SetTitle(id); | 234   SetTitle(id); | 
| 276 } | 235 } | 
| 277 | 236 | 
| 278 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload() | 237 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload() | 
| 279 { | 238 { | 
| 280   return new DownloadableSubscription_Parser(); | 239   return new DownloadableSubscription_Parser(); | 
| 281 } | 240 } | 
| 282 | 241 | 
| 283 OwnedString DownloadableSubscription::Serialize() const | 242 OwnedString DownloadableSubscription::Serialize() const | 
| 284 { | 243 { | 
| 285   OwnedString result(Subscription::Serialize()); | 244   OwnedString result(Subscription::Serialize()); | 
| 286   if (mFixedTitle) | 245   if (mFixedTitle) | 
| 287     result.append(u"fixedTitle=true\n"_str); | 246     result.append(ABP_TEXT("fixedTitle=true\n"_str)); | 
| 288   if (!mHomepage.empty()) | 247   if (!mHomepage.empty()) | 
| 289   { | 248   { | 
| 290     result.append(u"homepage="_str); | 249     result.append(ABP_TEXT("homepage="_str)); | 
| 291     result.append(mHomepage); | 250     result.append(mHomepage); | 
| 292     result.append(u'\n'); | 251     result.append(ABP_TEXT('\n')); | 
| 293   } | 252   } | 
| 294   if (mLastCheck) | 253   if (mLastCheck) | 
| 295   { | 254   { | 
| 296     result.append(u"lastCheck="_str); | 255     result.append(ABP_TEXT("lastCheck="_str)); | 
| 297     result.append(mLastCheck); | 256     result.append(mLastCheck); | 
| 298     result.append(u'\n'); | 257     result.append(ABP_TEXT('\n')); | 
| 299   } | 258   } | 
| 300   if (mHardExpiration) | 259   if (mHardExpiration) | 
| 301   { | 260   { | 
| 302     result.append(u"expires="_str); | 261     result.append(ABP_TEXT("expires="_str)); | 
| 303     result.append(mHardExpiration); | 262     result.append(mHardExpiration); | 
| 304     result.append(u'\n'); | 263     result.append(ABP_TEXT('\n')); | 
| 305   } | 264   } | 
| 306   if (mSoftExpiration) | 265   if (mSoftExpiration) | 
| 307   { | 266   { | 
| 308     result.append(u"softExpiration="_str); | 267     result.append(ABP_TEXT("softExpiration="_str)); | 
| 309     result.append(mSoftExpiration); | 268     result.append(mSoftExpiration); | 
| 310     result.append(u'\n'); | 269     result.append(ABP_TEXT('\n')); | 
| 311   } | 270   } | 
| 312   if (mLastDownload) | 271   if (mLastDownload) | 
| 313   { | 272   { | 
| 314     result.append(u"lastDownload="_str); | 273     result.append(ABP_TEXT("lastDownload="_str)); | 
| 315     result.append(mLastDownload); | 274     result.append(mLastDownload); | 
| 316     result.append(u'\n'); | 275     result.append(ABP_TEXT('\n')); | 
| 317   } | 276   } | 
| 318   if (!mDownloadStatus.empty()) | 277   if (!mDownloadStatus.empty()) | 
| 319   { | 278   { | 
| 320     result.append(u"downloadStatus="_str); | 279     result.append(ABP_TEXT("downloadStatus="_str)); | 
| 321     result.append(mDownloadStatus); | 280     result.append(mDownloadStatus); | 
| 322     result.append(u'\n'); | 281     result.append(ABP_TEXT('\n')); | 
| 323   } | 282   } | 
| 324   if (mLastSuccess) | 283   if (mLastSuccess) | 
| 325   { | 284   { | 
| 326     result.append(u"lastSuccess="_str); | 285     result.append(ABP_TEXT("lastSuccess="_str)); | 
| 327     result.append(mLastSuccess); | 286     result.append(mLastSuccess); | 
| 328     result.append(u'\n'); | 287     result.append(ABP_TEXT('\n')); | 
| 329   } | 288   } | 
| 330   if (mErrorCount) | 289   if (mErrorCount) | 
| 331   { | 290   { | 
| 332     result.append(u"errors="_str); | 291     result.append(ABP_TEXT("errors="_str)); | 
| 333     result.append(mErrorCount); | 292     result.append(mErrorCount); | 
| 334     result.append(u'\n'); | 293     result.append(ABP_TEXT('\n')); | 
| 335   } | 294   } | 
| 336   if (mDataRevision) | 295   if (mDataRevision) | 
| 337   { | 296   { | 
| 338     result.append(u"version="_str); | 297     result.append(ABP_TEXT("version="_str)); | 
| 339     result.append(mDataRevision); | 298     result.append(mDataRevision); | 
| 340     result.append(u'\n'); | 299     result.append(ABP_TEXT('\n')); | 
| 341   } | 300   } | 
| 342   if (!mRequiredVersion.empty()) | 301   if (!mRequiredVersion.empty()) | 
| 343   { | 302   { | 
| 344     result.append(u"requiredVersion="_str); | 303     result.append(ABP_TEXT("requiredVersion="_str)); | 
| 345     result.append(mRequiredVersion); | 304     result.append(mRequiredVersion); | 
| 346     result.append(u'\n'); | 305     result.append(ABP_TEXT('\n')); | 
| 347   } | 306   } | 
| 348   if (mDownloadCount) | 307   if (mDownloadCount) | 
| 349   { | 308   { | 
| 350     result.append(u"downloadCount="_str); | 309     result.append(ABP_TEXT("downloadCount="_str)); | 
| 351     result.append(mDownloadCount); | 310     result.append(mDownloadCount); | 
| 352     result.append(u'\n'); | 311     result.append(ABP_TEXT('\n')); | 
| 353   } | 312   } | 
| 354   return result; | 313   return result; | 
| 355 } | 314 } | 
| LEFT | RIGHT | 
|---|