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 }