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

Side by Side Diff: compiled/subscription/DownloadableSubscription.cpp

Issue 29606600: Issue 5146 - Implement DownloadableSubscription parsing in C++ (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Simplify the logic of Process() Created Dec. 4, 2017, 10:04 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « compiled/subscription/DownloadableSubscription.h ('k') | compiled/subscription/Subscription.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 bool doChecksum = true;
113 isHeader = line.find(ADBLOCK_HEADER) != String::npos;
114 if (!isHeader)
115 {
116 auto param = ParseParam(line);
117 if (!param.first.is_invalid())
118 {
119 if (param.first == u"checksum"_str)
120 {
121 mParams[param.first] = CleanUpChecksum(param.second);
122 doChecksum = false;
123 }
124 else
125 mParams[param.first] = param.second;
126 }
127 if (param.first.is_invalid())
128 mFiltersText.emplace_back(line);
129 }
130 // Checksum is an MD5 checksum (base64-encoded without the trailing "=") of
131 // all lines in UTF-8 without the checksum line, joined with "\n".
132 if (doChecksum)
133 {
134 if (!mFirstLine)
135 mChecksum.Update((const uint8_t*)"\n", 1);
136 else
137 mFirstLine = false;
138 mChecksum.Update(line);
139 }
140 }
141
142 int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires)
143 {
144 bool isHour = false;
145 StringScanner scanner(expires);
146 String::size_type numStart = 0;
147 String::size_type numLen = 0;
148 while(!scanner.done())
149 {
150 auto ch = scanner.next();
151 if (std::iswdigit(ch))
152 {
153 if (numLen == 0)
154 numStart = scanner.position();
155 numLen++;
156 }
157 else if (std::iswspace(ch))
158 {
159 if (numLen)
160 break;
161 }
162 else
163 {
164 if (numLen)
165 scanner.back();
166 break;
167 }
168 }
169
170 DependentString numStr(expires, numStart, numLen);
171 int64_t num = numStr.toInt<int64_t>();
172 if (num == 0)
173 return 0;
174
175 while (!scanner.done())
176 {
177 auto ch = scanner.next();
178 if (std::iswspace(ch))
179 continue;
180
181 if (ch == u'h')
182 isHour = true;
183
184 // assume we are done here. The rest is ignored.
185 break;
186 }
187 // check for overflow.
188 if ((isHour && (num > MAX_HOUR)) || (num > MAX_DAY))
189 return 0;
190
191 num *= isHour ? MILLIS_IN_HOUR : MILLIS_IN_DAY;
192 return num;
193 }
194
195 bool DownloadableSubscription_Parser::VerifyChecksum()
196 {
197 if (!mParams.find(u"checksum"_str))
198 return true;
199
200 if (mB64Checksum.is_invalid())
201 {
202 uint8_t checksum[MD5::CHECKSUM_LENGTH];
203 mChecksum.Final(checksum);
204 mB64Checksum = ToBase64(checksum, MD5::CHECKSUM_LENGTH);
205 }
206 return (mParams[u"checksum"_str] == mB64Checksum);
207 }
208
209 int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subs cription)
210 {
211 if (mB64Checksum.is_invalid())
212 VerifyChecksum(); // here we ignore the checksum, but we calculate it.
213
214 auto entry = mParams.find(u"title"_str);
215 if (entry)
216 {
217 subscription.SetTitle(entry->second);
218 subscription.SetFixedTitle(true);
219 }
220 else
221 subscription.SetFixedTitle(false);
222
223 int32_t version = 0;
224 entry = mParams.find(u"version"_str);
225 if (entry)
226 version = entry->second.toInt<int32_t>();
227 subscription.SetDataRevision(version);
228
229 int64_t expires = 0;
230 entry = mParams.find(u"expires"_str);
231 if (entry)
232 expires = ParseExpires(entry->second);
233
234 FilterNotifier::SubscriptionChange(
235 FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED,
236 subscription);
237
238 Subscription::Filters filters;
239 filters.reserve(mFiltersText.size());
240 for (auto text : mFiltersText)
241 {
242 DependentString dependent(text);
243 filters.emplace_back(Filter::FromText(dependent), false);
244 }
245
246 subscription.SetFilters(std::move(filters));
247 FilterNotifier::SubscriptionChange(
248 FilterNotifier::Topic::SUBSCRIPTION_FILTERS_REPLACED, subscription);
249
250 return expires;
251 }
252
253 namespace {
254 DependentString emptyString = u""_str;
255 }
256
257 const String& DownloadableSubscription_Parser::GetRedirect() const
258 {
259 auto entry = mParams.find(u"redirect"_str);
260 if (entry)
261 return entry->second;
262 return emptyString;
263 }
264
265 const String& DownloadableSubscription_Parser::GetHomepage() const
266 {
267 auto entry = mParams.find(u"homepage"_str);
268 if (entry)
269 return entry->second;
270 return emptyString;
271 }
19 272
20 DownloadableSubscription::DownloadableSubscription(const String& id) 273 DownloadableSubscription::DownloadableSubscription(const String& id)
21 : Subscription(classType, id), mFixedTitle(false), mLastCheck(0), 274 : Subscription(classType, id), mFixedTitle(false), mLastCheck(0),
22 mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0), 275 mHardExpiration(0), mSoftExpiration(0), mLastDownload(0), mLastSuccess(0),
23 mErrorCount(0), mDataRevision(0), mDownloadCount(0) 276 mErrorCount(0), mDataRevision(0), mDownloadCount(0)
24 { 277 {
25 SetTitle(id); 278 SetTitle(id);
26 } 279 }
27 280
281 DownloadableSubscription_Parser* DownloadableSubscription::ParseDownload()
282 {
283 return new DownloadableSubscription_Parser();
284 }
285
28 OwnedString DownloadableSubscription::Serialize() const 286 OwnedString DownloadableSubscription::Serialize() const
29 { 287 {
30 OwnedString result(Subscription::Serialize()); 288 OwnedString result(Subscription::Serialize());
31 if (mFixedTitle) 289 if (mFixedTitle)
32 result.append(u"fixedTitle=true\n"_str); 290 result.append(u"fixedTitle=true\n"_str);
33 if (!mHomepage.empty()) 291 if (!mHomepage.empty())
34 { 292 {
35 result.append(u"homepage="_str); 293 result.append(u"homepage="_str);
36 result.append(mHomepage); 294 result.append(mHomepage);
37 result.append(u'\n'); 295 result.append(u'\n');
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 result.append(u'\n'); 349 result.append(u'\n');
92 } 350 }
93 if (mDownloadCount) 351 if (mDownloadCount)
94 { 352 {
95 result.append(u"downloadCount="_str); 353 result.append(u"downloadCount="_str);
96 result.append(mDownloadCount); 354 result.append(mDownloadCount);
97 result.append(u'\n'); 355 result.append(u'\n');
98 } 356 }
99 return result; 357 return result;
100 } 358 }
OLDNEW
« no previous file with comments | « compiled/subscription/DownloadableSubscription.h ('k') | compiled/subscription/Subscription.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld