~ubuntu-branches/ubuntu/wily/psi/wily

« back to all changes in this revision

Viewing changes to iris/src/xmpp/xmpp-im/xmpp_ibb.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ibb.cpp - Inband bytestream
 
3
 * Copyright (C) 2001, 2002  Justin Karneges
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU Lesser General Public
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2.1 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library 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 GNU
 
13
 * Lesser General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Lesser General Public
 
16
 * License along with this library; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "xmpp_ibb.h"
 
22
 
 
23
#include <qtimer.h>
 
24
#include "xmpp_xmlcommon.h"
 
25
#include <QtCrypto>
 
26
 
 
27
#include <stdlib.h>
 
28
 
 
29
#define IBB_PACKET_SIZE   4096
 
30
#define IBB_PACKET_DELAY  0
 
31
 
 
32
using namespace XMPP;
 
33
 
 
34
static int num_conn = 0;
 
35
static int id_conn = 0;
 
36
 
 
37
//----------------------------------------------------------------------------
 
38
// IBBConnection
 
39
//----------------------------------------------------------------------------
 
40
class IBBConnection::Private
 
41
{
 
42
public:
 
43
        Private() {}
 
44
 
 
45
        int state;
 
46
        Jid peer;
 
47
        QString sid;
 
48
        IBBManager *m;
 
49
        JT_IBB *j;
 
50
        QDomElement comment;
 
51
        QString iq_id;
 
52
 
 
53
        int blockSize;
 
54
        QByteArray recvbuf, sendbuf;
 
55
        bool closePending, closing;
 
56
 
 
57
        int id;
 
58
};
 
59
 
 
60
IBBConnection::IBBConnection(IBBManager *m)
 
61
:ByteStream(m)
 
62
{
 
63
        d = new Private;
 
64
        d->m = m;
 
65
        d->j = 0;
 
66
        reset();
 
67
 
 
68
        ++num_conn;
 
69
        d->id = id_conn++;
 
70
        QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
 
71
        d->m->client()->debug(dstr);
 
72
}
 
73
 
 
74
void IBBConnection::reset(bool clear)
 
75
{
 
76
        d->m->unlink(this);
 
77
        d->state = Idle;
 
78
        d->closePending = false;
 
79
        d->closing = false;
 
80
 
 
81
        delete d->j;
 
82
        d->j = 0;
 
83
 
 
84
        d->sendbuf.resize(0);
 
85
        if(clear)
 
86
                d->recvbuf.resize(0);
 
87
}
 
88
 
 
89
IBBConnection::~IBBConnection()
 
90
{
 
91
        reset(true);
 
92
 
 
93
        --num_conn;
 
94
        QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
 
95
        d->m->client()->debug(dstr);
 
96
 
 
97
        delete d;
 
98
}
 
99
 
 
100
void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
 
101
{
 
102
        close();
 
103
        reset(true);
 
104
 
 
105
        d->state = Requesting;
 
106
        d->peer = peer;
 
107
        d->comment = comment;
 
108
 
 
109
        QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().toLatin1().data());
 
110
        d->m->client()->debug(dstr);
 
111
 
 
112
        d->j = new JT_IBB(d->m->client()->rootTask());
 
113
        connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
 
114
        d->j->request(d->peer, comment);
 
115
        d->j->go(true);
 
116
}
 
117
 
 
118
void IBBConnection::accept()
 
119
{
 
120
        if(d->state != WaitingForAccept)
 
121
                return;
 
122
 
 
123
        QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().toLatin1().data(), d->sid.toLatin1().data());
 
124
        d->m->client()->debug(dstr);
 
125
 
 
126
        d->m->doAccept(this, d->iq_id);
 
127
        d->state = Active;
 
128
        d->m->link(this);
 
129
}
 
130
 
 
131
void IBBConnection::close()
 
132
{
 
133
        if(d->state == Idle)
 
134
                return;
 
135
 
 
136
        if(d->state == WaitingForAccept) {
 
137
                d->m->doReject(this, d->iq_id, 403, "Rejected");
 
138
                reset();
 
139
                return;
 
140
        }
 
141
 
 
142
        QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
 
143
        d->m->client()->debug(dstr);
 
144
 
 
145
        if(d->state == Active) {
 
146
                // if there is data pending to be written, then pend the closing
 
147
                if(bytesToWrite() > 0) {
 
148
                        d->closePending = true;
 
149
                        trySend();
 
150
                        return;
 
151
                }
 
152
 
 
153
                // send a close packet
 
154
                JT_IBB *j = new JT_IBB(d->m->client()->rootTask());
 
155
                j->sendData(d->peer, d->sid, QByteArray(), true);
 
156
                j->go(true);
 
157
        }
 
158
 
 
159
        reset();
 
160
}
 
161
 
 
162
int IBBConnection::state() const
 
163
{
 
164
        return d->state;
 
165
}
 
166
 
 
167
Jid IBBConnection::peer() const
 
168
{
 
169
        return d->peer;
 
170
}
 
171
 
 
172
QString IBBConnection::streamid() const
 
173
{
 
174
        return d->sid;
 
175
}
 
176
 
 
177
QDomElement IBBConnection::comment() const
 
178
{
 
179
        return d->comment;
 
180
}
 
181
 
 
182
bool IBBConnection::isOpen() const
 
183
{
 
184
        if(d->state == Active)
 
185
                return true;
 
186
        else
 
187
                return false;
 
188
}
 
189
 
 
190
void IBBConnection::write(const QByteArray &a)
 
191
{
 
192
        if(d->state != Active || d->closePending || d->closing)
 
193
                return;
 
194
 
 
195
        // append to the end of our send buffer
 
196
        int oldsize = d->sendbuf.size();
 
197
        d->sendbuf.resize(oldsize + a.size());
 
198
        memcpy(d->sendbuf.data() + oldsize, a.data(), a.size());
 
199
 
 
200
        trySend();
 
201
}
 
202
 
 
203
QByteArray IBBConnection::read(int)
 
204
{
 
205
        // TODO: obey argument
 
206
        QByteArray a = d->recvbuf;
 
207
        d->recvbuf.resize(0);
 
208
        return a;
 
209
}
 
210
 
 
211
int IBBConnection::bytesAvailable() const
 
212
{
 
213
        return d->recvbuf.size();
 
214
}
 
215
 
 
216
int IBBConnection::bytesToWrite() const
 
217
{
 
218
        return d->sendbuf.size();
 
219
}
 
220
 
 
221
void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
 
222
{
 
223
        close();
 
224
        reset(true);
 
225
 
 
226
        d->state = WaitingForAccept;
 
227
        d->peer = peer;
 
228
        d->sid = sid;
 
229
        d->comment = comment;
 
230
        d->iq_id = iq_id;
 
231
}
 
232
 
 
233
void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
 
234
{
 
235
        // append to the end of our recv buffer
 
236
        int oldsize = d->recvbuf.size();
 
237
        d->recvbuf.resize(oldsize + a.size());
 
238
        memcpy(d->recvbuf.data() + oldsize, a.data(), a.size());
 
239
 
 
240
        readyRead();
 
241
 
 
242
        if(close) {
 
243
                reset();
 
244
                connectionClosed();
 
245
        }
 
246
}
 
247
 
 
248
void IBBConnection::ibb_finished()
 
249
{
 
250
        JT_IBB *j = d->j;
 
251
        d->j = 0;
 
252
 
 
253
        if(j->success()) {
 
254
                if(j->mode() == JT_IBB::ModeRequest) {
 
255
                        d->sid = j->streamid();
 
256
 
 
257
                        QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().toLatin1().data(), d->sid.toLatin1().data());
 
258
                        d->m->client()->debug(dstr);
 
259
 
 
260
                        d->state = Active;
 
261
                        d->m->link(this);
 
262
                        connected();
 
263
                }
 
264
                else {
 
265
                        bytesWritten(d->blockSize);
 
266
 
 
267
                        if(d->closing) {
 
268
                                reset();
 
269
                                delayedCloseFinished();
 
270
                        }
 
271
 
 
272
                        if(!d->sendbuf.isEmpty() || d->closePending)
 
273
                                QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
 
274
                }
 
275
        }
 
276
        else {
 
277
                if(j->mode() == JT_IBB::ModeRequest) {
 
278
                        QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().toLatin1().data());
 
279
                        d->m->client()->debug(dstr);
 
280
 
 
281
                        reset(true);
 
282
                        error(ErrRequest);
 
283
                }
 
284
                else {
 
285
                        reset(true);
 
286
                        error(ErrData);
 
287
                }
 
288
        }
 
289
}
 
290
 
 
291
void IBBConnection::trySend()
 
292
{
 
293
        // if we already have an active task, then don't do anything
 
294
        if(d->j)
 
295
                return;
 
296
 
 
297
        QByteArray a;
 
298
        if(!d->sendbuf.isEmpty()) {
 
299
                // take a chunk
 
300
                if(d->sendbuf.size() < IBB_PACKET_SIZE)
 
301
                        a.resize(d->sendbuf.size());
 
302
                else
 
303
                        a.resize(IBB_PACKET_SIZE);
 
304
                memcpy(a.data(), d->sendbuf.data(), a.size());
 
305
                d->sendbuf.resize(d->sendbuf.size() - a.size());
 
306
        }
 
307
 
 
308
        bool doClose = false;
 
309
        if(d->sendbuf.isEmpty() && d->closePending)
 
310
                doClose = true;
 
311
 
 
312
        // null operation?
 
313
        if(a.isEmpty() && !doClose)
 
314
                return;
 
315
 
 
316
        printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
 
317
        if(doClose)
 
318
                printf("and closing.\n");
 
319
        else
 
320
                printf("(%d bytes left)\n", d->sendbuf.size());
 
321
 
 
322
        if(doClose) {
 
323
                d->closePending = false;
 
324
                d->closing = true;
 
325
        }
 
326
 
 
327
        d->blockSize = a.size();
 
328
        d->j = new JT_IBB(d->m->client()->rootTask());
 
329
        connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
 
330
        d->j->sendData(d->peer, d->sid, a, doClose);
 
331
        d->j->go(true);
 
332
}
 
333
 
 
334
 
 
335
//----------------------------------------------------------------------------
 
336
// IBBManager
 
337
//----------------------------------------------------------------------------
 
338
class IBBManager::Private
 
339
{
 
340
public:
 
341
        Private() {}
 
342
 
 
343
        Client *client;
 
344
        IBBConnectionList activeConns;
 
345
        IBBConnectionList incomingConns;
 
346
        JT_IBB *ibb;
 
347
};
 
348
 
 
349
IBBManager::IBBManager(Client *parent)
 
350
:QObject(parent)
 
351
{
 
352
        d = new Private;
 
353
        d->client = parent;
 
354
        
 
355
        d->ibb = new JT_IBB(d->client->rootTask(), true);
 
356
        connect(d->ibb, SIGNAL(incomingRequest(const Jid &, const QString &, const QDomElement &)), SLOT(ibb_incomingRequest(const Jid &, const QString &, const QDomElement &)));
 
357
        connect(d->ibb, SIGNAL(incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)), SLOT(ibb_incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)));
 
358
}
 
359
 
 
360
IBBManager::~IBBManager()
 
361
{
 
362
        while (!d->incomingConns.isEmpty()) {
 
363
                delete d->incomingConns.takeFirst();
 
364
        }
 
365
        delete d->ibb;
 
366
        delete d;
 
367
}
 
368
 
 
369
Client *IBBManager::client() const
 
370
{
 
371
        return d->client;
 
372
}
 
373
 
 
374
IBBConnection *IBBManager::takeIncoming()
 
375
{
 
376
        if(d->incomingConns.isEmpty())
 
377
                return 0;
 
378
 
 
379
        IBBConnection *c = d->incomingConns.takeFirst();
 
380
        return c;
 
381
}
 
382
 
 
383
void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
 
384
{
 
385
        QString sid = genUniqueKey();
 
386
 
 
387
        // create a "waiting" connection
 
388
        IBBConnection *c = new IBBConnection(this);
 
389
        c->waitForAccept(from, sid, comment, id);
 
390
        d->incomingConns.append(c);
 
391
        incomingReady();
 
392
}
 
393
 
 
394
void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
 
395
{
 
396
        IBBConnection *c = findConnection(streamid, from);
 
397
        if(!c) {
 
398
                d->ibb->respondError(from, id, 404, "No such stream");
 
399
        }
 
400
        else {
 
401
                d->ibb->respondAck(from, id);
 
402
                c->takeIncomingData(data, close);
 
403
        }
 
404
}
 
405
 
 
406
QString IBBManager::genKey() const
 
407
{
 
408
        QString key = "ibb_";
 
409
 
 
410
        for(int i = 0; i < 4; ++i) {
 
411
                int word = rand() & 0xffff;
 
412
                for(int n = 0; n < 4; ++n) {
 
413
                        QString s;
 
414
                        s.sprintf("%x", (word >> (n * 4)) & 0xf);
 
415
                        key.append(s);
 
416
                }
 
417
        }
 
418
 
 
419
        return key;
 
420
}
 
421
 
 
422
QString IBBManager::genUniqueKey() const
 
423
{
 
424
        // get unused key
 
425
        QString key;
 
426
        while(1) {
 
427
                key = genKey();
 
428
 
 
429
                if(!findConnection(key))
 
430
                        break;
 
431
        }
 
432
 
 
433
        return key;
 
434
}
 
435
 
 
436
void IBBManager::link(IBBConnection *c)
 
437
{
 
438
        d->activeConns.append(c);
 
439
}
 
440
 
 
441
void IBBManager::unlink(IBBConnection *c)
 
442
{
 
443
        d->activeConns.removeAll(c);
 
444
}
 
445
 
 
446
IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
 
447
{
 
448
        foreach(IBBConnection* c, d->activeConns) {
 
449
                if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
 
450
                        return c;
 
451
        }
 
452
        return 0;
 
453
}
 
454
 
 
455
void IBBManager::doAccept(IBBConnection *c, const QString &id)
 
456
{
 
457
        d->ibb->respondSuccess(c->peer(), id, c->streamid());
 
458
}
 
459
 
 
460
void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
 
461
{
 
462
        d->ibb->respondError(c->peer(), id, code, str);
 
463
}
 
464
 
 
465
 
 
466
//----------------------------------------------------------------------------
 
467
// JT_IBB
 
468
//----------------------------------------------------------------------------
 
469
class JT_IBB::Private
 
470
{
 
471
public:
 
472
        Private() {}
 
473
 
 
474
        QDomElement iq;
 
475
        int mode;
 
476
        bool serve;
 
477
        Jid to;
 
478
        QString streamid;
 
479
};
 
480
 
 
481
JT_IBB::JT_IBB(Task *parent, bool serve)
 
482
:Task(parent)
 
483
{
 
484
        d = new Private;
 
485
        d->serve = serve;
 
486
}
 
487
 
 
488
JT_IBB::~JT_IBB()
 
489
{
 
490
        delete d;
 
491
}
 
492
 
 
493
void JT_IBB::request(const Jid &to, const QDomElement &comment)
 
494
{
 
495
        d->mode = ModeRequest;
 
496
        QDomElement iq;
 
497
        d->to = to;
 
498
        iq = createIQ(doc(), "set", to.full(), id());
 
499
        QDomElement query = doc()->createElement("query");
 
500
        query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
 
501
        iq.appendChild(query);
 
502
        query.appendChild(comment);
 
503
        d->iq = iq;
 
504
}
 
505
 
 
506
void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
 
507
{
 
508
        d->mode = ModeSendData;
 
509
        QDomElement iq;
 
510
        d->to = to;
 
511
        iq = createIQ(doc(), "set", to.full(), id());
 
512
        QDomElement query = doc()->createElement("query");
 
513
        query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
 
514
        iq.appendChild(query);
 
515
        query.appendChild(textTag(doc(), "streamid", streamid));
 
516
        if(!a.isEmpty())
 
517
                query.appendChild(textTag(doc(), "data", QCA::Base64().arrayToString(a)));
 
518
        if(close) {
 
519
                QDomElement c = doc()->createElement("close");
 
520
                query.appendChild(c);
 
521
        }
 
522
        d->iq = iq;
 
523
}
 
524
 
 
525
void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
 
526
{
 
527
        QDomElement iq = createIQ(doc(), "result", to.full(), id);
 
528
        QDomElement query = doc()->createElement("query");
 
529
        query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
 
530
        iq.appendChild(query);
 
531
        query.appendChild(textTag(doc(), "streamid", streamid));
 
532
        send(iq);
 
533
}
 
534
 
 
535
void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
 
536
{
 
537
        QDomElement iq = createIQ(doc(), "error", to.full(), id);
 
538
        QDomElement err = textTag(doc(), "error", str);
 
539
        err.setAttribute("code", QString::number(code));
 
540
        iq.appendChild(err);
 
541
        send(iq);
 
542
}
 
543
 
 
544
void JT_IBB::respondAck(const Jid &to, const QString &id)
 
545
{
 
546
        QDomElement iq = createIQ(doc(), "result", to.full(), id);
 
547
        send(iq);
 
548
}
 
549
 
 
550
void JT_IBB::onGo()
 
551
{
 
552
        send(d->iq);
 
553
}
 
554
 
 
555
bool JT_IBB::take(const QDomElement &e)
 
556
{
 
557
        if(d->serve) {
 
558
                // must be an iq-set tag
 
559
                if(e.tagName() != "iq" || e.attribute("type") != "set")
 
560
                        return false;
 
561
 
 
562
                if(queryNS(e) != "http://jabber.org/protocol/ibb")
 
563
                        return false;
 
564
 
 
565
                Jid from(e.attribute("from"));
 
566
                QString id = e.attribute("id");
 
567
                QDomElement q = queryTag(e);
 
568
 
 
569
                bool found;
 
570
                QDomElement s = findSubTag(q, "streamid", &found);
 
571
                if(!found) {
 
572
                        QDomElement comment = findSubTag(q, "comment", &found);
 
573
                        incomingRequest(from, id, comment);
 
574
                }
 
575
                else {
 
576
                        QString sid = tagContent(s);
 
577
                        QByteArray a;
 
578
                        bool close = false;
 
579
                        s = findSubTag(q, "data", &found);
 
580
                        if(found)
 
581
                                a = QCA::Base64().stringToArray(tagContent(s)).toByteArray();
 
582
                        s = findSubTag(q, "close", &found);
 
583
                        if(found)
 
584
                                close = true;
 
585
 
 
586
                        incomingData(from, sid, id, a, close);
 
587
                }
 
588
 
 
589
                return true;
 
590
        }
 
591
        else {
 
592
                Jid from(e.attribute("from"));
 
593
                if(e.attribute("id") != id() || !d->to.compare(from))
 
594
                        return false;
 
595
 
 
596
                if(e.attribute("type") == "result") {
 
597
                        QDomElement q = queryTag(e);
 
598
 
 
599
                        // request
 
600
                        if(d->mode == ModeRequest) {
 
601
                                bool found;
 
602
                                QDomElement s = findSubTag(q, "streamid", &found);
 
603
                                if(found)
 
604
                                        d->streamid = tagContent(s);
 
605
                                else
 
606
                                        d->streamid = "";
 
607
                                setSuccess();
 
608
                        }
 
609
                        // sendData
 
610
                        else {
 
611
                                // thank you for the ack, kind sir
 
612
                                setSuccess();
 
613
                        }
 
614
                }
 
615
                else {
 
616
                        setError(e);
 
617
                }
 
618
 
 
619
                return true;
 
620
        }
 
621
}
 
622
 
 
623
QString JT_IBB::streamid() const
 
624
{
 
625
        return d->streamid;
 
626
}
 
627
 
 
628
Jid JT_IBB::jid() const
 
629
{
 
630
        return d->to;
 
631
}
 
632
 
 
633
int JT_IBB::mode() const
 
634
{
 
635
        return d->mode;
 
636
}
 
637