1 /***************************************************************************
2 * Copyright (C) 2005-2020 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 "messagefilter.h"
22
23 #include <algorithm>
24
25 #include "buffermodel.h"
26 #include "buffersettings.h"
27 #include "client.h"
28 #include "clientignorelistmanager.h"
29 #include "messagemodel.h"
30 #include "networkmodel.h"
31 #include "util.h"
32
MessageFilter(QAbstractItemModel * source,QObject * parent)33 MessageFilter::MessageFilter(QAbstractItemModel* source, QObject* parent)
34 : QSortFilterProxyModel(parent)
35 , _messageTypeFilter(0)
36 {
37 init();
38 setSourceModel(source);
39 }
40
MessageFilter(MessageModel * source,const QList<BufferId> & buffers,QObject * parent)41 MessageFilter::MessageFilter(MessageModel* source, const QList<BufferId>& buffers, QObject* parent)
42 : QSortFilterProxyModel(parent)
43 , _validBuffers(toQSet(buffers))
44 , _messageTypeFilter(0)
45 {
46 init();
47 setSourceModel(source);
48 }
49
init()50 void MessageFilter::init()
51 {
52 setDynamicSortFilter(true);
53
54 _userNoticesTarget = _serverNoticesTarget = _errorMsgsTarget = -1;
55
56 BufferSettings defaultSettings;
57 defaultSettings.notify("UserNoticesTarget", this, &MessageFilter::messageRedirectionChanged);
58 defaultSettings.notify("ServerNoticesTarget", this, &MessageFilter::messageRedirectionChanged);
59 defaultSettings.notify("ErrorMsgsTarget", this, &MessageFilter::messageRedirectionChanged);
60 messageRedirectionChanged();
61
62 _messageTypeFilter = defaultSettings.messageFilter();
63 defaultSettings.notify("MessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
64
65 BufferSettings mySettings(MessageFilter::idString());
66 if (mySettings.hasFilter())
67 _messageTypeFilter = mySettings.messageFilter();
68 mySettings.notify("MessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
69 mySettings.notify("hasMessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
70 }
71
messageTypeFilterChanged()72 void MessageFilter::messageTypeFilterChanged()
73 {
74 int newFilter;
75 BufferSettings defaultSettings;
76 newFilter = BufferSettings().messageFilter();
77
78 BufferSettings mySettings(idString());
79 if (mySettings.hasFilter())
80 newFilter = mySettings.messageFilter();
81
82 if (_messageTypeFilter != newFilter) {
83 _messageTypeFilter = newFilter;
84 _filteredQuitMsgTime.clear();
85 invalidateFilter();
86 }
87 }
88
messageRedirectionChanged()89 void MessageFilter::messageRedirectionChanged()
90 {
91 BufferSettings bufferSettings;
92 bool changed = false;
93
94 if (_userNoticesTarget != bufferSettings.userNoticesTarget()) {
95 _userNoticesTarget = bufferSettings.userNoticesTarget();
96 changed = true;
97 }
98
99 if (_serverNoticesTarget != bufferSettings.serverNoticesTarget()) {
100 _serverNoticesTarget = bufferSettings.serverNoticesTarget();
101 changed = true;
102 }
103
104 if (_errorMsgsTarget != bufferSettings.errorMsgsTarget()) {
105 _errorMsgsTarget = bufferSettings.errorMsgsTarget();
106 changed = true;
107 }
108
109 if (changed)
110 invalidateFilter();
111 }
112
idString() const113 QString MessageFilter::idString() const
114 {
115 if (_validBuffers.isEmpty())
116 return "*";
117
118 QList<BufferId> bufferIds = _validBuffers.values();
119 std::sort(bufferIds.begin(), bufferIds.end());
120
121 QStringList bufferIdStrings;
122 foreach (BufferId id, bufferIds)
123 bufferIdStrings << QString::number(id.toInt());
124
125 return bufferIdStrings.join("|");
126 }
127
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const128 bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
129 {
130 Q_UNUSED(sourceParent);
131 QModelIndex sourceIdx = sourceModel()->index(sourceRow, 2);
132 Message::Type messageType = (Message::Type)sourceIdx.data(MessageModel::TypeRole).toInt();
133
134 // apply message type filter
135 if (_messageTypeFilter & messageType)
136 return false;
137
138 if (_validBuffers.isEmpty())
139 return true;
140
141 BufferId bufferId = sourceIdx.data(MessageModel::BufferIdRole).value<BufferId>();
142 if (!bufferId.isValid()) {
143 return true;
144 }
145
146 // MsgId msgId = sourceIdx.data(MessageModel::MsgIdRole).value<MsgId>();
147 Message::Flags flags = (Message::Flags)sourceIdx.data(MessageModel::FlagsRole).toInt();
148
149 NetworkId myNetworkId = networkId();
150 NetworkId msgNetworkId = Client::networkModel()->networkId(bufferId);
151 if (myNetworkId != msgNetworkId)
152 return false;
153
154 // ignorelist handling
155 // only match if message is not flagged as server msg
156 if (!(flags & Message::ServerMsg) && Client::ignoreListManager()
157 && Client::ignoreListManager()->match(sourceIdx.data(MessageModel::MessageRole).value<Message>(),
158 Client::networkModel()->networkName(bufferId)))
159 return false;
160
161 if (flags & Message::Redirected) {
162 int redirectionTarget = 0;
163 switch (messageType) {
164 case Message::Notice:
165 if (Client::networkModel()->bufferType(bufferId) != BufferInfo::ChannelBuffer) {
166 if (flags & Message::ServerMsg) {
167 // server notice
168 redirectionTarget = _serverNoticesTarget;
169 }
170 else {
171 redirectionTarget = _userNoticesTarget;
172 }
173 }
174 break;
175 case Message::Error:
176 redirectionTarget = _errorMsgsTarget;
177 break;
178 default:
179 break;
180 }
181
182 if (redirectionTarget & BufferSettings::DefaultBuffer && _validBuffers.contains(bufferId))
183 return true;
184
185 if (redirectionTarget & BufferSettings::CurrentBuffer && !(flags & Message::Backlog)) {
186 BufferId redirectedTo = sourceModel()->data(sourceIdx, MessageModel::RedirectedToRole).value<BufferId>();
187 if (!redirectedTo.isValid()) {
188 redirectedTo = Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
189 if (redirectedTo.isValid())
190 sourceModel()->setData(sourceIdx, QVariant::fromValue(redirectedTo), MessageModel::RedirectedToRole);
191 }
192
193 if (_validBuffers.contains(redirectedTo))
194 return true;
195 }
196
197 if (redirectionTarget & BufferSettings::StatusBuffer) {
198 QSet<BufferId>::const_iterator idIter = _validBuffers.constBegin();
199 while (idIter != _validBuffers.constEnd()) {
200 if (Client::networkModel()->bufferType(*idIter) == BufferInfo::StatusBuffer)
201 return true;
202 ++idIter;
203 }
204 }
205
206 return false;
207 }
208
209 if (_validBuffers.contains(bufferId)) {
210 return true;
211 }
212 else {
213 // show Quit messages in Query buffers:
214 if (bufferType() != BufferInfo::QueryBuffer)
215 return false;
216 if (!(messageType & Message::Quit))
217 return false;
218
219 if (myNetworkId != msgNetworkId)
220 return false;
221
222 // Extract timestamp and nickname from the new quit message
223 qint64 messageTimestamp = sourceModel()->data(sourceIdx, MessageModel::TimestampRole).value<QDateTime>().toMSecsSinceEpoch();
224 QString quiter = nickFromMask(sourceModel()->data(sourceIdx, MessageModel::MessageRole).value<Message>().sender()).toLower();
225
226 // Check that nickname matches query name
227 if (quiter != bufferName().toLower())
228 return false;
229
230 // Check if a quit message was already forwarded within +/- 1000 ms
231 static constexpr qint64 MAX_QUIT_DELTA_MS = 1 * 1000;
232 // No need to check if it's the appropriate buffer, each query has a unique message filter
233 if (std::binary_search(_filteredQuitMsgTime.begin(), _filteredQuitMsgTime.end(), messageTimestamp, [](qint64 a, qint64 b) {
234 return ((a + MAX_QUIT_DELTA_MS) < b);
235 })) {
236 // New element is less than if at least 1000 ms older/newer
237 // Match found, no need to forward another quit message
238 return false;
239 }
240
241 // Mark query as having a quit message inserted
242 auto* that = const_cast<MessageFilter*>(this);
243 that->_filteredQuitMsgTime.insert(messageTimestamp);
244 return true;
245 }
246 }
247
requestBacklog()248 void MessageFilter::requestBacklog()
249 {
250 QSet<BufferId>::const_iterator bufferIdIter = _validBuffers.constBegin();
251 while (bufferIdIter != _validBuffers.constEnd()) {
252 Client::messageModel()->requestBacklog(*bufferIdIter);
253 ++bufferIdIter;
254 }
255 }
256