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