Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
netsplit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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 "netsplit.h"
22 #include "network.h"
23 #include "util.h"
24 
25 #include <QRegExp>
26 
27 Netsplit::Netsplit(Network *network, QObject *parent)
28  : QObject(parent),
29  _network(network), _quitMsg(""), _sentQuit(false), _joinCounter(0), _quitCounter(0)
30 {
31  _discardTimer.setSingleShot(true);
32  _joinTimer.setSingleShot(true);
33  _quitTimer.setSingleShot(true);
34 
35  connect(&_discardTimer, SIGNAL(timeout()), this, SIGNAL(finished()));
36 
37  connect(&_joinTimer, SIGNAL(timeout()), this, SLOT(joinTimeout()));
38  connect(&_quitTimer, SIGNAL(timeout()), this, SLOT(quitTimeout()));
39 
40  // wait for a maximum of 1 hour until we discard the netsplit
41  _discardTimer.start(3600000);
42 }
43 
44 
45 void Netsplit::userQuit(const QString &sender, const QStringList &channels, const QString &msg)
46 {
47  if (_quitMsg.isEmpty())
48  _quitMsg = msg;
49  foreach(QString channel, channels) {
50  _quits[channel].append(sender);
51  }
52  _quitCounter++;
53  // now let's wait 10s to finish the netsplit-quit
54  _quitTimer.start(10000);
55 }
56 
57 
58 bool Netsplit::userJoined(const QString &sender, const QString &channel)
59 {
60  if (!_quits.contains(channel))
61  return false;
62 
63  QStringList &users = _quits[channel];
64 
65  QStringList::iterator userIter;
66  const QString senderNick = nickFromMask(sender);
67  for (userIter = users.begin(); userIter != users.end(); ++userIter) {
68  if (nickFromMask(*userIter) == senderNick)
69  break;
70  }
71  if (userIter == users.end())
72  return false;
73 
74  _joins[channel].first.append(*userIter);
75  _joins[channel].second.append(QString());
76 
77  users.erase(userIter);
78 
79  if (users.empty())
80  _quits.remove(channel);
81 
82  _joinCounter++;
83 
84  if (_quits.empty()) // all users joined already - no need to wait
85  _joinTimer.start(0);
86  else // wait 30s to finish the netsplit-join
87  _joinTimer.start(30000);
88 
89  return true;
90 }
91 
92 
93 bool Netsplit::userAlreadyJoined(const QString &sender, const QString &channel)
94 {
95  if (_joins.value(channel).first.contains(sender))
96  return true;
97  return false;
98 }
99 
100 
101 void Netsplit::addMode(const QString &sender, const QString &channel, const QString &mode)
102 {
103  if (!_joins.contains(channel))
104  return;
105  int idx = _joins.value(channel).first.indexOf(sender);
106  if (idx == -1)
107  return;
108  _joins[channel].second[idx].append(mode);
109 }
110 
111 
112 bool Netsplit::isNetsplit(const QString &quitMessage)
113 {
114  // check if we find some common chars that disqualify the netsplit as such
115  if (quitMessage.contains(':') || quitMessage.contains('/'))
116  return false;
117 
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("^(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+\\s(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+$");
121  if (hostRx.exactMatch(quitMessage))
122  return true;
123 
124  return false;
125 }
126 
127 
129 {
130  if (!_sentQuit) {
131  _quitTimer.stop();
132  quitTimeout();
133  }
134 
135  QHash<QString, QPair<QStringList, QStringList> >::iterator it;
136 
137  /*
138  Try to catch server jumpers.
139  If we have too few joins for a netsplit-quit,
140  we assume that the users manually changed servers and join them
141  without ending the netsplit.
142  A netsplit is assumed over only if at least 1/3 of all quits had their corresponding
143  join again.
144  */
145  if (_joinCounter < _quitCounter/3) {
146  for (it = _joins.begin(); it != _joins.end(); ++it)
147  emit earlyJoin(network(), it.key(), it.value().first, it.value().second);
148 
149  // we don't care about those anymore
150  _joins.clear();
151 
152  // restart the timer with 5min timeout
153  // This might happen a few times if netsplit lasts longer.
154  // As soon as another user joins, the timer is set to a shorter timeout again.
155  _joinTimer.start(300000);
156  return;
157  }
158 
159  // send netsplitJoin for every recorded channel
160  for (it = _joins.begin(); it != _joins.end(); ++it)
161  emit netsplitJoin(network(), it.key(), it.value().first, it.value().second, _quitMsg);
162  _joins.clear();
163  _discardTimer.stop();
164  emit finished();
165 }
166 
167 
169 {
170  // send netsplitQuit for every recorded channel
171  QHash<QString, QStringList>::iterator channelIter;
172  for (channelIter = _quits.begin(); channelIter != _quits.end(); ++channelIter) {
173  QStringList usersToSend;
174 
175  foreach(QString user, channelIter.value()) {
176  if (!_quitsWithMessageSent.value(channelIter.key()).contains(user)) {
177  usersToSend << user;
178  _quitsWithMessageSent[channelIter.key()].append(user);
179  }
180  }
181  // not yet sure how that could happen, but never send empty netsplit-quits
182  // anyway.
183  if (!usersToSend.isEmpty())
184  emit netsplitQuit(network(), channelIter.key(), usersToSend, _quitMsg);
185  }
186  _sentQuit = true;
187 }