1 /***************************************************************************
2 * Copyright (C) 2005-2013 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);
42 connect(socket, SIGNAL(readyRead()), SLOT(socketDataAvailable()));
46 void LegacyPeer::setSignalProxy(::SignalProxy *proxy)
48 RemotePeer::setSignalProxy(proxy);
50 // FIXME only in compat mode
52 // enable compression now if requested - the initial handshake is uncompressed in the legacy protocol!
53 _useCompression = socket()->property("UseCompression").toBool();
55 qDebug() << "Using compression for peer:" << qPrintable(socket()->peerAddress().toString());
61 void LegacyPeer::socketDataAvailable()
64 while (readSocketData(item)) {
65 // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere
67 handleHandshakeMessage(item);
69 handlePackedFunc(item);
74 bool LegacyPeer::readSocketData(QVariant &item)
76 if (_blockSize == 0) {
77 if (socket()->bytesAvailable() < 4)
79 _stream >> _blockSize;
82 if (_blockSize > 1 << 22) {
83 close("Peer tried to send package larger than max package size!");
87 if (_blockSize == 0) {
88 close("Peer tried to send 0 byte package!");
92 if (socket()->bytesAvailable() < _blockSize) {
93 emit transferProgress(socket()->bytesAvailable(), _blockSize);
97 emit transferProgress(_blockSize, _blockSize);
101 if (_useCompression) {
105 int nbytes = rawItem.size();
107 const char *data = rawItem.constData();
108 if (nbytes < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) {
109 close("Peer sent corrupted compressed data!");
114 rawItem = qUncompress(rawItem);
116 QDataStream itemStream(&rawItem, QIODevice::ReadOnly);
117 itemStream.setVersion(QDataStream::Qt_4_2);
124 if (!item.isValid()) {
125 close("Peer sent corrupt data: unable to load QVariant!");
133 void LegacyPeer::writeSocketData(const QVariant &item)
135 if (!socket()->isOpen()) {
136 qWarning() << Q_FUNC_INFO << "Can't write to a closed socket!";
141 QDataStream out(&block, QIODevice::WriteOnly);
142 out.setVersion(QDataStream::Qt_4_2);
144 if (_useCompression) {
146 QDataStream itemStream(&rawItem, QIODevice::WriteOnly);
147 itemStream.setVersion(QDataStream::Qt_4_2);
150 rawItem = qCompress(rawItem);
158 _stream << block; // also writes the length as part of the serialization format
162 /*** Handshake messages ***/
164 /* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
165 * a structure different from those being used after the handshake.
166 * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
169 void LegacyPeer::handleHandshakeMessage(const QVariant &msg)
171 QVariantMap m = msg.toMap();
173 QString msgType = m["MsgType"].toString();
174 if (msgType.isEmpty()) {
175 emit protocolError(tr("Invalid handshake message!"));
179 if (msgType == "ClientInit") {
180 // FIXME only in compat mode
181 uint ver = m["ProtocolVersion"].toUInt();
182 if (ver < coreNeedsProtocol) {
183 emit protocolVersionMismatch((int)ver, (int)coreNeedsProtocol);
187 #ifndef QT_NO_COMPRESS
188 // FIXME only in compat mode
189 if (m["UseCompression"].toBool()) {
190 socket()->setProperty("UseCompression", true);
193 handle(RegisterClient(m["ClientVersion"].toString(), m["UseSsl"].toBool()));
196 else if (msgType == "ClientInitReject") {
197 handle(ClientDenied(m["Error"].toString()));
200 else if (msgType == "ClientInitAck") {
201 // FIXME only in compat mode
202 uint ver = m["ProtocolVersion"].toUInt(); // actually an UInt
203 if (ver < clientNeedsProtocol) {
204 emit protocolVersionMismatch((int)ver, (int)clientNeedsProtocol);
207 #ifndef QT_NO_COMPRESS
208 if (m["SupportsCompression"].toBool())
209 socket()->setProperty("UseCompression", true);
212 handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool(), QDateTime()));
215 else if (msgType == "CoreSetupData") {
216 QVariantMap map = m["SetupData"].toMap();
217 handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
220 else if (msgType == "CoreSetupReject") {
221 handle(SetupFailed(m["Error"].toString()));
224 else if (msgType == "CoreSetupAck") {
228 else if (msgType == "ClientLogin") {
229 handle(Login(m["User"].toString(), m["Password"].toString()));
232 else if (msgType == "ClientLoginReject") {
233 handle(LoginFailed(m["Error"].toString()));
236 else if (msgType == "ClientLoginAck") {
237 handle(LoginSuccess());
240 else if (msgType == "SessionInit") {
241 QVariantMap map = m["SessionState"].toMap();
242 handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
246 emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
251 void LegacyPeer::dispatch(const RegisterClient &msg) {
253 m["MsgType"] = "ClientInit";
254 m["ClientVersion"] = msg.clientVersion;
255 m["ClientDate"] = Quassel::buildInfo().buildDate;
257 // FIXME only in compat mode
258 m["ProtocolVersion"] = protocolVersion;
259 m["UseSsl"] = msg.sslSupported;
260 #ifndef QT_NO_COMPRESS
261 m["UseCompression"] = true;
263 m["UseCompression"] = false;
270 void LegacyPeer::dispatch(const ClientDenied &msg) {
272 m["MsgType"] = "ClientInitReject";
273 m["Error"] = msg.errorString;
279 void LegacyPeer::dispatch(const ClientRegistered &msg) {
281 m["MsgType"] = "ClientInitAck";
282 m["CoreFeatures"] = msg.coreFeatures;
283 m["StorageBackends"] = msg.backendInfo;
285 // FIXME only in compat mode
286 m["ProtocolVersion"] = protocolVersion;
287 m["SupportSsl"] = msg.sslSupported;
288 m["SupportsCompression"] = socket()->property("UseCompression").toBool(); // this property gets already set in the ClientInit handler
290 // This is only used for old v10 clients (pre-0.5)
291 int uptime = msg.coreStartTime.secsTo(QDateTime::currentDateTime().toUTC());
292 int updays = uptime / 86400; uptime %= 86400;
293 int uphours = uptime / 3600; uptime %= 3600;
294 int upmins = uptime / 60;
295 m["CoreInfo"] = tr("<b>Quassel Core Version %1</b><br>"
297 "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
298 .arg(Quassel::buildInfo().buildDate)
299 .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(msg.coreStartTime.toString(Qt::TextDate));
301 m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
307 void LegacyPeer::dispatch(const SetupData &msg)
310 map["AdminUser"] = msg.adminUser;
311 map["AdminPasswd"] = msg.adminPassword;
312 map["Backend"] = msg.backend;
313 map["ConnectionProperties"] = msg.setupData;
316 m["MsgType"] = "CoreSetupData";
317 m["SetupData"] = map;
322 void LegacyPeer::dispatch(const SetupFailed &msg)
325 m["MsgType"] = "CoreSetupReject";
326 m["Error"] = msg.errorString;
332 void LegacyPeer::dispatch(const SetupDone &msg)
337 m["MsgType"] = "CoreSetupAck";
343 void LegacyPeer::dispatch(const Login &msg)
346 m["MsgType"] = "ClientLogin";
347 m["User"] = msg.user;
348 m["Password"] = msg.password;
354 void LegacyPeer::dispatch(const LoginFailed &msg)
357 m["MsgType"] = "ClientLoginReject";
358 m["Error"] = msg.errorString;
364 void LegacyPeer::dispatch(const LoginSuccess &msg)
369 m["MsgType"] = "ClientLoginAck";
375 void LegacyPeer::dispatch(const SessionState &msg)
378 m["MsgType"] = "SessionInit";
381 map["BufferInfos"] = msg.bufferInfos;
382 map["NetworkIds"] = msg.networkIds;
383 map["Identities"] = msg.identities;
384 m["SessionState"] = map;
390 /*** Standard messages ***/
392 void LegacyPeer::handlePackedFunc(const QVariant &packedFunc)
394 QVariantList params(packedFunc.toList());
396 if (params.isEmpty()) {
397 qWarning() << Q_FUNC_INFO << "Received incompatible data:" << packedFunc;
401 // TODO: make sure that this is a valid request type
402 RequestType requestType = (RequestType)params.takeFirst().value<int>();
403 switch (requestType) {
405 if (params.count() < 3) {
406 qWarning() << Q_FUNC_INFO << "Received invalid sync call:" << params;
409 QByteArray className = params.takeFirst().toByteArray();
410 QString objectName = params.takeFirst().toString();
411 QByteArray slotName = params.takeFirst().toByteArray();
412 handle(Protocol::SyncMessage(className, objectName, slotName, params));
416 if (params.empty()) {
417 qWarning() << Q_FUNC_INFO << "Received empty RPC call!";
420 QByteArray slotName = params.takeFirst().toByteArray();
421 handle(Protocol::RpcCall(slotName, params));
425 if (params.count() != 2) {
426 qWarning() << Q_FUNC_INFO << "Received invalid InitRequest:" << params;
429 QByteArray className = params[0].toByteArray();
430 QString objectName = params[1].toString();
431 handle(Protocol::InitRequest(className, objectName));
435 if (params.count() != 3) {
436 qWarning() << Q_FUNC_INFO << "Received invalid InitData:" << params;
439 QByteArray className = params[0].toByteArray();
440 QString objectName = params[1].toString();
441 QVariantMap initData = params[2].toMap();
442 handle(Protocol::InitData(className, objectName, initData));
446 if (params.count() != 1) {
447 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
450 // The legacy protocol would only send a QTime, no QDateTime
451 // so we assume it's sent today, which works in exactly the same cases as it did in the old implementation
452 QDateTime dateTime = QDateTime::currentDateTime().toUTC();
453 dateTime.setTime(params[0].toTime());
454 handle(Protocol::HeartBeat(dateTime));
457 case HeartBeatReply: {
458 if (params.count() != 1) {
459 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
462 // The legacy protocol would only send a QTime, no QDateTime
463 // so we assume it's sent today, which works in exactly the same cases as it did in the old implementation
464 QDateTime dateTime = QDateTime::currentDateTime().toUTC();
465 dateTime.setTime(params[0].toTime());
466 handle(Protocol::HeartBeatReply(dateTime));
474 void LegacyPeer::dispatch(const Protocol::SyncMessage &msg)
476 dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName << msg.slotName << msg.params);
480 void LegacyPeer::dispatch(const Protocol::RpcCall &msg)
482 dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
486 void LegacyPeer::dispatch(const Protocol::InitRequest &msg)
488 dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName);
492 void LegacyPeer::dispatch(const Protocol::InitData &msg)
494 dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << msg.initData);
498 void LegacyPeer::dispatch(const Protocol::HeartBeat &msg)
500 dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp.time());
504 void LegacyPeer::dispatch(const Protocol::HeartBeatReply &msg)
506 dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp.time());
510 void LegacyPeer::dispatchPackedFunc(const QVariantList &packedFunc)
512 writeSocketData(QVariant(packedFunc));