1 /***************************************************************************
2 * Copyright (C) 2005-2014 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <QHostAddress>
24 #include "legacypeer.h"
27 /* version.inc is no longer used for this */
28 const uint protocolVersion = 10;
29 const uint coreNeedsProtocol = protocolVersion;
30 const uint clientNeedsProtocol = protocolVersion;
32 using namespace Protocol;
34 LegacyPeer::LegacyPeer(::AuthHandler *authHandler, QTcpSocket *socket, QObject *parent)
35 : RemotePeer(authHandler, socket, parent),
37 _useCompression(false)
39 _stream.setDevice(socket);
40 _stream.setVersion(QDataStream::Qt_4_2);
44 void LegacyPeer::setSignalProxy(::SignalProxy *proxy)
46 RemotePeer::setSignalProxy(proxy);
48 // FIXME only in compat mode
50 // enable compression now if requested - the initial handshake is uncompressed in the legacy protocol!
51 _useCompression = socket()->property("UseCompression").toBool();
53 qDebug() << "Using compression for peer:" << qPrintable(socket()->peerAddress().toString());
59 void LegacyPeer::onSocketDataAvailable()
62 while (readSocketData(item)) {
63 // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere
65 handleHandshakeMessage(item);
67 handlePackedFunc(item);
72 bool LegacyPeer::readSocketData(QVariant &item)
74 if (_blockSize == 0) {
75 if (socket()->bytesAvailable() < 4)
77 _stream >> _blockSize;
80 if (_blockSize > 1 << 22) {
81 close("Peer tried to send package larger than max package size!");
85 if (_blockSize == 0) {
86 close("Peer tried to send 0 byte package!");
90 if (socket()->bytesAvailable() < _blockSize) {
91 emit transferProgress(socket()->bytesAvailable(), _blockSize);
95 emit transferProgress(_blockSize, _blockSize);
99 if (_useCompression) {
103 int nbytes = rawItem.size();
105 const char *data = rawItem.constData();
106 if (nbytes < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) {
107 close("Peer sent corrupted compressed data!");
112 rawItem = qUncompress(rawItem);
114 QDataStream itemStream(&rawItem, QIODevice::ReadOnly);
115 itemStream.setVersion(QDataStream::Qt_4_2);
122 if (!item.isValid()) {
123 close("Peer sent corrupt data: unable to load QVariant!");
131 void LegacyPeer::writeSocketData(const QVariant &item)
133 if (!socket()->isOpen()) {
134 qWarning() << Q_FUNC_INFO << "Can't write to a closed socket!";
139 QDataStream out(&block, QIODevice::WriteOnly);
140 out.setVersion(QDataStream::Qt_4_2);
142 if (_useCompression) {
144 QDataStream itemStream(&rawItem, QIODevice::WriteOnly);
145 itemStream.setVersion(QDataStream::Qt_4_2);
148 rawItem = qCompress(rawItem);
156 _stream << block; // also writes the length as part of the serialization format
160 /*** Handshake messages ***/
162 /* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
163 * a structure different from those being used after the handshake.
164 * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
167 void LegacyPeer::handleHandshakeMessage(const QVariant &msg)
169 QVariantMap m = msg.toMap();
171 QString msgType = m["MsgType"].toString();
172 if (msgType.isEmpty()) {
173 emit protocolError(tr("Invalid handshake message!"));
177 if (msgType == "ClientInit") {
178 // FIXME only in compat mode
179 uint ver = m["ProtocolVersion"].toUInt();
180 if (ver < coreNeedsProtocol) {
181 emit protocolVersionMismatch((int)ver, (int)coreNeedsProtocol);
185 #ifndef QT_NO_COMPRESS
186 // FIXME only in compat mode
187 if (m["UseCompression"].toBool()) {
188 socket()->setProperty("UseCompression", true);
191 handle(RegisterClient(m["ClientVersion"].toString(), m["UseSsl"].toBool()));
194 else if (msgType == "ClientInitReject") {
195 handle(ClientDenied(m["Error"].toString()));
198 else if (msgType == "ClientInitAck") {
199 // FIXME only in compat mode
200 uint ver = m["ProtocolVersion"].toUInt(); // actually an UInt
201 if (ver < clientNeedsProtocol) {
202 emit protocolVersionMismatch((int)ver, (int)clientNeedsProtocol);
205 #ifndef QT_NO_COMPRESS
206 if (m["SupportsCompression"].toBool())
207 socket()->setProperty("UseCompression", true);
210 handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool(), QDateTime()));
213 else if (msgType == "CoreSetupData") {
214 QVariantMap map = m["SetupData"].toMap();
215 handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
218 else if (msgType == "CoreSetupReject") {
219 handle(SetupFailed(m["Error"].toString()));
222 else if (msgType == "CoreSetupAck") {
226 else if (msgType == "ClientLogin") {
227 handle(Login(m["User"].toString(), m["Password"].toString()));
230 else if (msgType == "ClientLoginReject") {
231 handle(LoginFailed(m["Error"].toString()));
234 else if (msgType == "ClientLoginAck") {
235 handle(LoginSuccess());
238 else if (msgType == "SessionInit") {
239 QVariantMap map = m["SessionState"].toMap();
240 handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
244 emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
249 void LegacyPeer::dispatch(const RegisterClient &msg) {
251 m["MsgType"] = "ClientInit";
252 m["ClientVersion"] = msg.clientVersion;
253 m["ClientDate"] = Quassel::buildInfo().buildDate;
255 // FIXME only in compat mode
256 m["ProtocolVersion"] = protocolVersion;
257 m["UseSsl"] = msg.sslSupported;
258 #ifndef QT_NO_COMPRESS
259 m["UseCompression"] = true;
261 m["UseCompression"] = false;
268 void LegacyPeer::dispatch(const ClientDenied &msg) {
270 m["MsgType"] = "ClientInitReject";
271 m["Error"] = msg.errorString;
277 void LegacyPeer::dispatch(const ClientRegistered &msg) {
279 m["MsgType"] = "ClientInitAck";
280 m["CoreFeatures"] = msg.coreFeatures;
281 m["StorageBackends"] = msg.backendInfo;
283 // FIXME only in compat mode
284 m["ProtocolVersion"] = protocolVersion;
285 m["SupportSsl"] = msg.sslSupported;
286 m["SupportsCompression"] = socket()->property("UseCompression").toBool(); // this property gets already set in the ClientInit handler
288 // This is only used for old v10 clients (pre-0.5)
289 int uptime = msg.coreStartTime.secsTo(QDateTime::currentDateTime().toUTC());
290 int updays = uptime / 86400; uptime %= 86400;
291 int uphours = uptime / 3600; uptime %= 3600;
292 int upmins = uptime / 60;
293 m["CoreInfo"] = tr("<b>Quassel Core Version %1</b><br>"
295 "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
296 .arg(Quassel::buildInfo().buildDate)
297 .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(msg.coreStartTime.toString(Qt::TextDate));
299 m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
305 void LegacyPeer::dispatch(const SetupData &msg)
308 map["AdminUser"] = msg.adminUser;
309 map["AdminPasswd"] = msg.adminPassword;
310 map["Backend"] = msg.backend;
311 map["ConnectionProperties"] = msg.setupData;
314 m["MsgType"] = "CoreSetupData";
315 m["SetupData"] = map;
320 void LegacyPeer::dispatch(const SetupFailed &msg)
323 m["MsgType"] = "CoreSetupReject";
324 m["Error"] = msg.errorString;
330 void LegacyPeer::dispatch(const SetupDone &msg)
335 m["MsgType"] = "CoreSetupAck";
341 void LegacyPeer::dispatch(const Login &msg)
344 m["MsgType"] = "ClientLogin";
345 m["User"] = msg.user;
346 m["Password"] = msg.password;
352 void LegacyPeer::dispatch(const LoginFailed &msg)
355 m["MsgType"] = "ClientLoginReject";
356 m["Error"] = msg.errorString;
362 void LegacyPeer::dispatch(const LoginSuccess &msg)
367 m["MsgType"] = "ClientLoginAck";
373 void LegacyPeer::dispatch(const SessionState &msg)
376 m["MsgType"] = "SessionInit";
379 map["BufferInfos"] = msg.bufferInfos;
380 map["NetworkIds"] = msg.networkIds;
381 map["Identities"] = msg.identities;
382 m["SessionState"] = map;
388 /*** Standard messages ***/
390 void LegacyPeer::handlePackedFunc(const QVariant &packedFunc)
392 QVariantList params(packedFunc.toList());
394 if (params.isEmpty()) {
395 qWarning() << Q_FUNC_INFO << "Received incompatible data:" << packedFunc;
399 // TODO: make sure that this is a valid request type
400 RequestType requestType = (RequestType)params.takeFirst().value<int>();
401 switch (requestType) {
403 if (params.count() < 3) {
404 qWarning() << Q_FUNC_INFO << "Received invalid sync call:" << params;
407 QByteArray className = params.takeFirst().toByteArray();
408 QString objectName = params.takeFirst().toString();
409 QByteArray slotName = params.takeFirst().toByteArray();
410 handle(Protocol::SyncMessage(className, objectName, slotName, params));
414 if (params.empty()) {
415 qWarning() << Q_FUNC_INFO << "Received empty RPC call!";
418 QByteArray slotName = params.takeFirst().toByteArray();
419 handle(Protocol::RpcCall(slotName, params));
423 if (params.count() != 2) {
424 qWarning() << Q_FUNC_INFO << "Received invalid InitRequest:" << params;
427 QByteArray className = params[0].toByteArray();
428 QString objectName = params[1].toString();
429 handle(Protocol::InitRequest(className, objectName));
433 if (params.count() != 3) {
434 qWarning() << Q_FUNC_INFO << "Received invalid InitData:" << params;
437 QByteArray className = params[0].toByteArray();
438 QString objectName = params[1].toString();
439 QVariantMap initData = params[2].toMap();
440 handle(Protocol::InitData(className, objectName, initData));
444 if (params.count() != 1) {
445 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
448 // The legacy protocol would only send a QTime, no QDateTime
449 // so we assume it's sent today, which works in exactly the same cases as it did in the old implementation
450 QDateTime dateTime = QDateTime::currentDateTime().toUTC();
451 dateTime.setTime(params[0].toTime());
452 handle(Protocol::HeartBeat(dateTime));
455 case HeartBeatReply: {
456 if (params.count() != 1) {
457 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
460 // The legacy protocol would only send a QTime, no QDateTime
461 // so we assume it's sent today, which works in exactly the same cases as it did in the old implementation
462 QDateTime dateTime = QDateTime::currentDateTime().toUTC();
463 dateTime.setTime(params[0].toTime());
464 handle(Protocol::HeartBeatReply(dateTime));
472 void LegacyPeer::dispatch(const Protocol::SyncMessage &msg)
474 dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName << msg.slotName << msg.params);
478 void LegacyPeer::dispatch(const Protocol::RpcCall &msg)
480 dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
484 void LegacyPeer::dispatch(const Protocol::InitRequest &msg)
486 dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName);
490 void LegacyPeer::dispatch(const Protocol::InitData &msg)
492 dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << msg.initData);
496 void LegacyPeer::dispatch(const Protocol::HeartBeat &msg)
498 dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp.time());
502 void LegacyPeer::dispatch(const Protocol::HeartBeatReply &msg)
504 dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp.time());
508 void LegacyPeer::dispatchPackedFunc(const QVariantList &packedFunc)
510 writeSocketData(QVariant(packedFunc));