Sanitized AutoWho. Now rather than bulk-sending /WHO every minute for every channel,
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 8 Apr 2008 16:08:09 +0000 (16:08 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 8 Apr 2008 16:08:09 +0000 (16:08 +0000)
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
src/core/ircserverhandler.cpp
src/core/networkconnection.cpp
src/core/networkconnection.h
version.inc

index 4003783..94cf39e 100644 (file)
@@ -272,7 +272,7 @@ void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg) {
   if(conn) {
     conn->userInput(bufinfo, msg);
   } else {
   if(conn) {
     conn->userInput(bufinfo, msg);
   } else {
-    qWarning() << "Trying to send to unconnected network!";
+    qWarning() << "Trying to send to unconnected network:" << msg;
   }
 }
 
   }
 }
 
index 5af0c5a..74192f1 100644 (file)
@@ -259,7 +259,10 @@ void IrcServerHandler::handleMode(const QString &prefix, const QList<QByteArray>
 
 void IrcServerHandler::handleNick(const QString &prefix, const QList<QByteArray> &params) {
   IrcUser *ircuser = network()->updateNickFromMask(prefix);
 
 void IrcServerHandler::handleNick(const QString &prefix, const QList<QByteArray> &params) {
   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();
 
   QString newnick = serverDecode(params[0]);
   QString oldnick = ircuser->nick();
 
@@ -293,7 +296,10 @@ void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArra
 void IrcServerHandler::handlePart(const QString &prefix, const QList<QByteArray> &params) {
   IrcUser *ircuser = network()->updateNickFromMask(prefix);
   QString channel = serverDecode(params[0]);
 void IrcServerHandler::handlePart(const QString &prefix, const QList<QByteArray> &params) {
   IrcUser *ircuser = network()->updateNickFromMask(prefix);
   QString channel = serverDecode(params[0]);
-  Q_ASSERT(ircuser);
+  if(!ircuser) {
+    qWarning() << "IrcServerHandler::handlePart(): Unknown IrcUser!";
+    return;
+  }
 
   ircuser->partChannel(channel);
 
 
   ircuser->partChannel(channel);
 
@@ -312,7 +318,10 @@ void IrcServerHandler::handlePing(const QString &prefix, const QList<QByteArray>
 
 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> &params) {
   IrcUser *ircuser = network()->updateNickFromMask(prefix);
 
 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> &params) {
   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;
 
   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<QByteArray>
 
 /*  RPL_ENDOFWHO: "<name> :End of WHO list" */
 void IrcServerHandler::handle315(const QString &prefix, const QList<QByteArray> &params) {
 
 /*  RPL_ENDOFWHO: "<name> :End of WHO list" */
 void IrcServerHandler::handle315(const QString &prefix, const QList<QByteArray> &params) {
-  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 - "<nick> <integer> :seconds idle" 
 }
 
 /*  RPL_WHOISIDLE - "<nick> <integer> :seconds idle" 
@@ -602,8 +616,9 @@ void IrcServerHandler::handle352(const QString &prefix, const QList<QByteArray>
     ircuser->setRealName(serverDecode(params.last()).section(" ", 1));
   }
 
     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 */
 }
 
 /* RPL_NAMREPLY */
index 79c6c04..35d44ad 100644 (file)
@@ -46,11 +46,20 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q
     _autoReconnectCount(0)
 {
   _autoReconnectTimer.setSingleShot(true);
     _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<QString, QString> channels = coreSession()->persistentChannels(networkId());
   foreach(QString chan, channels.keys()) {
 
   QHash<QString, QString> 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(&_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()));
 
   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;
 }
 
   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);
 }
 
 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);
 }
 QString NetworkConnection::serverDecode(const QByteArray &string) const {
   return network()->decodeServerString(string);
 }
@@ -234,9 +203,11 @@ void NetworkConnection::networkInitialized(const QString &currentServer) {
   setConnectionState(Network::Initialized);
   network()->setConnected(true);
   emit connected(networkId());
   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
 
 
 #ifndef QT_NO_OPENSSL
 
-void NetworkConnection::sslErrors(const QList<QSslError> &errors) {
+void NetworkConnection::sslErrors(const QList<QSslError> &sslErrors) {
   socket.ignoreSslErrors();
   /* TODO errorhandling
   QVariantMap errmsg;
   socket.ignoreSslErrors();
   /* TODO errorhandling
   QVariantMap errmsg;
@@ -380,7 +351,11 @@ void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketSt
 }
 
 void NetworkConnection::socketDisconnected() {
 }
 
 void NetworkConnection::socketDisconnected() {
-  _whoTimer.stop();
+  _autoWhoCycleTimer.stop();
+  _autoWhoTimer.stop();
+  _autoWhoQueue.clear();
+  _autoWhoInProgress.clear();
+
   network()->setConnected(false);
   emit disconnected(networkId());
   if(_autoReconnectCount != 0) {
   network()->setConnected(false);
   emit disconnected(networkId());
   if(_autoReconnectCount != 0) {
@@ -425,18 +400,44 @@ void NetworkConnection::putCmd(const QString &cmd, const QVariantList &params, c
   putRawLine(msg);
 }
 
   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));
     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()]);
 }
 
 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);
 }
 
 void NetworkConnection::setChannelParted(const QString &channel) {
   removeChannelKey(channel);
+  _autoWhoQueue.removeAll(channel);
+  _autoWhoInProgress.remove(channel);
   emit channelParted(networkId(), channel);
 }
 
   emit channelParted(networkId(), channel);
 }
 
index b09a205..5f59313 100644 (file)
 # include <QTcpSocket>
 #endif
 
 # include <QTcpSocket>
 #endif
 
+#include "coresession.h"
 #include "identity.h"
 #include "message.h"
 #include "network.h"
 #include "signalproxy.h"
 
 #include "identity.h"
 #include "message.h"
 #include "network.h"
 #include "signalproxy.h"
 
-class CoreSession;
 class Network;
 
 class IrcServerHandler;
 class Network;
 
 class IrcServerHandler;
@@ -52,18 +52,18 @@ public:
   NetworkConnection(Network *network, CoreSession *session);
   ~NetworkConnection();
 
   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;
 
   //! 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 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);
 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);
 
   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);
 
 signals:
   // #void networkState(QString net, QVariantMap data);
@@ -138,6 +135,13 @@ private slots:
   void setConnectionState(Network::ConnectionState);
   void networkInitialized(const QString &currentServer);
 
   void setConnectionState(Network::ConnectionState);
   void networkInitialized(const QString &currentServer);
 
+  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<QSslError> &errors);
 #ifndef QT_NO_OPENSSL
   void socketEncrypted();
   void sslErrors(const QList<QSslError> &errors);
@@ -165,11 +169,18 @@ private:
   QTimer _autoReconnectTimer;
   int _autoReconnectCount;
 
   QTimer _autoReconnectTimer;
   int _autoReconnectCount;
 
-  QTimer _whoTimer;
-
   bool _previousConnectionAttemptFailed;
   int _lastUsedServerlistIndex;
 
   bool _previousConnectionAttemptFailed;
   int _lastUsedServerlistIndex;
 
+  bool _autoWhoEnabled;
+  QStringList _autoWhoQueue;
+  QSet<QString> _autoWhoInProgress;
+  int _autoWhoInterval;
+  int _autoWhoNickLimit;
+  int _autoWhoDelay;
+  QTimer _autoWhoTimer, _autoWhoCycleTimer;
+
+
   class ParseError : public Exception {
   public:
     ParseError(QString cmd, QString prefix, QStringList params);
   class ParseError : public Exception {
   public:
     ParseError(QString cmd, QString prefix, QStringList params);
index 1dd2a51..8096348 100644 (file)
@@ -5,7 +5,7 @@
 
   quasselVersion = "0.2.0-alpha5-pre";
   quasselDate = "2008-04-08";
 
   quasselVersion = "0.2.0-alpha5-pre";
   quasselDate = "2008-04-08";
-  quasselBuild = 711;
+  quasselBuild = 712;
 
   //! Minimum client build number the core needs
   clientBuildNeeded = 642;
 
   //! Minimum client build number the core needs
   clientBuildNeeded = 642;