From 464a20552a20ff976ef4d696f69bc73b389886ad Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Tue, 8 Apr 2008 16:08:09 +0000 Subject: [PATCH] Sanitized AutoWho. Now rather than bulk-sending /WHO every minute for every channel, we use a nice queue and a 3 second delay (should later be made configurable). This should prevent networks from throttling us. For the enduser this primarily means that that annoying lag when sending to certain networks such as Freenode should be history, at the cost of slightly slower away status updates. --- src/core/coresession.cpp | 2 +- src/core/ircserverhandler.cpp | 33 +++++++--- src/core/networkconnection.cpp | 109 +++++++++++++++++---------------- src/core/networkconnection.h | 49 +++++++++------ version.inc | 2 +- 5 files changed, 111 insertions(+), 84 deletions(-) diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index 4003783f..94cf39eb 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -272,7 +272,7 @@ void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg) { if(conn) { conn->userInput(bufinfo, msg); } else { - qWarning() << "Trying to send to unconnected network!"; + qWarning() << "Trying to send to unconnected network:" << msg; } } diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index 5af0c5ab..74192f17 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -259,7 +259,10 @@ void IrcServerHandler::handleMode(const QString &prefix, const QList void IrcServerHandler::handleNick(const QString &prefix, const QList ¶ms) { IrcUser *ircuser = network()->updateNickFromMask(prefix); - Q_ASSERT(ircuser); + if(!ircuser) { + qWarning() << "IrcServerHandler::handleNick(): Unknown IrcUser!"; + return; + } QString newnick = serverDecode(params[0]); QString oldnick = ircuser->nick(); @@ -293,7 +296,10 @@ void IrcServerHandler::handleNotice(const QString &prefix, const QList ¶ms) { IrcUser *ircuser = network()->updateNickFromMask(prefix); QString channel = serverDecode(params[0]); - Q_ASSERT(ircuser); + if(!ircuser) { + qWarning() << "IrcServerHandler::handlePart(): Unknown IrcUser!"; + return; + } ircuser->partChannel(channel); @@ -312,7 +318,10 @@ void IrcServerHandler::handlePing(const QString &prefix, const QList void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList ¶ms) { IrcUser *ircuser = network()->updateNickFromMask(prefix); - Q_ASSERT(ircuser); + if(!ircuser) { + qWarning() << "IrcServerHandler::handlePrivmsg(): Unknown IrcUser!"; + return; + } if(params.isEmpty()) { qWarning() << "IrcServerHandler::handlePrivmsg(): received PRIVMSG without target or message from:" << prefix; @@ -494,10 +503,15 @@ void IrcServerHandler::handle314(const QString &prefix, const QList /* RPL_ENDOFWHO: " :End of WHO list" */ void IrcServerHandler::handle315(const QString &prefix, const QList ¶ms) { - Q_UNUSED(prefix) - // FIXME temporarily made silent - Q_UNUSED(params) - // emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" "))); + Q_UNUSED(prefix); + QStringList p = serverDecode(params); + if(p.count()) { + if(networkConnection()->setAutoWhoDone(p[0])) { + return; // stay silent + } + p.takeLast(); // should be "End of WHO list" + emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] End of /WHO list for %1").arg(p.join(" "))); + } } /* RPL_WHOISIDLE - " :seconds idle" @@ -602,8 +616,9 @@ void IrcServerHandler::handle352(const QString &prefix, const QList ircuser->setRealName(serverDecode(params.last()).section(" ", 1)); } - // FIXME temporarily made silent - //emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" "))); + if(!networkConnection()->isAutoWhoInProgress(channel)) { + emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" "))); + } } /* RPL_NAMREPLY */ diff --git a/src/core/networkconnection.cpp b/src/core/networkconnection.cpp index 79c6c043..35d44ad9 100644 --- a/src/core/networkconnection.cpp +++ b/src/core/networkconnection.cpp @@ -46,11 +46,20 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q _autoReconnectCount(0) { _autoReconnectTimer.setSingleShot(true); - _previousConnectionAttemptFailed = false; - _lastUsedServerlistIndex = 0; - // TODO make configurable - _whoTimer.setInterval(90 * 1000); - _whoTimer.setSingleShot(false); + + _previousConnectionAttemptFailed = false; + _lastUsedServerlistIndex = 0; + + // TODO make autowho configurable (possibly per-network) + _autoWhoEnabled = true; + _autoWhoInterval = 90; + _autoWhoNickLimit = 0; // unlimited + _autoWhoDelay = 3; + + _autoWhoTimer.setInterval(_autoWhoDelay * 1000); + _autoWhoTimer.setSingleShot(false); + _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); + _autoWhoCycleTimer.setSingleShot(false); QHash channels = coreSession()->persistentChannels(networkId()); foreach(QString chan, channels.keys()) { @@ -58,7 +67,8 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q } connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect())); - connect(&_whoTimer, SIGNAL(timeout()), this, SLOT(sendWho())); + connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho())); + connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle())); connect(network, SIGNAL(currentServerSet(const QString &)), this, SLOT(networkInitialized(const QString &))); connect(network, SIGNAL(useAutoReconnectSet(bool)), this, SLOT(autoReconnectSettingsChanged())); @@ -90,53 +100,12 @@ NetworkConnection::~NetworkConnection() { delete _ctcpHandler; } -bool NetworkConnection::isConnected() const { - // return socket.state() == QAbstractSocket::ConnectedState; - return connectionState() == Network::Initialized; -} - -Network::ConnectionState NetworkConnection::connectionState() const { - return _connectionState; -} - void NetworkConnection::setConnectionState(Network::ConnectionState state) { _connectionState = state; network()->setConnectionState(state); emit connectionStateChanged(state); } -NetworkId NetworkConnection::networkId() const { - return network()->networkId(); -} - -QString NetworkConnection::networkName() const { - return network()->networkName(); -} - -Identity *NetworkConnection::identity() const { - return coreSession()->identity(network()->identity()); -} - -Network *NetworkConnection::network() const { - return _network; -} - -CoreSession *NetworkConnection::coreSession() const { - return _coreSession; -} - -IrcServerHandler *NetworkConnection::ircServerHandler() const { - return _ircServerHandler; -} - -UserInputHandler *NetworkConnection::userInputHandler() const { - return _userInputHandler; -} - -CtcpHandler *NetworkConnection::ctcpHandler() const { - return _ctcpHandler; -} - QString NetworkConnection::serverDecode(const QByteArray &string) const { return network()->decodeServerString(string); } @@ -234,9 +203,11 @@ void NetworkConnection::networkInitialized(const QString ¤tServer) { setConnectionState(Network::Initialized); network()->setConnected(true); emit connected(networkId()); - if(!Global::SPUTDEV) { - sendWho(); - _whoTimer.start(); + + if(_autoWhoEnabled) { + _autoWhoCycleTimer.start(); + _autoWhoTimer.start(); + startAutoWhoCycle(); // FIXME wait for autojoin to be completed } } @@ -304,7 +275,7 @@ void NetworkConnection::socketError(QAbstractSocket::SocketError) { #ifndef QT_NO_OPENSSL -void NetworkConnection::sslErrors(const QList &errors) { +void NetworkConnection::sslErrors(const QList &sslErrors) { socket.ignoreSslErrors(); /* TODO errorhandling QVariantMap errmsg; @@ -380,7 +351,11 @@ void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketSt } void NetworkConnection::socketDisconnected() { - _whoTimer.stop(); + _autoWhoCycleTimer.stop(); + _autoWhoTimer.stop(); + _autoWhoQueue.clear(); + _autoWhoInProgress.clear(); + network()->setConnected(false); emit disconnected(networkId()); if(_autoReconnectCount != 0) { @@ -425,18 +400,44 @@ void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, c putRawLine(msg); } -void NetworkConnection::sendWho() { - foreach(QString chan, network()->channels()) { +void NetworkConnection::sendAutoWho() { + while(!_autoWhoQueue.isEmpty()) { + QString chan = _autoWhoQueue.takeFirst(); + IrcChannel *ircchan = network()->ircChannel(chan); + if(!ircchan) continue; + if(_autoWhoNickLimit > 0 && ircchan->ircUsers().count() > _autoWhoNickLimit) continue; + _autoWhoInProgress.insert(chan); putRawLine("WHO " + serverEncode(chan)); + if(_autoWhoQueue.isEmpty() && _autoWhoEnabled && !_autoWhoCycleTimer.isActive()) { + // Timer was stopped, means a new cycle is due immediately + _autoWhoCycleTimer.start(); + startAutoWhoCycle(); + } + break; + } +} + +void NetworkConnection::startAutoWhoCycle() { + if(!_autoWhoQueue.isEmpty()) { + _autoWhoCycleTimer.stop(); + return; } + _autoWhoQueue = network()->channels(); +} + +bool NetworkConnection::setAutoWhoDone(const QString &channel) { + return _autoWhoInProgress.remove(channel); } void NetworkConnection::setChannelJoined(const QString &channel) { emit channelJoined(networkId(), channel, _channelKeys[channel.toLower()]); + _autoWhoQueue.prepend(channel); // prepend so this new chan is the first to be checked } void NetworkConnection::setChannelParted(const QString &channel) { removeChannelKey(channel); + _autoWhoQueue.removeAll(channel); + _autoWhoInProgress.remove(channel); emit channelParted(networkId(), channel); } diff --git a/src/core/networkconnection.h b/src/core/networkconnection.h index b09a2051..5f593137 100644 --- a/src/core/networkconnection.h +++ b/src/core/networkconnection.h @@ -33,12 +33,12 @@ # include #endif +#include "coresession.h" #include "identity.h" #include "message.h" #include "network.h" #include "signalproxy.h" -class CoreSession; class Network; class IrcServerHandler; @@ -52,18 +52,18 @@ public: NetworkConnection(Network *network, CoreSession *session); ~NetworkConnection(); - NetworkId networkId() const; - QString networkName() const; - Network *network() const; - Identity *identity() const; - CoreSession *coreSession() const; + inline NetworkId networkId() const { return network()->networkId(); } + inline QString networkName() const { return network()->networkName(); } + inline Network *network() const { return _network; } + inline Identity *identity() const { return coreSession()->identity(network()->identity()); } + inline CoreSession *coreSession() const { return _coreSession; } - bool isConnected() const; - Network::ConnectionState connectionState() const; + inline bool isConnected() const { return connectionState() == Network::Initialized; } + inline Network::ConnectionState connectionState() const { return _connectionState; } - IrcServerHandler *ircServerHandler() const; - UserInputHandler *userInputHandler() const; - CtcpHandler *ctcpHandler() const; + inline IrcServerHandler *ircServerHandler() const { return _ircServerHandler; } + inline UserInputHandler *userInputHandler() const { return _userInputHandler; } + inline CtcpHandler *ctcpHandler() const { return _ctcpHandler; } //! Decode a string using the server (network) decoding. QString serverDecode(const QByteArray &string) const; @@ -86,6 +86,8 @@ public: inline QString channelKey(const QString &channel) const { return _channelKeys.value(channel.toLower(), QString()); } inline QStringList persistentChannels() const { return _channelKeys.keys(); } + inline bool isAutoWhoInProgress(const QString &channel) const { return _autoWhoInProgress.contains(channel); } + public slots: // void setServerOptions(); void connectToIrc(bool reconnecting = false); @@ -100,12 +102,7 @@ public slots: void addChannelKey(const QString &channel, const QString &key); void removeChannelKey(const QString &channel); -private slots: - void sendPerform(); - void autoReconnectSettingsChanged(); - void doAutoReconnect(); - void sendWho(); - void nickChanged(const QString &newNick, const QString &oldNick); // this signal is inteded to rename query buffers in the storage backend + bool setAutoWhoDone(const QString &channel); signals: // #void networkState(QString net, QVariantMap data); @@ -138,6 +135,13 @@ private slots: void setConnectionState(Network::ConnectionState); void networkInitialized(const QString ¤tServer); + void sendPerform(); + void autoReconnectSettingsChanged(); + void doAutoReconnect(); + void sendAutoWho(); + void startAutoWhoCycle(); + void nickChanged(const QString &newNick, const QString &oldNick); // this signal is inteded to rename query buffers in the storage backend + #ifndef QT_NO_OPENSSL void socketEncrypted(); void sslErrors(const QList &errors); @@ -165,11 +169,18 @@ private: QTimer _autoReconnectTimer; int _autoReconnectCount; - QTimer _whoTimer; - bool _previousConnectionAttemptFailed; int _lastUsedServerlistIndex; + bool _autoWhoEnabled; + QStringList _autoWhoQueue; + QSet _autoWhoInProgress; + int _autoWhoInterval; + int _autoWhoNickLimit; + int _autoWhoDelay; + QTimer _autoWhoTimer, _autoWhoCycleTimer; + + class ParseError : public Exception { public: ParseError(QString cmd, QString prefix, QStringList params); diff --git a/version.inc b/version.inc index 1dd2a514..8096348c 100644 --- a/version.inc +++ b/version.inc @@ -5,7 +5,7 @@ quasselVersion = "0.2.0-alpha5-pre"; quasselDate = "2008-04-08"; - quasselBuild = 711; + quasselBuild = 712; //! Minimum client build number the core needs clientBuildNeeded = 642; -- 2.20.1