Implement custom deserializer to add our own sanity checks
[quassel.git] / src / common / protocols / datastream / datastreampeer.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include <QtEndian>
22 #include <QDataStream>
23 #include <QHostAddress>
24 #include <QTcpSocket>
25
26 #include "datastreampeer.h"
27 #include "serializers/serializers.h"
28
29 using namespace Protocol;
30
31 DataStreamPeer::DataStreamPeer(::AuthHandler *authHandler, QTcpSocket *socket, quint16 features, Compressor::CompressionLevel level, QObject *parent)
32     : RemotePeer(authHandler, socket, level, parent)
33 {
34     Q_UNUSED(features);
35 }
36
37
38 quint16 DataStreamPeer::supportedFeatures()
39 {
40     return 0;
41 }
42
43
44 bool DataStreamPeer::acceptsFeatures(quint16 peerFeatures)
45 {
46     Q_UNUSED(peerFeatures);
47     return true;
48 }
49
50
51 quint16 DataStreamPeer::enabledFeatures() const
52 {
53     return 0;
54 }
55
56
57 void DataStreamPeer::processMessage(const QByteArray &msg)
58 {
59     QDataStream stream(msg);
60     stream.setVersion(QDataStream::Qt_4_2);
61     QVariantList list;
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!");
66         return;
67     }
68
69     // if no sigproxy is set, we're in handshake mode
70     if (!signalProxy())
71         handleHandshakeMessage(list);
72     else
73         handlePackedFunc(list);
74 }
75
76
77 void DataStreamPeer::writeMessage(const QVariantMap &handshakeMsg)
78 {
79     QVariantList list;
80     QVariantMap::const_iterator it = handshakeMsg.begin();
81     while (it != handshakeMsg.end()) {
82         list << it.key().toUtf8() << it.value();
83         ++it;
84     }
85
86     writeMessage(list);
87 }
88
89
90 void DataStreamPeer::writeMessage(const QVariantList &sigProxyMsg)
91 {
92     QByteArray data;
93     QDataStream msgStream(&data, QIODevice::WriteOnly);
94     msgStream.setVersion(QDataStream::Qt_4_2);
95     msgStream << sigProxyMsg;
96
97     writeMessage(data);
98 }
99
100
101 /*** Handshake messages ***/
102
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.
106  */
107
108 void DataStreamPeer::handleHandshakeMessage(const QVariantList &mapData)
109 {
110     QVariantMap m;
111     for (int i = 0; i < mapData.count()/2; ++i)
112         m[QString::fromUtf8(mapData[2*i].toByteArray())] = mapData[2*i+1];
113
114     QString msgType = m["MsgType"].toString();
115     if (msgType.isEmpty()) {
116         emit protocolError(tr("Invalid handshake message!"));
117         return;
118     }
119
120     if (msgType == "ClientInit") {
121         handle(RegisterClient(m["ClientVersion"].toString(), m["ClientDate"].toString(), false)); // UseSsl obsolete
122     }
123
124     else if (msgType == "ClientInitReject") {
125         handle(ClientDenied(m["Error"].toString()));
126     }
127
128     else if (msgType == "ClientInitAck") {
129         handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), false)); // SupportsSsl is obsolete
130     }
131
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()));
135     }
136
137     else if (msgType == "CoreSetupReject") {
138         handle(SetupFailed(m["Error"].toString()));
139     }
140
141     else if (msgType == "CoreSetupAck") {
142         handle(SetupDone());
143     }
144
145     else if (msgType == "ClientLogin") {
146         handle(Login(m["User"].toString(), m["Password"].toString()));
147     }
148
149     else if (msgType == "ClientLoginReject") {
150         handle(LoginFailed(m["Error"].toString()));
151     }
152
153     else if (msgType == "ClientLoginAck") {
154         handle(LoginSuccess());
155     }
156
157     else if (msgType == "SessionInit") {
158         QVariantMap map = m["SessionState"].toMap();
159         handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
160     }
161
162     else {
163         emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
164     }
165 }
166
167
168 void DataStreamPeer::dispatch(const RegisterClient &msg) {
169     QVariantMap m;
170     m["MsgType"] = "ClientInit";
171     m["ClientVersion"] = msg.clientVersion;
172     m["ClientDate"] = msg.buildDate;
173
174     writeMessage(m);
175 }
176
177
178 void DataStreamPeer::dispatch(const ClientDenied &msg) {
179     QVariantMap m;
180     m["MsgType"] = "ClientInitReject";
181     m["Error"] = msg.errorString;
182
183     writeMessage(m);
184 }
185
186
187 void DataStreamPeer::dispatch(const ClientRegistered &msg) {
188     QVariantMap m;
189     m["MsgType"] = "ClientInitAck";
190     m["CoreFeatures"] = msg.coreFeatures;
191     m["StorageBackends"] = msg.backendInfo;
192     m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
193
194     writeMessage(m);
195 }
196
197
198 void DataStreamPeer::dispatch(const SetupData &msg)
199 {
200     QVariantMap map;
201     map["AdminUser"] = msg.adminUser;
202     map["AdminPasswd"] = msg.adminPassword;
203     map["Backend"] = msg.backend;
204     map["ConnectionProperties"] = msg.setupData;
205
206     QVariantMap m;
207     m["MsgType"] = "CoreSetupData";
208     m["SetupData"] = map;
209
210     writeMessage(m);
211 }
212
213
214 void DataStreamPeer::dispatch(const SetupFailed &msg)
215 {
216     QVariantMap m;
217     m["MsgType"] = "CoreSetupReject";
218     m["Error"] = msg.errorString;
219
220     writeMessage(m);
221 }
222
223
224 void DataStreamPeer::dispatch(const SetupDone &msg)
225 {
226     Q_UNUSED(msg)
227
228     QVariantMap m;
229     m["MsgType"] = "CoreSetupAck";
230
231     writeMessage(m);
232 }
233
234
235 void DataStreamPeer::dispatch(const Login &msg)
236 {
237     QVariantMap m;
238     m["MsgType"] = "ClientLogin";
239     m["User"] = msg.user;
240     m["Password"] = msg.password;
241
242     writeMessage(m);
243 }
244
245
246 void DataStreamPeer::dispatch(const LoginFailed &msg)
247 {
248     QVariantMap m;
249     m["MsgType"] = "ClientLoginReject";
250     m["Error"] = msg.errorString;
251
252     writeMessage(m);
253 }
254
255
256 void DataStreamPeer::dispatch(const LoginSuccess &msg)
257 {
258     Q_UNUSED(msg)
259
260     QVariantMap m;
261     m["MsgType"] = "ClientLoginAck";
262
263     writeMessage(m);
264 }
265
266
267 void DataStreamPeer::dispatch(const SessionState &msg)
268 {
269     QVariantMap m;
270     m["MsgType"] = "SessionInit";
271
272     QVariantMap map;
273     map["BufferInfos"] = msg.bufferInfos;
274     map["NetworkIds"] = msg.networkIds;
275     map["Identities"] = msg.identities;
276     m["SessionState"] = map;
277
278     writeMessage(m);
279 }
280
281
282 /*** Standard messages ***/
283
284 void DataStreamPeer::handlePackedFunc(const QVariantList &packedFunc)
285 {
286     QVariantList params(packedFunc);
287
288     if (params.isEmpty()) {
289         qWarning() << Q_FUNC_INFO << "Received incompatible data:" << packedFunc;
290         return;
291     }
292
293     // TODO: make sure that this is a valid request type
294     RequestType requestType = (RequestType)params.takeFirst().value<qint16>();
295     switch (requestType) {
296         case Sync: {
297             if (params.count() < 3) {
298                 qWarning() << Q_FUNC_INFO << "Received invalid sync call:" << params;
299                 return;
300             }
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));
305             break;
306         }
307         case RpcCall: {
308             if (params.empty()) {
309                 qWarning() << Q_FUNC_INFO << "Received empty RPC call!";
310                 return;
311             }
312             QByteArray slotName = params.takeFirst().toByteArray();
313             handle(Protocol::RpcCall(slotName, params));
314             break;
315         }
316         case InitRequest: {
317             if (params.count() != 2) {
318                 qWarning() << Q_FUNC_INFO << "Received invalid InitRequest:" << params;
319                 return;
320             }
321             QByteArray className = params[0].toByteArray();
322             QString objectName = QString::fromUtf8(params[1].toByteArray());
323             handle(Protocol::InitRequest(className, objectName));
324             break;
325         }
326         case InitData: {
327             if (params.count() < 2) {
328                 qWarning() << Q_FUNC_INFO << "Received invalid InitData:" << params;
329                 return;
330             }
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));
337             break;
338         }
339         case HeartBeat: {
340             if (params.count() != 1) {
341                 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
342                 return;
343             }
344             // Note: QDateTime instead of QTime as in the legacy protocol!
345             handle(Protocol::HeartBeat(params[0].toDateTime()));
346             break;
347         }
348         case HeartBeatReply: {
349             if (params.count() != 1) {
350                 qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
351                 return;
352             }
353             // Note: QDateTime instead of QTime as in the legacy protocol!
354             handle(Protocol::HeartBeatReply(params[0].toDateTime()));
355             break;
356         }
357
358     }
359 }
360
361
362 void DataStreamPeer::dispatch(const Protocol::SyncMessage &msg)
363 {
364     dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName.toUtf8() << msg.slotName << msg.params);
365 }
366
367
368 void DataStreamPeer::dispatch(const Protocol::RpcCall &msg)
369 {
370     dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
371 }
372
373
374 void DataStreamPeer::dispatch(const Protocol::InitRequest &msg)
375 {
376     dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName.toUtf8());
377 }
378
379
380 void DataStreamPeer::dispatch(const Protocol::InitData &msg)
381 {
382     QVariantList initData;
383     QVariantMap::const_iterator it = msg.initData.begin();
384     while (it != msg.initData.end()) {
385         initData << it.key().toUtf8() << it.value();
386         ++it;
387     }
388     dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName.toUtf8() << initData);
389 }
390
391
392 void DataStreamPeer::dispatch(const Protocol::HeartBeat &msg)
393 {
394     dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp);
395 }
396
397
398 void DataStreamPeer::dispatch(const Protocol::HeartBeatReply &msg)
399 {
400     dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp);
401 }
402
403
404 void DataStreamPeer::dispatchPackedFunc(const QVariantList &packedFunc)
405 {
406     writeMessage(packedFunc);
407 }