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 ***************************************************************************/
28 Netsplit::Netsplit(Network* network, QObject* parent)
36 _discardTimer.setSingleShot(true);
37 _joinTimer.setSingleShot(true);
38 _quitTimer.setSingleShot(true);
40 connect(&_discardTimer, &QTimer::timeout, this, &Netsplit::finished);
42 connect(&_joinTimer, &QTimer::timeout, this, &Netsplit::joinTimeout);
43 connect(&_quitTimer, &QTimer::timeout, this, &Netsplit::quitTimeout);
45 // wait for a maximum of 1 hour until we discard the netsplit
46 _discardTimer.start(3600000);
49 void Netsplit::userQuit(const QString& sender, const QStringList& channels, const QString& msg)
51 if (_quitMsg.isEmpty())
53 foreach (QString channel, channels) {
54 _quits[channel].append(sender);
57 // now let's wait 10s to finish the netsplit-quit
58 _quitTimer.start(10000);
61 bool Netsplit::userJoined(const QString& sender, const QString& channel)
63 if (!_quits.contains(channel))
66 QStringList& users = _quits[channel];
68 QStringList::iterator userIter;
69 const QString senderNick = nickFromMask(sender);
70 for (userIter = users.begin(); userIter != users.end(); ++userIter) {
71 if (nickFromMask(*userIter) == senderNick)
74 if (userIter == users.end())
77 _joins[channel].first.append(*userIter);
78 _joins[channel].second.append(QString());
80 users.erase(userIter);
83 _quits.remove(channel);
87 if (_quits.empty()) // all users joined already - no need to wait
89 else // wait 30s to finish the netsplit-join
90 _joinTimer.start(30000);
95 bool Netsplit::userAlreadyJoined(const QString& sender, const QString& channel)
97 if (_joins.value(channel).first.contains(sender))
102 void Netsplit::addMode(const QString& sender, const QString& channel, const QString& mode)
104 if (!_joins.contains(channel))
106 int idx = _joins.value(channel).first.indexOf(sender);
109 _joins[channel].second[idx].append(mode);
112 bool Netsplit::isNetsplit(const QString& quitMessage)
114 // check if we find some common chars that disqualify the netsplit as such
115 if (quitMessage.contains(':') || quitMessage.contains('/'))
118 // now test if message consists only of two dns names as the RFC requests
119 // but also allow the commonly used "*.net *.split"
120 QRegExp hostRx(R"(^(?:[\w\d-.]+|\*)\.[\w\d-]+\s(?:[\w\d-.]+|\*)\.[\w\d-]+$)");
121 if (hostRx.exactMatch(quitMessage))
127 void Netsplit::joinTimeout()
134 QHash<QString, QPair<QStringList, QStringList>>::iterator it;
137 Try to catch server jumpers.
138 If we have too few joins for a netsplit-quit,
139 we assume that the users manually changed servers and join them
140 without ending the netsplit.
141 A netsplit is assumed over only if at least 1/3 of all quits had their corresponding
144 if (_joinCounter < _quitCounter / 3) {
145 for (it = _joins.begin(); it != _joins.end(); ++it)
146 emit earlyJoin(network(), it.key(), it.value().first, it.value().second);
148 // we don't care about those anymore
151 // restart the timer with 5min timeout
152 // This might happen a few times if netsplit lasts longer.
153 // As soon as another user joins, the timer is set to a shorter timeout again.
154 _joinTimer.start(300000);
158 // send netsplitJoin for every recorded channel
159 for (it = _joins.begin(); it != _joins.end(); ++it)
160 emit netsplitJoin(network(), it.key(), it.value().first, it.value().second, _quitMsg);
162 _discardTimer.stop();
166 void Netsplit::quitTimeout()
168 // send netsplitQuit for every recorded channel
169 QHash<QString, QStringList>::iterator channelIter;
170 for (channelIter = _quits.begin(); channelIter != _quits.end(); ++channelIter) {
171 QStringList usersToSend;
173 foreach (QString user, channelIter.value()) {
174 if (!_quitsWithMessageSent.value(channelIter.key()).contains(user)) {
176 _quitsWithMessageSent[channelIter.key()].append(user);
179 // not yet sure how that could happen, but never send empty netsplit-quits
181 if (!usersToSend.isEmpty())
182 emit netsplitQuit(network(), channelIter.key(), usersToSend, _quitMsg);