minor api cleanup for requesting messages from backlog
[quassel.git] / src / core / ircserverhandler.cpp
index c2264ac..4d32f1d 100644 (file)
 #include "util.h"
 
 #include "coresession.h"
+#include "coreirclisthelper.h"
 #include "networkconnection.h"
 #include "network.h"
 #include "identity.h"
 #include "ctcphandler.h"
 
 #include "ircuser.h"
-#include "ircchannel.h"
+#include "coreircchannel.h"
+#include "logger.h"
 
 #include <QDebug>
 
@@ -39,7 +41,6 @@ IrcServerHandler::IrcServerHandler(NetworkConnection *parent)
 }
 
 IrcServerHandler::~IrcServerHandler() {
-
 }
 
 /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
@@ -107,8 +108,7 @@ void IrcServerHandler::handleServerMsg(QByteArray msg) {
 
 void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const QList<QByteArray> &rawparams) {
   // we assume that all this happens in server encoding
-  QStringList params;
-  foreach(QByteArray r, rawparams) params << serverDecode(r);
+  QStringList params = serverDecode(rawparams);
   uint num = cmd.toUInt();
   if(num) {
     // A lot of server messages don't really need their own handler because they don't do much.
@@ -144,12 +144,20 @@ void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const
         break;
       }
       // Ignore these commands.
-      case 366: case 376:
+      case 321: case 366: case 376:
         break;
 
       // Everything else will be marked in red, so we can add them somewhere.
       default:
-        emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", cmd + " " + params.join(" "), prefix);
+       if(_whois) {
+         // many nets define their own WHOIS fields. we fetch those not in need of special attention here:
+         emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", "[Whois] " + params.join(" "), prefix);
+       } else {
+         if(networkConnection()->coreSession()->ircListHelper()->requestInProgress(network()->networkId()))
+           networkConnection()->coreSession()->ircListHelper()->reportError(params.join(" "));
+         else
+           emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", cmd + " " + params.join(" "), prefix);
+       }
     }
     //qDebug() << prefix <<":"<<cmd<<params;
   } else {
@@ -170,7 +178,10 @@ void IrcServerHandler::handleJoin(const QString &prefix, const QList<QByteArray>
   emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix);
   //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params;
   ircuser->joinChannel(channel);
-  if(network()->isMe(ircuser)) networkConnection()->setChannelJoined(channel);
+  if(network()->isMe(ircuser)) {
+    networkConnection()->setChannelJoined(channel);
+    putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely
+  }
 }
 
 void IrcServerHandler::handleKick(const QString &prefix, const QList<QByteArray> &params) {
@@ -204,11 +215,15 @@ void IrcServerHandler::handleMode(const QString &prefix, const QList<QByteArray>
     emit displayMsg(Message::Mode, BufferInfo::ChannelBuffer, serverDecode(params[0]), serverDecode(params).join(" "), prefix);
 
     IrcChannel *channel = network()->ircChannel(params[0]);
-    // FIXME: currently the IrcChannels only support PREFIX-Modes for users
-    // This cannot be fixed unless the SignalProxy() doesn't rely on methodIds anymore
+    if(!channel) {
+      // we received mode information for a channel we're not in. that means probably we've just been kicked out or something like that
+      // anyways: we don't have a place to store the data --> discard the info.
+      return;
+    }
+
     QString modes = params[1];
     bool add = true;
-    int modeIndex = 2;
+    int paramOffset = 2;
     for(int c = 0; c < modes.length(); c++) {
       if(modes[c] == '+') {
        add = true;
@@ -219,15 +234,36 @@ void IrcServerHandler::handleMode(const QString &prefix, const QList<QByteArray>
        continue;
       }
 
-      // this is the part where we restrict the mode changes to PREFIXES:
-      if(network()->prefixModes().contains(modes[c]) && modeIndex < params.count()) {
-       IrcUser *ircUser = network()->ircUser(params[modeIndex]);
+      if(network()->prefixModes().contains(modes[c])) {
+       // user channel modes (op, voice, etc...)
+       if(paramOffset < params.count()) {
+         IrcUser *ircUser = network()->ircUser(params[paramOffset]);
+         if(add)
+           channel->addUserMode(ircUser, QString(modes[c]));
+         else
+           channel->removeUserMode(ircUser, QString(modes[c]));
+       } else {
+         qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
+       }
+       paramOffset++;
+      } else {
+       // regular channel modes
+       QString value;
+       Network::ChannelModeType modeType = network()->channelModeType(modes[c]);
+       if(modeType == Network::A_CHANMODE || modeType == Network::B_CHANMODE || (modeType == Network::C_CHANMODE && add)) {
+           if(paramOffset < params.count()) {
+             value = params[paramOffset];
+           } else {
+             qWarning() << "Received MODE with too few parameters:" << serverDecode(params);
+           }
+           paramOffset++;
+       }
+       
        if(add)
-         channel->addUserMode(ircUser, QString(modes[c]));
+         channel->addChannelMode(modes[c], value);
        else
-         channel->removeUserMode(ircUser, QString(modes[c]));
+         channel->removeChannelMode(modes[c], value);
       }
-      modeIndex++;
     }
     
   } else {
@@ -289,10 +325,32 @@ void IrcServerHandler::handleNotice(const QString &prefix, const QList<QByteArra
     return;
 
   QString target = serverDecode(params[0]);
-  if(prefix.isEmpty() || target == "AUTH")
+
+  // special treatment for welcome messages like:
+  // :ChanServ!ChanServ@services. NOTICE egst :[#apache] Welcome, this is #apache. Please read the in-channel topic message. This channel is being logged by IRSeekBot. If you have any question please see http://blog.freenode.net/?p=68
+  if(!network()->isChannelName(target)) {
+    QString msg = serverDecode(params[1]);
+    QRegExp welcomeRegExp("^\\[([^\\]]+)\\] ");
+    if(welcomeRegExp.indexIn(msg) != -1) {
+      QString channelname = welcomeRegExp.cap(1);
+      msg = msg.mid(welcomeRegExp.matchedLength());
+      CoreIrcChannel *chan = static_cast<CoreIrcChannel *>(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe
+      if(chan && !chan->receivedWelcomeMsg()) {
+       chan->setReceivedWelcomeMsg();
+       emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix);
+       return;
+      }
+    }
+  }
+
+  if(prefix.isEmpty() || target == "AUTH") {
     target = "";
-  else if(!network()->isChannelName(target))
-    target = nickFromMask(prefix);
+  } else {
+    if(!target.isEmpty() && network()->prefixes().contains(target[0]))
+      target = target.mid(1);
+    if(!network()->isChannelName(target))
+      target = nickFromMask(prefix);
+  }
 
   networkConnection()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]);
 }
@@ -323,6 +381,23 @@ void IrcServerHandler::handlePing(const QString &prefix, const QList<QByteArray>
   putCmd("PONG", params);
 }
 
+void IrcServerHandler::handlePong(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix);
+  // the server is supposed to send back what we passed as param. and we send a timestamp
+  // but using quote and whatnought one can send arbitrary pings, so we have to do some sanity checks
+  if(params.count() < 2)
+    return;
+
+  QString timestamp = serverDecode(params[1]);
+  QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
+  if(!sendTime.isValid()) {
+    emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", "PONG " + serverDecode(params).join(" "), prefix);
+    return;
+  }
+
+  network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
+}
+
 void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArray> &params) {
   if(!checkParamCount("IrcServerHandler::handlePrivmsg()", params, 1))
     return;
@@ -344,9 +419,8 @@ void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList<QByteArr
     ? QByteArray("")
     : params[1];
 
-  // are we the target?
-  if(network()->isMyNick(target))
-    target = nickFromMask(ircuser->nick());
+  if(!network()->isChannelName(target))
+    target = nickFromMask(prefix);
 
   // it's possible to pack multiple privmsgs into one param using ctcp
   // - > we let the ctcpHandler do the work
@@ -409,15 +483,16 @@ void IrcServerHandler::handle001(const QString &prefix, const QList<QByteArray>
 void IrcServerHandler::handle005(const QString &prefix, const QList<QByteArray> &params) {
   Q_UNUSED(prefix);
   const int numParams = params.size();
-  if(numParams < 1) {
-    qWarning() << "IrcServerHandler::handle005(): received RPL_ISUPPORT (005) with too few parameters:" << serverDecode(params);
+  if(numParams == 0) {
+    emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received RPL_ISUPPORT (005) without parameters!"), prefix);
     return;
   }
 
+  emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params).join(" "), prefix);
+
   QString rpl_isupport_suffix = serverDecode(params.last());
-  if(!rpl_isupport_suffix.toLower().contains("supported")) {
-    qWarning() << "Received invalid RPL_ISUPPORT! Suffix is:" << rpl_isupport_suffix << "Excpected: are supported by this server";
-    return;
+  if(!rpl_isupport_suffix.toLower().contains("are supported by this server")) {
+    emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Received non RFC compliant RPL_ISUPPORT: this can lead to unexpected behavior!"), prefix);
   }
 
   QString rawSupport;
@@ -506,6 +581,34 @@ void IrcServerHandler::handle301(const QString &prefix, const QList<QByteArray>
   }
 }
 
+// 305  RPL_UNAWAY
+//      ":You are no longer marked as being away"
+void IrcServerHandler::handle305(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix);
+  IrcUser *me = network()->me();
+  if(me)
+    me->setAway(false);
+
+  if(!network()->autoAwayActive()) {
+    if(!params.isEmpty())
+      emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
+  } else {
+    network()->setAutoAwayActive(false);
+  }
+}
+
+// 306  RPL_NOWAWAY
+//      ":You have been marked as being away"
+void IrcServerHandler::handle306(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix);
+  IrcUser *me = network()->me();
+  if(me)
+    me->setAway(true);
+
+  if(!params.isEmpty() && !network()->autoAwayActive())
+    emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", serverDecode(params[0]));
+}
+
 /* RPL_WHOISSERVICE - "<user> is registered nick" */
 void IrcServerHandler::handle307(const QString &prefix, const QList<QByteArray> &params) {
   Q_UNUSED(prefix)
@@ -677,6 +780,53 @@ void IrcServerHandler::handle320(const QString &prefix, const QList<QByteArray>
   emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Whois] %1").arg(serverDecode(params).join(" ")));
 }
 
+/* RPL_LIST -  "<channel> <# visible> :<topic>" */
+void IrcServerHandler::handle322(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix)
+  QString channelName;
+  quint32 userCount = 0;
+  QString topic;
+  
+  int paramCount = params.count();
+  switch(paramCount) {
+  case 3:
+    topic = serverDecode(params[2]);
+  case 2:
+    userCount = serverDecode(params[1]).toUInt();
+  case 1:
+    channelName = serverDecode(params[0]);
+  default:
+    break;
+  }
+  if(!networkConnection()->coreSession()->ircListHelper()->addChannel(network()->networkId(), channelName, userCount, topic))
+    emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Channel %1 has %2 users. Topic is: %3").arg(channelName).arg(userCount).arg(topic));
+}
+
+/* RPL_LISTEND ":End of LIST" */
+void IrcServerHandler::handle323(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix)
+  Q_UNUSED(params)
+
+  if(!networkConnection()->coreSession()->ircListHelper()->endOfChannelList(network()->networkId()))
+    emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("End of channel list"));
+}
+       
+/* RPL_CHANNELMODEIS - "<channel> <mode> <mode params>" */
+void IrcServerHandler::handle324(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix);
+  handleMode(prefix, params);
+}
+
+/* RPL_??? - "<channel> <creation time (unix)>" */
+void IrcServerHandler::handle329(const QString &prefix, const QList<QByteArray> &params) {
+  Q_UNUSED(prefix);
+  Q_UNUSED(params)
+#ifdef __GNUC__
+#  warning "Implement handle329 (Channel creation time)"
+#endif
+  // FIXME implement this... 
+}
+
 /* RPL_NOTOPIC */
 void IrcServerHandler::handle331(const QString &prefix, const QList<QByteArray> &params) {
   Q_UNUSED(prefix);
@@ -684,7 +834,10 @@ void IrcServerHandler::handle331(const QString &prefix, const QList<QByteArray>
     return;
 
   QString channel = serverDecode(params[0]);
-  network()->ircChannel(channel)->setTopic(QString());
+  IrcChannel *chan = network()->ircChannel(channel);
+  if(chan)
+    chan->setTopic(QString());
+
   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("No topic is set for %1.").arg(channel));
 }
 
@@ -696,7 +849,10 @@ void IrcServerHandler::handle332(const QString &prefix, const QList<QByteArray>
 
   QString channel = serverDecode(params[0]);
   QString topic = channelDecode(channel, params[1]);
-  network()->ircChannel(channel)->setTopic(topic);
+  IrcChannel *chan = network()->ircChannel(channel);
+  if(chan)
+    chan->setTopic(topic);
+
   emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic));
 }
 
@@ -738,7 +894,7 @@ void IrcServerHandler::handle352(const QString &prefix, const QList<QByteArray>
 /* RPL_NAMREPLY */
 void IrcServerHandler::handle353(const QString &prefix, const QList<QByteArray> &params) {
   Q_UNUSED(prefix);
-  if(!checkParamCount("IrcServerHandler::handle353()", params, 2))
+  if(!checkParamCount("IrcServerHandler::handle353()", params, 3))
     return;
     
   // param[0] is either "=", "*" or "@" indicating a public, private or secret channel
@@ -831,7 +987,7 @@ void IrcServerHandler::tryNextNick(const QString &errnick) {
 
 bool IrcServerHandler::checkParamCount(const QString &methodName, const QList<QByteArray> &params, int minParams) {
   if(params.count() < minParams) {
-    qWarning() << qPrintable(methodName) << "requieres" << minParams << "parameters but received only" << params.count() << serverDecode(params);
+    qWarning() << qPrintable(methodName) << "requires" << minParams << "parameters but received only" << params.count() << serverDecode(params);
     return false;
   } else {
     return true;