client: Fix an issue with redirection in MessageFilter
[quassel.git] / src / client / messagefilter.cpp
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
33 MessageFilter::MessageFilter(QAbstractItemModel* source, QObject* parent)
34     : QSortFilterProxyModel(parent)
35     , _messageTypeFilter(0)
36 {
37     init();
38     setSourceModel(source);
39 }
40
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
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
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
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
113 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
128 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
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 }