~ubuntu-branches/ubuntu/trusty/quassel/trusty-proposed

« back to all changes in this revision

Viewing changes to src/common/protocols/legacy/legacypeer.cpp

  • Committer: Package Import Robot
  • Author(s): Scott Kitterman
  • Date: 2014-02-18 23:18:25 UTC
  • mfrom: (1.1.54)
  • Revision ID: package-import@ubuntu.com-20140218231825-6vvoh451otn95pkn
Tags: 0.10~beta1-0ubuntu1
* New upstream beta relase
  - Drop debian/patches/upstream_fix_fullscreen_mode.diff which had been
    cherrypicked from upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/***************************************************************************
2
 
 *   Copyright (C) 2005-2013 by the Quassel Project                        *
 
2
 *   Copyright (C) 2005-2014 by the Quassel Project                        *
3
3
 *   devel@quassel-irc.org                                                 *
4
4
 *                                                                         *
5
5
 *   This program is free software; you can redistribute it and/or modify  *
18
18
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19
19
 ***************************************************************************/
20
20
 
 
21
#include <QHostAddress>
21
22
#include <QTcpSocket>
22
23
 
23
24
#include "legacypeer.h"
 
25
#include "quassel.h"
 
26
 
 
27
/* version.inc is no longer used for this */
 
28
const uint protocolVersion = 10;
 
29
const uint coreNeedsProtocol = protocolVersion;
 
30
const uint clientNeedsProtocol = protocolVersion;
24
31
 
25
32
using namespace Protocol;
26
33
 
27
 
LegacyPeer::LegacyPeer(QTcpSocket *socket, QObject *parent)
28
 
    : RemotePeer(socket, parent),
29
 
    _blockSize(0),
 
34
LegacyPeer::LegacyPeer(::AuthHandler *authHandler, QTcpSocket *socket, QObject *parent)
 
35
    : RemotePeer(authHandler, socket, Compressor::NoCompression, parent),
30
36
    _useCompression(false)
31
37
{
32
 
    _stream.setDevice(socket);
33
 
    _stream.setVersion(QDataStream::Qt_4_2);
34
38
 
35
 
    connect(socket, SIGNAL(readyRead()), SLOT(socketDataAvailable()));
36
39
}
37
40
 
38
41
 
40
43
{
41
44
    RemotePeer::setSignalProxy(proxy);
42
45
 
 
46
    // FIXME only in compat mode
43
47
    if (proxy) {
44
48
        // enable compression now if requested - the initial handshake is uncompressed in the legacy protocol!
45
49
        _useCompression = socket()->property("UseCompression").toBool();
 
50
        if (_useCompression)
 
51
            qDebug() << "Using compression for peer:" << qPrintable(socket()->peerAddress().toString());
46
52
    }
47
53
 
48
54
}
49
55
 
50
56
 
51
 
void LegacyPeer::socketDataAvailable()
 
57
void LegacyPeer::processMessage(const QByteArray &msg)
52
58
{
 
59
    QDataStream stream(msg);
 
60
    stream.setVersion(QDataStream::Qt_4_2);
 
61
 
53
62
    QVariant item;
54
 
    while (readSocketData(item)) {
55
 
        // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere
56
 
        if (!signalProxy())
57
 
            emit dataReceived(item);
58
 
        else
59
 
            handlePackedFunc(item);
60
 
    }
61
 
}
62
 
 
63
 
 
64
 
bool LegacyPeer::readSocketData(QVariant &item)
65
 
{
66
 
    if (_blockSize == 0) {
67
 
        if (socket()->bytesAvailable() < 4)
68
 
            return false;
69
 
        _stream >> _blockSize;
70
 
    }
71
 
 
72
 
    if (_blockSize > 1 << 22) {
73
 
        close("Peer tried to send package larger than max package size!");
74
 
        return false;
75
 
    }
76
 
 
77
 
    if (_blockSize == 0) {
78
 
        close("Peer tried to send 0 byte package!");
79
 
        return false;
80
 
    }
81
 
 
82
 
    if (socket()->bytesAvailable() < _blockSize) {
83
 
        emit transferProgress(socket()->bytesAvailable(), _blockSize);
84
 
        return false;
85
 
    }
86
 
 
87
 
    emit transferProgress(_blockSize, _blockSize);
88
 
 
89
 
    _blockSize = 0;
90
 
 
91
63
    if (_useCompression) {
92
64
        QByteArray rawItem;
93
 
        _stream >> rawItem;
 
65
        stream >> rawItem;
94
66
 
95
67
        int nbytes = rawItem.size();
96
68
        if (nbytes <= 4) {
97
69
            const char *data = rawItem.constData();
98
70
            if (nbytes < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) {
99
71
                close("Peer sent corrupted compressed data!");
100
 
                return false;
 
72
                return;
101
73
            }
102
74
        }
103
75
 
108
80
        itemStream >> item;
109
81
    }
110
82
    else {
111
 
        _stream >> item;
 
83
        stream >> item;
112
84
    }
113
85
 
114
 
    if (!item.isValid()) {
 
86
    if (stream.status() != QDataStream::Ok || !item.isValid()) {
115
87
        close("Peer sent corrupt data: unable to load QVariant!");
116
 
        return false;
 
88
        return;
117
89
    }
118
90
 
119
 
    return true;
 
91
    // if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere
 
92
    if (!signalProxy())
 
93
        handleHandshakeMessage(item);
 
94
    else
 
95
        handlePackedFunc(item);
120
96
}
121
97
 
122
98
 
123
 
void LegacyPeer::writeSocketData(const QVariant &item)
 
99
void LegacyPeer::writeMessage(const QVariant &item)
124
100
{
125
 
    if (!socket()->isOpen()) {
126
 
        qWarning() << Q_FUNC_INFO << "Can't write to a closed socket!";
127
 
        return;
128
 
    }
129
 
 
130
101
    QByteArray block;
131
102
    QDataStream out(&block, QIODevice::WriteOnly);
132
103
    out.setVersion(QDataStream::Qt_4_2);
145
116
        out << item;
146
117
    }
147
118
 
148
 
    _stream << block;  // also writes the length as part of the serialization format
149
 
}
150
 
 
 
119
    writeMessage(block);
 
120
}
 
121
 
 
122
 
 
123
/*** Handshake messages ***/
 
124
 
 
125
/* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
 
126
 * a structure different from those being used after the handshake.
 
127
 * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
 
128
 */
 
129
 
 
130
void LegacyPeer::handleHandshakeMessage(const QVariant &msg)
 
131
{
 
132
    QVariantMap m = msg.toMap();
 
133
 
 
134
    QString msgType = m["MsgType"].toString();
 
135
    if (msgType.isEmpty()) {
 
136
        emit protocolError(tr("Invalid handshake message!"));
 
137
        return;
 
138
    }
 
139
 
 
140
    if (msgType == "ClientInit") {
 
141
        // FIXME only in compat mode
 
142
        uint ver = m["ProtocolVersion"].toUInt();
 
143
        if (ver < coreNeedsProtocol) {
 
144
            emit protocolVersionMismatch((int)ver, (int)coreNeedsProtocol);
 
145
            return;
 
146
        }
 
147
 
 
148
#ifndef QT_NO_COMPRESS
 
149
        // FIXME only in compat mode
 
150
        if (m["UseCompression"].toBool()) {
 
151
            socket()->setProperty("UseCompression", true);
 
152
        }
 
153
#endif
 
154
        handle(RegisterClient(m["ClientVersion"].toString(), m["UseSsl"].toBool()));
 
155
    }
 
156
 
 
157
    else if (msgType == "ClientInitReject") {
 
158
        handle(ClientDenied(m["Error"].toString()));
 
159
    }
 
160
 
 
161
    else if (msgType == "ClientInitAck") {
 
162
        // FIXME only in compat mode
 
163
        uint ver = m["ProtocolVersion"].toUInt(); // actually an UInt
 
164
        if (ver < clientNeedsProtocol) {
 
165
            emit protocolVersionMismatch((int)ver, (int)clientNeedsProtocol);
 
166
            return;
 
167
        }
 
168
#ifndef QT_NO_COMPRESS
 
169
        if (m["SupportsCompression"].toBool())
 
170
            socket()->setProperty("UseCompression", true);
 
171
#endif
 
172
 
 
173
        handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool(), QDateTime()));
 
174
    }
 
175
 
 
176
    else if (msgType == "CoreSetupData") {
 
177
        QVariantMap map = m["SetupData"].toMap();
 
178
        handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
 
179
    }
 
180
 
 
181
    else if (msgType == "CoreSetupReject") {
 
182
        handle(SetupFailed(m["Error"].toString()));
 
183
    }
 
184
 
 
185
    else if (msgType == "CoreSetupAck") {
 
186
        handle(SetupDone());
 
187
    }
 
188
 
 
189
    else if (msgType == "ClientLogin") {
 
190
        handle(Login(m["User"].toString(), m["Password"].toString()));
 
191
    }
 
192
 
 
193
    else if (msgType == "ClientLoginReject") {
 
194
        handle(LoginFailed(m["Error"].toString()));
 
195
    }
 
196
 
 
197
    else if (msgType == "ClientLoginAck") {
 
198
        handle(LoginSuccess());
 
199
    }
 
200
 
 
201
    else if (msgType == "SessionInit") {
 
202
        QVariantMap map = m["SessionState"].toMap();
 
203
        handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
 
204
    }
 
205
 
 
206
    else {
 
207
        emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
 
208
    }
 
209
}
 
210
 
 
211
 
 
212
void LegacyPeer::dispatch(const RegisterClient &msg) {
 
213
    QVariantMap m;
 
214
    m["MsgType"] = "ClientInit";
 
215
    m["ClientVersion"] = msg.clientVersion;
 
216
    m["ClientDate"] = Quassel::buildInfo().buildDate;
 
217
 
 
218
    // FIXME only in compat mode
 
219
    m["ProtocolVersion"] = protocolVersion;
 
220
    m["UseSsl"] = msg.sslSupported;
 
221
#ifndef QT_NO_COMPRESS
 
222
    m["UseCompression"] = true;
 
223
#else
 
224
    m["UseCompression"] = false;
 
225
#endif
 
226
 
 
227
    writeMessage(m);
 
228
}
 
229
 
 
230
 
 
231
void LegacyPeer::dispatch(const ClientDenied &msg) {
 
232
    QVariantMap m;
 
233
    m["MsgType"] = "ClientInitReject";
 
234
    m["Error"] = msg.errorString;
 
235
 
 
236
    writeMessage(m);
 
237
}
 
238
 
 
239
 
 
240
void LegacyPeer::dispatch(const ClientRegistered &msg) {
 
241
    QVariantMap m;
 
242
    m["MsgType"] = "ClientInitAck";
 
243
    m["CoreFeatures"] = msg.coreFeatures;
 
244
    m["StorageBackends"] = msg.backendInfo;
 
245
 
 
246
    // FIXME only in compat mode
 
247
    m["ProtocolVersion"] = protocolVersion;
 
248
    m["SupportSsl"] = msg.sslSupported;
 
249
    m["SupportsCompression"] = socket()->property("UseCompression").toBool(); // this property gets already set in the ClientInit handler
 
250
 
 
251
    // This is only used for old v10 clients (pre-0.5)
 
252
    int uptime = msg.coreStartTime.secsTo(QDateTime::currentDateTime().toUTC());
 
253
    int updays = uptime / 86400; uptime %= 86400;
 
254
    int uphours = uptime / 3600; uptime %= 3600;
 
255
    int upmins = uptime / 60;
 
256
    m["CoreInfo"] = tr("<b>Quassel Core Version %1</b><br>"
 
257
                       "Built: %2<br>"
 
258
                       "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
 
259
                       .arg(Quassel::buildInfo().buildDate)
 
260
                       .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(msg.coreStartTime.toString(Qt::TextDate));
 
261
 
 
262
    m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
 
263
 
 
264
    writeMessage(m);
 
265
}
 
266
 
 
267
 
 
268
void LegacyPeer::dispatch(const SetupData &msg)
 
269
{
 
270
    QVariantMap map;
 
271
    map["AdminUser"] = msg.adminUser;
 
272
    map["AdminPasswd"] = msg.adminPassword;
 
273
    map["Backend"] = msg.backend;
 
274
    map["ConnectionProperties"] = msg.setupData;
 
275
 
 
276
    QVariantMap m;
 
277
    m["MsgType"] = "CoreSetupData";
 
278
    m["SetupData"] = map;
 
279
    writeMessage(m);
 
280
}
 
281
 
 
282
 
 
283
void LegacyPeer::dispatch(const SetupFailed &msg)
 
284
{
 
285
    QVariantMap m;
 
286
    m["MsgType"] = "CoreSetupReject";
 
287
    m["Error"] = msg.errorString;
 
288
 
 
289
    writeMessage(m);
 
290
}
 
291
 
 
292
 
 
293
void LegacyPeer::dispatch(const SetupDone &msg)
 
294
{
 
295
    Q_UNUSED(msg)
 
296
 
 
297
    QVariantMap m;
 
298
    m["MsgType"] = "CoreSetupAck";
 
299
 
 
300
    writeMessage(m);
 
301
}
 
302
 
 
303
 
 
304
void LegacyPeer::dispatch(const Login &msg)
 
305
{
 
306
    QVariantMap m;
 
307
    m["MsgType"] = "ClientLogin";
 
308
    m["User"] = msg.user;
 
309
    m["Password"] = msg.password;
 
310
 
 
311
    writeMessage(m);
 
312
}
 
313
 
 
314
 
 
315
void LegacyPeer::dispatch(const LoginFailed &msg)
 
316
{
 
317
    QVariantMap m;
 
318
    m["MsgType"] = "ClientLoginReject";
 
319
    m["Error"] = msg.errorString;
 
320
 
 
321
    writeMessage(m);
 
322
}
 
323
 
 
324
 
 
325
void LegacyPeer::dispatch(const LoginSuccess &msg)
 
326
{
 
327
    Q_UNUSED(msg)
 
328
 
 
329
    QVariantMap m;
 
330
    m["MsgType"] = "ClientLoginAck";
 
331
 
 
332
    writeMessage(m);
 
333
}
 
334
 
 
335
 
 
336
void LegacyPeer::dispatch(const SessionState &msg)
 
337
{
 
338
    QVariantMap m;
 
339
    m["MsgType"] = "SessionInit";
 
340
 
 
341
    QVariantMap map;
 
342
    map["BufferInfos"] = msg.bufferInfos;
 
343
    map["NetworkIds"] = msg.networkIds;
 
344
    map["Identities"] = msg.identities;
 
345
    m["SessionState"] = map;
 
346
 
 
347
    writeMessage(m);
 
348
}
 
349
 
 
350
 
 
351
/*** Standard messages ***/
151
352
 
152
353
void LegacyPeer::handlePackedFunc(const QVariant &packedFunc)
153
354
{
158
359
        return;
159
360
    }
160
361
 
 
362
    // TODO: make sure that this is a valid request type
161
363
    RequestType requestType = (RequestType)params.takeFirst().value<int>();
162
364
    switch (requestType) {
163
365
        case Sync: {
198
400
            QByteArray className = params[0].toByteArray();
199
401
            QString objectName = params[1].toString();
200
402
            QVariantMap initData = params[2].toMap();
 
403
 
 
404
            // we need to special-case IrcUsersAndChannels here, since the format changed
 
405
            if (className == "Network")
 
406
                fromLegacyIrcUsersAndChannels(initData);
201
407
            handle(Protocol::InitData(className, objectName, initData));
202
408
            break;
203
409
        }
232
438
 
233
439
void LegacyPeer::dispatch(const Protocol::SyncMessage &msg)
234
440
{
235
 
    dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className() << msg.objectName() << msg.slotName() << msg.params());
 
441
    dispatchPackedFunc(QVariantList() << (qint16)Sync << msg.className << msg.objectName << msg.slotName << msg.params);
236
442
}
237
443
 
238
444
 
239
445
void LegacyPeer::dispatch(const Protocol::RpcCall &msg)
240
446
{
241
 
    dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName() << msg.params());
 
447
    dispatchPackedFunc(QVariantList() << (qint16)RpcCall << msg.slotName << msg.params);
242
448
}
243
449
 
244
450
 
245
451
void LegacyPeer::dispatch(const Protocol::InitRequest &msg)
246
452
{
247
 
    dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className() << msg.objectName());
 
453
    dispatchPackedFunc(QVariantList() << (qint16)InitRequest << msg.className << msg.objectName);
248
454
}
249
455
 
250
456
 
251
457
void LegacyPeer::dispatch(const Protocol::InitData &msg)
252
458
{
253
 
    dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className() << msg.objectName() << msg.initData());
 
459
    // We need to special-case IrcUsersAndChannels, as the format changed
 
460
    if (msg.className == "Network") {
 
461
        QVariantMap initData = msg.initData;
 
462
        toLegacyIrcUsersAndChannels(initData);
 
463
        dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << initData);
 
464
    }
 
465
    else
 
466
        dispatchPackedFunc(QVariantList() << (qint16)InitData << msg.className << msg.objectName << msg.initData);
254
467
}
255
468
 
256
469
 
257
470
void LegacyPeer::dispatch(const Protocol::HeartBeat &msg)
258
471
{
259
 
    dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp().time());
 
472
    dispatchPackedFunc(QVariantList() << (qint16)HeartBeat << msg.timestamp.time());
260
473
}
261
474
 
262
475
 
263
476
void LegacyPeer::dispatch(const Protocol::HeartBeatReply &msg)
264
477
{
265
 
    dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp().time());
 
478
    dispatchPackedFunc(QVariantList() << (qint16)HeartBeatReply << msg.timestamp.time());
266
479
}
267
480
 
268
481
 
269
482
void LegacyPeer::dispatchPackedFunc(const QVariantList &packedFunc)
270
483
{
271
 
    writeSocketData(QVariant(packedFunc));
 
484
    writeMessage(QVariant(packedFunc));
 
485
}
 
486
 
 
487
 
 
488
// Handle the changed format for Network's initData
 
489
// cf. Network::initIrcUsersAndChannels()
 
490
void LegacyPeer::fromLegacyIrcUsersAndChannels(QVariantMap &initData)
 
491
{
 
492
    const QVariantMap &legacyMap = initData["IrcUsersAndChannels"].toMap();
 
493
    QVariantMap newMap;
 
494
 
 
495
    QHash<QString, QVariantList> users;
 
496
    foreach(const QVariant &v, legacyMap["users"].toMap().values()) {
 
497
        const QVariantMap &map = v.toMap();
 
498
        foreach(const QString &key, map.keys())
 
499
            users[key] << map[key];
 
500
    }
 
501
    QVariantMap userMap;
 
502
    foreach(const QString &key, users.keys())
 
503
        userMap[key] = users[key];
 
504
    newMap["Users"] = userMap;
 
505
 
 
506
    QHash<QString, QVariantList> channels;
 
507
    foreach(const QVariant &v, legacyMap["channels"].toMap().values()) {
 
508
        const QVariantMap &map = v.toMap();
 
509
        foreach(const QString &key, map.keys())
 
510
            channels[key] << map[key];
 
511
    }
 
512
    QVariantMap channelMap;
 
513
    foreach(const QString &key, channels.keys())
 
514
        channelMap[key] = channels[key];
 
515
    newMap["Channels"] = channelMap;
 
516
 
 
517
    initData["IrcUsersAndChannels"] = newMap;
 
518
}
 
519
 
 
520
 
 
521
void LegacyPeer::toLegacyIrcUsersAndChannels(QVariantMap &initData)
 
522
{
 
523
    const QVariantMap &usersAndChannels = initData["IrcUsersAndChannels"].toMap();
 
524
    QVariantMap legacyMap;
 
525
 
 
526
    // toMap() and toList() are cheap, so no need to copy to a hash
 
527
 
 
528
    QVariantMap userMap;
 
529
    const QVariantMap &users = usersAndChannels["Users"].toMap();
 
530
 
 
531
    int size = users["nick"].toList().size(); // we know this key exists
 
532
    for(int i = 0; i < size; i++) {
 
533
        QVariantMap map;
 
534
        foreach(const QString &key, users.keys())
 
535
            map[key] = users[key].toList().at(i);
 
536
        QString hostmask = QString("%1!%2@%3").arg(map["nick"].toString(), map["user"].toString(), map["host"].toString());
 
537
        userMap[hostmask.toLower()] = map;
 
538
    }
 
539
    legacyMap["users"] = userMap;
 
540
 
 
541
    QVariantMap channelMap;
 
542
    const QVariantMap &channels = usersAndChannels["Channels"].toMap();
 
543
 
 
544
    size = channels["name"].toList().size();
 
545
    for(int i = 0; i < size; i++) {
 
546
        QVariantMap map;
 
547
        foreach(const QString &key, channels.keys())
 
548
            map[key] = channels[key].toList().at(i);
 
549
        channelMap[map["name"].toString().toLower()] = map;
 
550
    }
 
551
    legacyMap["channels"] = channelMap;
 
552
 
 
553
    initData["IrcUsersAndChannels"] = legacyMap;
272
554
}