1 /***************************************************************************
2 * Copyright (C) 2005-2010 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
27 Netsplit::Netsplit(Network *network, QObject *parent)
29 _network(network), _quitMsg(""), _sentQuit(false), _joinCounter(0), _quitCounter(0)
31 _discardTimer.setSingleShot(true);
32 _joinTimer.setSingleShot(true);
33 _quitTimer.setSingleShot(true);
35 connect(&_discardTimer, SIGNAL(timeout()), this, SIGNAL(finished()));
37 connect(&_joinTimer, SIGNAL(timeout()), this, SLOT(joinTimeout()));
38 connect(&_quitTimer, SIGNAL(timeout()), this, SLOT(quitTimeout()));
40 // wait for a maximum of 1 hour until we discard the netsplit
41 _discardTimer.start(3600000);
44 void Netsplit::userQuit(const QString &sender, const QStringList &channels, const QString &msg)
46 if(_quitMsg.isEmpty())
48 foreach(QString channel, channels) {
49 _quits[channel].append(sender);
52 // now let's wait 10s to finish the netsplit-quit
53 _quitTimer.start(10000);
56 bool Netsplit::userJoined(const QString &sender, const QString &channel) {
57 if(!_quits.contains(channel))
60 QStringList &users = _quits[channel];
62 QStringList::iterator userIter;
63 const QString senderNick = nickFromMask(sender);
64 for(userIter = users.begin(); userIter != users.end(); ++userIter) {
65 if(nickFromMask(*userIter) == senderNick)
68 if(userIter == users.end())
71 _joins[channel].first.append(*userIter);
72 _joins[channel].second.append(QString());
74 users.erase(userIter);
77 _quits.remove(channel);
81 if(_quits.empty()) // all users joined already - no need to wait
83 else // wait 30s to finish the netsplit-join
84 _joinTimer.start(30000);
89 bool Netsplit::userAlreadyJoined(const QString &sender, const QString &channel) {
90 if(_joins.value(channel).first.contains(sender))
95 void Netsplit::addMode(const QString &sender, const QString &channel, const QString &mode) {
96 if(!_joins.contains(channel))
98 int idx = _joins.value(channel).first.indexOf(sender);
101 _joins[channel].second[idx].append(mode);
104 bool Netsplit::isNetsplit(const QString &quitMessage)
106 // check if we find some common chars that disqualify the netsplit as such
107 if(quitMessage.contains(':') || quitMessage.contains('/'))
110 // now test if message consists only of two dns names as the RFC requests
111 // but also allow the commonly used "*.net *.split"
112 QRegExp hostRx("^(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+\\s(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+$");
113 if(hostRx.exactMatch(quitMessage))
119 void Netsplit::joinTimeout()
126 QHash<QString, QPair<QStringList, QStringList> >::iterator it;
129 Try to catch server jumpers.
130 If we have too few joins for a netsplit-quit,
131 we assume that the users manually changed servers and join them
132 without ending the netsplit.
133 A netsplit is assumed over only if at least 1/3 of all quits had their corresponding
136 if(_joinCounter < _quitCounter/3) {
137 for(it = _joins.begin(); it != _joins.end(); ++it)
138 emit earlyJoin(network(), it.key(), it.value().first, it.value().second);
140 // we don't care about those anymore
143 // restart the timer with 5min timeout
144 // This might happen a few times if netsplit lasts longer.
145 // As soon as another user joins, the timer is set to a shorter timeout again.
146 _joinTimer.start(300000);
150 // send netsplitJoin for every recorded channel
151 for(it = _joins.begin(); it != _joins.end(); ++it)
152 emit netsplitJoin(network(), it.key(), it.value().first, it.value().second ,_quitMsg);
154 _discardTimer.stop();
158 void Netsplit::quitTimeout()
160 // send netsplitQuit for every recorded channel
161 QHash<QString, QStringList>::iterator channelIter;
162 for(channelIter = _quits.begin(); channelIter != _quits.end(); ++channelIter) {
164 QStringList usersToSend;
166 foreach(QString user, channelIter.value()) {
167 if(!_quitsWithMessageSent.value(channelIter.key()).contains(user)) {
169 _quitsWithMessageSent[channelIter.key()].append(user);
172 // not yet sure how that could happen, but never send empty netsplit-quits
174 if(!usersToSend.isEmpty())
175 emit netsplitQuit(network(), channelIter.key(), usersToSend, _quitMsg);