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

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

Issue 29606600: Issue 5146 - Implement DownloadableSubscription parsing in C++ (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Added md5 checksum. Reorganized some code. Created Nov. 28, 2017, 10:46 p.m.
Right Patch Set: Removed Md5sum and associated code Created Aug. 14, 2018, 12:38 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') | compiled/subscription/Subscription.h » ('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 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 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld