File indexing completed on 2024-06-23 04:03:41
0001 /* 0002 * ibb.cpp - Inband bytestream 0003 * Copyright (C) 2001, 2002 Justin Karneges 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Lesser General Public 0007 * License as published by the Free Software Foundation; either 0008 * either version 2 0009 of the License, or (at your option) any later version.1 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public 0017 * License along with this library; if not, write to the Free Software 0018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 0019 * 0020 */ 0021 0022 #include "xmpp_ibb.h" 0023 0024 #include <QTimer> 0025 #include "xmpp_xmlcommon.h" 0026 #include <QtCrypto> 0027 0028 #include <stdlib.h> 0029 0030 #define IBB_PACKET_SIZE 4096 0031 #define IBB_PACKET_DELAY 0 0032 0033 using namespace XMPP; 0034 0035 static int num_conn = 0; 0036 static int id_conn = 0; 0037 0038 //---------------------------------------------------------------------------- 0039 // IBBConnection 0040 //---------------------------------------------------------------------------- 0041 class IBBConnection::Private 0042 { 0043 public: 0044 Private() {} 0045 0046 int state; 0047 Jid peer; 0048 QString sid; 0049 IBBManager *m; 0050 JT_IBB *j; 0051 QDomElement comment; 0052 QString iq_id; 0053 0054 int blockSize; 0055 QByteArray recvbuf, sendbuf; 0056 bool closePending, closing; 0057 0058 int id; 0059 }; 0060 0061 IBBConnection::IBBConnection(IBBManager *m) 0062 :ByteStream(m) 0063 { 0064 d = new Private; 0065 d->m = m; 0066 d->j = 0; 0067 reset(); 0068 0069 ++num_conn; 0070 d->id = id_conn++; 0071 QString dstr = QString::asprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn); 0072 d->m->client()->debug(dstr); 0073 } 0074 0075 void IBBConnection::reset(bool clear) 0076 { 0077 d->m->unlink(this); 0078 d->state = Idle; 0079 d->closePending = false; 0080 d->closing = false; 0081 0082 delete d->j; 0083 d->j = 0; 0084 0085 d->sendbuf.resize(0); 0086 if(clear) 0087 d->recvbuf.resize(0); 0088 } 0089 0090 IBBConnection::~IBBConnection() 0091 { 0092 reset(true); 0093 0094 --num_conn; 0095 QString dstr = QString::asprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn); 0096 d->m->client()->debug(dstr); 0097 0098 delete d; 0099 } 0100 0101 void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment) 0102 { 0103 close(); 0104 reset(true); 0105 0106 d->state = Requesting; 0107 d->peer = peer; 0108 d->comment = comment; 0109 0110 QString dstr = QString::asprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().toLatin1().data()); 0111 d->m->client()->debug(dstr); 0112 0113 d->j = new JT_IBB(d->m->client()->rootTask()); 0114 connect(d->j, &Task::finished, this, &IBBConnection::ibb_finished); 0115 d->j->request(d->peer, comment); 0116 d->j->go(true); 0117 } 0118 0119 void IBBConnection::accept() 0120 { 0121 if(d->state != WaitingForAccept) 0122 return; 0123 0124 QString dstr = QString::asprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().toLatin1().data(), d->sid.toLatin1().data()); 0125 d->m->client()->debug(dstr); 0126 0127 d->m->doAccept(this, d->iq_id); 0128 d->state = Active; 0129 d->m->link(this); 0130 } 0131 0132 void IBBConnection::close() 0133 { 0134 if(d->state == Idle) 0135 return; 0136 0137 if(d->state == WaitingForAccept) { 0138 d->m->doReject(this, d->iq_id, 403, "Rejected"); 0139 reset(); 0140 return; 0141 } 0142 0143 QString dstr = QString::asprintf("IBBConnection[%d]: closing\n", d->id); 0144 d->m->client()->debug(dstr); 0145 0146 if(d->state == Active) { 0147 // if there is data pending to be written, then pend the closing 0148 if(bytesToWrite() > 0) { 0149 d->closePending = true; 0150 trySend(); 0151 return; 0152 } 0153 0154 // send a close packet 0155 JT_IBB *j = new JT_IBB(d->m->client()->rootTask()); 0156 j->sendData(d->peer, d->sid, QByteArray(), true); 0157 j->go(true); 0158 } 0159 0160 reset(); 0161 } 0162 0163 int IBBConnection::state() const 0164 { 0165 return d->state; 0166 } 0167 0168 Jid IBBConnection::peer() const 0169 { 0170 return d->peer; 0171 } 0172 0173 QString IBBConnection::streamid() const 0174 { 0175 return d->sid; 0176 } 0177 0178 QDomElement IBBConnection::comment() const 0179 { 0180 return d->comment; 0181 } 0182 0183 bool IBBConnection::isOpen() const 0184 { 0185 if(d->state == Active) 0186 return true; 0187 else 0188 return false; 0189 } 0190 0191 void IBBConnection::write(const QByteArray &a) 0192 { 0193 if(d->state != Active || d->closePending || d->closing) 0194 return; 0195 0196 // append to the end of our send buffer 0197 int oldsize = d->sendbuf.size(); 0198 d->sendbuf.resize(oldsize + a.size()); 0199 memcpy(d->sendbuf.data() + oldsize, a.data(), a.size()); 0200 0201 trySend(); 0202 } 0203 0204 QByteArray IBBConnection::read(int) 0205 { 0206 // TODO: obey argument 0207 QByteArray a = d->recvbuf; 0208 d->recvbuf.resize(0); 0209 return a; 0210 } 0211 0212 int IBBConnection::bytesAvailable() const 0213 { 0214 return d->recvbuf.size(); 0215 } 0216 0217 int IBBConnection::bytesToWrite() const 0218 { 0219 return d->sendbuf.size(); 0220 } 0221 0222 void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id) 0223 { 0224 close(); 0225 reset(true); 0226 0227 d->state = WaitingForAccept; 0228 d->peer = peer; 0229 d->sid = sid; 0230 d->comment = comment; 0231 d->iq_id = iq_id; 0232 } 0233 0234 void IBBConnection::takeIncomingData(const QByteArray &a, bool close) 0235 { 0236 // append to the end of our recv buffer 0237 int oldsize = d->recvbuf.size(); 0238 d->recvbuf.resize(oldsize + a.size()); 0239 memcpy(d->recvbuf.data() + oldsize, a.data(), a.size()); 0240 0241 readyRead(); 0242 0243 if(close) { 0244 reset(); 0245 connectionClosed(); 0246 } 0247 } 0248 0249 void IBBConnection::ibb_finished() 0250 { 0251 JT_IBB *j = d->j; 0252 d->j = 0; 0253 0254 if(j->success()) { 0255 if(j->mode() == JT_IBB::ModeRequest) { 0256 d->sid = j->streamid(); 0257 0258 const QString dstr = QString::asprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().toLatin1().data(), d->sid.toLatin1().data()); 0259 d->m->client()->debug(dstr); 0260 0261 d->state = Active; 0262 d->m->link(this); 0263 connected(); 0264 } 0265 else { 0266 bytesWritten(d->blockSize); 0267 0268 if(d->closing) { 0269 reset(); 0270 delayedCloseFinished(); 0271 } 0272 0273 if(!d->sendbuf.isEmpty() || d->closePending) 0274 QTimer::singleShot(IBB_PACKET_DELAY, this, &IBBConnection::trySend); 0275 } 0276 } 0277 else { 0278 if(j->mode() == JT_IBB::ModeRequest) { 0279 const QString dstr = QString::asprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().toLatin1().data()); 0280 d->m->client()->debug(dstr); 0281 0282 reset(true); 0283 error(ErrRequest); 0284 } 0285 else { 0286 reset(true); 0287 error(ErrData); 0288 } 0289 } 0290 } 0291 0292 void IBBConnection::trySend() 0293 { 0294 // if we already have an active task, then don't do anything 0295 if(d->j) 0296 return; 0297 0298 QByteArray a; 0299 if(!d->sendbuf.isEmpty()) { 0300 // take a chunk 0301 if(d->sendbuf.size() < IBB_PACKET_SIZE) 0302 a.resize(d->sendbuf.size()); 0303 else 0304 a.resize(IBB_PACKET_SIZE); 0305 memcpy(a.data(), d->sendbuf.data(), a.size()); 0306 d->sendbuf.resize(d->sendbuf.size() - a.size()); 0307 } 0308 0309 bool doClose = false; 0310 if(d->sendbuf.isEmpty() && d->closePending) 0311 doClose = true; 0312 0313 // null operation? 0314 if(a.isEmpty() && !doClose) 0315 return; 0316 0317 printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size()); 0318 if(doClose) 0319 printf("and closing.\n"); 0320 else 0321 printf("(%d bytes left)\n", d->sendbuf.size()); 0322 0323 if(doClose) { 0324 d->closePending = false; 0325 d->closing = true; 0326 } 0327 0328 d->blockSize = a.size(); 0329 d->j = new JT_IBB(d->m->client()->rootTask()); 0330 connect(d->j, &Task::finished, this, &IBBConnection::ibb_finished); 0331 d->j->sendData(d->peer, d->sid, a, doClose); 0332 d->j->go(true); 0333 } 0334 0335 0336 //---------------------------------------------------------------------------- 0337 // IBBManager 0338 //---------------------------------------------------------------------------- 0339 class IBBManager::Private 0340 { 0341 public: 0342 Private() {} 0343 0344 Client *client; 0345 IBBConnectionList activeConns; 0346 IBBConnectionList incomingConns; 0347 JT_IBB *ibb; 0348 }; 0349 0350 IBBManager::IBBManager(Client *parent) 0351 :QObject(parent) 0352 { 0353 d = new Private; 0354 d->client = parent; 0355 0356 d->ibb = new JT_IBB(d->client->rootTask(), true); 0357 connect(d->ibb, &JT_IBB::incomingRequest, this, &IBBManager::ibb_incomingRequest); 0358 connect(d->ibb, &JT_IBB::incomingData, this, &IBBManager::ibb_incomingData); 0359 } 0360 0361 IBBManager::~IBBManager() 0362 { 0363 while (!d->incomingConns.isEmpty()) { 0364 delete d->incomingConns.takeFirst(); 0365 } 0366 delete d->ibb; 0367 delete d; 0368 } 0369 0370 Client *IBBManager::client() const 0371 { 0372 return d->client; 0373 } 0374 0375 IBBConnection *IBBManager::takeIncoming() 0376 { 0377 if(d->incomingConns.isEmpty()) 0378 return 0; 0379 0380 IBBConnection *c = d->incomingConns.takeFirst(); 0381 return c; 0382 } 0383 0384 void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment) 0385 { 0386 QString sid = genUniqueKey(); 0387 0388 // create a "waiting" connection 0389 IBBConnection *c = new IBBConnection(this); 0390 c->waitForAccept(from, sid, comment, id); 0391 d->incomingConns.append(c); 0392 incomingReady(); 0393 } 0394 0395 void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close) 0396 { 0397 IBBConnection *c = findConnection(streamid, from); 0398 if(!c) { 0399 d->ibb->respondError(from, id, 404, "No such stream"); 0400 } 0401 else { 0402 d->ibb->respondAck(from, id); 0403 c->takeIncomingData(data, close); 0404 } 0405 } 0406 0407 QString IBBManager::genKey() const 0408 { 0409 QString key = "ibb_"; 0410 0411 for(int i = 0; i < 4; ++i) { 0412 int word = rand() & 0xffff; 0413 for(int n = 0; n < 4; ++n) { 0414 const QString s = QString::asprintf("%x", (word >> (n * 4)) & 0xf); 0415 key.append(s); 0416 } 0417 } 0418 0419 return key; 0420 } 0421 0422 QString IBBManager::genUniqueKey() const 0423 { 0424 // get unused key 0425 QString key; 0426 while(1) { 0427 key = genKey(); 0428 0429 if(!findConnection(key)) 0430 break; 0431 } 0432 0433 return key; 0434 } 0435 0436 void IBBManager::link(IBBConnection *c) 0437 { 0438 d->activeConns.append(c); 0439 } 0440 0441 void IBBManager::unlink(IBBConnection *c) 0442 { 0443 d->activeConns.removeAll(c); 0444 } 0445 0446 IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const 0447 { 0448 foreach(IBBConnection* c, d->activeConns) { 0449 if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) ) 0450 return c; 0451 } 0452 return 0; 0453 } 0454 0455 void IBBManager::doAccept(IBBConnection *c, const QString &id) 0456 { 0457 d->ibb->respondSuccess(c->peer(), id, c->streamid()); 0458 } 0459 0460 void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str) 0461 { 0462 d->ibb->respondError(c->peer(), id, code, str); 0463 } 0464 0465 0466 //---------------------------------------------------------------------------- 0467 // JT_IBB 0468 //---------------------------------------------------------------------------- 0469 class JT_IBB::Private 0470 { 0471 public: 0472 Private() {} 0473 0474 QDomElement iq; 0475 int mode; 0476 bool serve; 0477 Jid to; 0478 QString streamid; 0479 }; 0480 0481 JT_IBB::JT_IBB(Task *parent, bool serve) 0482 :Task(parent) 0483 { 0484 d = new Private; 0485 d->serve = serve; 0486 } 0487 0488 JT_IBB::~JT_IBB() 0489 { 0490 delete d; 0491 } 0492 0493 void JT_IBB::request(const Jid &to, const QDomElement &comment) 0494 { 0495 d->mode = ModeRequest; 0496 QDomElement iq; 0497 d->to = to; 0498 iq = createIQ(doc(), "set", to.full(), id()); 0499 QDomElement query = doc()->createElement("query"); 0500 query.setAttribute("xmlns", "http://jabber.org/protocol/ibb"); 0501 iq.appendChild(query); 0502 query.appendChild(comment); 0503 d->iq = iq; 0504 } 0505 0506 void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close) 0507 { 0508 d->mode = ModeSendData; 0509 QDomElement iq; 0510 d->to = to; 0511 iq = createIQ(doc(), "set", to.full(), id()); 0512 QDomElement query = doc()->createElement("query"); 0513 query.setAttribute("xmlns", "http://jabber.org/protocol/ibb"); 0514 iq.appendChild(query); 0515 query.appendChild(textTag(doc(), "streamid", streamid)); 0516 if(!a.isEmpty()) 0517 query.appendChild(textTag(doc(), "data", QCA::Base64().arrayToString(a))); 0518 if(close) { 0519 QDomElement c = doc()->createElement("close"); 0520 query.appendChild(c); 0521 } 0522 d->iq = iq; 0523 } 0524 0525 void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid) 0526 { 0527 QDomElement iq = createIQ(doc(), "result", to.full(), id); 0528 QDomElement query = doc()->createElement("query"); 0529 query.setAttribute("xmlns", "http://jabber.org/protocol/ibb"); 0530 iq.appendChild(query); 0531 query.appendChild(textTag(doc(), "streamid", streamid)); 0532 send(iq); 0533 } 0534 0535 void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str) 0536 { 0537 QDomElement iq = createIQ(doc(), "error", to.full(), id); 0538 QDomElement err = textTag(doc(), "error", str); 0539 err.setAttribute("code", QString::number(code)); 0540 iq.appendChild(err); 0541 send(iq); 0542 } 0543 0544 void JT_IBB::respondAck(const Jid &to, const QString &id) 0545 { 0546 QDomElement iq = createIQ(doc(), "result", to.full(), id); 0547 send(iq); 0548 } 0549 0550 void JT_IBB::onGo() 0551 { 0552 send(d->iq); 0553 } 0554 0555 bool JT_IBB::take(const QDomElement &e) 0556 { 0557 if(d->serve) { 0558 // must be an iq-set tag 0559 if(e.tagName() != "iq" || e.attribute("type") != "set") 0560 return false; 0561 0562 if(queryNS(e) != "http://jabber.org/protocol/ibb") 0563 return false; 0564 0565 Jid from(e.attribute("from")); 0566 QString id = e.attribute("id"); 0567 QDomElement q = queryTag(e); 0568 0569 bool found; 0570 QDomElement s = findSubTag(q, "streamid", &found); 0571 if(!found) { 0572 QDomElement comment = findSubTag(q, "comment", &found); 0573 incomingRequest(from, id, comment); 0574 } 0575 else { 0576 QString sid = tagContent(s); 0577 QByteArray a; 0578 bool close = false; 0579 s = findSubTag(q, "data", &found); 0580 if(found) 0581 a = QCA::Base64().stringToArray(tagContent(s)).toByteArray(); 0582 s = findSubTag(q, "close", &found); 0583 if(found) 0584 close = true; 0585 0586 incomingData(from, sid, id, a, close); 0587 } 0588 0589 return true; 0590 } 0591 else { 0592 Jid from(e.attribute("from")); 0593 if(e.attribute("id") != id() || !d->to.compare(from)) 0594 return false; 0595 0596 if(e.attribute("type") == "result") { 0597 QDomElement q = queryTag(e); 0598 0599 // request 0600 if(d->mode == ModeRequest) { 0601 bool found; 0602 QDomElement s = findSubTag(q, "streamid", &found); 0603 if(found) 0604 d->streamid = tagContent(s); 0605 else 0606 d->streamid = ""; 0607 setSuccess(); 0608 } 0609 // sendData 0610 else { 0611 // thank you for the ack, kind sir 0612 setSuccess(); 0613 } 0614 } 0615 else { 0616 setError(e); 0617 } 0618 0619 return true; 0620 } 0621 } 0622 0623 QString JT_IBB::streamid() const 0624 { 0625 return d->streamid; 0626 } 0627 0628 Jid JT_IBB::jid() const 0629 { 0630 return d->to; 0631 } 0632 0633 int JT_IBB::mode() const 0634 { 0635 return d->mode; 0636 } 0637