X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcommon%2Fprotocols%2Flegacy%2Flegacypeer.cpp;h=caf1bb22b942027a297a9e697ceddbaf9efae88a;hp=0766c5b3841ca87d383c85744de34066b4492166;hb=2b777e99fc9f74d4ed21491710260664a1721d1f;hpb=e583f46bbff3e4c73716b30b6a6d08adb6e44915 diff --git a/src/common/protocols/legacy/legacypeer.cpp b/src/common/protocols/legacy/legacypeer.cpp index 0766c5b3..caf1bb22 100644 --- a/src/common/protocols/legacy/legacypeer.cpp +++ b/src/common/protocols/legacy/legacypeer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2013 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 * @@ -19,10 +19,12 @@ ***************************************************************************/ #include +#include #include #include "legacypeer.h" #include "quassel.h" +#include "serializers/serializers.h" /* version.inc is no longer used for this */ const uint protocolVersion = 10; @@ -31,15 +33,11 @@ const uint clientNeedsProtocol = protocolVersion; using namespace Protocol; -LegacyPeer::LegacyPeer(::AuthHandler *authHandler, QTcpSocket *socket, QObject *parent) - : RemotePeer(authHandler, socket, parent), - _blockSize(0), +LegacyPeer::LegacyPeer(::AuthHandler *authHandler, QTcpSocket *socket, Compressor::CompressionLevel level, QObject *parent) + : RemotePeer(authHandler, socket, level, parent), _useCompression(false) { - _stream.setDevice(socket); - _stream.setVersion(QDataStream::Qt_4_2); - connect(socket, SIGNAL(readyRead()), SLOT(socketDataAvailable())); } @@ -58,56 +56,25 @@ void LegacyPeer::setSignalProxy(::SignalProxy *proxy) } -void LegacyPeer::socketDataAvailable() +void LegacyPeer::processMessage(const QByteArray &msg) { - QVariant item; - while (readSocketData(item)) { - // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere - if (!signalProxy()) - handleHandshakeMessage(item); - else - handlePackedFunc(item); - } -} - - -bool LegacyPeer::readSocketData(QVariant &item) -{ - if (_blockSize == 0) { - if (socket()->bytesAvailable() < 4) - return false; - _stream >> _blockSize; - } - - if (_blockSize > 1 << 22) { - close("Peer tried to send package larger than max package size!"); - return false; - } - - if (_blockSize == 0) { - close("Peer tried to send 0 byte package!"); - return false; - } - - if (socket()->bytesAvailable() < _blockSize) { - emit transferProgress(socket()->bytesAvailable(), _blockSize); - return false; - } - - emit transferProgress(_blockSize, _blockSize); - - _blockSize = 0; + QDataStream stream(msg); + stream.setVersion(QDataStream::Qt_4_2); + QVariant item; if (_useCompression) { QByteArray rawItem; - _stream >> rawItem; + if (!Serializers::deserialize(stream, features(), rawItem)) { + close("Peer sent corrupt data: unable to load QVariant!"); + return; + } int nbytes = rawItem.size(); if (nbytes <= 4) { const char *data = rawItem.constData(); if (nbytes < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) { close("Peer sent corrupted compressed data!"); - return false; + return; } } @@ -115,28 +82,32 @@ bool LegacyPeer::readSocketData(QVariant &item) QDataStream itemStream(&rawItem, QIODevice::ReadOnly); itemStream.setVersion(QDataStream::Qt_4_2); - itemStream >> item; - } - else { - _stream >> item; + if (!Serializers::deserialize(itemStream, features(), item)) { + close("Peer sent corrupt data: unable to load QVariant!"); + return; + } + } else { + if (!Serializers::deserialize(stream, features(), item)) { + close("Peer sent corrupt data: unable to load QVariant!"); + return; + } } - if (!item.isValid()) { + if (stream.status() != QDataStream::Ok || !item.isValid()) { close("Peer sent corrupt data: unable to load QVariant!"); - return false; + return; } - return true; + // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere + if (!signalProxy()) + handleHandshakeMessage(item); + else + handlePackedFunc(item); } -void LegacyPeer::writeSocketData(const QVariant &item) +void LegacyPeer::writeMessage(const QVariant &item) { - if (!socket()->isOpen()) { - qWarning() << Q_FUNC_INFO << "Can't write to a closed socket!"; - return; - } - QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_2); @@ -155,7 +126,7 @@ void LegacyPeer::writeSocketData(const QVariant &item) out << item; } - _stream << block; // also writes the length as part of the serialization format + writeMessage(block); } @@ -190,7 +161,10 @@ void LegacyPeer::handleHandshakeMessage(const QVariant &msg) socket()->setProperty("UseCompression", true); } #endif - handle(RegisterClient(m["ClientVersion"].toString(), m["UseSsl"].toBool())); + handle(RegisterClient{Quassel::Features{m["FeatureList"].toStringList(), Quassel::LegacyFeatures(m["Features"].toUInt())}, + m["ClientVersion"].toString(), + m["ClientDate"].toString(), + m["UseSsl"].toBool()}); } else if (msgType == "ClientInitReject") { @@ -209,12 +183,16 @@ void LegacyPeer::handleHandshakeMessage(const QVariant &msg) socket()->setProperty("UseCompression", true); #endif - handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool(), QDateTime())); + handle(ClientRegistered{Quassel::Features{m["FeatureList"].toStringList(), Quassel::LegacyFeatures(m["CoreFeatures"].toUInt())}, + m["Configured"].toBool(), + m["StorageBackends"].toList(), + m["Authenticators"].toList(), + m["SupportSsl"].toBool()}); } else if (msgType == "CoreSetupData") { QVariantMap map = m["SetupData"].toMap(); - handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap())); + handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap(), map["Authenticator"].toString(), map["AuthProperties"].toMap())); } else if (msgType == "CoreSetupReject") { @@ -251,8 +229,10 @@ void LegacyPeer::handleHandshakeMessage(const QVariant &msg) void LegacyPeer::dispatch(const RegisterClient &msg) { QVariantMap m; m["MsgType"] = "ClientInit"; + m["Features"] = static_cast(msg.features.toLegacyFeatures()); + m["FeatureList"] = msg.features.toStringList(); m["ClientVersion"] = msg.clientVersion; - m["ClientDate"] = Quassel::buildInfo().buildDate; + m["ClientDate"] = msg.buildDate; // FIXME only in compat mode m["ProtocolVersion"] = protocolVersion; @@ -263,7 +243,7 @@ void LegacyPeer::dispatch(const RegisterClient &msg) { m["UseCompression"] = false; #endif - writeSocketData(m); + writeMessage(m); } @@ -272,35 +252,35 @@ void LegacyPeer::dispatch(const ClientDenied &msg) { m["MsgType"] = "ClientInitReject"; m["Error"] = msg.errorString; - writeSocketData(m); + writeMessage(m); } void LegacyPeer::dispatch(const ClientRegistered &msg) { QVariantMap m; m["MsgType"] = "ClientInitAck"; - m["CoreFeatures"] = msg.coreFeatures; + if (hasFeature(Quassel::Feature::ExtendedFeatures)) { + m["FeatureList"] = msg.features.toStringList(); + } + else { + m["CoreFeatures"] = static_cast(msg.features.toLegacyFeatures()); + } m["StorageBackends"] = msg.backendInfo; + if (hasFeature(Quassel::Feature::Authenticators)) { + m["Authenticators"] = msg.authenticatorInfo; + } // FIXME only in compat mode m["ProtocolVersion"] = protocolVersion; m["SupportSsl"] = msg.sslSupported; m["SupportsCompression"] = socket()->property("UseCompression").toBool(); // this property gets already set in the ClientInit handler - // This is only used for old v10 clients (pre-0.5) - int uptime = msg.coreStartTime.secsTo(QDateTime::currentDateTime().toUTC()); - int updays = uptime / 86400; uptime %= 86400; - int uphours = uptime / 3600; uptime %= 3600; - int upmins = uptime / 60; - m["CoreInfo"] = tr("Quassel Core Version %1
" - "Built: %2
" - "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString) - .arg(Quassel::buildInfo().buildDate) - .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(msg.coreStartTime.toString(Qt::TextDate)); + // This is only used for display by really old v10 clients (pre-0.5), and we no longer set this + m["CoreInfo"] = QString(); m["LoginEnabled"] = m["Configured"] = msg.coreConfigured; - writeSocketData(m); + writeMessage(m); } @@ -312,10 +292,14 @@ void LegacyPeer::dispatch(const SetupData &msg) map["Backend"] = msg.backend; map["ConnectionProperties"] = msg.setupData; + // Auth backend properties. + map["Authenticator"] = msg.authenticator; + map["AuthProperties"] = msg.authSetupData; + QVariantMap m; m["MsgType"] = "CoreSetupData"; m["SetupData"] = map; - writeSocketData(m); + writeMessage(m); } @@ -325,7 +309,7 @@ void LegacyPeer::dispatch(const SetupFailed &msg) m["MsgType"] = "CoreSetupReject"; m["Error"] = msg.errorString; - writeSocketData(m); + writeMessage(m); } @@ -336,7 +320,7 @@ void LegacyPeer::dispatch(const SetupDone &msg) QVariantMap m; m["MsgType"] = "CoreSetupAck"; - writeSocketData(m); + writeMessage(m); } @@ -347,7 +331,7 @@ void LegacyPeer::dispatch(const Login &msg) m["User"] = msg.user; m["Password"] = msg.password; - writeSocketData(m); + writeMessage(m); } @@ -357,7 +341,7 @@ void LegacyPeer::dispatch(const LoginFailed &msg) m["MsgType"] = "ClientLoginReject"; m["Error"] = msg.errorString; - writeSocketData(m); + writeMessage(m); } @@ -368,7 +352,7 @@ void LegacyPeer::dispatch(const LoginSuccess &msg) QVariantMap m; m["MsgType"] = "ClientLoginAck"; - writeSocketData(m); + writeMessage(m); } @@ -383,7 +367,7 @@ void LegacyPeer::dispatch(const SessionState &msg) map["Identities"] = msg.identities; m["SessionState"] = map; - writeSocketData(m); + writeMessage(m); } @@ -439,6 +423,10 @@ void LegacyPeer::handlePackedFunc(const QVariant &packedFunc) QByteArray className = params[0].toByteArray(); QString objectName = params[1].toString(); QVariantMap initData = params[2].toMap(); + + // we need to special-case IrcUsersAndChannels here, since the format changed + if (className == "Network") + fromLegacyIrcUsersAndChannels(initData); handle(Protocol::InitData(className, objectName, initData)); break; } @@ -491,7 +479,14 @@ void LegacyPeer::dispatch(const Protocol::InitRequest &msg) void LegacyPeer::dispatch(const Protocol::InitData &msg) { - dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << msg.initData); + // We need to special-case IrcUsersAndChannels, as the format changed + if (msg.className == "Network") { + QVariantMap initData = msg.initData; + toLegacyIrcUsersAndChannels(initData); + dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << initData); + } + else + dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << msg.initData); } @@ -509,5 +504,74 @@ void LegacyPeer::dispatch(const Protocol::HeartBeatReply &msg) void LegacyPeer::dispatchPackedFunc(const QVariantList &packedFunc) { - writeSocketData(QVariant(packedFunc)); + writeMessage(QVariant(packedFunc)); +} + + +// Handle the changed format for Network's initData +// cf. Network::initIrcUsersAndChannels() +void LegacyPeer::fromLegacyIrcUsersAndChannels(QVariantMap &initData) +{ + const QVariantMap &legacyMap = initData["IrcUsersAndChannels"].toMap(); + QVariantMap newMap; + + QHash users; + foreach(const QVariant &v, legacyMap["users"].toMap().values()) { + const QVariantMap &map = v.toMap(); + foreach(const QString &key, map.keys()) + users[key] << map[key]; + } + QVariantMap userMap; + foreach(const QString &key, users.keys()) + userMap[key] = users[key]; + newMap["Users"] = userMap; + + QHash channels; + foreach(const QVariant &v, legacyMap["channels"].toMap().values()) { + const QVariantMap &map = v.toMap(); + foreach(const QString &key, map.keys()) + channels[key] << map[key]; + } + QVariantMap channelMap; + foreach(const QString &key, channels.keys()) + channelMap[key] = channels[key]; + newMap["Channels"] = channelMap; + + initData["IrcUsersAndChannels"] = newMap; +} + + +void LegacyPeer::toLegacyIrcUsersAndChannels(QVariantMap &initData) +{ + const QVariantMap &usersAndChannels = initData["IrcUsersAndChannels"].toMap(); + QVariantMap legacyMap; + + // toMap() and toList() are cheap, so no need to copy to a hash + + QVariantMap userMap; + const QVariantMap &users = usersAndChannels["Users"].toMap(); + + int size = users["nick"].toList().size(); // we know this key exists + for(int i = 0; i < size; i++) { + QVariantMap map; + foreach(const QString &key, users.keys()) + map[key] = users[key].toList().at(i); + QString hostmask = QString("%1!%2@%3").arg(map["nick"].toString(), map["user"].toString(), map["host"].toString()); + userMap[hostmask.toLower()] = map; + } + legacyMap["users"] = userMap; + + QVariantMap channelMap; + const QVariantMap &channels = usersAndChannels["Channels"].toMap(); + + size = channels["name"].toList().size(); + for(int i = 0; i < size; i++) { + QVariantMap map; + foreach(const QString &key, channels.keys()) + map[key] = channels[key].toList().at(i); + channelMap[map["name"].toString().toLower()] = map; + } + legacyMap["channels"] = channelMap; + + initData["IrcUsersAndChannels"] = legacyMap; }