| OLD | NEW | 
|---|
| 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> | 
|  | 19 #include <limits> | 
|  | 20 | 
| 18 #include "DownloadableSubscription.h" | 21 #include "DownloadableSubscription.h" | 
|  | 22 #include "../Base64.h" | 
|  | 23 #include "../FilterNotifier.h" | 
|  | 24 #include "../StringScanner.h" | 
|  | 25 #include "../filter/CommentFilter.h" | 
|  | 26 | 
|  | 27 namespace { | 
|  | 28   constexpr int MILLIS_IN_HOUR = 60 * 60 * 1000; | 
|  | 29   constexpr int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; | 
|  | 30   // limits | 
|  | 31   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 | 
|  | 34   typedef std::pair<DependentString, DependentString> Param; | 
|  | 35 | 
|  | 36   Param ParseParam(const String& text) | 
|  | 37   { | 
|  | 38     Param param; | 
|  | 39 | 
|  | 40     if (text[0] == u'!') | 
|  | 41     { | 
|  | 42       bool foundColon = false; | 
|  | 43       String::size_type beginParam = 0; | 
|  | 44       String::size_type endParam = 0; | 
|  | 45       String::size_type beginValue = 0; | 
|  | 46       for (String::size_type i = 1; i < text.length(); i++) | 
|  | 47       { | 
|  | 48         switch (text[i]) | 
|  | 49         { | 
|  | 50         case ' ': | 
|  | 51         case '\t': | 
|  | 52           if (beginParam > 0 && !foundColon) | 
|  | 53           { | 
|  | 54             endParam = i; | 
|  | 55           } | 
|  | 56           break; | 
|  | 57         case ':': | 
|  | 58           foundColon = true; | 
|  | 59           endParam = i; | 
|  | 60           break; | 
|  | 61         default: | 
|  | 62           if (foundColon) | 
|  | 63           { | 
|  | 64             beginValue = i; | 
|  | 65           } | 
|  | 66           else | 
|  | 67           { | 
|  | 68             if (beginParam == 0) | 
|  | 69               beginParam = i; | 
|  | 70           } | 
|  | 71           break; | 
|  | 72         } | 
|  | 73         if (beginValue > 0) | 
|  | 74           break; | 
|  | 75       } | 
|  | 76       if (beginValue > 0) | 
|  | 77       { | 
|  | 78         param.first = DependentString(text, beginParam, endParam - beginParam); | 
|  | 79         param.first.toLower(); | 
|  | 80         param.second = DependentString( | 
|  | 81           text, beginValue, text.length() - beginValue); | 
|  | 82       } | 
|  | 83     } | 
|  | 84     return param; | 
|  | 85   } | 
|  | 86 } | 
|  | 87 | 
|  | 88 DownloadableSubscription_Parser::DownloadableSubscription_Parser() | 
|  | 89   : mFirstLine(true) | 
|  | 90 { | 
|  | 91   annotate_address(this, "DownloadableSubscription_Parser"); | 
|  | 92 } | 
|  | 93 | 
|  | 94 namespace { | 
|  | 95   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 } | 
|  | 108 | 
|  | 109 void DownloadableSubscription_Parser::Process(const String& line) | 
|  | 110 { | 
|  | 111   bool isHeader = false; | 
|  | 112   isHeader = line.find(ADBLOCK_HEADER) != String::npos; | 
|  | 113   if (!isHeader) | 
|  | 114   { | 
|  | 115     auto param = ParseParam(line); | 
|  | 116     if (param.first.is_invalid()) | 
|  | 117       mFiltersText.emplace_back(line); | 
|  | 118     else if (param.first == u"checksum"_str) | 
|  | 119     { | 
|  | 120       mParams[param.first] = CleanUpChecksum(param.second); | 
|  | 121       return; | 
|  | 122     } | 
|  | 123     else | 
|  | 124       mParams[param.first] = param.second; | 
|  | 125   } | 
|  | 126   // Checksum is an MD5 checksum (base64-encoded without the trailing "=") of | 
|  | 127   // all lines in UTF-8 without the checksum line, joined with "\n". | 
|  | 128   if (!mFirstLine) | 
|  | 129     mChecksum.Update((const uint8_t*)"\n", 1); | 
|  | 130   else | 
|  | 131     mFirstLine = false; | 
|  | 132   mChecksum.Update(line); | 
|  | 133 } | 
|  | 134 | 
|  | 135 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) | 
|  | 136 { | 
|  | 137   bool isHour = false; | 
|  | 138   StringScanner scanner(expires); | 
|  | 139   String::size_type numStart = 0; | 
|  | 140   String::size_type numLen = 0; | 
|  | 141   while(!scanner.done()) | 
|  | 142   { | 
|  | 143     auto ch = scanner.next(); | 
|  | 144     if (std::iswdigit(ch)) | 
|  | 145     { | 
|  | 146       if (numLen == 0) | 
|  | 147         numStart = scanner.position(); | 
|  | 148       numLen++; | 
|  | 149     } | 
|  | 150     else if (std::iswspace(ch)) | 
|  | 151     { | 
|  | 152       if (numLen) | 
|  | 153         break; | 
|  | 154     } | 
|  | 155     else | 
|  | 156     { | 
|  | 157       if (numLen) | 
|  | 158         scanner.back(); | 
|  | 159       break; | 
|  | 160     } | 
|  | 161   } | 
|  | 162 | 
|  | 163   DependentString numStr(expires, numStart, numLen); | 
|  | 164   int64_t num = numStr.toInt<int64_t>(); | 
|  | 165   if (num == 0) | 
|  | 166     return 0; | 
|  | 167 | 
|  | 168   while (!scanner.done()) | 
|  | 169   { | 
|  | 170     auto ch = scanner.next(); | 
|  | 171     if (std::iswspace(ch)) | 
|  | 172       continue; | 
|  | 173 | 
|  | 174     if (ch == u'h') | 
|  | 175       isHour = true; | 
|  | 176 | 
|  | 177     // assume we are done here. The rest is ignored. | 
|  | 178     break; | 
|  | 179   } | 
|  | 180   // check for overflow. | 
|  | 181   if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY)) | 
|  | 182     return 0; | 
|  | 183 | 
|  | 184   num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY; | 
|  | 185   return num; | 
|  | 186 } | 
|  | 187 | 
|  | 188 bool DownloadableSubscription_Parser::VerifyChecksum() | 
|  | 189 { | 
|  | 190   if (!mParams.find(u"checksum"_str)) | 
|  | 191     return true; | 
|  | 192 | 
|  | 193   if (mB64Checksum.is_invalid()) | 
|  | 194   { | 
|  | 195     uint8_t checksum[MD5::CHECKSUM_LENGTH]; | 
|  | 196     mChecksum.Final(checksum); | 
|  | 197     mB64Checksum = ToBase64(checksum, MD5::CHECKSUM_LENGTH); | 
|  | 198   } | 
|  | 199   return (mParams[u"checksum"_str] == mB64Checksum); | 
|  | 200 } | 
|  | 201 | 
|  | 202 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs
     cription) | 
|  | 203 { | 
|  | 204   if (mB64Checksum.is_invalid()) | 
|  | 205     VerifyChecksum(); // here we ignore the checksum, but we calculate it. | 
|  | 206 | 
|  | 207   auto entry = mParams.find(u"title"_str); | 
|  | 208   if (entry) | 
|  | 209   { | 
|  | 210     subscription.SetTitle(entry->second); | 
|  | 211     subscription.SetFixedTitle(true); | 
|  | 212   } | 
|  | 213   else | 
|  | 214     subscription.SetFixedTitle(false); | 
|  | 215 | 
|  | 216   int32_t version = 0; | 
|  | 217   entry = mParams.find(u"version"_str); | 
|  | 218   if (entry) | 
|  | 219     version = entry->second.toInt<int32_t>(); | 
|  | 220   subscription.SetDataRevision(version); | 
|  | 221 | 
|  | 222   int64_t expires = 0; | 
|  | 223   entry = mParams.find(u"expires"_str); | 
|  | 224   if (entry) | 
|  | 225     expires = ParseExpires(entry->second); | 
|  | 226 | 
|  | 227   FilterNotifier::SubscriptionChange( | 
|  | 228     FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, | 
|  | 229     subscription); | 
|  | 230 | 
|  | 231   Subscription::Filters filters; | 
|  | 232   filters.reserve(mFiltersText.size()); | 
|  | 233   for (auto text : mFiltersText) | 
|  | 234   { | 
|  | 235     DependentString dependent(text); | 
|  | 236     filters.emplace_back(Filter::FromText(dependent), false); | 
|  | 237   } | 
|  | 238 | 
|  | 239   subscription.SetFilters(std::move(filters)); | 
|  | 240   FilterNotifier::SubscriptionChange( | 
|  | 241     FilterNotifier::Topic::SUBSCRIPTION_FILTERS_REPLACED, subscription); | 
|  | 242 | 
|  | 243   return expires; | 
|  | 244 } | 
|  | 245 | 
|  | 246 namespace { | 
|  | 247   DependentString emptyString = u""_str; | 
|  | 248 } | 
|  | 249 | 
|  | 250 const String& DownloadableSubscription_Parser::GetRedirect() const | 
|  | 251 { | 
|  | 252   auto entry = mParams.find(u"redirect"_str); | 
|  | 253   if (entry) | 
|  | 254     return entry->second; | 
|  | 255   return emptyString; | 
|  | 256 } | 
|  | 257 | 
|  | 258 const String& DownloadableSubscription_Parser::GetHomepage() const | 
|  | 259 { | 
|  | 260   auto entry = mParams.find(u"homepage"_str); | 
|  | 261   if (entry) | 
|  | 262     return entry->second; | 
|  | 263   return emptyString; | 
|  | 264 } | 
| 19 | 265 | 
| 20 DownloadableSubscription::DownloadableSubscription(const String& id) | 266 DownloadableSubscription::DownloadableSubscription(const String& id) | 
| 21     : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), | 267     : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), | 
| 22       mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), | 268       mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), | 
| 23       mErrorCount(0), mDataRevision(0), mDownloadCount(0) | 269       mErrorCount(0), mDataRevision(0), mDownloadCount(0) | 
| 24 { | 270 { | 
| 25   SetTitle(id); | 271   SetTitle(id); | 
| 26 } | 272 } | 
| 27 | 273 | 
|  | 274 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload() | 
|  | 275 { | 
|  | 276   return new DownloadableSubscription_Parser(); | 
|  | 277 } | 
|  | 278 | 
| 28 OwnedString DownloadableSubscription::Serialize() const | 279 OwnedString DownloadableSubscription::Serialize() const | 
| 29 { | 280 { | 
| 30   OwnedString result(Subscription::Serialize()); | 281   OwnedString result(Subscription::Serialize()); | 
| 31   if (mFixedTitle) | 282   if (mFixedTitle) | 
| 32     result.append(u"fixedTitle=true\n"_str); | 283     result.append(u"fixedTitle=true\n"_str); | 
| 33   if (!mHomepage.empty()) | 284   if (!mHomepage.empty()) | 
| 34   { | 285   { | 
| 35     result.append(u"homepage="_str); | 286     result.append(u"homepage="_str); | 
| 36     result.append(mHomepage); | 287     result.append(mHomepage); | 
| 37     result.append(u'\n'); | 288     result.append(u'\n'); | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 91     result.append(u'\n'); | 342     result.append(u'\n'); | 
| 92   } | 343   } | 
| 93   if (mDownloadCount) | 344   if (mDownloadCount) | 
| 94   { | 345   { | 
| 95     result.append(u"downloadCount="_str); | 346     result.append(u"downloadCount="_str); | 
| 96     result.append(mDownloadCount); | 347     result.append(mDownloadCount); | 
| 97     result.append(u'\n'); | 348     result.append(u'\n'); | 
| 98   } | 349   } | 
| 99   return result; | 350   return result; | 
| 100 } | 351 } | 
| OLD | NEW | 
|---|