Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
datastreampeer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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 
28 using namespace Protocol;
29 
30 DataStreamPeer::DataStreamPeer(::AuthHandler *authHandler, QTcpSocket *socket, quint16 features, Compressor::CompressionLevel level, QObject *parent)
31  : RemotePeer(authHandler, socket, level, parent)
32 {
33  Q_UNUSED(features);
34 }
35 
36 
38 {
39  return 0;
40 }
41 
42 
43 bool DataStreamPeer::acceptsFeatures(quint16 peerFeatures)
44 {
45  Q_UNUSED(peerFeatures);
46  return true;
47 }
48 
49 
51 {
52  return 0;
53 }
54 
55 
56 void DataStreamPeer::processMessage(const QByteArray &msg)
57 {
58  QDataStream stream(msg);
59  stream.setVersion(QDataStream::Qt_4_2);
60  QVariantList list;
61  stream >> list;
62  if (stream.status() != QDataStream::Ok) {
63  close("Peer sent corrupt data, closing down!");
64  return;
65  }
66 
67  // if no sigproxy is set, we're in handshake mode
68  if (!signalProxy())
70  else
71  handlePackedFunc(list);
72 }
73 
74 
75 void DataStreamPeer::writeMessage(const QVariantMap &handshakeMsg)
76 {
77  QVariantList list;
78  QVariantMap::const_iterator it = handshakeMsg.begin();
79  while (it != handshakeMsg.end()) {
80  list << it.key().toUtf8() << it.value();
81  ++it;
82  }
83 
84  writeMessage(list);
85 }
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 
99 /*** Handshake messages ***/
100 
101 /* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
102  * a structure different from those being used after the handshake.
103  * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
104  */
105 
106 void DataStreamPeer::handleHandshakeMessage(const QVariantList &mapData)
107 {
108  QVariantMap m;
109  for (int i = 0; i < mapData.count()/2; ++i)
110  m[QString::fromUtf8(mapData[2*i].toByteArray())] = mapData[2*i+1];
111 
112  QString msgType = m["MsgType"].toString();
113  if (msgType.isEmpty()) {
114  emit protocolError(tr("Invalid handshake message!"));
115  return;
116  }
117 
118  if (msgType == "ClientInit") {
119  handle(RegisterClient(m["ClientVersion"].toString(), m["ClientDate"].toString(), false)); // UseSsl obsolete
120  }
121 
122  else if (msgType == "ClientInitReject") {
123  handle(ClientDenied(m["Error"].toString()));
124  }
125 
126  else if (msgType == "ClientInitAck") {
127  handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), false, QString())); // SupportsSsl and coreInfo obsolete
128  }
129 
130  else if (msgType == "CoreSetupData") {
131  QVariantMap map = m["SetupData"].toMap();
132  handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
133  }
134 
135  else if (msgType == "CoreSetupReject") {
136  handle(SetupFailed(m["Error"].toString()));
137  }
138 
139  else if (msgType == "CoreSetupAck") {
140  handle(SetupDone());
141  }
142 
143  else if (msgType == "ClientLogin") {
144  handle(Login(m["User"].toString(), m["Password"].toString()));
145  }
146 
147  else if (msgType == "ClientLoginReject") {
148  handle(LoginFailed(m["Error"].toString()));
149  }
150 
151  else if (msgType == "ClientLoginAck") {
152  handle(LoginSuccess());
153  }
154 
155  else if (msgType == "SessionInit") {
156  QVariantMap map = m["SessionState"].toMap();
157  handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
158  }
159 
160  else {
161  emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
162  }
163 }
164 
165 
167  QVariantMap m;
168  m["MsgType"] = "ClientInit";
169  m["ClientVersion"] = msg.clientVersion;
170  m["ClientDate"] = msg.buildDate;
171 
172  writeMessage(m);
173 }
174 
175 
177  QVariantMap m;
178  m["MsgType"] = "ClientInitReject";
179  m["Error"] = msg.errorString;
180 
181  writeMessage(m);
182 }
183 
184 
186  QVariantMap m;
187  m["MsgType"] = "ClientInitAck";
188  m["CoreFeatures"] = msg.coreFeatures;
189  m["StorageBackends"] = msg.backendInfo;
190  m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
191 
192  writeMessage(m);
193 }
194 
195 
197 {
198  QVariantMap map;
199  map["AdminUser"] = msg.adminUser;
200  map["AdminPasswd"] = msg.adminPassword;
201  map["Backend"] = msg.backend;
202  map["ConnectionProperties"] = msg.setupData;
203 
204  QVariantMap m;
205  m["MsgType"] = "CoreSetupData";
206  m["SetupData"] = map;
207 
208  writeMessage(m);
209 }
210 
211 
213 {
214  QVariantMap m;
215  m["MsgType"] = "CoreSetupReject";
216  m["Error"] = msg.errorString;
217 
218  writeMessage(m);
219 }
220 
221 
223 {
224  Q_UNUSED(msg)
225 
226  QVariantMap m;
227  m["MsgType"] = "CoreSetupAck";
228 
229  writeMessage(m);
230 }
231 
232 
234 {
235  QVariantMap m;
236  m["MsgType"] = "ClientLogin";
237  m["User"] = msg.user;
238  m["Password"] = msg.password;
239 
240  writeMessage(m);
241 }
242 
243 
245 {
246  QVariantMap m;
247  m["MsgType"] = "ClientLoginReject";
248  m["Error"] = msg.errorString;
249 
250  writeMessage(m);
251 }
252 
253 
255 {
256  Q_UNUSED(msg)
257 
258  QVariantMap m;
259  m["MsgType"] = "ClientLoginAck";
260 
261  writeMessage(m);
262 }
263 
264 
266 {
267  QVariantMap m;
268  m["MsgType"] = "SessionInit";
269 
270  QVariantMap map;
271  map["BufferInfos"] = msg.bufferInfos;
272  map["NetworkIds"] = msg.networkIds;
273  map["Identities"] = msg.identities;
274  m["SessionState"] = map;
275 
276  writeMessage(m);
277 }
278 
279 
280 /*** Standard messages ***/
281 
282 void DataStreamPeer::handlePackedFunc(const QVariantList &packedFunc)
283 {
284  QVariantList params(packedFunc);
285 
286  if (params.isEmpty()) {
287  qWarning() << Q_FUNC_INFO << "Received incompatible data:" << packedFunc;
288  return;
289  }
290 
291  // TODO: make sure that this is a valid request type
292  RequestType requestType = (RequestType)params.takeFirst().value<qint16>();
293  switch (requestType) {
294  case Sync: {
295  if (params.count() < 3) {
296  qWarning() << Q_FUNC_INFO << "Received invalid sync call:" << params;
297  return;
298  }
299  QByteArray className = params.takeFirst().toByteArray();
300  QString objectName = QString::fromUtf8(params.takeFirst().toByteArray());
301  QByteArray slotName = params.takeFirst().toByteArray();
302  handle(Protocol::SyncMessage(className, objectName, slotName, params));
303  break;
304  }
305  case RpcCall: {
306  if (params.empty()) {
307  qWarning() << Q_FUNC_INFO << "Received empty RPC call!";
308  return;
309  }
310  QByteArray slotName = params.takeFirst().toByteArray();
311  handle(Protocol::RpcCall(slotName, params));
312  break;
313  }
314  case InitRequest: {
315  if (params.count() != 2) {
316  qWarning() << Q_FUNC_INFO << "Received invalid InitRequest:" << params;
317  return;
318  }
319  QByteArray className = params[0].toByteArray();
320  QString objectName = QString::fromUtf8(params[1].toByteArray());
321  handle(Protocol::InitRequest(className, objectName));
322  break;
323  }
324  case InitData: {
325  if (params.count() < 2) {
326  qWarning() << Q_FUNC_INFO << "Received invalid InitData:" << params;
327  return;
328  }
329  QByteArray className = params.takeFirst().toByteArray();
330  QString objectName = QString::fromUtf8(params.takeFirst().toByteArray());
331  QVariantMap initData;
332  for (int i = 0; i < params.count()/2; ++i)
333  initData[QString::fromUtf8(params[2*i].toByteArray())] = params[2*i+1];
334  handle(Protocol::InitData(className, objectName, initData));
335  break;
336  }
337  case HeartBeat: {
338  if (params.count() != 1) {
339  qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
340  return;
341  }
342  // Note: QDateTime instead of QTime as in the legacy protocol!
343  handle(Protocol::HeartBeat(params[0].toDateTime()));
344  break;
345  }
346  case HeartBeatReply: {
347  if (params.count() != 1) {
348  qWarning() << Q_FUNC_INFO << "Received invalid HeartBeat:" << params;
349  return;
350  }
351  // Note: QDateTime instead of QTime as in the legacy protocol!
352  handle(Protocol::HeartBeatReply(params[0].toDateTime()));
353  break;
354  }
355 
356  }
357 }
358 
359 
361 {
362  dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName.toUtf8() << msg.slotName << msg.params);
363 }
364 
365 
367 {
368  dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
369 }
370 
371 
373 {
374  dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName.toUtf8());
375 }
376 
377 
379 {
380  QVariantList initData;
381  QVariantMap::const_iterator it = msg.initData.begin();
382  while (it != msg.initData.end()) {
383  initData << it.key().toUtf8() << it.value();
384  ++it;
385  }
386  dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName.toUtf8() << initData);
387 }
388 
389 
391 {
392  dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp);
393 }
394 
395 
397 {
398  dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp);
399 }
400 
401 
402 void DataStreamPeer::dispatchPackedFunc(const QVariantList &packedFunc)
403 {
404  writeMessage(packedFunc);
405 }