qa: Avoid deprecation warnings for QList/QSet conversions
[quassel.git] / src / common / highlightrulemanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "highlightrulemanager.h"
22
23 #include <QDebug>
24
25 #include "expressionmatch.h"
26 #include "util.h"
27
28 HighlightRuleManager& HighlightRuleManager::operator=(const HighlightRuleManager& other)
29 {
30     if (this == &other)
31         return *this;
32
33     SyncableObject::operator=(other);
34     _highlightRuleList = other._highlightRuleList;
35     _nicksCaseSensitive = other._nicksCaseSensitive;
36     _highlightNick = other._highlightNick;
37     return *this;
38 }
39
40 int HighlightRuleManager::indexOf(int id) const
41 {
42     for (int i = 0; i < _highlightRuleList.count(); i++) {
43         if (_highlightRuleList[i].id() == id)
44             return i;
45     }
46     return -1;
47 }
48
49 int HighlightRuleManager::nextId()
50 {
51     int max = 0;
52     for (int i = 0; i < _highlightRuleList.count(); i++) {
53         int id = _highlightRuleList[i].id();
54         if (id > max) {
55             max = id;
56         }
57     }
58     return max + 1;
59 }
60
61 QVariantMap HighlightRuleManager::initHighlightRuleList() const
62 {
63     QVariantList id;
64     QVariantMap highlightRuleListMap;
65     QStringList name;
66     QVariantList isRegEx;
67     QVariantList isCaseSensitive;
68     QVariantList isActive;
69     QVariantList isInverse;
70     QStringList sender;
71     QStringList channel;
72
73     for (int i = 0; i < _highlightRuleList.count(); i++) {
74         id << _highlightRuleList[i].id();
75         name << _highlightRuleList[i].contents();
76         isRegEx << _highlightRuleList[i].isRegEx();
77         isCaseSensitive << _highlightRuleList[i].isCaseSensitive();
78         isActive << _highlightRuleList[i].isEnabled();
79         isInverse << _highlightRuleList[i].isInverse();
80         sender << _highlightRuleList[i].sender();
81         channel << _highlightRuleList[i].chanName();
82     }
83
84     highlightRuleListMap["id"] = id;
85     highlightRuleListMap["name"] = name;
86     highlightRuleListMap["isRegEx"] = isRegEx;
87     highlightRuleListMap["isCaseSensitive"] = isCaseSensitive;
88     highlightRuleListMap["isEnabled"] = isActive;
89     highlightRuleListMap["isInverse"] = isInverse;
90     highlightRuleListMap["sender"] = sender;
91     highlightRuleListMap["channel"] = channel;
92     return highlightRuleListMap;
93 }
94
95 void HighlightRuleManager::initSetHighlightRuleList(const QVariantMap& highlightRuleList)
96 {
97     QVariantList id = highlightRuleList["id"].toList();
98     QStringList name = highlightRuleList["name"].toStringList();
99     QVariantList isRegEx = highlightRuleList["isRegEx"].toList();
100     QVariantList isCaseSensitive = highlightRuleList["isCaseSensitive"].toList();
101     QVariantList isActive = highlightRuleList["isEnabled"].toList();
102     QVariantList isInverse = highlightRuleList["isInverse"].toList();
103     QStringList sender = highlightRuleList["sender"].toStringList();
104     QStringList channel = highlightRuleList["channel"].toStringList();
105
106     int count = id.count();
107     if (count != name.count() || count != isRegEx.count() || count != isCaseSensitive.count() || count != isActive.count()
108         || count != isInverse.count() || count != sender.count() || count != channel.count()) {
109         qWarning() << "Corrupted HighlightRuleList settings! (Count mismatch)";
110         return;
111     }
112
113     _highlightRuleList.clear();
114     for (int i = 0; i < name.count(); i++) {
115         _highlightRuleList << HighlightRule(id[i].toInt(),
116                                             name[i],
117                                             isRegEx[i].toBool(),
118                                             isCaseSensitive[i].toBool(),
119                                             isActive[i].toBool(),
120                                             isInverse[i].toBool(),
121                                             sender[i],
122                                             channel[i]);
123     }
124 }
125
126 void HighlightRuleManager::addHighlightRule(
127     int id, const QString& name, bool isRegEx, bool isCaseSensitive, bool isActive, bool isInverse, const QString& sender, const QString& channel)
128 {
129     if (contains(id)) {
130         return;
131     }
132
133     HighlightRule newItem = HighlightRule(id, name, isRegEx, isCaseSensitive, isActive, isInverse, sender, channel);
134     _highlightRuleList << newItem;
135
136     SYNC(ARG(id), ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isActive), ARG(isInverse), ARG(sender), ARG(channel))
137 }
138
139 bool HighlightRuleManager::match(const NetworkId& netId,
140                                  const QString& msgContents,
141                                  const QString& msgSender,
142                                  Message::Type msgType,
143                                  Message::Flags msgFlags,
144                                  const QString& bufferName,
145                                  const QString& currentNick,
146                                  const QStringList& identityNicks)
147 {
148     if (!((msgType & (Message::Plain | Message::Notice | Message::Action)) && !(msgFlags & Message::Self))) {
149         return false;
150     }
151
152     bool matches = false;
153
154     for (int i = 0; i < _highlightRuleList.count(); i++) {
155         auto& rule = _highlightRuleList.at(i);
156         if (!rule.isEnabled())
157             continue;
158
159         // Skip if channel name doesn't match and channel rule is not empty
160         //
161         // Match succeeds if...
162         //   Channel name matches a defined rule
163         //   Defined rule is empty
164         // And take the inverse of the above
165         if (!rule.chanNameMatcher().match(bufferName, true)) {
166             // A channel name rule is specified and does NOT match the current buffer name, skip
167             // this rule
168             continue;
169         }
170
171         // Check message according to specified rule, allowing empty rules to match
172         bool contentsMatch = rule.contentsMatcher().match(stripFormatCodes(msgContents), true);
173
174         // Check sender according to specified rule, allowing empty rules to match
175         bool senderMatch = rule.senderMatcher().match(msgSender, true);
176
177         if (contentsMatch && senderMatch) {
178             // If an inverse rule matches, then we know that we never want to return a highlight.
179             if (rule.isInverse()) {
180                 return false;
181             }
182             else {
183                 matches = true;
184             }
185         }
186     }
187
188     if (matches)
189         return true;
190
191     // Check nicknames
192     if (_highlightNick != HighlightNickType::NoNick && !currentNick.isEmpty()) {
193         // Nickname matching allowed and current nickname is known
194         // Run the nickname matcher on the unformatted string
195         if (_nickMatcher.match(stripFormatCodes(msgContents), netId, currentNick, identityNicks)) {
196             return true;
197         }
198     }
199
200     return false;
201 }
202
203 void HighlightRuleManager::removeHighlightRule(int highlightRule)
204 {
205     removeAt(indexOf(highlightRule));
206     SYNC(ARG(highlightRule))
207 }
208
209 void HighlightRuleManager::toggleHighlightRule(int highlightRule)
210 {
211     int idx = indexOf(highlightRule);
212     if (idx == -1)
213         return;
214     _highlightRuleList[idx].setIsEnabled(!_highlightRuleList[idx].isEnabled());
215     SYNC(ARG(highlightRule))
216 }
217
218 bool HighlightRuleManager::match(const Message& msg, const QString& currentNick, const QStringList& identityNicks)
219 {
220     return match(msg.bufferInfo().networkId(),
221                  msg.contents(),
222                  msg.sender(),
223                  msg.type(),
224                  msg.flags(),
225                  msg.bufferInfo().bufferName(),
226                  currentNick,
227                  identityNicks);
228 }
229
230 /**************************************************************************
231  * HighlightRule
232  *************************************************************************/
233 bool HighlightRuleManager::HighlightRule::operator!=(const HighlightRule& other) const
234 {
235     return (_id != other._id || _contents != other._contents || _isRegEx != other._isRegEx || _isCaseSensitive != other._isCaseSensitive
236             || _isEnabled != other._isEnabled || _isInverse != other._isInverse || _sender != other._sender || _chanName != other._chanName);
237     // Don't compare ExpressionMatch objects as they are created as needed from the above
238 }
239
240 void HighlightRuleManager::HighlightRule::determineExpressions() const
241 {
242     // Don't update if not needed
243     if (!_cacheInvalid) {
244         return;
245     }
246
247     // Set up matching rules
248     // Message is either phrase or regex
249     ExpressionMatch::MatchMode contentsMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchPhrase;
250     // Sender and channel are either multiple wildcard entries or regex
251     ExpressionMatch::MatchMode scopeMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchMultiWildcard;
252
253     _contentsMatch = ExpressionMatch(_contents, contentsMode, _isCaseSensitive);
254     _senderMatch = ExpressionMatch(_sender, scopeMode, _isCaseSensitive);
255     _chanNameMatch = ExpressionMatch(_chanName, scopeMode, _isCaseSensitive);
256
257     _cacheInvalid = false;
258 }