78
82
d->closePending = false;
79
83
d->closing = false;
89
94
IBBConnection::~IBBConnection()
96
d->sendBuf.clear(); // drop buffer to make closing procedure fast
94
QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
95
d->m->client()->debug(dstr);
101
qDebug("IBBConnection[%d]: destructing, count=%d", d->id, num_conn);
100
void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
107
void IBBConnection::connectToJid(const Jid &peer, const QString &sid)
105
112
d->state = Requesting;
107
d->comment = comment;
109
QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().toLatin1().data());
110
d->m->client()->debug(dstr);
117
qDebug("IBBConnection[%d]: initiating request to %s", d->id, qPrintable(peer.full()));
112
120
d->j = new JT_IBB(d->m->client()->rootTask());
113
121
connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
114
d->j->request(d->peer, comment);
122
d->j->request(d->peer, d->sid);
192
201
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
208
QByteArray IBBConnection::read(int)
205
210
// TODO: obey argument
206
QByteArray a = d->recvbuf;
207
d->recvbuf.resize(0);
211
QByteArray a = d->recvBuf;
212
d->recvBuf.resize(0);
211
216
int IBBConnection::bytesAvailable() const
213
return d->recvbuf.size();
218
return d->recvBuf.size();
216
221
int IBBConnection::bytesToWrite() const
218
return d->sendbuf.size();
223
return d->sendBuf.size();
221
void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
226
void IBBConnection::waitForAccept(const Jid &peer, const QString &iq_id,
227
const QString &sid, int blockSize,
228
const QString &stanza)
226
233
d->state = WaitingForAccept;
229
d->comment = comment;
230
235
d->iq_id = iq_id;
237
d->blockSize = blockSize;
233
void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
242
void IBBConnection::takeIncomingData(const IBBData &ibbData)
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());
244
if (ibbData.seq != d->seq) {
245
d->m->doReject(this, d->iq_id, Stanza::Error::UnexpectedRequest, "Invalid sequence");
248
if (ibbData.data.size() > d->blockSize) {
249
d->m->doReject(this, d->iq_id, Stanza::Error::BadRequest, "Too much data");
253
d->recvBuf += ibbData.data;
258
void IBBConnection::setRemoteClosed()
261
emit connectionClosed();
248
264
void IBBConnection::ibb_finished()
253
269
if(j->success()) {
254
270
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().toLatin1().data(), d->sid.toLatin1().data());
258
d->m->client()->debug(dstr);
273
qDebug("IBBConnection[%d]: %s [%s] accepted.", d->id,
274
qPrintable(d->peer.full()), qPrintable(d->sid));
260
276
d->state = Active;
261
277
d->m->link(this);
265
bytesWritten(d->blockSize);
269
delayedCloseFinished();
283
emit delayedCloseFinished();
272
if(!d->sendbuf.isEmpty() || d->closePending)
286
if(!d->sendBuf.isEmpty() || d->closePending)
273
287
QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
289
bytesWritten(j->bytesWritten()); // will delete this connection if no bytes left.
277
293
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);
295
qDebug("IBBConnection[%d]: %s refused.", d->id, qPrintable(d->peer.full()));
282
298
error(ErrRequest);
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());
313
QByteArray a = d->sendBuf.left(d->blockSize); // IBB_PACKET_SIZE
314
d->sendBuf.remove(0, a.size());
317
if (!d->closePending)
318
return; // null operation?
323
319
d->closePending = false;
324
320
d->closing = true;
322
qDebug("IBBConnection[%d]: closing", d->id);
327
qDebug("IBBConnection[%d]: sending [%d] bytes (%d bytes left)",
328
d->id, a.size(), d->sendBuf.size());
327
d->blockSize = a.size();
328
332
d->j = new JT_IBB(d->m->client()->rootTask());
329
333
connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
330
d->j->sendData(d->peer, d->sid, a, doClose);
335
d->j->close(d->peer, d->sid);
338
d->j->sendData(d->peer, IBBData(d->sid, d->seq++, a));
345
//----------------------------------------------------------------------------
347
//----------------------------------------------------------------------------
348
IBBData& IBBData::fromXml(const QDomElement &e)
350
sid = e.attribute("sid"),
351
seq = e.attribute("seq").toInt(),
352
data = QCA::Base64().stringToArray(e.text()).toByteArray();
356
QDomElement IBBData::toXml(QDomDocument *doc) const
358
QDomElement query = textTag(doc, "data",
359
QCA::Base64().arrayToString(data)).toElement();
360
query.setAttribute("xmlns", IBB_NS);
361
query.setAttribute("seq", QString::number(seq));
362
query.setAttribute("sid", sid);
335
366
//----------------------------------------------------------------------------
337
368
//----------------------------------------------------------------------------
349
380
IBBManager::IBBManager(Client *parent)
381
: BytestreamManager(parent)
353
384
d->client = parent;
355
386
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)));
388
SIGNAL(incomingRequest(const Jid &, const QString &,
389
const QString &, int, const QString &)),
390
SLOT(ibb_incomingRequest(const Jid &, const QString &,
391
const QString &, int,
394
SIGNAL(incomingData(const Jid &, const QString &, const IBBData &, Stanza::Kind)),
395
SLOT(takeIncomingData(const Jid &, const QString &, const IBBData &, Stanza::Kind)));
397
SIGNAL(closeRequest(const Jid &, const QString &, const QString &)),
398
SLOT(ibb_closeRequest(const Jid &, const QString &, const QString &)));
360
401
IBBManager::~IBBManager()
362
while (!d->incomingConns.isEmpty()) {
363
delete d->incomingConns.takeFirst();
403
qDeleteAll(d->incomingConns);
404
d->incomingConns.clear();
409
const char* IBBManager::ns()
369
414
Client *IBBManager::client() const
371
416
return d->client;
419
BSConnection *IBBManager::createConnection()
421
return new IBBConnection(this);
374
424
IBBConnection *IBBManager::takeIncoming()
376
if(d->incomingConns.isEmpty())
379
IBBConnection *c = d->incomingConns.takeFirst();
426
return d->incomingConns.isEmpty()? 0 : d->incomingConns.takeFirst();
383
void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
429
void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id,
430
const QString &sid, int blockSize,
431
const QString &stanza)
385
QString sid = genUniqueKey();
387
433
// create a "waiting" connection
388
434
IBBConnection *c = new IBBConnection(this);
389
c->waitForAccept(from, sid, comment, id);
435
c->waitForAccept(from, id, sid, blockSize, stanza);
390
436
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");
437
emit incomingReady();
440
void IBBManager::takeIncomingData(const Jid &from, const QString &id,
441
const IBBData &data, Stanza::Kind sKind)
443
IBBConnection *c = findConnection(data.sid, from);
445
if (sKind == Stanza::IQ) {
446
d->ibb->respondError(from, id, Stanza::Error::ItemNotFound, "No such stream");
448
// TODO imeplement xep-0079 error processing in case of Stanza::Message
451
if (sKind == Stanza::IQ) {
452
d->ibb->respondAck(from, id);
454
c->takeIncomingData(data);
458
void IBBManager::ibb_closeRequest(const Jid &from, const QString &id,
461
IBBConnection *c = findConnection(sid, from);
463
d->ibb->respondError(from, id, Stanza::Error::ItemNotFound, "No such stream");
401
466
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))
467
c->setRemoteClosed();
471
bool IBBManager::isAcceptableSID(const XMPP::Jid &jid, const QString&sid) const
473
return findConnection(sid, jid) == NULL;
476
const char* IBBManager::sidPrefix() const
436
481
void IBBManager::link(IBBConnection *c)
493
void JT_IBB::request(const Jid &to, const QDomElement &comment)
540
void JT_IBB::request(const Jid &to, const QString &sid)
495
542
d->mode = ModeRequest;
498
545
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);
506
void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
508
d->mode = ModeSendData;
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));
517
query.appendChild(textTag(doc(), "data", QCA::Base64().arrayToString(a)));
519
QDomElement c = doc()->createElement("close");
520
query.appendChild(c);
525
void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
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));
535
void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
546
QDomElement query = doc()->createElement("open");
548
query.setAttribute("xmlns", IBB_NS);
549
query.setAttribute("sid", sid);
550
query.setAttribute("block-size", IBB_PACKET_SIZE);
551
query.setAttribute("stanza", "iq");
552
iq.appendChild(query);
556
void JT_IBB::sendData(const Jid &to, const IBBData &ibbData)
558
d->mode = ModeSendData;
561
d->bytesWritten = ibbData.data.size();
562
iq = createIQ(doc(), "set", to.full(), id());
563
iq.appendChild(ibbData.toXml(doc()));
567
void JT_IBB::close(const Jid &to, const QString &sid)
569
d->mode = ModeSendData;
572
iq = createIQ(doc(), "set", to.full(), id());
573
QDomElement query = iq.appendChild(doc()->createElement("close")).toElement();
574
query.setAttribute("xmlns", IBB_NS);
575
query.setAttribute("sid", sid);
580
void JT_IBB::respondError(const Jid &to, const QString &id,
581
Stanza::Error::ErrorCond cond, const QString &text)
537
583
QDomElement iq = createIQ(doc(), "error", to.full(), id);
538
QDomElement err = textTag(doc(), "error", str);
539
err.setAttribute("code", QString::number(code));
584
Stanza::Error error(Stanza::Error::Cancel, cond, text);
585
iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS()));
544
589
void JT_IBB::respondAck(const Jid &to, const QString &id)
546
QDomElement iq = createIQ(doc(), "result", to.full(), id);
591
send( createIQ(doc(), "result", to.full(), id) );
550
594
void JT_IBB::onGo()
559
603
if(e.tagName() != "iq" || e.attribute("type") != "set")
562
if(queryNS(e) != "http://jabber.org/protocol/ibb")
565
Jid from(e.attribute("from"));
566
607
QString id = e.attribute("id");
567
QDomElement q = queryTag(e);
570
QDomElement s = findSubTag(q, "streamid", &found);
572
QDomElement comment = findSubTag(q, "comment", &found);
573
incomingRequest(from, id, comment);
576
QString sid = tagContent(s);
579
s = findSubTag(q, "data", &found);
581
a = QCA::Base64().stringToArray(tagContent(s)).toByteArray();
582
s = findSubTag(q, "close", &found);
586
incomingData(from, sid, id, a, close);
608
QString from = e.attribute("from");
609
QDomElement openEl = findSubTag(e, "open", &found);
610
if (found && openEl.attribute("xmlns") == IBB_NS) {
611
emit incomingRequest(Jid(from), id,
612
openEl.attribute("sid"),
613
openEl.attribute("block-size").toInt(),
614
openEl.attribute("stanza"));
617
QDomElement dataEl = findSubTag(e, "data", &found);
618
if (found && dataEl.attribute("xmlns") == IBB_NS) {
620
emit incomingData(Jid(from), id, data.fromXml(dataEl), Stanza::IQ);
623
QDomElement closeEl = findSubTag(e, "close", &found);
624
if (found && closeEl.attribute("xmlns") == IBB_NS) {
625
emit closeRequest(Jid(from), id, closeEl.attribute("sid"));
592
631
Jid from(e.attribute("from"));