Replace build date with commit date (#159)
[quassel.git] / src / client / clientauthhandler.cpp
index 15efef3..c8ed735 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2014 by the Quassel Project                        *
+ *   Copyright (C) 2005-2016 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 #include "clientsettings.h"
 #include "peerfactory.h"
 
+#if QT_VERSION < 0x050000
+#    include "../../3rdparty/sha512/sha512.h"
+#endif
+
 using namespace Protocol;
 
 ClientAuthHandler::ClientAuthHandler(CoreAccount account, QObject *parent)
@@ -114,7 +118,9 @@ void ClientAuthHandler::onSocketStateChanged(QAbstractSocket::SocketState socket
                 text = tr("Disconnected");
                 // Ensure the disconnected() signal is sent even if we haven't reached the Connected state yet.
                 // The baseclass implementation will make sure to only send the signal once.
-                onSocketDisconnected();
+                // However, we do want to prefer a potential socket error signal that may be on route already, so
+                // give this a chance to overtake us by spinning the loop...
+                QTimer::singleShot(0, this, SLOT(onSocketDisconnected()));
             }
             break;
         default:
@@ -167,13 +173,14 @@ void ClientAuthHandler::onSocketConnected()
         _probing = true;
 
         QDataStream stream(socket()); // stream handles the endianness for us
+        stream.setVersion(QDataStream::Qt_4_2);
 
         quint32 magic = Protocol::magic;
 #ifdef HAVE_SSL
         if (_account.useSsl())
             magic |= Protocol::Encryption;
 #endif
-        //magic |= Protocol::Compression; // not implemented yet
+        magic |= Protocol::Compression;
 
         stream << magic;
 
@@ -195,7 +202,7 @@ void ClientAuthHandler::onSocketConnected()
 
     qDebug() << "Legacy core detected, switching to compatibility mode";
 
-    RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(Protocol::LegacyProtocol, 0), this, socket(), this);
+    RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(Protocol::LegacyProtocol, 0), this, socket(), Compressor::NoCompression, this);
     // Only needed for the legacy peer, as all others check the protocol version before instantiation
     connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
 
@@ -222,7 +229,13 @@ void ClientAuthHandler::onReadyRead()
     quint16 protoFeatures = static_cast<quint16>(reply>>8 & 0xffff);
     _connectionFeatures = static_cast<quint8>(reply>>24);
 
-    RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(type, protoFeatures), this, socket(), this);
+    Compressor::CompressionLevel level;
+    if (_connectionFeatures & Protocol::Compression)
+        level = Compressor::BestCompression;
+    else
+        level = Compressor::NoCompression;
+
+    RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(type, protoFeatures), this, socket(), level, this);
     if (!peer) {
         qWarning() << "No valid protocol supported for this core!";
         emit errorPopup(tr("<b>Incompatible Quassel Core!</b><br>"
@@ -275,7 +288,7 @@ void ClientAuthHandler::startRegistration()
     useSsl = _account.useSsl();
 #endif
 
-    _peer->dispatch(RegisterClient(Quassel::buildInfo().fancyVersionString, useSsl));
+    _peer->dispatch(RegisterClient(Quassel::buildInfo().fancyVersionString, Quassel::buildInfo().commitDate, useSsl));
 }
 
 
@@ -414,6 +427,7 @@ void ClientAuthHandler::checkAndEnableSsl(bool coreSupportsSsl)
             }
             s.setAccountValue("ShowNoCoreSslWarning", false);
             s.setAccountValue("SslCert", QString());
+            s.setAccountValue("SslCertDigestVersion", QVariant(QVariant::Int));
         }
         if (_legacy)
             onConnectionReady();
@@ -435,6 +449,7 @@ void ClientAuthHandler::onSslSocketEncrypted()
         // That way, a warning will appear in case it becomes invalid at some point
         CoreAccountSettings s;
         s.setAccountValue("SSLCert", QString());
+        s.setAccountValue("SslCertDigestVersion", QVariant(QVariant::Int));
     }
 
     emit encrypted(true);
@@ -453,8 +468,27 @@ void ClientAuthHandler::onSslErrors()
 
     CoreAccountSettings s;
     QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
+    ClientAuthHandler::DigestVersion knownDigestVersion = static_cast<ClientAuthHandler::DigestVersion>(s.accountValue("SslCertDigestVersion").toInt());
+
+    QByteArray calculatedDigest;
+    switch (knownDigestVersion) {
+    case ClientAuthHandler::DigestVersion::Md5:
+        calculatedDigest = socket->peerCertificate().digest(QCryptographicHash::Md5);
+        break;
 
-    if (knownDigest != socket->peerCertificate().digest()) {
+    case ClientAuthHandler::DigestVersion::Sha2_512:
+#if QT_VERSION >= 0x050000
+        calculatedDigest = socket->peerCertificate().digest(QCryptographicHash::Sha512);
+#else
+        calculatedDigest = sha2_512(socket->peerCertificate().toDer());
+#endif
+        break;
+
+    default:
+        qWarning() << "Certificate digest version" << QString(knownDigestVersion) << "is not supported";
+    }
+    
+    if (knownDigest != calculatedDigest) {
         bool accepted = false;
         bool permanently = false;
         emit handleSslErrors(socket, &accepted, &permanently);
@@ -464,13 +498,42 @@ void ClientAuthHandler::onSslErrors()
             return;
         }
 
-        if (permanently)
-            s.setAccountValue("SslCert", socket->peerCertificate().digest());
-        else
+        if (permanently) {
+#if QT_VERSION >= 0x050000
+            s.setAccountValue("SslCert", socket->peerCertificate().digest(QCryptographicHash::Sha512));
+#else
+            s.setAccountValue("SslCert", sha2_512(socket->peerCertificate().toDer()));
+#endif
+            s.setAccountValue("SslCertDigestVersion", ClientAuthHandler::DigestVersion::Latest);
+        }
+        else {
             s.setAccountValue("SslCert", QString());
+            s.setAccountValue("SslCertDigestVersion", QVariant(QVariant::Int));
+        }
+    }
+    else if (knownDigestVersion != ClientAuthHandler::DigestVersion::Latest) {
+#if QT_VERSION >= 0x050000
+        s.setAccountValue("SslCert", socket->peerCertificate().digest(QCryptographicHash::Sha512));
+#else
+        s.setAccountValue("SslCert", sha2_512(socket->peerCertificate().toDer()));
+#endif
+        s.setAccountValue("SslCertDigestVersion", ClientAuthHandler::DigestVersion::Latest);
     }
 
     socket->ignoreSslErrors();
 }
 
+#if QT_VERSION < 0x050000
+QByteArray ClientAuthHandler::sha2_512(const QByteArray &input) {
+    unsigned char output[64];
+    sha512((unsigned char*) input.constData(), input.size(), output, false);
+    // QByteArray::fromRawData() cannot be used here because that constructor
+    // does not copy "output" and the data is clobbered when the variable goes
+    // out of scope.
+    QByteArray result;
+    result.append((char*) output, 64);
+    return result;
+}
+#endif
+
 #endif /* HAVE_SSL */