X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fclient%2Fclientsyncer.cpp;h=64d909fa55bf8b61efb888d02cd041c2a0a3c114;hp=655875ffd31e8a65cacaef772b0ef41a2c79a997;hb=3282d98e3b3324f1b1fb573b03dca9e4e247417c;hpb=f4ae0007ac4524612ae73a778ca491659cf22393 diff --git a/src/client/clientsyncer.cpp b/src/client/clientsyncer.cpp index 655875ff..64d909fa 100644 --- a/src/client/clientsyncer.cpp +++ b/src/client/clientsyncer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-08 by the Quassel IRC Team * + * Copyright (C) 2005-09 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -26,20 +26,17 @@ #include "client.h" #include "identity.h" -#include "ircuser.h" -#include "ircchannel.h" #include "network.h" #include "networkmodel.h" #include "quassel.h" #include "signalproxy.h" +#include "util.h" ClientSyncer::ClientSyncer(QObject *parent) - : QObject(parent) + : QObject(parent), + _socket(0), + _blockSize(0) { - socket = 0; - blockSize = 0; - - connect(Client::signalProxy(), SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected())); } ClientSyncer::~ClientSyncer() { @@ -47,7 +44,7 @@ ClientSyncer::~ClientSyncer() { void ClientSyncer::coreHasData() { QVariant item; - while(SignalProxy::readDataFromDevice(socket, blockSize, item)) { + while(SignalProxy::readDataFromDevice(_socket, _blockSize, item)) { emit recvPartialItem(1,1); QVariantMap msg = item.toMap(); if(!msg.contains("MsgType")) { @@ -82,71 +79,55 @@ void ClientSyncer::coreHasData() { return; } } - if(blockSize > 0) { - emit recvPartialItem(socket->bytesAvailable(), blockSize); + if(_blockSize > 0) { + emit recvPartialItem(_socket->bytesAvailable(), _blockSize); } } void ClientSyncer::coreSocketError(QAbstractSocket::SocketError) { - qDebug() << "coreSocketError" << socket << socket->errorString(); - emit connectionError(socket->errorString()); - socket->deleteLater(); + qDebug() << "coreSocketError" << _socket << _socket->errorString(); + emit connectionError(_socket->errorString()); + resetConnection(); } void ClientSyncer::disconnectFromCore() { - if(socket) socket->close(); + resetConnection(); } void ClientSyncer::connectToCore(const QVariantMap &conn) { - // TODO implement SSL + resetConnection(); coreConnectionInfo = conn; - //if(isConnected()) { - // emit coreConnectionError(tr("Already connected to Core!")); - // return; - // } - if(socket != 0) { - socket->deleteLater(); - socket = 0; - } + if(conn["Host"].toString().isEmpty()) { - emit connectionError(tr("Internal connections not yet supported.")); - return; // FIXME implement internal connections - //clientMode = LocalCore; - socket = new QBuffer(this); - connect(socket, SIGNAL(readyRead()), this, SLOT(coreHasData())); - socket->open(QIODevice::ReadWrite); - //QVariant state = connectToLocalCore(coreConnectionInfo["User"].toString(), coreConnectionInfo["Password"].toString()); - //syncToCore(state); - coreSocketConnected(); - } else { - //clientMode = RemoteCore; - //emit coreConnectionMsg(tr("Connecting...")); - Q_ASSERT(!socket); + emit connectionError(tr("No Host to connect to specified.")); + return; + } + Q_ASSERT(!_socket); #ifdef HAVE_SSL - QSslSocket *sock = new QSslSocket(Client::instance()); + QSslSocket *sock = new QSslSocket(Client::instance()); #else - if(conn["useSsl"].toBool()) { - emit connectionError(tr("This client is built without SSL Support!
Disable the usage of SSL in the account settings.")); - emit encrypted(false); - return; - } - QTcpSocket *sock = new QTcpSocket(Client::instance()); + if(conn["useSsl"].toBool()) { + emit connectionError(tr("This client is built without SSL Support!
Disable the usage of SSL in the account settings.")); + return; + } + QTcpSocket *sock = new QTcpSocket(Client::instance()); #endif + #ifndef QT_NO_NETWORKPROXY - if(conn.contains("useProxy") && conn["useProxy"].toBool()) { - QNetworkProxy proxy((QNetworkProxy::ProxyType)conn["proxyType"].toInt(), conn["proxyHost"].toString(), conn["proxyPort"].toUInt(), conn["proxyUser"].toString(), conn["proxyPassword"].toString()); - sock->setProxy(proxy); - } -#endif - socket = sock; - connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData())); - connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected())); - connect(sock, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected())); - connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError))); - connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); - sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt()); + if(conn.contains("useProxy") && conn["useProxy"].toBool()) { + QNetworkProxy proxy((QNetworkProxy::ProxyType)conn["proxyType"].toInt(), conn["proxyHost"].toString(), conn["proxyPort"].toUInt(), conn["proxyUser"].toString(), conn["proxyPassword"].toString()); + sock->setProxy(proxy); } +#endif + + _socket = sock; + connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData())); + connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected())); + connect(sock, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected())); + connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError))); + connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); + sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt()); } void ClientSyncer::coreSocketConnected() { @@ -166,23 +147,45 @@ void ClientSyncer::coreSocketConnected() { clientInit["UseCompression"] = false; #endif - SignalProxy::writeDataToDevice(socket, clientInit); + SignalProxy::writeDataToDevice(_socket, clientInit); } void ClientSyncer::useInternalCore() { + AccountId internalAccountId; + + CoreAccountSettings accountSettings; + QList knownAccounts = accountSettings.knownAccounts(); + foreach(AccountId id, knownAccounts) { + if(!id.isValid()) + continue; + QVariantMap data = accountSettings.retrieveAccountData(id); + if(data.contains("InternalAccount") && data["InternalAccount"].toBool()) { + internalAccountId = id; + break; + } + } + + if(!internalAccountId.isValid()) { + for(AccountId i = 1;; i++) { + if(!knownAccounts.contains(i)) { + internalAccountId = i; + break; + } + } + QVariantMap data; + data["InternalAccount"] = true; + accountSettings.storeAccountData(internalAccountId, data); + } + + coreConnectionInfo["AccountId"] = QVariant::fromValue(internalAccountId); + emit startInternalCore(this); emit connectToInternalCore(Client::instance()->signalProxy()); } void ClientSyncer::coreSocketDisconnected() { emit socketDisconnected(); - Client::instance()->disconnectFromCore(); - - // FIXME handle disconnects gracefully in here as well! - - coreConnectionInfo.clear(); - netsToSync.clear(); - blockSize = 0; - //restartPhaseNull(); + resetConnection(); + // FIXME handle disconnects gracefully } void ClientSyncer::clientInitAck(const QVariantMap &msg) { @@ -196,43 +199,49 @@ void ClientSyncer::clientInitAck(const QVariantMap &msg) { } emit connectionMsg(msg["CoreInfo"].toString()); +#ifndef QT_NO_COMPRESS + if(msg["SupportsCompression"].toBool()) { + _socket->setProperty("UseCompression", true); + } +#endif + + _coreMsgBuffer = msg; #ifdef HAVE_SSL if(coreConnectionInfo["useSsl"].toBool()) { if(msg["SupportSsl"].toBool()) { - QSslSocket *sslSocket = qobject_cast(socket); + QSslSocket *sslSocket = qobject_cast(_socket); Q_ASSERT(sslSocket); + connect(sslSocket, SIGNAL(encrypted()), this, SLOT(sslSocketEncrypted())); connect(sslSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); + sslSocket->startClientEncryption(); - emit encrypted(true); - Client::instance()->setSecuredConnection(); } else { emit connectionError(tr("The Quassel Core you are trying to connect to does not support SSL!
If you want to connect anyways, disable the usage of SSL in the account settings.")); - emit encrypted(false); disconnectFromCore(); - return; } + return; } #endif + // if we use SSL we wait for the next step until every SSL warning has been cleared + connectionReady(); +} -#ifndef QT_NO_COMPRESS - if(msg["SupportsCompression"].toBool()) { - socket->setProperty("UseCompression", true); - } -#endif - - if(!msg["Configured"].toBool()) { +void ClientSyncer::connectionReady() { + if(!_coreMsgBuffer["Configured"].toBool()) { // start wizard - emit startCoreSetup(msg["StorageBackends"].toList()); - } else if(msg["LoginEnabled"].toBool()) { + emit startCoreSetup(_coreMsgBuffer["StorageBackends"].toList()); + } else if(_coreMsgBuffer["LoginEnabled"].toBool()) { emit startLogin(); } + _coreMsgBuffer.clear(); + resetWarningsHandler(); } void ClientSyncer::doCoreSetup(const QVariant &setupData) { QVariantMap setup; setup["MsgType"] = "CoreSetupData"; setup["SetupData"] = setupData; - SignalProxy::writeDataToDevice(socket, setup); + SignalProxy::writeDataToDevice(_socket, setup); } void ClientSyncer::loginToCore(const QString &user, const QString &passwd) { @@ -241,27 +250,31 @@ void ClientSyncer::loginToCore(const QString &user, const QString &passwd) { clientLogin["MsgType"] = "ClientLogin"; clientLogin["User"] = user; clientLogin["Password"] = passwd; - SignalProxy::writeDataToDevice(socket, clientLogin); + SignalProxy::writeDataToDevice(_socket, clientLogin); } void ClientSyncer::internalSessionStateReceived(const QVariant &packedState) { QVariantMap state = packedState.toMap(); emit sessionProgress(1, 1); - // Client::instance()->setConnectedToCore(socket, AccountId()); + Client::instance()->setConnectedToCore(coreConnectionInfo["AccountId"].value()); syncToCore(state); } void ClientSyncer::sessionStateReceived(const QVariantMap &state) { emit sessionProgress(1, 1); disconnect(this, SIGNAL(recvPartialItem(quint32, quint32)), this, SIGNAL(sessionProgress(quint32, quint32))); - disconnect(socket, 0, this, 0); // rest of communication happens through SignalProxy - //Client::signalProxy()->addPeer(socket); - Client::instance()->setConnectedToCore(socket, coreConnectionInfo["AccountId"].value()); + + // rest of communication happens through SignalProxy... + disconnect(_socket, 0, this, 0); + // ... but we still want to be notified about errors... + connect(_socket, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError))); + + Client::instance()->setConnectedToCore(coreConnectionInfo["AccountId"].value(), _socket); syncToCore(state); } void ClientSyncer::syncToCore(const QVariantMap &sessionState) { - // create identities foreach(QVariant vid, sessionState["Identities"].toList()) { Client::instance()->coreIdentityCreated(vid.value()); @@ -285,6 +298,8 @@ void ClientSyncer::syncToCore(const QVariantMap &sessionState) { // create network objects foreach(QVariant networkid, networkids) { NetworkId netid = networkid.value(); + if(Client::network(netid)) + continue; Network *net = new Network(netid, Client::instance()); netsToSync.insert(net); connect(net, SIGNAL(initDone()), this, SLOT(networkInitDone())); @@ -306,14 +321,93 @@ void ClientSyncer::checkSyncState() { } } +void ClientSyncer::setWarningsHandler(const char *slot) { + resetWarningsHandler(); + connect(this, SIGNAL(handleIgnoreWarnings(bool)), this, slot); +} + +void ClientSyncer::resetWarningsHandler() { + disconnect(this, SIGNAL(handleIgnoreWarnings(bool)), this, 0); +} + +void ClientSyncer::resetConnection() { + if(_socket) { + disconnect(_socket, 0, this, 0); + _socket->deleteLater(); + _socket = 0; + } + _blockSize = 0; + + coreConnectionInfo.clear(); + _coreMsgBuffer.clear(); + + netsToSync.clear(); + numNetsToSync = 0; +} + #ifdef HAVE_SSL +void ClientSyncer::ignoreSslWarnings(bool permanently) { + QSslSocket *sock = qobject_cast(_socket); + if(sock) { + // ensure that a proper state is displayed and no longer a warning + emit socketStateChanged(sock->state()); + } + if(permanently) { + if(!sock) + qWarning() << Q_FUNC_INFO << "unable to save cert digest! Socket is either a nullptr or not a QSslSocket"; + else + KnownHostsSettings().saveKnownHost(sock); + } + emit connectionMsg(_coreMsgBuffer["CoreInfo"].toString()); + connectionReady(); +} + +void ClientSyncer::sslSocketEncrypted() { + QSslSocket *socket = qobject_cast(sender()); + Q_ASSERT(socket); + + // if there were sslErrors we already had extensive error handling + // no need to check for a digest change again. + if(!socket->sslErrors().isEmpty()) + return; + + QByteArray knownDigest = KnownHostsSettings().knownDigest(socket); + if(knownDigest == socket->peerCertificate().digest()) { + connectionReady(); + return; + } + + QStringList warnings; + if(!knownDigest.isEmpty()) { + warnings << tr("Cert Digest changed! was: %1").arg(QString(prettyDigest(knownDigest))); + } + + setWarningsHandler(SLOT(ignoreSslWarnings(bool))); + emit connectionWarnings(warnings); +} + void ClientSyncer::sslErrors(const QList &errors) { - qDebug() << "SSL Errors:"; + QSslSocket *socket = qobject_cast(sender()); + Q_ASSERT(socket); + + socket->ignoreSslErrors(); + + QByteArray knownDigest = KnownHostsSettings().knownDigest(socket); + if(knownDigest == socket->peerCertificate().digest()) { + connectionReady(); + return; + } + + QStringList warnings; + foreach(QSslError err, errors) - qDebug() << " " << err; + warnings << err.errorString(); - QSslSocket *socket = qobject_cast(sender()); - if(socket) - socket->ignoreSslErrors(); + if(!knownDigest.isEmpty()) { + warnings << tr("Cert Digest changed! was: %1").arg(QString(prettyDigest(knownDigest))); + } + + setWarningsHandler(SLOT(ignoreSslWarnings(bool))); + emit connectionWarnings(warnings); } #endif