1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 "buffersettings.h"
27 #include "buffermodel.h"
28 #include "messagemodel.h"
29 #include "networkmodel.h"
30 #include "clientignorelistmanager.h"
32 MessageFilter::MessageFilter(QAbstractItemModel *source, QObject *parent)
33 : QSortFilterProxyModel(parent),
37 setSourceModel(source);
41 MessageFilter::MessageFilter(MessageModel *source, const QList<BufferId> &buffers, QObject *parent)
42 : QSortFilterProxyModel(parent),
43 _validBuffers(buffers.toSet()),
47 setSourceModel(source);
51 void MessageFilter::init()
53 setDynamicSortFilter(true);
55 _userNoticesTarget = _serverNoticesTarget = _errorMsgsTarget = -1;
57 BufferSettings defaultSettings;
58 defaultSettings.notify("UserNoticesTarget", this, SLOT(messageRedirectionChanged()));
59 defaultSettings.notify("ServerNoticesTarget", this, SLOT(messageRedirectionChanged()));
60 defaultSettings.notify("ErrorMsgsTarget", this, SLOT(messageRedirectionChanged()));
61 messageRedirectionChanged();
63 _messageTypeFilter = defaultSettings.messageFilter();
64 defaultSettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
66 BufferSettings mySettings(MessageFilter::idString());
67 if (mySettings.hasFilter())
68 _messageTypeFilter = mySettings.messageFilter();
69 mySettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
70 mySettings.notify("hasMessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
74 void MessageFilter::messageTypeFilterChanged()
77 BufferSettings defaultSettings;
78 newFilter = BufferSettings().messageFilter();
80 BufferSettings mySettings(idString());
81 if (mySettings.hasFilter())
82 newFilter = mySettings.messageFilter();
84 if (_messageTypeFilter != newFilter) {
85 _messageTypeFilter = newFilter;
86 _filteredQuitMsgTime.clear();
92 void MessageFilter::messageRedirectionChanged()
94 BufferSettings bufferSettings;
97 if (_userNoticesTarget != bufferSettings.userNoticesTarget()) {
98 _userNoticesTarget = bufferSettings.userNoticesTarget();
102 if (_serverNoticesTarget != bufferSettings.serverNoticesTarget()) {
103 _serverNoticesTarget = bufferSettings.serverNoticesTarget();
107 if (_errorMsgsTarget != bufferSettings.errorMsgsTarget()) {
108 _errorMsgsTarget = bufferSettings.errorMsgsTarget();
117 QString MessageFilter::idString() const
119 if (_validBuffers.isEmpty())
122 QList<BufferId> bufferIds = _validBuffers.toList();
125 QStringList bufferIdStrings;
126 foreach(BufferId id, bufferIds)
127 bufferIdStrings << QString::number(id.toInt());
129 return bufferIdStrings.join("|");
133 bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
135 Q_UNUSED(sourceParent);
136 QModelIndex sourceIdx = sourceModel()->index(sourceRow, 2);
137 Message::Type messageType = (Message::Type)sourceIdx.data(MessageModel::TypeRole).toInt();
139 // apply message type filter
140 if (_messageTypeFilter & messageType)
143 if (_validBuffers.isEmpty())
146 BufferId bufferId = sourceIdx.data(MessageModel::BufferIdRole).value<BufferId>();
147 if (!bufferId.isValid()) {
151 // MsgId msgId = sourceIdx.data(MessageModel::MsgIdRole).value<MsgId>();
152 Message::Flags flags = (Message::Flags)sourceIdx.data(MessageModel::FlagsRole).toInt();
154 NetworkId myNetworkId = networkId();
155 NetworkId msgNetworkId = Client::networkModel()->networkId(bufferId);
156 if (myNetworkId != msgNetworkId)
159 // ignorelist handling
160 // only match if message is not flagged as server msg
161 if (!(flags & Message::ServerMsg) && Client::ignoreListManager()
162 && Client::ignoreListManager()->match(sourceIdx.data(MessageModel::MessageRole).value<Message>(), Client::networkModel()->networkName(bufferId)))
165 if (flags & Message::Redirected) {
166 int redirectionTarget = 0;
167 switch (messageType) {
168 case Message::Notice:
169 if (Client::networkModel()->bufferType(bufferId) != BufferInfo::ChannelBuffer) {
170 if (flags & Message::ServerMsg) {
172 redirectionTarget = _serverNoticesTarget;
175 redirectionTarget = _userNoticesTarget;
180 redirectionTarget = _errorMsgsTarget;
186 if (redirectionTarget & BufferSettings::DefaultBuffer && _validBuffers.contains(bufferId))
189 if (redirectionTarget & BufferSettings::CurrentBuffer && !(flags & Message::Backlog)) {
190 BufferId redirectedTo = sourceModel()->data(sourceIdx, MessageModel::RedirectedToRole).value<BufferId>();
191 if (!redirectedTo.isValid()) {
192 BufferId redirectedTo = Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
193 if (redirectedTo.isValid())
194 sourceModel()->setData(sourceIdx, QVariant::fromValue<BufferId>(redirectedTo), MessageModel::RedirectedToRole);
197 if (_validBuffers.contains(redirectedTo))
201 if (redirectionTarget & BufferSettings::StatusBuffer) {
202 QSet<BufferId>::const_iterator idIter = _validBuffers.constBegin();
203 while (idIter != _validBuffers.constEnd()) {
204 if (Client::networkModel()->bufferType(*idIter) == BufferInfo::StatusBuffer)
213 if (_validBuffers.contains(bufferId)) {
217 // show Quit messages in Query buffers:
218 if (bufferType() != BufferInfo::QueryBuffer)
220 if (!(messageType & Message::Quit))
223 if (myNetworkId != msgNetworkId)
226 // Extract timestamp and nickname from the new quit message
227 qint64 messageTimestamp = sourceModel()->data(sourceIdx, MessageModel::TimestampRole)
228 .value<QDateTime>().toMSecsSinceEpoch();
229 QString quiter = nickFromMask(sourceModel()->data(sourceIdx, MessageModel::MessageRole)
230 .value<Message>().sender()).toLower();
232 // Check that nickname matches query name
233 if (quiter != bufferName().toLower())
236 // Check if a quit message was already forwarded within +/- 1000 ms
237 static constexpr qint64 MAX_QUIT_DELTA_MS = 1 * 1000;
238 // No need to check if it's the appropriate buffer, each query has a unique message filter
239 if (std::binary_search(_filteredQuitMsgTime.begin(), _filteredQuitMsgTime.end(),
241 [](qint64 a, qint64 b) { return ((a + MAX_QUIT_DELTA_MS) < b); } )) {
242 // New element is less than if at least 1000 ms older/newer
243 // Match found, no need to forward another quit message
247 // Mark query as having a quit message inserted
248 auto *that = const_cast<MessageFilter *>(this);
249 that->_filteredQuitMsgTime.insert(messageTimestamp);
255 void MessageFilter::requestBacklog()
257 QSet<BufferId>::const_iterator bufferIdIter = _validBuffers.constBegin();
258 while (bufferIdIter != _validBuffers.constEnd()) {
259 Client::messageModel()->requestBacklog(*bufferIdIter);