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