public:
/** The different types a message can have for display */
enum Type {
- Plain = 0x0001,
- Notice = 0x0002,
- Action = 0x0004,
- Nick = 0x0008,
- Mode = 0x0010,
- Join = 0x0020,
- Part = 0x0040,
- Quit = 0x0080,
- Kick = 0x0100,
- Kill = 0x0200,
- Server = 0x0400,
- Info = 0x0800,
- Error = 0x1000,
- DayChange = 0x2000,
- Topic = 0x4000
+ Plain = 0x00001,
+ Notice = 0x00002,
+ Action = 0x00004,
+ Nick = 0x00008,
+ Mode = 0x00010,
+ Join = 0x00020,
+ Part = 0x00040,
+ Quit = 0x00080,
+ Kick = 0x00100,
+ Kill = 0x00200,
+ Server = 0x00400,
+ Info = 0x00800,
+ Error = 0x01000,
+ DayChange = 0x02000,
+ Topic = 0x04000,
+ NetsplitJoin = 0x08000,
+ NetsplitQuit = 0x10000,
};
// DO NOT CHANGE without knowing what you do, some of these flags are stored in the database
coreusersettings.cpp
ctcphandler.cpp
ircserverhandler.cpp
+ netsplit.cpp
postgresqlstorage.cpp
sessionthread.cpp
sqlitestorage.cpp
coresession.h
ctcphandler.h
ircserverhandler.h
+ netsplit.h
postgresqlstorage.h
sqlitestorage.h
storage.h
QString channel = serverDecode(params[0]);
IrcUser *ircuser = network()->updateNickFromMask(prefix);
- emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix);
+
+ bool handledByNetsplit = false;
+ if(!_netsplits.empty()) {
+ foreach(Netsplit* n, _netsplits) {
+ handledByNetsplit = n->userJoined(prefix, channel);
+ if(handledByNetsplit)
+ break;
+ }
+ }
+
+ // normal join
+ if(!handledByNetsplit) {
+ emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix);
+ ircuser->joinChannel(channel);
+ }
//qDebug() << "IrcServerHandler::handleJoin()" << prefix << params;
- ircuser->joinChannel(channel);
+
if(network()->isMe(ircuser)) {
network()->setChannelJoined(channel);
putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely
if(params.count() > 0)
msg = userDecode(ircuser->nick(), params[0]);
- foreach(QString channel, ircuser->channels())
- emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix);
-
- ircuser->quit();
+ // check if netsplit
+ if(Netsplit::isNetsplit(msg)) {
+ Netsplit *n;
+ if(!_netsplits.contains(msg)) {
+ n = new Netsplit();
+ connect(n, SIGNAL(finished()), this, SLOT(handleNetsplitFinished()));
+ connect(n, SIGNAL(netsplitJoin(QString,QStringList,QString)), this, SLOT(handleNetsplitJoin(QString,QStringList,QString)));
+ connect(n, SIGNAL(netsplitQuit(QString,QStringList,QString)), this, SLOT(handleNetsplitQuit(QString,QStringList,QString)));
+ _netsplits.insert(msg, n);
+ }
+ else {
+ n = _netsplits[msg];
+ }
+ // add this user to the netsplit
+ n->userQuit(prefix, ircuser->channels(),msg);
+ }
+ // normal quit
+ else {
+ foreach(QString channel, ircuser->channels())
+ emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix);
+ ircuser->quit();
+ }
}
void IrcServerHandler::handleTopic(const QString &prefix, const QList<QByteArray> ¶ms) {
tryNextNick(errnick);
}
+/* Handle signals from Netsplit objects */
+
+void IrcServerHandler::handleNetsplitJoin(const QString &channel, const QStringList &users, const QString& quitMessage)
+{
+ QString msg = users.join(":").append(':').append(quitMessage);
+ emit displayMsg(Message::NetsplitJoin, BufferInfo::ChannelBuffer, channel, msg);
+
+ foreach(QString user, users) {
+ IrcUser *iu = network()->ircUser(nickFromMask(user));
+ if(iu)
+ iu->joinChannel(channel);
+ }
+}
+
+void IrcServerHandler::handleNetsplitQuit(const QString &channel, const QStringList &users, const QString& quitMessage)
+{
+ QString msg = users.join(":").append(':').append(quitMessage);
+ emit displayMsg(Message::NetsplitQuit, BufferInfo::ChannelBuffer, channel, msg);
+ foreach(QString user, users) {
+ IrcUser *iu = network()->ircUser(nickFromMask(user));
+ if(iu)
+ iu->quit();
+ }
+}
+
+void IrcServerHandler::handleNetsplitFinished()
+{
+ Netsplit* n = qobject_cast<Netsplit*>(sender());
+ _netsplits.remove(_netsplits.key(n));
+ n->deleteLater();
+}
+
/* */
// FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong
#define IRCSERVERHANDLER_H
#include "basichandler.h"
+#include "netsplit.h"
class IrcServerHandler : public BasicHandler {
Q_OBJECT
void defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> ¶ms);
+private slots:
+ //! Joins after a netsplit
+ /** This slot handles a bulk-join after a netsplit is over
+ * \param channel The channel the users joined
+ * \param users The list of users that joind the channel
+ * \param quitMessage The message we received when the netsplit occured
+ */
+ void handleNetsplitJoin(const QString &channel, const QStringList &users, const QString &quitMessage);
+
+ //! Quits after a netsplit
+ /** This slot handles a bulk-quit after a netsplit occured
+ * \param channel The channel the users quitted
+ * \param users The list of users that got split
+ * \param quitMessage The message we received when the netsplit occured
+ */
+ void handleNetsplitQuit(const QString &channel, const QStringList &users, const QString &quitMessage);
+
+ //! Netsplit finished
+ /** This slot deletes the netsplit object that sent the finished() signal
+ */
+ void handleNetsplitFinished();
+
private:
void tryNextNick(const QString &errnick, bool erroneus = false);
bool checkParamCount(const QString &methodName, const QList<QByteArray> ¶ms, int minParams);
bool _whois;
QString _target;
+
+ // structure to organize netsplits
+ // key: quit message
+ // value: the corresponding netsplit object
+ QHash<QString, Netsplit*> _netsplits;
};
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-09 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "netsplit.h"
+
+#include <QRegExp>
+
+Netsplit::Netsplit()
+ : _quitMsg("")
+{
+ _discardTimer.setSingleShot(true);
+ _joinTimer.setSingleShot(true);
+ _quitTimer.setSingleShot(true);
+
+ connect(&_discardTimer, SIGNAL(timeout()), this, SIGNAL(finished()));
+
+ connect(&_joinTimer, SIGNAL(timeout()), this, SLOT(joinTimeout()));
+ connect(&_quitTimer, SIGNAL(timeout()), this, SLOT(quitTimeout()));
+
+ // wait for a maximum of 1 hour until we discard the netsplit
+ _discardTimer.start(3600000);
+}
+
+void Netsplit::userQuit(const QString &sender, const QStringList &channels, const QString &msg)
+{
+ if(_quitMsg.isEmpty())
+ _quitMsg = msg;
+ foreach(QString channel, channels) {
+ _quits[channel].append(sender);
+ }
+ // now let's wait 5s to finish the netsplit-quit
+ _quitTimer.start(5000);
+}
+
+bool Netsplit::userJoined(const QString &sender, const QString &channel) {
+ if(!_quits.contains(channel))
+ return false;
+
+ QStringList &users = _quits[channel];
+ int idx = users.indexOf(sender);
+ if(idx == -1)
+ return false;
+
+ _joins[channel].append(users.takeAt(idx));
+ if(users.empty())
+ _quits.remove(channel);
+
+ // now let's wait 5s to finish the netsplit-join
+ _joinTimer.start(5000);
+ return true;
+}
+
+bool Netsplit::isNetsplit(const QString &quitMessage)
+{
+ // check if we find some common chars that disqualify the netsplit as such
+ if(quitMessage.contains(':') || quitMessage.contains('/'))
+ return false;
+
+ // now test if message consists only of two dns names as the RFC requests
+ // but also allow the commonly used "*.net *.split"
+ QRegExp hostRx("^(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+\\s(?:[\\w\\d-.]+|\\*)\\.[\\w\\d-]+$");
+ if(hostRx.exactMatch(quitMessage))
+ return true;
+
+ return false;
+}
+
+void Netsplit::joinTimeout()
+{
+ QHash<QString, QStringList>::iterator it;
+ for(it = _joins.begin(); it != _joins.end(); ++it)
+ emit netsplitJoin(it.key(), it.value(),_quitMsg);
+ _joins.clear();
+ _discardTimer.stop();
+ emit finished();
+}
+
+void Netsplit::quitTimeout()
+{
+ QHash<QString, QStringList>::iterator it;
+ for(it = _quits.begin(); it != _quits.end(); ++it)
+ emit netsplitQuit(it.key(), it.value(),_quitMsg);
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-09 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef NETSPLIT_H
+#define NETSPLIT_H
+
+#include <QObject>
+#include <QTimer>
+#include <QHash>
+#include <QStringList>
+
+class Netsplit : public QObject
+{
+ Q_OBJECT
+public:
+ Netsplit();
+
+ //! Add a user to the netsplit
+ /** Call this method if you noticed a netsplit.
+ * \note This method doesn't check if it really is a netsplit.
+ * \note Check with isNetsplit(const QString &quitMessage) before calling it!
+ *
+ * \param sender The sender string of the quitting user
+ * \param channels The channels that user shared with us
+ * \param msg The quit message
+ */
+ void userQuit(const QString &sender, const QStringList &channels, const QString &msg);
+
+ //! Remove a user from the netsplit
+ /** Call this method if a user joined after a netsplit occured.
+
+ *
+ * \param sender The sender string of the joined user
+ * \param channels The channels that user shares with us
+ * \return true if user was found in the netsplit
+ */
+ bool userJoined(const QString &sender, const QString &channel);
+
+ //! Check if a string matches the criteria for a netsplit
+ /** \param quitMessage The message to be checked
+ * \return true if the message is a netsplit
+ */
+ static bool isNetsplit(const QString &quitMessage);
+
+signals:
+ //! A bulk-join of netsplitted users timed out
+ /** Whenever _joinTimer() times out, we consider the bulk-join to be finished and emit that signal
+ * for every channel
+ * \param channel The IRC channel
+ * \param users A list of all users that joined that channel
+ * \param quitMessage The Quitmessage and thus the servers that got split
+ */
+ void netsplitJoin(const QString &channel, const QStringList &users, const QString &quitMessage);
+
+ //! A bulk-quit of netsplitted users timed out
+ /** Whenever _quitTimer() times out, we consider the bulk-quit to be finished and emit that signal
+ * for every channel
+ * \param channel The IRC channel
+ * \param users A list of all users that quitted in that channel
+ * \param quitMessage The Quitmessage and thus the servers that got split
+ */
+ void netsplitQuit(const QString &channel, const QStringList &users, const QString &quitMessage);
+
+ //! The Netsplit is considered finished
+ /** This signal is emitted right after all netsplitJoin signals have been sent or whenever the
+ * internal timer signals a timeout.
+ * Simply delete the object and remove it from structures when you receive that signal.
+ */
+ void finished();
+
+private slots:
+ void joinTimeout();
+ void quitTimeout();
+
+private:
+ QString _quitMsg;
+ QHash<QString, QStringList> _joins;
+ QHash<QString, QStringList> _quits;
+
+ QTimer _joinTimer;
+ QTimer _quitTimer;
+ QTimer _discardTimer;
+};
+
+#endif // NETSPLIT_H