1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "messagefilter.h"
25 #include "buffermodel.h"
26 #include "buffersettings.h"
28 #include "clientignorelistmanager.h"
29 #include "messagemodel.h"
30 #include "networkmodel.h"
33 MessageFilter::MessageFilter(QAbstractItemModel* source, QObject* parent)
34 : QSortFilterProxyModel(parent)
35 , _messageTypeFilter(0)
38 setSourceModel(source);
41 MessageFilter::MessageFilter(MessageModel* source, const QList<BufferId>& buffers, QObject* parent)
42 : QSortFilterProxyModel(parent)
43 , _validBuffers(toQSet(buffers))
44 , _messageTypeFilter(0)
47 setSourceModel(source);
50 void MessageFilter::init()
52 setDynamicSortFilter(true);
54 _userNoticesTarget = _serverNoticesTarget = _errorMsgsTarget = -1;
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();
62 _messageTypeFilter = defaultSettings.messageFilter();
63 defaultSettings.notify("MessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
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);
72 void MessageFilter::messageTypeFilterChanged()
75 BufferSettings defaultSettings;
76 newFilter = BufferSettings().messageFilter();
78 BufferSettings mySettings(idString());
79 if (mySettings.hasFilter())
80 newFilter = mySettings.messageFilter();
82 if (_messageTypeFilter != newFilter) {
83 _messageTypeFilter = newFilter;
84 _filteredQuitMsgTime.clear();
89 void MessageFilter::messageRedirectionChanged()
91 BufferSettings bufferSettings;
94 if (_userNoticesTarget != bufferSettings.userNoticesTarget()) {
95 _userNoticesTarget = bufferSettings.userNoticesTarget();
99 if (_serverNoticesTarget != bufferSettings.serverNoticesTarget()) {
100 _serverNoticesTarget = bufferSettings.serverNoticesTarget();
104 if (_errorMsgsTarget != bufferSettings.errorMsgsTarget()) {
105 _errorMsgsTarget = bufferSettings.errorMsgsTarget();
113 QString MessageFilter::idString() const
115 if (_validBuffers.isEmpty())
118 QList<BufferId> bufferIds = _validBuffers.values();
119 std::sort(bufferIds.begin(), bufferIds.end());
121 QStringList bufferIdStrings;
122 foreach (BufferId id, bufferIds)
123 bufferIdStrings << QString::number(id.toInt());
125 return bufferIdStrings.join("|");
128 bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
130 Q_UNUSED(sourceParent);
131 QModelIndex sourceIdx = sourceModel()->index(sourceRow, 2);
132 Message::Type messageType = (Message::Type)sourceIdx.data(MessageModel::TypeRole).toInt();
134 // apply message type filter
135 if (_messageTypeFilter & messageType)
138 if (_validBuffers.isEmpty())
141 BufferId bufferId = sourceIdx.data(MessageModel::BufferIdRole).value<BufferId>();
142 if (!bufferId.isValid()) {
146 // MsgId msgId = sourceIdx.data(MessageModel::MsgIdRole).value<MsgId>();
147 Message::Flags flags = (Message::Flags)sourceIdx.data(MessageModel::FlagsRole).toInt();
149 NetworkId myNetworkId = networkId();
150 NetworkId msgNetworkId = Client::networkModel()->networkId(bufferId);
151 if (myNetworkId != msgNetworkId)
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)))
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) {
168 redirectionTarget = _serverNoticesTarget;
171 redirectionTarget = _userNoticesTarget;
176 redirectionTarget = _errorMsgsTarget;
182 if (redirectionTarget & BufferSettings::DefaultBuffer && _validBuffers.contains(bufferId))
185 if (redirectionTarget & BufferSettings::CurrentBuffer && !(flags & Message::Backlog)) {
186 BufferId redirectedTo = sourceModel()->data(sourceIdx, MessageModel::RedirectedToRole).value<BufferId>();
187 if (!redirectedTo.isValid()) {
188 BufferId redirectedTo = Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
189 if (redirectedTo.isValid())
190 sourceModel()->setData(sourceIdx, QVariant::fromValue(redirectedTo), MessageModel::RedirectedToRole);
193 if (_validBuffers.contains(redirectedTo))
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)
209 if (_validBuffers.contains(bufferId)) {
213 // show Quit messages in Query buffers:
214 if (bufferType() != BufferInfo::QueryBuffer)
216 if (!(messageType & Message::Quit))
219 if (myNetworkId != msgNetworkId)
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();
226 // Check that nickname matches query name
227 if (quiter != bufferName().toLower())
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);
236 // New element is less than if at least 1000 ms older/newer
237 // Match found, no need to forward another quit message
241 // Mark query as having a quit message inserted
242 auto* that = const_cast<MessageFilter*>(this);
243 that->_filteredQuitMsgTime.insert(messageTimestamp);
248 void MessageFilter::requestBacklog()
250 QSet<BufferId>::const_iterator bufferIdIter = _validBuffers.constBegin();
251 while (bufferIdIter != _validBuffers.constEnd()) {
252 Client::messageModel()->requestBacklog(*bufferIdIter);