client: Fix duplicated query quit messages
[quassel.git] / src / client / messagefilter.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 "buffersettings.h"
26 #include "client.h"
27 #include "buffermodel.h"
28 #include "messagemodel.h"
29 #include "networkmodel.h"
30 #include "clientignorelistmanager.h"
31
32 MessageFilter::MessageFilter(QAbstractItemModel *source, QObject *parent)
33     : QSortFilterProxyModel(parent),
34     _messageTypeFilter(0)
35 {
36     init();
37     setSourceModel(source);
38 }
39
40
41 MessageFilter::MessageFilter(MessageModel *source, const QList<BufferId> &buffers, QObject *parent)
42     : QSortFilterProxyModel(parent),
43     _validBuffers(buffers.toSet()),
44     _messageTypeFilter(0)
45 {
46     init();
47     setSourceModel(source);
48 }
49
50
51 void MessageFilter::init()
52 {
53     setDynamicSortFilter(true);
54
55     _userNoticesTarget = _serverNoticesTarget = _errorMsgsTarget = -1;
56
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();
62
63     _messageTypeFilter = defaultSettings.messageFilter();
64     defaultSettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
65
66     BufferSettings mySettings(idString());
67     if (mySettings.hasFilter())
68         _messageTypeFilter = mySettings.messageFilter();
69     mySettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
70     mySettings.notify("hasMessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
71 }
72
73
74 void MessageFilter::messageTypeFilterChanged()
75 {
76     int newFilter;
77     BufferSettings defaultSettings;
78     newFilter = BufferSettings().messageFilter();
79
80     BufferSettings mySettings(idString());
81     if (mySettings.hasFilter())
82         newFilter = mySettings.messageFilter();
83
84     if (_messageTypeFilter != newFilter) {
85         _messageTypeFilter = newFilter;
86         _filteredQuitMsgTime.clear();
87         invalidateFilter();
88     }
89 }
90
91
92 void MessageFilter::messageRedirectionChanged()
93 {
94     BufferSettings bufferSettings;
95     bool changed = false;
96
97     if (_userNoticesTarget != bufferSettings.userNoticesTarget()) {
98         _userNoticesTarget = bufferSettings.userNoticesTarget();
99         changed = true;
100     }
101
102     if (_serverNoticesTarget != bufferSettings.serverNoticesTarget()) {
103         _serverNoticesTarget = bufferSettings.serverNoticesTarget();
104         changed = true;
105     }
106
107     if (_errorMsgsTarget != bufferSettings.errorMsgsTarget()) {
108         _errorMsgsTarget = bufferSettings.errorMsgsTarget();
109         changed = true;
110     }
111
112     if (changed)
113         invalidateFilter();
114 }
115
116
117 QString MessageFilter::idString() const
118 {
119     if (_validBuffers.isEmpty())
120         return "*";
121
122     QList<BufferId> bufferIds = _validBuffers.toList();
123     qSort(bufferIds);
124
125     QStringList bufferIdStrings;
126     foreach(BufferId id, bufferIds)
127     bufferIdStrings << QString::number(id.toInt());
128
129     return bufferIdStrings.join("|");
130 }
131
132
133 bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
134 {
135     Q_UNUSED(sourceParent);
136     QModelIndex sourceIdx = sourceModel()->index(sourceRow, 2);
137     Message::Type messageType = (Message::Type)sourceIdx.data(MessageModel::TypeRole).toInt();
138
139     // apply message type filter
140     if (_messageTypeFilter & messageType)
141         return false;
142
143     if (_validBuffers.isEmpty())
144         return true;
145
146     BufferId bufferId = sourceIdx.data(MessageModel::BufferIdRole).value<BufferId>();
147     if (!bufferId.isValid()) {
148         return true;
149     }
150
151     // MsgId msgId = sourceIdx.data(MessageModel::MsgIdRole).value<MsgId>();
152     Message::Flags flags = (Message::Flags)sourceIdx.data(MessageModel::FlagsRole).toInt();
153
154     NetworkId myNetworkId = networkId();
155     NetworkId msgNetworkId = Client::networkModel()->networkId(bufferId);
156     if (myNetworkId != msgNetworkId)
157         return false;
158
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)))
163         return false;
164
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) {
171                     // server notice
172                     redirectionTarget = _serverNoticesTarget;
173                 }
174                 else {
175                     redirectionTarget = _userNoticesTarget;
176                 }
177             }
178             break;
179         case Message::Error:
180             redirectionTarget = _errorMsgsTarget;
181             break;
182         default:
183             break;
184         }
185
186         if (redirectionTarget & BufferSettings::DefaultBuffer && _validBuffers.contains(bufferId))
187             return true;
188
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);
195             }
196
197             if (_validBuffers.contains(redirectedTo))
198                 return true;
199         }
200
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)
205                     return true;
206                 ++idIter;
207             }
208         }
209
210         return false;
211     }
212
213     if (_validBuffers.contains(bufferId)) {
214         return true;
215     }
216     else {
217         // show Quit messages in Query buffers:
218         if (bufferType() != BufferInfo::QueryBuffer)
219             return false;
220         if (!(messageType & Message::Quit))
221             return false;
222
223         if (myNetworkId != msgNetworkId)
224             return false;
225
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();
231
232         // Check that nickname matches query name
233         if (quiter != bufferName().toLower())
234             return false;
235
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(),
240                                messageTimestamp,
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
244             return false;
245         }
246
247         // Mark query as having a quit message inserted
248         MessageFilter *that = const_cast<MessageFilter *>(this);
249         that->_filteredQuitMsgTime.insert(messageTimestamp);
250         return true;
251     }
252 }
253
254
255 void MessageFilter::requestBacklog()
256 {
257     QSet<BufferId>::const_iterator bufferIdIter = _validBuffers.constBegin();
258     while (bufferIdIter != _validBuffers.constEnd()) {
259         Client::messageModel()->requestBacklog(*bufferIdIter);
260         ++bufferIdIter;
261     }
262 }