X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fclient%2Fclientsyncer.cpp;h=64d909fa55bf8b61efb888d02cd041c2a0a3c114;hp=9534b6fd84d8444a3a4c1a5748a0a71aceba71d2;hb=3282d98e3b3324f1b1fb573b03dca9e4e247417c;hpb=26c15c14a067c8709d2e04ef9d8965550dcee52d diff --git a/src/client/clientsyncer.cpp b/src/client/clientsyncer.cpp index 9534b6fd..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 * @@ -20,33 +20,31 @@ #include "clientsyncer.h" -#include +#ifndef QT_NO_NETWORKPROXY +# include +#endif #include "client.h" -#include "global.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) { - socket = 0; - blockSize = 0; - - connect(Client::signalProxy(), SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected())); - +ClientSyncer::ClientSyncer(QObject *parent) + : QObject(parent), + _socket(0), + _blockSize(0) +{ } 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")) { @@ -81,58 +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) { - 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); - QTcpSocket *sock = new QTcpSocket(Client::instance()); - 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); - } - 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()); + emit connectionError(tr("No Host to connect to specified.")); + return; } + + Q_ASSERT(!_socket); +#ifdef HAVE_SSL + 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.")); + 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()); } void ClientSyncer::coreSocketConnected() { @@ -142,49 +137,111 @@ void ClientSyncer::coreSocketConnected() { //emit coreConnectionMsg(tr("Synchronizing to core...")); QVariantMap clientInit; clientInit["MsgType"] = "ClientInit"; - clientInit["ClientVersion"] = Global::quasselVersion; - clientInit["ClientDate"] = Global::quasselDate; - clientInit["ClientBuild"] = Global::quasselBuild; // this is a minimum, since we probably won't update for every commit - clientInit["UseSsl"] = false; // FIXME implement SSL - SignalProxy::writeDataToDevice(socket, clientInit); + clientInit["ClientVersion"] = Quassel::buildInfo().fancyVersionString; + clientInit["ClientDate"] = Quassel::buildInfo().buildDate; + clientInit["ProtocolVersion"] = Quassel::buildInfo().protocolVersion; + clientInit["UseSsl"] = coreConnectionInfo["useSsl"]; +#ifndef QT_NO_COMPRESS + clientInit["UseCompression"] = true; +#else + clientInit["UseCompression"] = false; +#endif + + SignalProxy::writeDataToDevice(_socket, clientInit); } -void ClientSyncer::coreSocketDisconnected() { - emit socketDisconnected(); - Client::instance()->disconnectFromCore(); +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; + } + } - // FIXME handle disconnects gracefully in here as well! + 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.clear(); - netsToSync.clear(); - channelsToSync.clear(); - usersToSync.clear(); - blockSize = 0; - //restartPhaseNull(); + coreConnectionInfo["AccountId"] = QVariant::fromValue(internalAccountId); + emit startInternalCore(this); + emit connectToInternalCore(Client::instance()->signalProxy()); +} + +void ClientSyncer::coreSocketDisconnected() { + emit socketDisconnected(); + resetConnection(); + // FIXME handle disconnects gracefully } void ClientSyncer::clientInitAck(const QVariantMap &msg) { // Core has accepted our version info and sent its own. Let's see if we accept it as well... - if(msg["CoreBuild"].toUInt() < Global::coreBuildNeeded) { + uint ver = msg["ProtocolVersion"].toUInt(); + if(ver < Quassel::buildInfo().clientNeedsProtocol) { emit connectionError(tr("The Quassel Core you are trying to connect to is too old!
" - "Need at least a Core Version %1 (Build >= %2) to connect.").arg(Global::quasselVersion).arg(Global::coreBuildNeeded)); + "Need at least core/client protocol v%1 to connect.").arg(Quassel::buildInfo().clientNeedsProtocol)); disconnectFromCore(); return; } emit connectionMsg(msg["CoreInfo"].toString()); - if(!msg["Configured"].toBool()) { + +#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); + Q_ASSERT(sslSocket); + connect(sslSocket, SIGNAL(encrypted()), this, SLOT(sslSocketEncrypted())); + connect(sslSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); + + sslSocket->startClientEncryption(); + } 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.")); + disconnectFromCore(); + } + return; + } +#endif + // if we use SSL we wait for the next step until every SSL warning has been cleared + connectionReady(); +} + +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) { @@ -193,20 +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(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()); @@ -215,30 +283,26 @@ void ClientSyncer::syncToCore(const QVariantMap &sessionState) { // create buffers // FIXME: get rid of this crap QVariantList bufferinfos = sessionState["BufferInfos"].toList(); - foreach(QVariant vinfo, bufferinfos) Client::buffer(vinfo.value()); // create Buffers and BufferItems + NetworkModel *networkModel = Client::networkModel(); + Q_ASSERT(networkModel); + foreach(QVariant vinfo, bufferinfos) + networkModel->bufferUpdated(vinfo.value()); // create BufferItems QVariantList networkids = sessionState["NetworkIds"].toList(); - // prepare sync progress thingys... FIXME: Care about removal of networks + // prepare sync progress thingys... + // FIXME: Care about removal of networks numNetsToSync = networkids.count(); - numChannelsToSync = 0; //sessionState["IrcChannelCount"].toUInt(); - numUsersToSync = 0; // sessionState["IrcUserCount"].toUInt(); qDebug() << numUsersToSync; emit networksProgress(0, numNetsToSync); - emit channelsProgress(0, numChannelsToSync); - emit ircUsersProgress(0, numUsersToSync); // 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())); - connect(net, SIGNAL(ircUserInitDone(IrcUser *)), this, SLOT(ircUserInitDone(IrcUser *))); - connect(net, SIGNAL(ircUserAdded(IrcUser *)), this, SLOT(ircUserAdded(IrcUser *))); - connect(net, SIGNAL(ircUserRemoved(QObject *)), this, SLOT(ircUserRemoved(QObject *))); - connect(net, SIGNAL(ircChannelInitDone(IrcChannel *)), this, SLOT(ircChannelInitDone(IrcChannel *))); - connect(net, SIGNAL(ircChannelAdded(IrcChannel *)), this, SLOT(ircChannelAdded(IrcChannel *))); - connect(net, SIGNAL(ircChannelRemoved(QObject *)), this, SLOT(ircChannelRemoved(QObject *))); Client::addNetwork(net); } checkSyncState(); @@ -250,83 +314,100 @@ void ClientSyncer::networkInitDone() { checkSyncState(); } -void ClientSyncer::ircChannelInitDone(IrcChannel *chan) { - channelsToSync.remove(chan); - emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync); - checkSyncState(); -} - -void ClientSyncer::ircChannelAdded(IrcChannel *chan) { - if(!chan->isInitialized()) { - channelsToSync.insert(chan); - numChannelsToSync++; - emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync); - checkSyncState(); +void ClientSyncer::checkSyncState() { + if(netsToSync.isEmpty()) { + Client::instance()->setSyncedToCore(); + emit syncFinished(); } } -void ClientSyncer::ircChannelRemoved(QObject *chan) { - if(channelsToSync.contains(chan)) { - numChannelsToSync--; - channelsToSync.remove(chan); - emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync); - checkSyncState(); - } +void ClientSyncer::setWarningsHandler(const char *slot) { + resetWarningsHandler(); + connect(this, SIGNAL(handleIgnoreWarnings(bool)), this, slot); } -void ClientSyncer::ircUserInitDone(IrcUser *user) { - usersToSync.remove(user); - emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync); - checkSyncState(); +void ClientSyncer::resetWarningsHandler() { + disconnect(this, SIGNAL(handleIgnoreWarnings(bool)), this, 0); } -void ClientSyncer::ircUserAdded(IrcUser *user) { - if(!user->isInitialized()) { - usersToSync.insert(user); - numUsersToSync++; - emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync); - checkSyncState(); +void ClientSyncer::resetConnection() { + if(_socket) { + disconnect(_socket, 0, this, 0); + _socket->deleteLater(); + _socket = 0; } + _blockSize = 0; + + coreConnectionInfo.clear(); + _coreMsgBuffer.clear(); + + netsToSync.clear(); + numNetsToSync = 0; } -void ClientSyncer::ircUserRemoved(QObject *user) { - if(usersToSync.contains(user)) { - numUsersToSync--; - usersToSync.remove(user); - emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync); - checkSyncState(); +#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::checkSyncState() { - if(usersToSync.count() + channelsToSync.count() + netsToSync.count() == 0) { - // done syncing! - /* - qDebug() << "done"; - foreach(Network *net, _networks.values()) { - //disconnect(net, 0, this, SLOT(networkInitDone())); - //disconnect(net, 0, this, SLOT(ircUserInitDone(IrcUser *))); - //disconnect(net, 0, this, SLOT(ircUserAdded(IrcUser *))); - //disconnect(net, 0, this, SLOT(ircUserRemoved(QObject *))); - //disconnect(net, 0, this, SLOT(ircChannelInitDone(IrcChannel *))); - //disconnect(net, 0, this, SLOT(ircChannelAdded(IrcChannel *))); - //disconnect(net, 0, this, SLOT(ircChannelRemoved(QObject *))); - qDebug() << "disconnecting"; - disconnect(net, SIGNAL(initDone()), this, SLOT(networkInitDone())); - disconnect(net, SIGNAL(ircUserInitDone(IrcUser *)), this, SLOT(ircUserInitDone(IrcUser *))); - disconnect(net, SIGNAL(ircUserAdded(IrcUser *)), this, SLOT(ircUserAdded(IrcUser *))); - disconnect(net, SIGNAL(ircUserRemoved(QObject *)), this, SLOT(ircUserRemoved(QObject *))); - disconnect(net, SIGNAL(ircChannelInitDone(IrcChannel *)), this, SLOT(ircChannelInitDone(IrcChannel *))); - disconnect(net, SIGNAL(ircChannelAdded(IrcChannel *)), this, SLOT(ircChannelAdded(IrcChannel *))); - disconnect(net, SIGNAL(ircChannelRemoved(QObject *)), this, SLOT(ircChannelRemoved(QObject *))); - } - */ +void ClientSyncer::sslSocketEncrypted() { + QSslSocket *socket = qobject_cast(sender()); + Q_ASSERT(socket); - Client::instance()->setSyncedToCore(); - emit syncFinished(); - //emit connected(); - //emit connectionStateChanged(true); + // 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) { + 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) + warnings << err.errorString(); + + if(!knownDigest.isEmpty()) { + warnings << tr("Cert Digest changed! was: %1").arg(QString(prettyDigest(knownDigest))); + } + + setWarningsHandler(SLOT(ignoreSslWarnings(bool))); + emit connectionWarnings(warnings); +} +#endif