Index: compiled/subscription/DownloadableSubscription.cpp |
=================================================================== |
--- a/compiled/subscription/DownloadableSubscription.cpp |
+++ b/compiled/subscription/DownloadableSubscription.cpp |
@@ -81,59 +81,133 @@ |
text, beginValue, text.length() - beginValue); |
} |
} |
return param; |
} |
} |
DownloadableSubscription_Parser::DownloadableSubscription_Parser() |
- : mFirstLine(true) |
{ |
annotate_address(this, "DownloadableSubscription_Parser"); |
} |
namespace { |
const DependentString ADBLOCK_HEADER(u"[Adblock"_str); |
+ const DependentString ADBLOCK_PLUS_EXTRA_HEADER(u"Plus"_str); |
+ |
+ const DependentString ERROR_INVALID_DATA(u"synchronize_invalid_data"_str); |
// Only check for trailing base64 padding. There should be at most 2 '='. |
// In that case return a truncated string. |
DependentString CleanUpChecksum(const String& checksum) |
{ |
const auto len = checksum.length(); |
if ((len > 22 && len <= 24) && |
(checksum[22] == u'=' && (len == 23 || checksum[23] == u'='))) |
return DependentString(checksum, 0, 22); |
return DependentString(checksum); |
} |
} |
-void DownloadableSubscription_Parser::Process(const String& line) |
+/// Return true if more line expected. |
+bool DownloadableSubscription_Parser::GetNextLine(DependentString& buffer, DependentString& line) |
{ |
- bool isHeader = false; |
- isHeader = line.find(ADBLOCK_HEADER) != String::npos; |
- if (!isHeader) |
+ StringScanner scanner(buffer); |
+ String::value_type ch = 0; |
+ while (ch != u'\r' && ch != u'\n') |
{ |
- auto param = ParseParam(line); |
- if (param.first.is_invalid()) |
- mFiltersText.emplace_back(line); |
- else if (param.first == u"checksum"_str) |
+ ch = scanner.next(); |
+ if (ch == 0) |
+ break; |
+ } |
+ |
+ auto eol = scanner.position(); |
+ line.reset(buffer, 0, eol); |
+ if (eol == 0 || ch == 0) |
+ return false; |
+ while (scanner.skipOne(u'\r') || scanner.skipOne(u'\n')) |
+ ; |
+ buffer.reset(buffer, scanner.position() + 1); |
+ return true; |
+} |
+ |
+bool DownloadableSubscription_Parser::Process(const String& buffer) |
+{ |
+ DependentString currentBuffer(buffer); |
+ bool firstLine = true; |
+ |
+ DependentString line; |
+ while (true) |
+ { |
+ bool more = GetNextLine(currentBuffer, line); |
+ if (firstLine) |
{ |
- mParams[param.first] = CleanUpChecksum(param.second); |
- return; |
+ if (!ProcessFirstLine(line)) |
+ { |
+ mError = ERROR_INVALID_DATA; |
+ return false; |
+ } |
+ firstLine = false; |
} |
else |
- mParams[param.first] = param.second; |
+ ProcessLine(line); |
+ if (!more) |
+ break; |
} |
+ return true; |
+} |
+ |
+bool DownloadableSubscription_Parser::ProcessFirstLine(const String& line) |
+{ |
+ auto index = line.find(ADBLOCK_HEADER); |
+ if (index == String::npos) |
+ return false; |
+ |
+ DependentString minVersion; |
+ DependentString current(line, index + ADBLOCK_HEADER.length()); |
+ StringScanner scanner(current); |
+ if (scanner.skipWhiteSpace() && scanner.skipString(ADBLOCK_PLUS_EXTRA_HEADER)) |
+ scanner.skipWhiteSpace(); |
+ index = scanner.position() + 1; |
+ String::value_type ch = u'\0'; |
+ while((ch = scanner.next()) && (ch == u'.' || std::iswdigit(ch))) |
+ ; |
+ if (ch) |
+ scanner.back(); |
+ if (scanner.position() + 1 > index) |
+ minVersion.reset(current, index, scanner.position() + 1 - index); |
+ |
+ if (ch != u']') |
+ return false; |
+ |
+ mRequiredVersion = minVersion; |
+ mChecksum.Update(line); |
+ return true; |
+} |
+ |
+void DownloadableSubscription_Parser::ProcessLine(const String& line) |
+{ |
+ auto param = ParseParam(line); |
+ if (param.first.is_invalid()) |
+ { |
+ if (!line.empty()) |
+ mFiltersText.emplace_back(line); |
+ } |
+ else if (param.first == u"checksum"_str) |
+ { |
+ mParams[param.first] = CleanUpChecksum(param.second); |
+ return; |
+ } |
+ else |
+ mParams[param.first] = param.second; |
+ |
// Checksum is an MD5 checksum (base64-encoded without the trailing "=") of |
// all lines in UTF-8 without the checksum line, joined with "\n". |
- if (!mFirstLine) |
- mChecksum.Update((const uint8_t*)"\n", 1); |
- else |
- mFirstLine = false; |
+ mChecksum.Update((const uint8_t*)"\n", 1); |
mChecksum.Update(line); |
} |
int64_t DownloadableSubscription_Parser::ParseExpires(const String& expires) |
{ |
bool isHour = false; |
StringScanner scanner(expires); |
String::size_type numStart = 0; |
@@ -199,16 +273,23 @@ |
return (mParams[u"checksum"_str] == mB64Checksum); |
} |
int64_t DownloadableSubscription_Parser::Finalize(DownloadableSubscription& subscription) |
{ |
if (mB64Checksum.is_invalid()) |
VerifyChecksum(); // here we ignore the checksum, but we calculate it. |
+ FilterNotifier::SubscriptionChange( |
+ FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, |
+ subscription); |
+ |
+ if (!mRequiredVersion.empty()) |
+ subscription.SetRequiredVersion(mRequiredVersion); |
+ |
auto entry = mParams.find(u"title"_str); |
if (entry) |
{ |
subscription.SetTitle(entry->second); |
subscription.SetFixedTitle(true); |
} |
else |
subscription.SetFixedTitle(false); |
@@ -219,20 +300,16 @@ |
version = entry->second.toInt<int32_t>(); |
subscription.SetDataRevision(version); |
int64_t expires = 0; |
entry = mParams.find(u"expires"_str); |
if (entry) |
expires = ParseExpires(entry->second); |
- FilterNotifier::SubscriptionChange( |
- FilterNotifier::Topic::SUBSCRIPTION_BEFORE_FILTERS_REPLACED, |
- subscription); |
- |
Subscription::Filters filters; |
filters.reserve(mFiltersText.size()); |
for (auto text : mFiltersText) |
{ |
DependentString dependent(text); |
filters.emplace_back(Filter::FromText(dependent), false); |
} |