Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: compiled/subscription/DownloadableSubscription.cpp

Issue 29630576: Issue 5146 - Part 2: Process http response in C++ (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Created Dec. 5, 2017, 9:19 a.m.
Right Patch Set: Rebased Created Aug. 14, 2018, 12:45 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « compiled/subscription/DownloadableSubscription.h ('k') | lib/synchronizer.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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
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
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
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 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld