File indexing completed on 2024-06-23 04:03:38
0001 /* 0002 * filetransfer.cpp - File Transfer 0003 * Copyright (C) 2004 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 "filetransfer.h" 0023 0024 #include <QList> 0025 #include <QTimer> 0026 #include <QPointer> 0027 #include <QFileInfo> 0028 #include "xmpp_xmlcommon.h" 0029 #include "s5b.h" 0030 0031 #define SENDBUFSIZE 65536 0032 0033 using namespace XMPP; 0034 0035 // firstChildElement 0036 // 0037 // Get an element's first child element 0038 static QDomElement firstChildElement(const QDomElement &e) 0039 { 0040 for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { 0041 if(n.isElement()) 0042 return n.toElement(); 0043 } 0044 return QDomElement(); 0045 } 0046 0047 //---------------------------------------------------------------------------- 0048 // FileTransfer 0049 //---------------------------------------------------------------------------- 0050 class FileTransfer::Private 0051 { 0052 public: 0053 FileTransferManager *m; 0054 JT_FT *ft; 0055 Jid peer; 0056 QString fname; 0057 qlonglong size; 0058 qlonglong sent; 0059 QString desc; 0060 bool rangeSupported; 0061 qlonglong rangeOffset, rangeLength, length; 0062 QString streamType; 0063 bool needStream; 0064 QString id, iq_id; 0065 S5BConnection *c; 0066 Jid proxy; 0067 int state; 0068 bool sender; 0069 }; 0070 0071 FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent) 0072 :QObject(parent) 0073 { 0074 d = new Private; 0075 d->m = m; 0076 d->ft = 0; 0077 d->c = 0; 0078 reset(); 0079 } 0080 0081 FileTransfer::FileTransfer(const FileTransfer& other) 0082 : QObject(other.parent()) 0083 { 0084 d = new Private; 0085 *d = *other.d; 0086 d->m = other.d->m; 0087 d->ft = 0; 0088 d->c = 0; 0089 reset(); 0090 0091 if (d->m->isActive(&other)) 0092 d->m->link(this); 0093 } 0094 0095 FileTransfer::~FileTransfer() 0096 { 0097 reset(); 0098 delete d; 0099 } 0100 0101 FileTransfer *FileTransfer::copy() const 0102 { 0103 return new FileTransfer(*this); 0104 } 0105 0106 void FileTransfer::reset() 0107 { 0108 d->m->unlink(this); 0109 0110 delete d->ft; 0111 d->ft = 0; 0112 0113 delete d->c; 0114 d->c = 0; 0115 0116 d->state = Idle; 0117 d->needStream = false; 0118 d->sent = 0; 0119 d->sender = false; 0120 } 0121 0122 void FileTransfer::setProxy(const Jid &proxy) 0123 { 0124 d->proxy = proxy; 0125 } 0126 0127 void FileTransfer::sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc) 0128 { 0129 d->state = Requesting; 0130 d->peer = to; 0131 d->fname = fname; 0132 d->size = size; 0133 d->desc = desc; 0134 d->sender = true; 0135 d->id = d->m->link(this); 0136 0137 d->ft = new JT_FT(d->m->client()->rootTask()); 0138 connect(d->ft, &Task::finished, this, &FileTransfer::ft_finished); 0139 QStringList list; 0140 list += "http://jabber.org/protocol/bytestreams"; 0141 d->ft->request(to, d->id, fname, size, desc, list); 0142 d->ft->go(true); 0143 } 0144 0145 int FileTransfer::dataSizeNeeded() const 0146 { 0147 int pending = d->c->bytesToWrite(); 0148 if(pending >= SENDBUFSIZE) 0149 return 0; 0150 qlonglong left = d->length - (d->sent + pending); 0151 int size = SENDBUFSIZE - pending; 0152 if((qlonglong)size > left) 0153 size = (int)left; 0154 return size; 0155 } 0156 0157 void FileTransfer::writeFileData(const QByteArray &a) 0158 { 0159 int pending = d->c->bytesToWrite(); 0160 qlonglong left = d->length - (d->sent + pending); 0161 if(left == 0) 0162 return; 0163 0164 QByteArray block; 0165 if((qlonglong)a.size() > left) { 0166 block = a; 0167 block.resize((uint)left); 0168 } 0169 else 0170 block = a; 0171 d->c->write(block); 0172 } 0173 0174 Jid FileTransfer::peer() const 0175 { 0176 return d->peer; 0177 } 0178 0179 QString FileTransfer::fileName() const 0180 { 0181 return d->fname; 0182 } 0183 0184 qlonglong FileTransfer::fileSize() const 0185 { 0186 return d->size; 0187 } 0188 0189 QString FileTransfer::description() const 0190 { 0191 return d->desc; 0192 } 0193 0194 bool FileTransfer::rangeSupported() const 0195 { 0196 return d->rangeSupported; 0197 } 0198 0199 qlonglong FileTransfer::offset() const 0200 { 0201 return d->rangeOffset; 0202 } 0203 0204 qlonglong FileTransfer::length() const 0205 { 0206 return d->length; 0207 } 0208 0209 void FileTransfer::accept(qlonglong offset, qlonglong length) 0210 { 0211 d->state = Connecting; 0212 d->rangeOffset = offset; 0213 d->rangeLength = length; 0214 if(length > 0) 0215 d->length = length; 0216 else 0217 d->length = d->size; 0218 d->streamType = "http://jabber.org/protocol/bytestreams"; 0219 d->m->con_accept(this); 0220 } 0221 0222 void FileTransfer::close() 0223 { 0224 if(d->state == Idle) 0225 return; 0226 if(d->state == WaitingForAccept) 0227 d->m->con_reject(this); 0228 else if(d->state == Active) 0229 d->c->close(); 0230 reset(); 0231 } 0232 0233 S5BConnection *FileTransfer::s5bConnection() const 0234 { 0235 return d->c; 0236 } 0237 0238 void FileTransfer::ft_finished() 0239 { 0240 JT_FT *ft = d->ft; 0241 d->ft = 0; 0242 0243 if(ft->success()) { 0244 d->state = Connecting; 0245 d->rangeOffset = ft->rangeOffset(); 0246 d->length = ft->rangeLength(); 0247 if(d->length == 0) 0248 d->length = d->size - d->rangeOffset; 0249 d->streamType = ft->streamType(); 0250 d->c = d->m->client()->s5bManager()->createConnection(); 0251 connect(d->c, &S5BConnection::connected, this, &FileTransfer::s5b_connected); 0252 connect(d->c, &ByteStream::connectionClosed, this, &FileTransfer::s5b_connectionClosed); 0253 connect(d->c, &ByteStream::bytesWritten, this, &FileTransfer::s5b_bytesWritten); 0254 connect(d->c, &ByteStream::error, this, &FileTransfer::s5b_error); 0255 0256 if(d->proxy.isValid()) 0257 d->c->setProxy(d->proxy); 0258 d->c->connectToJid(d->peer, d->id); 0259 accepted(); 0260 } 0261 else { 0262 reset(); 0263 if(ft->statusCode() == 403) 0264 error(ErrReject); 0265 else if(ft->statusCode() == 400) 0266 error(Err400); 0267 else 0268 error(ErrNeg); 0269 } 0270 } 0271 0272 void FileTransfer::takeConnection(S5BConnection *c) 0273 { 0274 d->c = c; 0275 connect(d->c, &S5BConnection::connected, this, &FileTransfer::s5b_connected); 0276 connect(d->c, &ByteStream::connectionClosed, this, &FileTransfer::s5b_connectionClosed); 0277 connect(d->c, &ByteStream::readyRead, this, &FileTransfer::s5b_readyRead); 0278 connect(d->c, &ByteStream::error, this, &FileTransfer::s5b_error); 0279 if(d->proxy.isValid()) 0280 d->c->setProxy(d->proxy); 0281 accepted(); 0282 QTimer::singleShot(0, this, &FileTransfer::doAccept); 0283 } 0284 0285 void FileTransfer::s5b_connected() 0286 { 0287 d->state = Active; 0288 connected(); 0289 } 0290 0291 void FileTransfer::s5b_connectionClosed() 0292 { 0293 reset(); 0294 error(ErrStream); 0295 } 0296 0297 void FileTransfer::s5b_readyRead() 0298 { 0299 QByteArray a = d->c->read(); 0300 qlonglong need = d->length - d->sent; 0301 if((qlonglong)a.size() > need) 0302 a.resize((uint)need); 0303 d->sent += a.size(); 0304 if(d->sent == d->length) 0305 reset(); 0306 readyRead(a); 0307 } 0308 0309 void FileTransfer::s5b_bytesWritten(int x) 0310 { 0311 d->sent += x; 0312 if(d->sent == d->length) 0313 reset(); 0314 bytesWritten(x); 0315 } 0316 0317 void FileTransfer::s5b_error(int x) 0318 { 0319 reset(); 0320 if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect) 0321 error(ErrConnect); 0322 else if(x == S5BConnection::ErrProxy) 0323 error(ErrProxy); 0324 else 0325 error(ErrStream); 0326 } 0327 0328 void FileTransfer::man_waitForAccept(const FTRequest &req) 0329 { 0330 d->state = WaitingForAccept; 0331 d->peer = req.from; 0332 d->id = req.id; 0333 d->iq_id = req.iq_id; 0334 d->fname = req.fname; 0335 d->size = req.size; 0336 d->desc = req.desc; 0337 d->rangeSupported = req.rangeSupported; 0338 } 0339 0340 void FileTransfer::doAccept() 0341 { 0342 d->c->accept(); 0343 } 0344 0345 //---------------------------------------------------------------------------- 0346 // FileTransferManager 0347 //---------------------------------------------------------------------------- 0348 class FileTransferManager::Private 0349 { 0350 public: 0351 Client *client; 0352 QList<FileTransfer*> list, incoming; 0353 JT_PushFT *pft; 0354 }; 0355 0356 FileTransferManager::FileTransferManager(Client *client) 0357 :QObject(client) 0358 { 0359 d = new Private; 0360 d->client = client; 0361 0362 d->pft = new JT_PushFT(d->client->rootTask()); 0363 connect(d->pft, &JT_PushFT::incoming, this, &FileTransferManager::pft_incoming); 0364 } 0365 0366 FileTransferManager::~FileTransferManager() 0367 { 0368 while (!d->incoming.isEmpty()) { 0369 delete d->incoming.takeFirst(); 0370 } 0371 delete d->pft; 0372 delete d; 0373 } 0374 0375 Client *FileTransferManager::client() const 0376 { 0377 return d->client; 0378 } 0379 0380 FileTransfer *FileTransferManager::createTransfer() 0381 { 0382 FileTransfer *ft = new FileTransfer(this); 0383 return ft; 0384 } 0385 0386 FileTransfer *FileTransferManager::takeIncoming() 0387 { 0388 if(d->incoming.isEmpty()) 0389 return 0; 0390 0391 FileTransfer *ft = d->incoming.takeFirst(); 0392 0393 // move to active list 0394 d->list.append(ft); 0395 return ft; 0396 } 0397 0398 bool FileTransferManager::isActive(const FileTransfer *ft) const 0399 { 0400 return d->list.contains(const_cast<FileTransfer*>(ft)) > 0; 0401 } 0402 0403 void FileTransferManager::pft_incoming(const FTRequest &req) 0404 { 0405 bool found = false; 0406 for(QStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) { 0407 if((*it) == "http://jabber.org/protocol/bytestreams") { 0408 found = true; 0409 break; 0410 } 0411 } 0412 if(!found) { 0413 d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types"); 0414 return; 0415 } 0416 if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) { 0417 d->pft->respondError(req.from, req.iq_id, 400, "SID in use"); 0418 return; 0419 } 0420 0421 FileTransfer *ft = new FileTransfer(this); 0422 ft->man_waitForAccept(req); 0423 d->incoming.append(ft); 0424 incomingReady(); 0425 } 0426 0427 void FileTransferManager::s5b_incomingReady(S5BConnection *c) 0428 { 0429 FileTransfer *ft = 0; 0430 foreach(FileTransfer* i, d->list) { 0431 if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) { 0432 ft = i; 0433 break; 0434 } 0435 } 0436 if(!ft) { 0437 c->close(); 0438 delete c; 0439 return; 0440 } 0441 ft->takeConnection(c); 0442 } 0443 0444 QString FileTransferManager::link(FileTransfer *ft) 0445 { 0446 d->list.append(ft); 0447 return d->client->s5bManager()->genUniqueSID(ft->d->peer); 0448 } 0449 0450 void FileTransferManager::con_accept(FileTransfer *ft) 0451 { 0452 ft->d->needStream = true; 0453 d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType); 0454 } 0455 0456 void FileTransferManager::con_reject(FileTransfer *ft) 0457 { 0458 d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined"); 0459 } 0460 0461 void FileTransferManager::unlink(FileTransfer *ft) 0462 { 0463 d->list.removeAll(ft); 0464 } 0465 0466 //---------------------------------------------------------------------------- 0467 // JT_FT 0468 //---------------------------------------------------------------------------- 0469 class JT_FT::Private 0470 { 0471 public: 0472 QDomElement iq; 0473 Jid to; 0474 qlonglong size, rangeOffset, rangeLength; 0475 QString streamType; 0476 QStringList streamTypes; 0477 }; 0478 0479 JT_FT::JT_FT(Task *parent) 0480 :Task(parent) 0481 { 0482 d = new Private; 0483 } 0484 0485 JT_FT::~JT_FT() 0486 { 0487 delete d; 0488 } 0489 0490 void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, qlonglong size, const QString &desc, const QStringList &streamTypes) 0491 { 0492 QDomElement iq; 0493 d->to = to; 0494 iq = createIQ(doc(), "set", to.full(), id()); 0495 QDomElement si = doc()->createElement("si"); 0496 si.setAttribute("xmlns", "http://jabber.org/protocol/si"); 0497 si.setAttribute("id", _id); 0498 si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer"); 0499 0500 QDomElement file = doc()->createElement("file"); 0501 file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer"); 0502 file.setAttribute("name", fname); 0503 file.setAttribute("size", QString::number(size)); 0504 if(!desc.isEmpty()) { 0505 QDomElement de = doc()->createElement("desc"); 0506 de.appendChild(doc()->createTextNode(desc)); 0507 file.appendChild(de); 0508 } 0509 QDomElement range = doc()->createElement("range"); 0510 file.appendChild(range); 0511 si.appendChild(file); 0512 0513 QDomElement feature = doc()->createElement("feature"); 0514 feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg"); 0515 QDomElement x = doc()->createElement("x"); 0516 x.setAttribute("xmlns", "jabber:x:data"); 0517 x.setAttribute("type", "form"); 0518 0519 QDomElement field = doc()->createElement("field"); 0520 field.setAttribute("var", "stream-method"); 0521 field.setAttribute("type", "list-single"); 0522 for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) { 0523 QDomElement option = doc()->createElement("option"); 0524 QDomElement value = doc()->createElement("value"); 0525 value.appendChild(doc()->createTextNode(*it)); 0526 option.appendChild(value); 0527 field.appendChild(option); 0528 } 0529 0530 x.appendChild(field); 0531 feature.appendChild(x); 0532 0533 si.appendChild(feature); 0534 iq.appendChild(si); 0535 0536 d->streamTypes = streamTypes; 0537 d->size = size; 0538 d->iq = iq; 0539 } 0540 0541 qlonglong JT_FT::rangeOffset() const 0542 { 0543 return d->rangeOffset; 0544 } 0545 0546 qlonglong JT_FT::rangeLength() const 0547 { 0548 return d->rangeLength; 0549 } 0550 0551 QString JT_FT::streamType() const 0552 { 0553 return d->streamType; 0554 } 0555 0556 void JT_FT::onGo() 0557 { 0558 send(d->iq); 0559 } 0560 0561 bool JT_FT::take(const QDomElement &x) 0562 { 0563 if(!iqVerify(x, d->to, id())) 0564 return false; 0565 0566 if(x.attribute("type") == "result") { 0567 QDomElement si = firstChildElement(x); 0568 if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") { 0569 setError(900, ""); 0570 return true; 0571 } 0572 0573 QString id = si.attribute("id"); 0574 0575 qlonglong range_offset = 0; 0576 qlonglong range_length = 0; 0577 0578 QDomElement file = si.elementsByTagName("file").item(0).toElement(); 0579 if(!file.isNull()) { 0580 QDomElement range = file.elementsByTagName("range").item(0).toElement(); 0581 if(!range.isNull()) { 0582 int x; 0583 bool ok; 0584 if(range.hasAttribute("offset")) { 0585 x = range.attribute("offset").toLongLong(&ok); 0586 if(!ok || x < 0) { 0587 setError(900, ""); 0588 return true; 0589 } 0590 range_offset = x; 0591 } 0592 if(range.hasAttribute("length")) { 0593 x = range.attribute("length").toLongLong(&ok); 0594 if(!ok || x < 0) { 0595 setError(900, ""); 0596 return true; 0597 } 0598 range_length = x; 0599 } 0600 } 0601 } 0602 0603 if(range_offset > d->size || (range_length > (d->size - range_offset))) { 0604 setError(900, ""); 0605 return true; 0606 } 0607 0608 QString streamtype; 0609 QDomElement feature = si.elementsByTagName("feature").item(0).toElement(); 0610 if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") { 0611 QDomElement x = feature.elementsByTagName("x").item(0).toElement(); 0612 if(!x.isNull() && x.attribute("type") == "submit") { 0613 QDomElement field = x.elementsByTagName("field").item(0).toElement(); 0614 if(!field.isNull() && field.attribute("var") == "stream-method") { 0615 QDomElement value = field.elementsByTagName("value").item(0).toElement(); 0616 if(!value.isNull()) 0617 streamtype = value.text(); 0618 } 0619 } 0620 } 0621 0622 // must be one of the offered streamtypes 0623 bool found = false; 0624 for(QStringList::ConstIterator it = d->streamTypes.constBegin(); it != d->streamTypes.constEnd(); ++it) { 0625 if((*it) == streamtype) { 0626 found = true; 0627 break; 0628 } 0629 } 0630 if(!found) 0631 return true; 0632 0633 d->rangeOffset = range_offset; 0634 d->rangeLength = range_length; 0635 d->streamType = streamtype; 0636 setSuccess(); 0637 } 0638 else { 0639 setError(x); 0640 } 0641 0642 return true; 0643 } 0644 0645 //---------------------------------------------------------------------------- 0646 // JT_PushFT 0647 //---------------------------------------------------------------------------- 0648 JT_PushFT::JT_PushFT(Task *parent) 0649 :Task(parent) 0650 { 0651 } 0652 0653 JT_PushFT::~JT_PushFT() 0654 { 0655 } 0656 0657 void JT_PushFT::respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType) 0658 { 0659 QDomElement iq = createIQ(doc(), "result", to.full(), id); 0660 QDomElement si = doc()->createElement("si"); 0661 si.setAttribute("xmlns", "http://jabber.org/protocol/si"); 0662 0663 if(rangeOffset != 0 || rangeLength != 0) { 0664 QDomElement file = doc()->createElement("file"); 0665 file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer"); 0666 QDomElement range = doc()->createElement("range"); 0667 if(rangeOffset > 0) 0668 range.setAttribute("offset", QString::number(rangeOffset)); 0669 if(rangeLength > 0) 0670 range.setAttribute("length", QString::number(rangeLength)); 0671 file.appendChild(range); 0672 si.appendChild(file); 0673 } 0674 0675 QDomElement feature = doc()->createElement("feature"); 0676 feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg"); 0677 QDomElement x = doc()->createElement("x"); 0678 x.setAttribute("xmlns", "jabber:x:data"); 0679 x.setAttribute("type", "submit"); 0680 0681 QDomElement field = doc()->createElement("field"); 0682 field.setAttribute("var", "stream-method"); 0683 QDomElement value = doc()->createElement("value"); 0684 value.appendChild(doc()->createTextNode(streamType)); 0685 field.appendChild(value); 0686 0687 x.appendChild(field); 0688 feature.appendChild(x); 0689 0690 si.appendChild(feature); 0691 iq.appendChild(si); 0692 send(iq); 0693 } 0694 0695 void JT_PushFT::respondError(const Jid &to, const QString &id, int code, const QString &str) 0696 { 0697 QDomElement iq = createIQ(doc(), "error", to.full(), id); 0698 QDomElement err = textTag(doc(), "error", str); 0699 err.setAttribute("code", QString::number(code)); 0700 iq.appendChild(err); 0701 send(iq); 0702 } 0703 0704 bool JT_PushFT::take(const QDomElement &e) 0705 { 0706 // must be an iq-set tag 0707 if(e.tagName() != "iq") 0708 return false; 0709 if(e.attribute("type") != "set") 0710 return false; 0711 0712 QDomElement si = firstChildElement(e); 0713 if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") 0714 return false; 0715 if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer") 0716 return false; 0717 0718 Jid from(e.attribute("from")); 0719 QString id = si.attribute("id"); 0720 0721 QDomElement file = si.elementsByTagName("file").item(0).toElement(); 0722 if(file.isNull()) 0723 return true; 0724 0725 QString fname = file.attribute("name"); 0726 if(fname.isEmpty()) { 0727 respondError(from, id, 400, "Bad file name"); 0728 return true; 0729 } 0730 0731 // ensure kosher 0732 { 0733 QFileInfo fi(fname); 0734 fname = fi.fileName(); 0735 } 0736 0737 bool ok; 0738 qlonglong size = file.attribute("size").toLongLong(&ok); 0739 if(!ok || size < 0) { 0740 respondError(from, id, 400, "Bad file size"); 0741 return true; 0742 } 0743 0744 QString desc; 0745 QDomElement de = file.elementsByTagName("desc").item(0).toElement(); 0746 if(!de.isNull()) 0747 desc = de.text(); 0748 0749 bool rangeSupported = false; 0750 QDomElement range = file.elementsByTagName("range").item(0).toElement(); 0751 if(!range.isNull()) 0752 rangeSupported = true; 0753 0754 QStringList streamTypes; 0755 QDomElement feature = si.elementsByTagName("feature").item(0).toElement(); 0756 if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") { 0757 QDomElement x = feature.elementsByTagName("x").item(0).toElement(); 0758 if(!x.isNull() /*&& x.attribute("type") == "form"*/) { 0759 QDomElement field = x.elementsByTagName("field").item(0).toElement(); 0760 if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") { 0761 QDomNodeList nl = field.elementsByTagName("option"); 0762 for(int n = 0; n < nl.count(); ++n) { 0763 QDomElement e = nl.item(n).toElement(); 0764 QDomElement value = e.elementsByTagName("value").item(0).toElement(); 0765 if(!value.isNull()) 0766 streamTypes += value.text(); 0767 } 0768 } 0769 } 0770 } 0771 0772 FTRequest r; 0773 r.from = from; 0774 r.iq_id = e.attribute("id"); 0775 r.id = id; 0776 r.fname = fname; 0777 r.size = size; 0778 r.desc = desc; 0779 r.rangeSupported = rangeSupported; 0780 r.streamTypes = streamTypes; 0781 0782 incoming(r); 0783 return true; 0784 }