1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 ***************************************************************************/
22 #include <QDataStream>
23 #include <QHostAddress>
26 #include "datastreampeer.h"
27 #include "serializers/serializers.h"
29 using namespace Protocol;
31 DataStreamPeer::DataStreamPeer(::AuthHandler *authHandler, QTcpSocket *socket, quint16 features, Compressor::CompressionLevel level, QObject *parent)
32 : RemotePeer(authHandler, socket, level, parent)
38 quint16 DataStreamPeer::supportedFeatures()
44 bool DataStreamPeer::acceptsFeatures(quint16 peerFeatures)
46 Q_UNUSED(peerFeatures);
51 quint16 DataStreamPeer::enabledFeatures() const
57 void DataStreamPeer::processMessage(const QByteArray &msg)
59 QDataStream stream(msg);
60 stream.setVersion(QDataStream::Qt_4_2);
62 if (!Serializers::deserialize(stream, list))
63 close("Peer sent corrupt data, closing down!");
64 if (stream.status() != QDataStream::Ok) {
65 close("Peer sent corrupt data, closing down!");
69 // if no sigproxy is set, we're in handshake mode
71 handleHandshakeMessage(list);
73 handlePackedFunc(list);
77 void DataStreamPeer::writeMessage(const QVariantMap &handshakeMsg)
80 QVariantMap::const_iterator it = handshakeMsg.begin();
81 while (it != handshakeMsg.end()) {
82 list << it.key().toUtf8() << it.value();
90 void DataStreamPeer::writeMessage(const QVariantList &sigProxyMsg)
93 QDataStream msgStream(&data, QIODevice::WriteOnly);
94 msgStream.setVersion(QDataStream::Qt_4_2);
95 msgStream << sigProxyMsg;
101 /*** Handshake messages ***/
103 /* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
104 * a structure different from those being used after the handshake.
105 * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
108 void DataStreamPeer::handleHandshakeMessage(const QVariantList &mapData)
111 for (int i = 0; i < mapData.count()/2; ++i)
112 m[QString::fromUtf8(mapData[2*i].toByteArray())] = mapData[2*i+1];
114 QString msgType = m["MsgType"].toString();
115 if (msgType.isEmpty()) {
116 emit protocolError(tr("Invalid handshake message!"));
120 if (msgType == "ClientInit") {
121 handle(RegisterClient(m["ClientVersion"].toString(), m["ClientDate"].toString(), false)); // UseSsl obsolete
124 else if (msgType == "ClientInitReject") {
125 handle(ClientDenied(m["Error"].toString()));
128 else if (msgType == "ClientInitAck") {
129 handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), false)); // SupportsSsl is obsolete
132 else if (msgType == "CoreSetupData") {
133 QVariantMap map = m["SetupData"].toMap();
134 handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
137 else if (msgType == "CoreSetupReject") {
138 handle(SetupFailed(m["Error"].toString()));
141 else if (msgType == "CoreSetupAck") {
145 else if (msgType == "ClientLogin") {
146 handle(Login(m["User"].toString(), m["Password"].toString()));
149 else if (msgType == "ClientLoginReject") {
150 handle(LoginFailed(m["Error"].toString()));
153 else if (msgType == "ClientLoginAck") {
154 handle(LoginSuccess());
157 else if (msgType == "SessionInit") {
158 QVariantMap map = m["SessionState"].toMap();
159 handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
163 emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
168 void DataStreamPeer::dispatch(const RegisterClient &msg) {
170 m["MsgType"] = "ClientInit";
171 m["ClientVersion"] = msg.clientVersion;
172 m["ClientDate"] = msg.buildDate;
178 void DataStreamPeer::dispatch(const ClientDenied &msg) {
180 m["MsgType"] = "ClientInitReject";
181 m["Error"] = msg.errorString;
187 void DataStreamPeer::dispatch(const ClientRegistered &msg) {
189 m["MsgType"] = "ClientInitAck";
190 m["CoreFeatures"] = msg.coreFeatures;
191 m["StorageBackends"] = msg.backendInfo;
192 m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
198 void DataStreamPeer::dispatch(const SetupData &msg)
201 map["AdminUser"] = msg.adminUser;
202 map["AdminPasswd"] = msg.adminPassword;
203 map["Backend"] = msg.backend;
204 map["ConnectionProperties"] = msg.setupData;
207 m["MsgType"] = "CoreSetupData";
208 m["SetupData"] = map;
214 void DataStreamPeer::dispatch(const SetupFailed &msg)
217 m["MsgType"] = "CoreSetupReject";
218 m["Error"] = msg.errorString;
224 void DataStreamPeer::dispatch(const SetupDone &msg)
229 m["MsgType"] = "CoreSetupAck";
235 void DataStreamPeer::dispatch(const Login &msg)
238 m["MsgType"] = "ClientLogin";
239 m["User"] = msg.user;
240 m["Password"] = msg.password;
246 void DataStreamPeer::dispatch(const LoginFailed &msg)
249 m["MsgType"] = "ClientLoginReject";
250 m["Error"] = msg.errorString;
256 void DataStreamPeer::dispatch(const LoginSuccess &msg)
261 m["MsgType"] = "ClientLoginAck";
267 void DataStreamPeer::dispatch(const SessionState &msg)
270 m["MsgType"] = "SessionInit";
273 map["BufferInfos"] = msg.bufferInfos;
274 map["NetworkIds"] = msg.networkIds;
275 map["Identities"] = msg.identities;
276 m["SessionState"] = map;
282 /*** Standard messages ***/
284 void DataStreamPeer::handlePackedFunc(const QVariantList &packedFunc)
286 QVariantList params(packedFunc);
288 if (params.isEmpty()) {
289 qWarning() << Q_FUNC_INFO << "Received incompatible data:" << packedFunc;
293 // TODO: make sure that this is a valid request type
294 RequestType requestType = (RequestType)params.takeFirst().value<qint16>();
295 switch (requestType) {
297 if (params.count() < 3) {
298 qWarning() << Q_FUNC_INFO << "Received invalid sync call:" << params;
301 QByteArray className = params.takeFirst().toByteArray();
302 QString objectName = QString::fromUtf8(params.takeFirst().toByteArray());
303 QByteArray slotName = params.takeFirst().toByteArray();
304 handle(Protocol::SyncMessage(className, objectName, slotName, params));
308 if (params.empty()) {
309 qWarning() << Q_FUNC_INFO << "Received empty RPC call!";
312 QByteArray slotName = params.takeFirst().toByteArray();
313 handle(Protocol::RpcCall(slotName, params));
317 if (params.count() != 2) {
318 qWarning() << Q_FUNC_INFO << "Received invalid InitRequest:" << params;
321 QByteArray className = params[0].toByteArray();
322 QString objectName = QString::fromUtf8(params[1].toByteArray());
323 handle(Protocol::InitRequest(className, objectName));
327 if (params.count() < 2) {
328 qWarning() << Q_FUNC_INFO << "Received invalid InitData:" << params;
331 QByteArray className = params.takeFirst().toByteArray();
332 QString objectName = QString::fromUtf8(params.takeFirst().toByteArray());
333 QVariantMap initData;
334 for (int i = 0; i < params.count()/2; ++i)
335 initData[QString::fromUtf8(params[2*i].toByteArray())] = params[2*i+1];
336 handle(Protocol::InitData(className, objectName, initData));
340 if (params.count() != 1) {
341 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
344 // Note: QDateTime instead of QTime as in the legacy protocol!
345 handle(Protocol::HeartBeat(params[0].toDateTime()));
348 case HeartBeatReply: {
349 if (params.count() != 1) {
350 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
353 // Note: QDateTime instead of QTime as in the legacy protocol!
354 handle(Protocol::HeartBeatReply(params[0].toDateTime()));
362 void DataStreamPeer::dispatch(const Protocol::SyncMessage &msg)
364 dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName.toUtf8() << msg.slotName << msg.params);
368 void DataStreamPeer::dispatch(const Protocol::RpcCall &msg)
370 dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
374 void DataStreamPeer::dispatch(const Protocol::InitRequest &msg)
376 dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName.toUtf8());
380 void DataStreamPeer::dispatch(const Protocol::InitData &msg)
382 QVariantList initData;
383 QVariantMap::const_iterator it = msg.initData.begin();
384 while (it != msg.initData.end()) {
385 initData << it.key().toUtf8() << it.value();
388 dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName.toUtf8() << initData);
392 void DataStreamPeer::dispatch(const Protocol::HeartBeat &msg)
394 dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp);
398 void DataStreamPeer::dispatch(const Protocol::HeartBeatReply &msg)
400 dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp);
404 void DataStreamPeer::dispatchPackedFunc(const QVariantList &packedFunc)
406 writeMessage(packedFunc);