modernize: Reformat ALL the source... again!
[quassel.git] / src / client / coreconnection.cpp
index eeaa5e2..982d0c8 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2012 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  *
 
 #include "coreconnection.h"
 
-#ifndef QT_NO_NETWORKPROXY
-#  include <QNetworkProxy>
-#endif
-
 #include "client.h"
+#include "clientauthhandler.h"
 #include "clientsettings.h"
 #include "coreaccountmodel.h"
 #include "identity.h"
-#include "internalconnection.h"
+#include "internalpeer.h"
 #include "network.h"
 #include "networkmodel.h"
 #include "quassel.h"
 #include "signalproxy.h"
 #include "util.h"
 
-#include "protocols/legacy/legacyconnection.h"
+#include "protocols/legacy/legacypeer.h"
 
-CoreConnection::CoreConnection(CoreAccountModel *model, QObject *parent)
-    : QObject(parent),
-    _model(model),
-    _state(Disconnected),
-    _wantReconnect(false),
-    _progressMinimum(0),
-    _progressMaximum(-1),
-    _progressValue(-1),
-    _wasReconnect(false),
-    _requestedDisconnect(false),
-    _resetting(false)
+CoreConnection::CoreConnection(QObject* parent)
+    : QObject(parent)
+    , _authHandler(nullptr)
 {
     qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
 }
 
-
 void CoreConnection::init()
 {
     Client::signalProxy()->setHeartBeatInterval(30);
-    connect(Client::signalProxy(), SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
-    connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), SIGNAL(lagUpdated(int)));
+    connect(Client::signalProxy(), &SignalProxy::lagUpdated, this, &CoreConnection::lagUpdated);
 
     _reconnectTimer.setSingleShot(true);
-    connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout()));
+    connect(&_reconnectTimer, &QTimer::timeout, this, &CoreConnection::reconnectTimeout);
 
-#ifdef HAVE_KDE
-    connect(Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)),
-        SLOT(solidNetworkStatusChanged(Solid::Networking::Status)));
-#endif
+    _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
+    connect(_qNetworkConfigurationManager.data(), &QNetworkConfigurationManager::onlineStateChanged, this, &CoreConnection::onlineStateChanged);
 
     CoreConnectionSettings s;
-    s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60);
-    s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60);
-    s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant)));
+    s.initAndNotify("PingTimeoutInterval", this, &CoreConnection::pingTimeoutIntervalChanged, 60);
+    s.initAndNotify("ReconnectInterval", this, &CoreConnection::reconnectIntervalChanged, 60);
+    s.notify("NetworkDetectionMode", this, &CoreConnection::networkDetectionModeChanged);
     networkDetectionModeChanged(s.networkDetectionMode());
 }
 
+CoreAccountModel* CoreConnection::accountModel() const
+{
+    return Client::coreAccountModel();
+}
 
-void CoreConnection::setProgressText(const QString &text)
+void CoreConnection::setProgressText(const QStringtext)
 {
     if (_progressText != text) {
         _progressText = text;
@@ -83,7 +72,6 @@ void CoreConnection::setProgressText(const QString &text)
     }
 }
 
-
 void CoreConnection::setProgressValue(int value)
 {
     if (_progressValue != value) {
@@ -92,7 +80,6 @@ void CoreConnection::setProgressValue(int value)
     }
 }
 
-
 void CoreConnection::setProgressMinimum(int minimum)
 {
     if (_progressMinimum != minimum) {
@@ -101,7 +88,6 @@ void CoreConnection::setProgressMinimum(int minimum)
     }
 }
 
-
 void CoreConnection::setProgressMaximum(int maximum)
 {
     if (_progressMaximum != maximum) {
@@ -110,7 +96,6 @@ void CoreConnection::setProgressMaximum(int maximum)
     }
 }
 
-
 void CoreConnection::updateProgress(int value, int max)
 {
     if (max != _progressMaximum) {
@@ -120,32 +105,26 @@ void CoreConnection::updateProgress(int value, int max)
     setProgressValue(value);
 }
 
-
 void CoreConnection::reconnectTimeout()
 {
-    if (!_connection) {
+    if (!_peer) {
         CoreConnectionSettings s;
         if (_wantReconnect && s.autoReconnect()) {
-#ifdef HAVE_KDE
-            // If using Solid, we don't want to reconnect if we're offline
-            if (s.networkDetectionMode() == CoreConnectionSettings::UseSolid) {
-                if (Solid::Networking::status() != Solid::Networking::Connected
-                    && Solid::Networking::status() != Solid::Networking::Unknown) {
+            // If using QNetworkConfigurationManager, we don't want to reconnect if we're offline
+            if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) {
+                if (!_qNetworkConfigurationManager->isOnline()) {
                     return;
                 }
             }
-#endif /* HAVE_KDE */
-
             reconnectToCore();
         }
     }
 }
 
-
-void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
+void CoreConnection::networkDetectionModeChanged(const QVariant& vmode)
 {
     CoreConnectionSettings s;
-    CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
+    auto mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
     if (mode == CoreConnectionSettings::UsePingTimeout)
         Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
     else {
@@ -153,215 +132,116 @@ void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
     }
 }
 
-
-void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
+void CoreConnection::pingTimeoutIntervalChanged(const QVariant& interval)
 {
     CoreConnectionSettings s;
     if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
         Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30);  // interval is 30 seconds
 }
 
-
-void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
+void CoreConnection::reconnectIntervalChanged(const QVariant& interval)
 {
     _reconnectTimer.setInterval(interval.toInt() * 1000);
 }
 
-
-#ifdef HAVE_KDE
-
-void CoreConnection::solidNetworkStatusChanged(Solid::Networking::Status status)
+void CoreConnection::onlineStateChanged(bool isOnline)
 {
     CoreConnectionSettings s;
-    if (s.networkDetectionMode() != CoreConnectionSettings::UseSolid)
+    if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
         return;
 
-    switch (status) {
-    case Solid::Networking::Unknown:
-    case Solid::Networking::Connected:
-        //qDebug() << "Solid: Network status changed to connected or unknown";
+    if (isOnline) {
+        // qDebug() << "QNetworkConfigurationManager reports Online";
         if (state() == Disconnected) {
             if (_wantReconnect && s.autoReconnect()) {
                 reconnectToCore();
             }
         }
-        break;
-    case Solid::Networking::Disconnecting:
-    case Solid::Networking::Unconnected:
+    }
+    else {
+        // qDebug() << "QNetworkConfigurationManager reports Offline";
         if (state() != Disconnected && !isLocalConnection())
             disconnectFromCore(tr("Network is down"), true);
-        break;
-    default:
-        break;
     }
 }
 
-
-#endif
+QPointer<Peer> CoreConnection::peer() const
+{
+    if (_peer) {
+        return _peer;
+    }
+    return _authHandler ? _authHandler->peer() : nullptr;
+}
 
 bool CoreConnection::isEncrypted() const
 {
-    return _connection && _connection->isSecure();
+    return _peer && _peer->isSecure();
 }
 
-
 bool CoreConnection::isLocalConnection() const
 {
     if (!isConnected())
         return false;
     if (currentAccount().isInternal())
         return true;
-    if (_connection->isLocal())
-        return true;
+    if (_authHandler)
+        return _authHandler->isLocal();
+    if (_peer)
+        return _peer->isLocal();
 
     return false;
 }
 
-
-void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState)
-{
-    QString text;
-
-    switch (socketState) {
-    case QAbstractSocket::UnconnectedState:
-        text = tr("Disconnected");
-        break;
-    case QAbstractSocket::HostLookupState:
-        text = tr("Looking up %1...").arg(currentAccount().hostName());
-        break;
-    case QAbstractSocket::ConnectingState:
-        text = tr("Connecting to %1...").arg(currentAccount().hostName());
-        break;
-    case QAbstractSocket::ConnectedState:
-        text = tr("Connected to %1").arg(currentAccount().hostName());
-        break;
-    case QAbstractSocket::ClosingState:
-        text = tr("Disconnecting from %1...").arg(currentAccount().hostName());
-        break;
-    default:
-        break;
-    }
-
-    if (!text.isEmpty())
-        emit progressTextChanged(text);
-
-    setState(socketState);
-}
-
-
-void CoreConnection::setState(QAbstractSocket::SocketState socketState)
+void CoreConnection::onConnectionReady()
 {
-    ConnectionState state;
-
-    switch (socketState) {
-    case QAbstractSocket::UnconnectedState:
-        state = Disconnected;
-        break;
-    case QAbstractSocket::HostLookupState:
-    case QAbstractSocket::ConnectingState:
-    case QAbstractSocket::ConnectedState: // we'll set it to Connected in connectionReady()
-        state = Connecting;
-        break;
-    default:
-        state = Disconnected;
-    }
-
-    setState(state);
+    setState(Connected);
 }
 
-
 void CoreConnection::setState(ConnectionState state)
 {
     if (state != _state) {
         _state = state;
         emit stateChanged(state);
+        if (state == Connected)
+            _wantReconnect = true;
         if (state == Disconnected)
             emit disconnected();
     }
 }
 
-
-void CoreConnection::coreSocketError(QAbstractSocket::SocketError)
+void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString& errorString)
 {
-    disconnectFromCore(_socket->errorString(), true);
-}
-
+    Q_UNUSED(error)
 
-void CoreConnection::coreSocketDisconnected()
-{
-    _wasReconnect = !_requestedDisconnect;
-    resetConnection(true);
-    // FIXME handle disconnects gracefully
+    disconnectFromCore(errorString, true);
 }
 
-
-// note: this still expects the legacy protocol
-// noteĀ²: after cleaning this up, we can probably get rid of _socket altogether
-void CoreConnection::coreHasData(const QVariant &item)
+void CoreConnection::coreSocketDisconnected()
 {
-    QVariantMap msg = item.toMap();
-    if (!msg.contains("MsgType")) {
-        // This core is way too old and does not even speak our init protocol...
-        emit connectionErrorPopup(tr("The Quassel Core you try to connect to is too old! Please consider upgrading."));
-        disconnectFromCore(QString(), false);
-        return;
-    }
-    if (msg["MsgType"] == "ClientInitAck") {
-        clientInitAck(msg);
-    }
-    else if (msg["MsgType"] == "ClientInitReject") {
-        emit connectionErrorPopup(msg["Error"].toString());
-        disconnectFromCore(QString(), false);
-        return;
-    }
-    else if (msg["MsgType"] == "CoreSetupAck") {
-        emit coreSetupSuccess();
-    }
-    else if (msg["MsgType"] == "CoreSetupReject") {
-        emit coreSetupFailed(msg["Error"].toString());
-    }
-    else if (msg["MsgType"] == "ClientLoginReject") {
-        loginFailed(msg["Error"].toString());
-    }
-    else if (msg["MsgType"] == "ClientLoginAck") {
-        loginSuccess();
-    }
-    else if (msg["MsgType"] == "SessionInit") {
-        // that's it, let's hand over to the signal proxy
-        // if the connection is an orphan, the signalProxy adopts it.
-        // -> we don't need to care about it anymore
-
-        disconnect(_connection, 0, this, 0);
-
-        _connection->setParent(0);
-        Client::signalProxy()->addPeer(_connection);
-
-        sessionStateReceived(msg["SessionState"].toMap());
-    }
-    else {
-        disconnectFromCore(tr("Invalid data received from core"), false);
-        return;
-    }
+    setState(Disconnected);
+    _wasReconnect = false;
+    resetConnection(_wantReconnect);
 }
 
-
 void CoreConnection::disconnectFromCore()
 {
-    if (_socket) {
-        _requestedDisconnect = true;
-        disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
-    }
+    disconnectFromCore(QString(), false);  // requested disconnect, so don't try to reconnect
 }
 
-
-void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
+void CoreConnection::disconnectFromCore(const QString& errorString, bool wantReconnect)
 {
-    if (!wantReconnect)
+    if (wantReconnect)
+        _reconnectTimer.start();
+    else
         _reconnectTimer.stop();
 
-    _wasReconnect = wantReconnect; // store if disconnect was requested
+    _wantReconnect = wantReconnect;  // store if disconnect was requested
+    _wasReconnect = false;
 
-    resetConnection(wantReconnect);
+    if (_authHandler)
+        _authHandler->close();
+    else if (_peer)
+        _peer->close();
 
     if (errorString.isEmpty())
         emit connectionError(tr("Disconnected"));
@@ -369,7 +249,6 @@ void CoreConnection::disconnectFromCore(const QString &errorString, bool wantRec
         emit connectionError(errorString);
 }
 
-
 void CoreConnection::resetConnection(bool wantReconnect)
 {
     if (_resetting)
@@ -378,34 +257,30 @@ void CoreConnection::resetConnection(bool wantReconnect)
 
     _wantReconnect = wantReconnect;
 
-    if (_connection) {
-        disconnect(_socket, 0, this, 0);
-        disconnect(_connection, 0, this, 0);
-        _connection->close();
-
-        if (_connection->parent() == this)
-            _connection->deleteLater(); // if it's not us, it belongs to the sigproxy which will delete it
-        _socket = 0;      // socket is owned and will be deleted by RemoteConnection
-        _connection = 0;
-    }
-    else if (_socket) {
-        disconnect(_socket, 0, this, 0);
-        _socket->deleteLater();
-        _socket = 0;
+    if (_authHandler) {
+        disconnect(_authHandler, nullptr, this, nullptr);
+        _authHandler->close();
+        _authHandler->deleteLater();
+        _authHandler = nullptr;
     }
 
-    _requestedDisconnect = false;
+    if (_peer) {
+        disconnect(_peer, nullptr, this, nullptr);
+        // peer belongs to the sigproxy and thus gets deleted by it
+        _peer->close();
+        _peer = nullptr;
+    }
 
-    _coreMsgBuffer.clear();
     _netsToSync.clear();
     _numNetsToSync = 0;
 
-    setProgressMaximum(-1); // disable
+    setProgressMaximum(-1);  // disable
     setState(Disconnected);
     emit lagUpdated(-1);
 
     emit connectionMsg(tr("Disconnected from core."));
     emit encrypted(false);
+    setState(Disconnected);
 
     // initiate if a reconnect if appropriate
     CoreConnectionSettings s;
@@ -416,14 +291,14 @@ void CoreConnection::resetConnection(bool wantReconnect)
     _resetting = false;
 }
 
-
 void CoreConnection::reconnectToCore()
 {
-    if (currentAccount().isValid())
+    if (currentAccount().isValid()) {
+        _wasReconnect = true;
         connectToCore(currentAccount().accountId());
+    }
 }
 
-
 bool CoreConnection::connectToCore(AccountId accId)
 {
     if (isConnected())
@@ -463,336 +338,156 @@ bool CoreConnection::connectToCore(AccountId accId)
     return true;
 }
 
-
 void CoreConnection::connectToCurrentAccount()
 {
-    resetConnection(false);
+    if (_authHandler) {
+        qWarning() << Q_FUNC_INFO << "Already connected!";
+        return;
+    }
 
     if (currentAccount().isInternal()) {
         if (Quassel::runMode() != Quassel::Monolithic) {
             qWarning() << "Cannot connect to internal core in client-only mode!";
             return;
         }
-        emit startInternalCore();
-
-        InternalConnection *conn = new InternalConnection();
-        Client::instance()->signalProxy()->addPeer(conn); // sigproxy will take ownership
-        emit connectToInternalCore(conn);
 
+        auto* peer = new InternalPeer();
+        _peer = peer;
+        Client::instance()->signalProxy()->addPeer(peer);  // sigproxy will take ownership
+        emit connectionMsg(tr("Initializing..."));
+        emit connectToInternalCore(peer);
+        setState(Connected);
         return;
     }
 
-    CoreAccountSettings s;
-
-    Q_ASSERT(!_socket);
+    _authHandler = new ClientAuthHandler(currentAccount(), this);
+
+    connect(_authHandler, &ClientAuthHandler::disconnected, this, &CoreConnection::coreSocketDisconnected);
+    connect(_authHandler, &ClientAuthHandler::connectionReady, this, &CoreConnection::onConnectionReady);
+    connect(_authHandler, &ClientAuthHandler::socketError, this, &CoreConnection::coreSocketError);
+    connect(_authHandler, &ClientAuthHandler::transferProgress, this, &CoreConnection::updateProgress);
+    connect(_authHandler,
+            &ClientAuthHandler::requestDisconnect,
+            this,
+            selectOverload<const QString&, bool>(&CoreConnection::disconnectFromCore));
+
+    connect(_authHandler, &ClientAuthHandler::errorMessage, this, &CoreConnection::connectionError);
+    connect(_authHandler, &ClientAuthHandler::errorPopup, this, &CoreConnection::connectionErrorPopup, Qt::QueuedConnection);
+    connect(_authHandler, &ClientAuthHandler::statusMessage, this, &CoreConnection::connectionMsg);
+    connect(_authHandler, &ClientAuthHandler::encrypted, this, &CoreConnection::encrypted);
+    connect(_authHandler, &ClientAuthHandler::startCoreSetup, this, &CoreConnection::startCoreSetup);
+    connect(_authHandler, &ClientAuthHandler::coreSetupFailed, this, &CoreConnection::coreSetupFailed);
+    connect(_authHandler, &ClientAuthHandler::coreSetupSuccessful, this, &CoreConnection::coreSetupSuccess);
+    connect(_authHandler, &ClientAuthHandler::userAuthenticationRequired, this, &CoreConnection::userAuthenticationRequired);
+    connect(_authHandler, &ClientAuthHandler::handleNoSslInClient, this, &CoreConnection::handleNoSslInClient);
+    connect(_authHandler, &ClientAuthHandler::handleNoSslInCore, this, &CoreConnection::handleNoSslInCore);
 #ifdef HAVE_SSL
-    QSslSocket *sock = new QSslSocket(this);
-    // make sure the warning is shown if we happen to connect without SSL support later
-    s.setAccountValue("ShowNoClientSslWarning", true);
-#else
-    if (_account.useSsl()) {
-        if (s.accountValue("ShowNoClientSslWarning", true).toBool()) {
-            bool accepted = false;
-            emit handleNoSslInClient(&accepted);
-            if (!accepted) {
-                emit connectionError(tr("Unencrypted connection canceled"));
-                return;
-            }
-            s.setAccountValue("ShowNoClientSslWarning", false);
-        }
-    }
-    QTcpSocket *sock = new QTcpSocket(this);
+    connect(_authHandler, &ClientAuthHandler::handleSslErrors, this, &CoreConnection::handleSslErrors);
 #endif
+    connect(_authHandler, &ClientAuthHandler::loginSuccessful, this, &CoreConnection::onLoginSuccessful);
+    connect(_authHandler, &ClientAuthHandler::handshakeComplete, this, &CoreConnection::onHandshakeComplete);
 
-#ifndef QT_NO_NETWORKPROXY
-    if (_account.useProxy()) {
-        QNetworkProxy proxy(_account.proxyType(), _account.proxyHostName(), _account.proxyPort(), _account.proxyUser(), _account.proxyPassword());
-        sock->setProxy(proxy);
-    }
-#endif
-
-    _socket = sock;
-    connect(sock, SIGNAL(connected()), SLOT(coreSocketConnected()));
-    connect(sock, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
-    connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(coreSocketError(QAbstractSocket::SocketError)));
-    connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
-
-    emit connectionMsg(tr("Connecting to %1...").arg(currentAccount().accountName()));
-    sock->connectToHost(_account.hostName(), _account.port());
+    setState(Connecting);
+    _authHandler->connectToCore();
 }
 
-
-void CoreConnection::coreSocketConnected()
-{
-    // Create the connection which will handle the incoming data
-    Q_ASSERT(!_connection);
-    _connection = new LegacyConnection(_socket, this);
-    connect(_connection, SIGNAL(dataReceived(QVariant)), SLOT(coreHasData(QVariant)));
-    connect(_connection, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
-
-    // Phase One: Send client info and wait for core info
-
-    emit connectionMsg(tr("Synchronizing to core..."));
-
-    QVariantMap clientInit;
-    clientInit["MsgType"] = "ClientInit";
-    clientInit["ClientVersion"] = Quassel::buildInfo().fancyVersionString;
-    clientInit["ClientDate"] = Quassel::buildInfo().buildDate;
-    clientInit["ProtocolVersion"] = Quassel::buildInfo().protocolVersion;
-    clientInit["UseSsl"] = _account.useSsl();
-#ifndef QT_NO_COMPRESS
-    clientInit["UseCompression"] = true;
-#else
-    clientInit["UseCompression"] = false;
-#endif
-
-    qobject_cast<RemoteConnection *>(_connection)->writeSocketData(clientInit);
-}
-
-
-void CoreConnection::clientInitAck(const QVariantMap &msg)
-{
-    // Core has accepted our version info and sent its own. Let's see if we accept it as well...
-    uint ver = msg["ProtocolVersion"].toUInt();
-    if (ver < Quassel::buildInfo().clientNeedsProtocol) {
-        emit connectionErrorPopup(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
-                                     "Need at least core/client protocol v%1 to connect.").arg(Quassel::buildInfo().clientNeedsProtocol));
-        disconnectFromCore(QString(), false);
-        return;
-    }
-
-    Client::setCoreFeatures((Quassel::Features)msg["CoreFeatures"].toUInt());
-
-#ifndef QT_NO_COMPRESS
-    if (msg["SupportsCompression"].toBool()) {
-        _socket->setProperty("UseCompression", true);
-    }
-#endif
-
-    _coreMsgBuffer = msg;
-
-#ifdef HAVE_SSL
-    CoreAccountSettings s;
-    if (currentAccount().useSsl()) {
-        if (msg["SupportSsl"].toBool()) {
-            // Make sure the warning is shown next time we don't have SSL in the core
-            s.setAccountValue("ShowNoCoreSslWarning", true);
-
-            QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_socket);
-            Q_ASSERT(sslSocket);
-            connect(sslSocket, SIGNAL(encrypted()), SLOT(sslSocketEncrypted()));
-            connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(sslErrors()));
-            sslSocket->startClientEncryption();
-        }
-        else {
-            if (s.accountValue("ShowNoCoreSslWarning", true).toBool()) {
-                bool accepted = false;
-                emit handleNoSslInCore(&accepted);
-                if (!accepted) {
-                    disconnectFromCore(tr("Unencrypted connection canceled"), false);
-                    return;
-                }
-                s.setAccountValue("ShowNoCoreSslWarning", false);
-                s.setAccountValue("SslCert", QString());
-            }
-            connectionReady();
-        }
-        return;
-    }
-#endif
-    // if we use SSL we wait for the next step until every SSL warning has been cleared
-    connectionReady();
-}
-
-
-#ifdef HAVE_SSL
-
-void CoreConnection::sslSocketEncrypted()
-{
-    QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
-    Q_ASSERT(socket);
-
-    if (!socket->sslErrors().count()) {
-        // Cert is valid, so we don't want to store it as known
-        // That way, a warning will appear in case it becomes invalid at some point
-        CoreAccountSettings s;
-        s.setAccountValue("SSLCert", QString());
-    }
-
-    emit encrypted(true);
-    connectionReady();
-}
-
-
-void CoreConnection::sslErrors()
-{
-    QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
-    Q_ASSERT(socket);
-
-    CoreAccountSettings s;
-    QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
-
-    if (knownDigest != socket->peerCertificate().digest()) {
-        bool accepted = false;
-        bool permanently = false;
-        emit handleSslErrors(socket, &accepted, &permanently);
-
-        if (!accepted) {
-            disconnectFromCore(tr("Unencrypted connection canceled"), false);
-            return;
-        }
-
-        if (permanently)
-            s.setAccountValue("SslCert", socket->peerCertificate().digest());
-        else
-            s.setAccountValue("SslCert", QString());
-    }
-
-    socket->ignoreSslErrors();
-}
-
-
-#endif /* HAVE_SSL */
-
-void CoreConnection::connectionReady()
-{
-    setState(Connected);
-    emit connectionMsg(tr("Connected to %1").arg(currentAccount().accountName()));
-
-    if (!_coreMsgBuffer["Configured"].toBool()) {
-        // start wizard
-        emit startCoreSetup(_coreMsgBuffer["StorageBackends"].toList());
-    }
-    else if (_coreMsgBuffer["LoginEnabled"].toBool()) {
-        loginToCore();
-    }
-    _coreMsgBuffer.clear();
-}
-
-
-void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
-{
-    _account.setUser(user);
-    _account.setPassword(password);
-    _account.setStorePassword(remember);
-    loginToCore();
-}
-
-
-void CoreConnection::loginToCore(const QString &prevError)
+void CoreConnection::setupCore(const Protocol::SetupData& setupData)
 {
-    emit connectionMsg(tr("Logging in..."));
-    if (currentAccount().user().isEmpty() || currentAccount().password().isEmpty() || !prevError.isEmpty()) {
-        bool valid = false;
-        emit userAuthenticationRequired(&_account, &valid, prevError); // *must* be a synchronous call
-        if (!valid || currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) {
-            disconnectFromCore(tr("Login canceled"), false);
-            return;
-        }
-    }
-
-    QVariantMap clientLogin;
-    clientLogin["MsgType"] = "ClientLogin";
-    clientLogin["User"] = currentAccount().user();
-    clientLogin["Password"] = currentAccount().password();
-    qobject_cast<RemoteConnection*>(_connection)->writeSocketData(clientLogin);
+    _authHandler->setupCore(setupData);
 }
 
-
-void CoreConnection::loginFailed(const QString &error)
+void CoreConnection::loginToCore(const QString& user, const QString& password, bool remember)
 {
-    loginToCore(error);
+    _authHandler->login(user, password, remember);
 }
 
-
-void CoreConnection::loginSuccess()
+void CoreConnection::onLoginSuccessful(const CoreAccount& account)
 {
     updateProgress(0, 0);
 
     // save current account data
-    _model->createOrUpdateAccount(currentAccount());
-    _model->save();
+    accountModel()->createOrUpdateAccount(account);
+    accountModel()->save();
 
     _reconnectTimer.stop();
 
     setProgressText(tr("Receiving session state"));
     setState(Synchronizing);
-    emit connectionMsg(tr("Synchronizing to %1...").arg(currentAccount().accountName()));
+    emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
 }
 
-
-void CoreConnection::sessionStateReceived(const QVariantMap &state)
+void CoreConnection::onHandshakeComplete(RemotePeer* peer, const Protocol::SessionState& sessionState)
 {
     updateProgress(100, 100);
 
-    syncToCore(state);
-}
+    disconnect(_authHandler, nullptr, this, nullptr);
+    _authHandler->deleteLater();
+    _authHandler = nullptr;
+
+    _peer = peer;
+    connect(peer, &Peer::disconnected, this, &CoreConnection::coreSocketDisconnected);
+    connect(peer, &RemotePeer::statusMessage, this, &CoreConnection::connectionMsg);
+    connect(peer, &RemotePeer::socketError, this, &CoreConnection::coreSocketError);
+
+    Client::signalProxy()->addPeer(_peer);  // sigproxy takes ownership of the peer!
 
+    syncToCore(sessionState);
+}
 
-void CoreConnection::internalSessionStateReceived(const QVariant &packedState)
+void CoreConnection::internalSessionStateReceived(const Protocol::SessionState& sessionState)
 {
     updateProgress(100, 100);
-
     setState(Synchronizing);
-    syncToCore(packedState.toMap());
+    syncToCore(sessionState);
 }
 
-
-void CoreConnection::syncToCore(const QVariantMap &sessionState)
+void CoreConnection::syncToCore(const Protocol::SessionState& sessionState)
 {
-    if (sessionState.contains("CoreFeatures"))
-        Client::setCoreFeatures((Quassel::Features)sessionState["CoreFeatures"].toUInt());
-
     setProgressText(tr("Receiving network states"));
     updateProgress(0, 100);
 
     // create identities
-    foreach(QVariant vid, sessionState["Identities"].toList()) {
+    foreach (const QVariant& vid, sessionState.identities) {
         Client::instance()->coreIdentityCreated(vid.value<Identity>());
     }
 
     // create buffers
     // FIXME: get rid of this crap -- why?
-    QVariantList bufferinfos = sessionState["BufferInfos"].toList();
-    NetworkModel *networkModel = Client::networkModel();
+    NetworkModel* networkModel = Client::networkModel();
     Q_ASSERT(networkModel);
-    foreach(QVariant vinfo, bufferinfos)
-    networkModel->bufferUpdated(vinfo.value<BufferInfo>());  // create BufferItems
-
-    QVariantList networkids = sessionState["NetworkIds"].toList();
+    foreach (const QVariant& vinfo, sessionState.bufferInfos)
+        networkModel->bufferUpdated(vinfo.value<BufferInfo>());  // create BufferItems
 
     // prepare sync progress thingys...
     // FIXME: Care about removal of networks
-    _numNetsToSync = networkids.count();
+    _numNetsToSync = sessionState.networkIds.count();
     updateProgress(0, _numNetsToSync);
 
     // create network objects
-    foreach(QVariant networkid, networkids) {
+    foreach (const QVariant& networkid, sessionState.networkIds) {
         NetworkId netid = networkid.value<NetworkId>();
         if (Client::network(netid))
             continue;
-        Network *net = new Network(netid, Client::instance());
+        auto* net = new Network(netid, Client::instance());
         _netsToSync.insert(net);
-        connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
-        connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
+        connect(net, &SyncableObject::initDone, this, &CoreConnection::networkInitDone);
+        connect(net, &QObject::destroyed, this, &CoreConnection::networkInitDone);
         Client::addNetwork(net);
     }
     checkSyncState();
 }
 
-
 // this is also called for destroyed networks!
 void CoreConnection::networkInitDone()
 {
-    QObject *net = sender();
+    QObjectnet = sender();
     Q_ASSERT(net);
-    disconnect(net, 0, this, 0);
+    disconnect(net, nullptr, this, nullptr);
     _netsToSync.remove(net);
     updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
     checkSyncState();
 }
 
-
 void CoreConnection::checkSyncState()
 {
     if (_netsToSync.isEmpty() && state() >= Synchronizing) {
@@ -802,12 +497,3 @@ void CoreConnection::checkSyncState()
         emit synchronized();
     }
 }
-
-
-void CoreConnection::doCoreSetup(const QVariant &setupData)
-{
-    QVariantMap setup;
-    setup["MsgType"] = "CoreSetupData";
-    setup["SetupData"] = setupData;
-    qobject_cast<RemoteConnection *>(_connection)->writeSocketData(setup);
-}