2
* ibb.cpp - Inband bytestream
3
* Copyright (C) 2001, 2002 Justin Karneges
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.
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.
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
24
#include"xmpp_xmlcommon.h"
29
#define IBB_PACKET_SIZE 4096
30
#define IBB_PACKET_DELAY 0
34
static int num_conn = 0;
35
static int id_conn = 0;
37
//----------------------------------------------------------------------------
39
//----------------------------------------------------------------------------
40
class IBBConnection::Private
54
QByteArray recvbuf, sendbuf;
55
bool closePending, closing;
60
IBBConnection::IBBConnection(IBBManager *m)
70
QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
71
d->m->client()->debug(dstr);
74
void IBBConnection::reset(bool clear)
78
d->closePending = false;
89
IBBConnection::~IBBConnection()
94
QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
95
d->m->client()->debug(dstr);
100
void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
105
d->state = Requesting;
107
d->comment = comment;
109
QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().latin1());
110
d->m->client()->debug(dstr);
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);
118
void IBBConnection::accept()
120
if(d->state != WaitingForAccept)
123
QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
124
d->m->client()->debug(dstr);
126
d->m->doAccept(this, d->iq_id);
131
void IBBConnection::close()
136
if(d->state == WaitingForAccept) {
137
d->m->doReject(this, d->iq_id, 403, "Rejected");
142
QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
143
d->m->client()->debug(dstr);
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;
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);
162
int IBBConnection::state() const
167
Jid IBBConnection::peer() const
172
QString IBBConnection::streamid() const
177
QDomElement IBBConnection::comment() const
182
bool IBBConnection::isOpen() const
184
if(d->state == Active)
190
void IBBConnection::write(const QByteArray &a)
192
if(d->state != Active || d->closePending || d->closing)
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());
203
QByteArray IBBConnection::read(int)
205
// TODO: obey argument
206
QByteArray a = d->recvbuf.copy();
207
d->recvbuf.resize(0);
211
int IBBConnection::bytesAvailable() const
213
return d->recvbuf.size();
216
int IBBConnection::bytesToWrite() const
218
return d->sendbuf.size();
221
void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
226
d->state = WaitingForAccept;
229
d->comment = comment;
233
void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
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());
248
void IBBConnection::ibb_finished()
254
if(j->mode() == JT_IBB::ModeRequest) {
255
d->sid = j->streamid();
257
QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().latin1(), d->sid.latin1());
258
d->m->client()->debug(dstr);
265
bytesWritten(d->blockSize);
269
delayedCloseFinished();
272
if(!d->sendbuf.isEmpty() || d->closePending)
273
QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
277
if(j->mode() == JT_IBB::ModeRequest) {
278
QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().latin1());
279
d->m->client()->debug(dstr);
291
void IBBConnection::trySend()
293
// if we already have an active task, then don't do anything
298
if(!d->sendbuf.isEmpty()) {
300
if(d->sendbuf.size() < IBB_PACKET_SIZE)
301
a.resize(d->sendbuf.size());
303
a.resize(IBB_PACKET_SIZE);
304
memcpy(a.data(), d->sendbuf.data(), a.size());
305
d->sendbuf.resize(d->sendbuf.size() - a.size());
308
bool doClose = false;
309
if(d->sendbuf.isEmpty() && d->closePending)
313
if(a.isEmpty() && !doClose)
316
printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
318
printf("and closing.\n");
320
printf("(%d bytes left)\n", d->sendbuf.size());
323
d->closePending = false;
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);
335
//----------------------------------------------------------------------------
337
//----------------------------------------------------------------------------
338
class IBBManager::Private
344
IBBConnectionList activeConns;
345
IBBConnectionList incomingConns;
349
IBBManager::IBBManager(Client *parent)
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)));
360
IBBManager::~IBBManager()
362
d->incomingConns.setAutoDelete(true);
363
d->incomingConns.clear();
368
Client *IBBManager::client() const
373
IBBConnection *IBBManager::takeIncoming()
375
if(d->incomingConns.isEmpty())
378
IBBConnection *c = d->incomingConns.getFirst();
379
d->incomingConns.removeRef(c);
383
void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
385
QString sid = genUniqueKey();
387
// create a "waiting" connection
388
IBBConnection *c = new IBBConnection(this);
389
c->waitForAccept(from, sid, comment, id);
390
d->incomingConns.append(c);
394
void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
396
IBBConnection *c = findConnection(streamid, from);
398
d->ibb->respondError(from, id, 404, "No such stream");
401
d->ibb->respondAck(from, id);
402
c->takeIncomingData(data, close);
406
QString IBBManager::genKey() const
408
QString key = "ibb_";
410
for(int i = 0; i < 4; ++i) {
411
int word = rand() & 0xffff;
412
for(int n = 0; n < 4; ++n) {
414
s.sprintf("%x", (word >> (n * 4)) & 0xf);
422
QString IBBManager::genUniqueKey() const
429
if(!findConnection(key))
436
void IBBManager::link(IBBConnection *c)
438
d->activeConns.append(c);
441
void IBBManager::unlink(IBBConnection *c)
443
d->activeConns.removeRef(c);
446
IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
448
IBBConnectionListIt it(d->activeConns);
449
for(IBBConnection *c; (c = it.current()); ++it) {
450
if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
456
void IBBManager::doAccept(IBBConnection *c, const QString &id)
458
d->ibb->respondSuccess(c->peer(), id, c->streamid());
461
void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
463
d->ibb->respondError(c->peer(), id, code, str);
467
//----------------------------------------------------------------------------
469
//----------------------------------------------------------------------------
470
class JT_IBB::Private
482
JT_IBB::JT_IBB(Task *parent, bool serve)
494
void JT_IBB::request(const Jid &to, const QDomElement &comment)
496
d->mode = ModeRequest;
499
iq = createIQ(doc(), "set", to.full(), id());
500
QDomElement query = doc()->createElement("query");
501
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
502
iq.appendChild(query);
503
query.appendChild(comment);
507
void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
509
d->mode = ModeSendData;
512
iq = createIQ(doc(), "set", to.full(), id());
513
QDomElement query = doc()->createElement("query");
514
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
515
iq.appendChild(query);
516
query.appendChild(textTag(doc(), "streamid", streamid));
518
query.appendChild(textTag(doc(), "data", Base64::arrayToString(a)));
520
QDomElement c = doc()->createElement("close");
521
query.appendChild(c);
526
void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
528
QDomElement iq = createIQ(doc(), "result", to.full(), id);
529
QDomElement query = doc()->createElement("query");
530
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
531
iq.appendChild(query);
532
query.appendChild(textTag(doc(), "streamid", streamid));
536
void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
538
QDomElement iq = createIQ(doc(), "error", to.full(), id);
539
QDomElement err = textTag(doc(), "error", str);
540
err.setAttribute("code", QString::number(code));
545
void JT_IBB::respondAck(const Jid &to, const QString &id)
547
QDomElement iq = createIQ(doc(), "result", to.full(), id);
556
bool JT_IBB::take(const QDomElement &e)
559
// must be an iq-set tag
560
if(e.tagName() != "iq" || e.attribute("type") != "set")
563
if(queryNS(e) != "http://jabber.org/protocol/ibb")
566
Jid from(e.attribute("from"));
567
QString id = e.attribute("id");
568
QDomElement q = queryTag(e);
571
QDomElement s = findSubTag(q, "streamid", &found);
573
QDomElement comment = findSubTag(q, "comment", &found);
574
incomingRequest(from, id, comment);
577
QString sid = tagContent(s);
580
s = findSubTag(q, "data", &found);
582
a = Base64::stringToArray(tagContent(s));
583
s = findSubTag(q, "close", &found);
587
incomingData(from, sid, id, a, close);
593
Jid from(e.attribute("from"));
594
if(e.attribute("id") != id() || !d->to.compare(from))
597
if(e.attribute("type") == "result") {
598
QDomElement q = queryTag(e);
601
if(d->mode == ModeRequest) {
603
QDomElement s = findSubTag(q, "streamid", &found);
605
d->streamid = tagContent(s);
612
// thank you for the ack, kind sir
624
QString JT_IBB::streamid() const
629
Jid JT_IBB::jid() const
634
int JT_IBB::mode() const