aboutsummaryrefslogtreecommitdiff
path: root/staging/adblock/rule.h
blob: aaab49ae693e6fc792209571898d47bc0ca53948 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#ifndef SMOLBOTE_ADBLOCK_RULE_H
#define SMOLBOTE_ADBLOCK_RULE_H

#include "options.h"
#include <QObject>
#include <QRegularExpression>
#include <QString>
#include <QStringMatcher>

namespace AdblockPlus
{
class Rule
{
public:
    virtual ~Rule() = default;
    /**
     * requestUrl: requested URL
     * initiatorUrl: URL of document that initiated navigation
     * firstPartyUrl: URL of the page that issued the request
     */
    virtual bool hasMatch(const QStringRef &requestUrl,
        const QStringRef &initiatorUrl,
        const QStringRef &firstPartyUrl,
        QWebEngineUrlRequestInfo::ResourceType resourceType = QWebEngineUrlRequestInfo::ResourceTypeMainFrame) const = 0;

    bool shouldRedirect() const
    {
        return options.redirect;
    }
    bool shouldBlock() const
    {
        return !options.exception;
    }

protected:
    Rule(const Options &opt)
        : options(opt)
    {
    }
    const Options options;
};

//  The separator character can be anything but
//  a letter, a digit, or one of the following: _, -, ., %.
//  The end of the address is also accepted as a separator.
inline bool isSeparator(const QChar &c)
{
    return !c.isLetter() && !c.isDigit() && c != '_' && c != '-' && c != '.' && c != '%';
}

class MatcherRule : public Rule
{
public:
    enum MatchPosition {
        DomainMatch,
        UrlStartsWith,
        UrlEndsWith,
        UrlContains,
        UrlEquals
    };

    MatcherRule(const QString &pattern, const Options &opt, const MatchPosition pos = UrlContains)
        : Rule(opt)
        , position(pos)
        , matcher(pattern, opt.matchcase ? Qt::CaseSensitive : Qt::CaseInsensitive)
        , patternLength(pattern.length())
    {
    }
    explicit MatcherRule(const MatcherRule &) = delete;
    MatcherRule &operator=(const MatcherRule &) = delete;

    explicit MatcherRule(MatcherRule &&) = delete;
    MatcherRule &operator=(MatcherRule &&) = delete;

    ~MatcherRule() = default;
    bool hasMatch(const QStringRef &url,
        const QStringRef &initiatorUrl,
        const QStringRef &firstPartyUrl,
        QWebEngineUrlRequestInfo::ResourceType resourceType = QWebEngineUrlRequestInfo::ResourceTypeMainFrame) const override
    {
        const auto index = matcher.indexIn(url);
        if(index == -1) {
            return false;
        }

        switch(position) {
        case DomainMatch:
            // match if
            // there is only one : left of the index
            // and
            // character after pattern is separator or end of string
            return (url.left(index).count(':') <= 1 && (index + patternLength == url.length() || isSeparator(url[index + patternLength])));
        case UrlStartsWith:
            return (index == 0);
        case UrlEndsWith:
            return (index == url.length() - patternLength);
        case UrlContains:
            return (index != -1);
        case UrlEquals:
            return (index == 0 && patternLength == url.length());
        }

        return false;
    }

private:
    const MatchPosition position;
    const QStringMatcher matcher;
    const int patternLength;
};

class RegexRule : public Rule
{
public:
    RegexRule(const QString &rule, const Options &opt)
        : Rule(opt)
        , regex(rule)
    {
        if(!opt.matchcase) {
            regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
        }
    }
    explicit RegexRule(const RegexRule &) = delete;
    RegexRule &operator=(const RegexRule &) = delete;

    explicit RegexRule(RegexRule &&) = delete;
    RegexRule &operator=(RegexRule &&) = delete;

    ~RegexRule() = default;
    bool hasMatch(const QStringRef &url,
        const QStringRef &initiatorUrl,
        const QStringRef &firstPartyUrl,
        QWebEngineUrlRequestInfo::ResourceType resourceType = QWebEngineUrlRequestInfo::ResourceTypeMainFrame) const override
    {
        const auto match = regex.match(url);
        return match.hasMatch();
    }

private:
    QRegularExpression regex;
};

} // namespace AdblockPlus

#endif // SMOLBOTE_ADBLOCK_RULE_H