core: Handle single-parameter PONG replies
[quassel.git] / src / core / coresessioneventprocessor.cpp
index 7c414ba..c4c01e5 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2016 by the Quassel Project                        *
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -636,13 +636,36 @@ void CoreSessionEventProcessor::processIrcEventPing(IrcEvent *e)
 
 void CoreSessionEventProcessor::processIrcEventPong(IrcEvent *e)
 {
-    // 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 (checkParamCount(e, 2)) {
-        QString timestamp = e->params().at(1);
-        QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
-        if (sendTime.isValid())
-            e->network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
+    // Ensure we get at least one parameter
+    if (!checkParamCount(e, 1))
+        return;
+
+    // Some IRC servers respond with only one parameter, others respond with two, with the latter
+    // being the text sent.  Handle both situations.
+    QString timestamp;
+    if (e->params().count() < 2) {
+        // Only one parameter received
+        // :localhost PONG 02:43:49.565
+        timestamp = e->params().at(0);
+    } else {
+        // Two parameters received, pick the second
+        // :localhost PONG localhost :02:43:49.565
+        timestamp = e->params().at(1);
+    }
+
+    // The server is supposed to send back what we passed as parameter, and we send a timestamp.
+    // However, using quote and whatnot, one can send arbitrary pings, and IRC servers may decide to
+    // ignore our requests entirely and send whatever they want, so we have to do some sanity
+    // checks.
+    //
+    // Attempt to parse the timestamp
+    QTime sendTime = QTime::fromString(timestamp, "hh:mm:ss.zzz");
+    if (sendTime.isValid()) {
+        // Calculate latency from time difference, divided by 2 to account for round-trip time
+        e->network()->setLatency(sendTime.msecsTo(QTime::currentTime()) / 2);
+    } else {
+        // Just in case it's a wonky server, log a debug message to make this easier to track down
+        qDebug() << "Received valid PONG with invalid timestamp, parameters are" << e->params();
     }
 }
 
@@ -846,7 +869,9 @@ void CoreSessionEventProcessor::processIrcEvent301(IrcEvent *e)
     if (ircuser) {
         ircuser->setAway(true);
         ircuser->setAwayMessage(e->params().at(1));
-        //ircuser->setLastAwayMessage(now);
+        // lastAwayMessageTime is set in EventStringifier::processIrcEvent301(), no need to set it
+        // here too
+        //ircuser->setLastAwayMessageTime(now);
     }
 }
 
@@ -959,8 +984,18 @@ void CoreSessionEventProcessor::processIrcEvent317(IrcEvent *e)
 
     int idleSecs = e->params()[1].toInt();
     if (e->params().count() > 3) { // if we have more then 3 params we have the above mentioned "real life" situation
-        int logintime = e->params()[2].toInt();
-        loginTime = QDateTime::fromTime_t(logintime);
+        // Allow for 64-bit time
+        qint64 logintime = e->params()[2].toLongLong();
+        // Time in IRC protocol is defined as seconds.  Convert from seconds instead.
+        // See https://doc.qt.io/qt-5/qdatetime.html#fromSecsSinceEpoch
+#if QT_VERSION >= 0x050800
+        loginTime = QDateTime::fromSecsSinceEpoch(logintime);
+#else
+        // fromSecsSinceEpoch() was added in Qt 5.8.  Manually downconvert to seconds for
+        // now.
+        // See https://doc.qt.io/qt-5/qdatetime.html#fromMSecsSinceEpoch
+        loginTime = QDateTime::fromMSecsSinceEpoch((qint64)(logintime * 1000));
+#endif
     }
 
     IrcUser *ircuser = e->network()->ircUser(e->params()[0]);
@@ -985,10 +1020,13 @@ void CoreSessionEventProcessor::processIrcEvent322(IrcEvent *e)
     switch (e->params().count()) {
     case 3:
         topic = e->params()[2];
+        [[clang::fallthrough]];
     case 2:
         userCount = e->params()[1].toUInt();
+        [[clang::fallthrough]];
     case 1:
         channelName = e->params()[0];
+        [[clang::fallthrough]];
     default:
         break;
     }
@@ -1541,12 +1579,27 @@ void CoreSessionEventProcessor::handleCtcpPing(CtcpEvent *e)
 
 void CoreSessionEventProcessor::handleCtcpTime(CtcpEvent *e)
 {
-    e->setReply(QDateTime::currentDateTime().toString());
+    // Explicitly specify the Qt default DateTime format string to allow for modification
+    // Qt::TextDate default roughly corresponds to...
+    // > ddd MMM d yyyy HH:mm:ss
+    //
+    // See https://doc.qt.io/qt-5/qdatetime.html#toString
+    // And https://doc.qt.io/qt-5/qt.html#DateFormat-enum
+#if QT_VERSION > 0x050000
+    // Append the timezone identifier "t", so other other IRC users have a frame of reference for
+    // the current timezone.  This could be figured out before by manually comparing to UTC, so this
+    // is just convenience.
+
+    // Alas, "t" was only added in Qt 5
+    e->setReply(QDateTime::currentDateTime().toString("ddd MMM d yyyy HH:mm:ss t"));
+#else
+    e->setReply(QDateTime::currentDateTime().toString("ddd MMM d yyyy HH:mm:ss"));
+#endif
 }
 
 
 void CoreSessionEventProcessor::handleCtcpVersion(CtcpEvent *e)
 {
-    e->setReply(QString("Quassel IRC %1 (built on %2) -- http://www.quassel-irc.org")
+    e->setReply(QString("Quassel IRC %1 (built on %2) -- https://www.quassel-irc.org")
         .arg(Quassel::buildInfo().plainVersionString).arg(Quassel::buildInfo().commitDate));
 }