You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
759 lines
18 KiB
759 lines
18 KiB
/*
|
|
* filetransfer.cpp - File Transfer
|
|
* Copyright (C) 2004 Justin Karneges
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "filetransfer.h"
|
|
|
|
#include <tqtimer.h>
|
|
#include <tqptrlist.h>
|
|
#include <tqguardedptr.h>
|
|
#include <tqfileinfo.h>
|
|
#include "xmpp_xmlcommon.h"
|
|
#include "s5b.h"
|
|
|
|
#define SENDBUFSIZE 65536
|
|
|
|
using namespace XMPP;
|
|
|
|
// firstChildElement
|
|
//
|
|
// Get an element's first child element
|
|
static TQDomElement firstChildElement(const TQDomElement &e)
|
|
{
|
|
for(TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
|
|
if(n.isElement())
|
|
return n.toElement();
|
|
}
|
|
return TQDomElement();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FileTransfer
|
|
//----------------------------------------------------------------------------
|
|
class FileTransfer::Private
|
|
{
|
|
public:
|
|
FileTransferManager *m;
|
|
JT_FT *ft;
|
|
Jid peer;
|
|
TQString fname;
|
|
TQ_LLONG size;
|
|
TQ_LLONG sent;
|
|
TQString desc;
|
|
bool rangeSupported;
|
|
TQ_LLONG rangeOffset, rangeLength, length;
|
|
TQString streamType;
|
|
bool needStream;
|
|
TQString id, iq_id;
|
|
S5BConnection *c;
|
|
Jid proxy;
|
|
int state;
|
|
bool sender;
|
|
};
|
|
|
|
FileTransfer::FileTransfer(FileTransferManager *m, TQObject *parent)
|
|
:TQObject(parent)
|
|
{
|
|
d = new Private;
|
|
d->m = m;
|
|
d->ft = 0;
|
|
d->c = 0;
|
|
reset();
|
|
}
|
|
|
|
FileTransfer::~FileTransfer()
|
|
{
|
|
reset();
|
|
delete d;
|
|
}
|
|
|
|
void FileTransfer::reset()
|
|
{
|
|
d->m->unlink(this);
|
|
|
|
delete d->ft;
|
|
d->ft = 0;
|
|
|
|
delete d->c;
|
|
d->c = 0;
|
|
|
|
d->state = Idle;
|
|
d->needStream = false;
|
|
d->sent = 0;
|
|
d->sender = false;
|
|
}
|
|
|
|
void FileTransfer::setProxy(const Jid &proxy)
|
|
{
|
|
d->proxy = proxy;
|
|
}
|
|
|
|
void FileTransfer::sendFile(const Jid &to, const TQString &fname, TQ_LLONG size, const TQString &desc)
|
|
{
|
|
d->state = Requesting;
|
|
d->peer = to;
|
|
d->fname = fname;
|
|
d->size = size;
|
|
d->desc = desc;
|
|
d->sender = true;
|
|
d->id = d->m->link(this);
|
|
|
|
d->ft = new JT_FT(d->m->client()->rootTask());
|
|
connect(d->ft, TQT_SIGNAL(finished()), TQT_SLOT(ft_finished()));
|
|
TQStringList list;
|
|
list += "http://jabber.org/protocol/bytestreams";
|
|
d->ft->request(to, d->id, fname, size, desc, list);
|
|
d->ft->go(true);
|
|
}
|
|
|
|
int FileTransfer::dataSizeNeeded() const
|
|
{
|
|
int pending = d->c->bytesToWrite();
|
|
if(pending >= SENDBUFSIZE)
|
|
return 0;
|
|
TQ_LLONG left = d->length - (d->sent + pending);
|
|
int size = SENDBUFSIZE - pending;
|
|
if((TQ_LLONG)size > left)
|
|
size = (int)left;
|
|
return size;
|
|
}
|
|
|
|
void FileTransfer::writeFileData(const TQByteArray &a)
|
|
{
|
|
int pending = d->c->bytesToWrite();
|
|
TQ_LLONG left = d->length - (d->sent + pending);
|
|
if(left == 0)
|
|
return;
|
|
|
|
TQByteArray block;
|
|
if((TQ_LLONG)a.size() > left) {
|
|
block = a.copy();
|
|
block.resize((uint)left);
|
|
}
|
|
else
|
|
block = a;
|
|
d->c->write(block);
|
|
}
|
|
|
|
Jid FileTransfer::peer() const
|
|
{
|
|
return d->peer;
|
|
}
|
|
|
|
TQString FileTransfer::fileName() const
|
|
{
|
|
return d->fname;
|
|
}
|
|
|
|
TQ_LLONG FileTransfer::fileSize() const
|
|
{
|
|
return d->size;
|
|
}
|
|
|
|
TQString FileTransfer::description() const
|
|
{
|
|
return d->desc;
|
|
}
|
|
|
|
bool FileTransfer::rangeSupported() const
|
|
{
|
|
return d->rangeSupported;
|
|
}
|
|
|
|
TQ_LLONG FileTransfer::offset() const
|
|
{
|
|
return d->rangeOffset;
|
|
}
|
|
|
|
TQ_LLONG FileTransfer::length() const
|
|
{
|
|
return d->length;
|
|
}
|
|
|
|
void FileTransfer::accept(TQ_LLONG offset, TQ_LLONG length)
|
|
{
|
|
d->state = Connecting;
|
|
d->rangeOffset = offset;
|
|
d->rangeLength = length;
|
|
if(length > 0)
|
|
d->length = length;
|
|
else
|
|
d->length = d->size;
|
|
d->streamType = "http://jabber.org/protocol/bytestreams";
|
|
d->m->con_accept(this);
|
|
}
|
|
|
|
void FileTransfer::close()
|
|
{
|
|
if(d->state == Idle)
|
|
return;
|
|
if(d->state == WaitingForAccept)
|
|
d->m->con_reject(this);
|
|
else if(d->state == Active)
|
|
d->c->close();
|
|
reset();
|
|
}
|
|
|
|
S5BConnection *FileTransfer::s5bConnection() const
|
|
{
|
|
return d->c;
|
|
}
|
|
|
|
void FileTransfer::ft_finished()
|
|
{
|
|
JT_FT *ft = d->ft;
|
|
d->ft = 0;
|
|
|
|
if(ft->success()) {
|
|
d->state = Connecting;
|
|
d->rangeOffset = ft->rangeOffset();
|
|
d->length = ft->rangeLength();
|
|
if(d->length == 0)
|
|
d->length = d->size - d->rangeOffset;
|
|
d->streamType = ft->streamType();
|
|
d->c = d->m->client()->s5bManager()->createConnection();
|
|
connect(d->c, TQT_SIGNAL(connected()), TQT_SLOT(s5b_connected()));
|
|
connect(d->c, TQT_SIGNAL(connectionClosed()), TQT_SLOT(s5b_connectionClosed()));
|
|
connect(d->c, TQT_SIGNAL(bytesWritten(int)), TQT_SLOT(s5b_bytesWritten(int)));
|
|
connect(d->c, TQT_SIGNAL(error(int)), TQT_SLOT(s5b_error(int)));
|
|
|
|
if(d->proxy.isValid())
|
|
d->c->setProxy(d->proxy);
|
|
d->c->connectToJid(d->peer, d->id);
|
|
accepted();
|
|
}
|
|
else {
|
|
reset();
|
|
if(ft->statusCode() == 403)
|
|
error(ErrReject);
|
|
else
|
|
error(ErrNeg);
|
|
}
|
|
}
|
|
|
|
void FileTransfer::takeConnection(S5BConnection *c)
|
|
{
|
|
d->c = c;
|
|
connect(d->c, TQT_SIGNAL(connected()), TQT_SLOT(s5b_connected()));
|
|
connect(d->c, TQT_SIGNAL(connectionClosed()), TQT_SLOT(s5b_connectionClosed()));
|
|
connect(d->c, TQT_SIGNAL(readyRead()), TQT_SLOT(s5b_readyRead()));
|
|
connect(d->c, TQT_SIGNAL(error(int)), TQT_SLOT(s5b_error(int)));
|
|
if(d->proxy.isValid())
|
|
d->c->setProxy(d->proxy);
|
|
accepted();
|
|
TQTimer::singleShot(0, this, TQT_SLOT(doAccept()));
|
|
}
|
|
|
|
void FileTransfer::s5b_connected()
|
|
{
|
|
d->state = Active;
|
|
connected();
|
|
}
|
|
|
|
void FileTransfer::s5b_connectionClosed()
|
|
{
|
|
reset();
|
|
error(ErrStream);
|
|
}
|
|
|
|
void FileTransfer::s5b_readyRead()
|
|
{
|
|
TQByteArray a = d->c->read();
|
|
TQ_LLONG need = d->length - d->sent;
|
|
if((TQ_LLONG)a.size() > need)
|
|
a.resize((uint)need);
|
|
d->sent += a.size();
|
|
if(d->sent == d->length)
|
|
reset();
|
|
readyRead(a);
|
|
}
|
|
|
|
void FileTransfer::s5b_bytesWritten(int x)
|
|
{
|
|
d->sent += x;
|
|
if(d->sent == d->length)
|
|
reset();
|
|
bytesWritten(x);
|
|
}
|
|
|
|
void FileTransfer::s5b_error(int x)
|
|
{
|
|
reset();
|
|
if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect)
|
|
error(ErrConnect);
|
|
else if(x == S5BConnection::ErrProxy)
|
|
error(ErrProxy);
|
|
else
|
|
error(ErrStream);
|
|
}
|
|
|
|
void FileTransfer::man_waitForAccept(const FTRequest &req)
|
|
{
|
|
d->state = WaitingForAccept;
|
|
d->peer = req.from;
|
|
d->id = req.id;
|
|
d->iq_id = req.iq_id;
|
|
d->fname = req.fname;
|
|
d->size = req.size;
|
|
d->desc = req.desc;
|
|
d->rangeSupported = req.rangeSupported;
|
|
}
|
|
|
|
void FileTransfer::doAccept()
|
|
{
|
|
d->c->accept();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FileTransferManager
|
|
//----------------------------------------------------------------------------
|
|
class FileTransferManager::Private
|
|
{
|
|
public:
|
|
Client *client;
|
|
TQPtrList<FileTransfer> list, incoming;
|
|
JT_PushFT *pft;
|
|
};
|
|
|
|
FileTransferManager::FileTransferManager(Client *client)
|
|
:TQObject(client)
|
|
{
|
|
d = new Private;
|
|
d->client = client;
|
|
|
|
d->pft = new JT_PushFT(d->client->rootTask());
|
|
connect(d->pft, TQT_SIGNAL(incoming(const FTRequest &)), TQT_SLOT(pft_incoming(const FTRequest &)));
|
|
}
|
|
|
|
FileTransferManager::~FileTransferManager()
|
|
{
|
|
d->incoming.setAutoDelete(true);
|
|
d->incoming.clear();
|
|
delete d->pft;
|
|
delete d;
|
|
}
|
|
|
|
Client *FileTransferManager::client() const
|
|
{
|
|
return d->client;
|
|
}
|
|
|
|
FileTransfer *FileTransferManager::createTransfer()
|
|
{
|
|
FileTransfer *ft = new FileTransfer(this);
|
|
return ft;
|
|
}
|
|
|
|
FileTransfer *FileTransferManager::takeIncoming()
|
|
{
|
|
if(d->incoming.isEmpty())
|
|
return 0;
|
|
|
|
FileTransfer *ft = d->incoming.getFirst();
|
|
d->incoming.removeRef(ft);
|
|
|
|
// move to active list
|
|
d->list.append(ft);
|
|
return ft;
|
|
}
|
|
|
|
void FileTransferManager::pft_incoming(const FTRequest &req)
|
|
{
|
|
bool found = false;
|
|
for(TQStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) {
|
|
if((*it) == "http://jabber.org/protocol/bytestreams") {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found) {
|
|
d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types");
|
|
return;
|
|
}
|
|
if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) {
|
|
d->pft->respondError(req.from, req.iq_id, 400, "SID in use");
|
|
return;
|
|
}
|
|
|
|
FileTransfer *ft = new FileTransfer(this);
|
|
ft->man_waitForAccept(req);
|
|
d->incoming.append(ft);
|
|
incomingReady();
|
|
}
|
|
|
|
void FileTransferManager::s5b_incomingReady(S5BConnection *c)
|
|
{
|
|
TQPtrListIterator<FileTransfer> it(d->list);
|
|
FileTransfer *ft = 0;
|
|
for(FileTransfer *i; (i = it.current()); ++it) {
|
|
if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) {
|
|
ft = i;
|
|
break;
|
|
}
|
|
}
|
|
if(!ft) {
|
|
c->close();
|
|
delete c;
|
|
return;
|
|
}
|
|
ft->takeConnection(c);
|
|
}
|
|
|
|
TQString FileTransferManager::link(FileTransfer *ft)
|
|
{
|
|
d->list.append(ft);
|
|
return d->client->s5bManager()->genUniqueSID(ft->d->peer);
|
|
}
|
|
|
|
void FileTransferManager::con_accept(FileTransfer *ft)
|
|
{
|
|
ft->d->needStream = true;
|
|
d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType);
|
|
}
|
|
|
|
void FileTransferManager::con_reject(FileTransfer *ft)
|
|
{
|
|
d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined");
|
|
}
|
|
|
|
void FileTransferManager::unlink(FileTransfer *ft)
|
|
{
|
|
d->list.removeRef(ft);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// JT_FT
|
|
//----------------------------------------------------------------------------
|
|
class JT_FT::Private
|
|
{
|
|
public:
|
|
TQDomElement iq;
|
|
Jid to;
|
|
TQ_LLONG size, rangeOffset, rangeLength;
|
|
TQString streamType;
|
|
TQStringList streamTypes;
|
|
};
|
|
|
|
JT_FT::JT_FT(Task *parent)
|
|
:Task(parent)
|
|
{
|
|
d = new Private;
|
|
}
|
|
|
|
JT_FT::~JT_FT()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void JT_FT::request(const Jid &to, const TQString &_id, const TQString &fname, TQ_LLONG size, const TQString &desc, const TQStringList &streamTypes)
|
|
{
|
|
TQDomElement iq;
|
|
d->to = to;
|
|
iq = createIQ(doc(), "set", to.full(), id());
|
|
TQDomElement si = doc()->createElement("si");
|
|
si.setAttribute("xmlns", "http://jabber.org/protocol/si");
|
|
si.setAttribute("id", _id);
|
|
si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer");
|
|
|
|
TQDomElement file = doc()->createElement("file");
|
|
file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
|
|
file.setAttribute("name", fname);
|
|
file.setAttribute("size", TQString::number(size));
|
|
if(!desc.isEmpty()) {
|
|
TQDomElement de = doc()->createElement("desc");
|
|
de.appendChild(doc()->createTextNode(desc));
|
|
file.appendChild(de);
|
|
}
|
|
TQDomElement range = doc()->createElement("range");
|
|
file.appendChild(range);
|
|
si.appendChild(file);
|
|
|
|
TQDomElement feature = doc()->createElement("feature");
|
|
feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
|
|
TQDomElement x = doc()->createElement("x");
|
|
x.setAttribute("xmlns", "jabber:x:data");
|
|
x.setAttribute("type", "form");
|
|
|
|
TQDomElement field = doc()->createElement("field");
|
|
field.setAttribute("var", "stream-method");
|
|
field.setAttribute("type", "list-single");
|
|
for(TQStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
|
|
TQDomElement option = doc()->createElement("option");
|
|
TQDomElement value = doc()->createElement("value");
|
|
value.appendChild(doc()->createTextNode(*it));
|
|
option.appendChild(value);
|
|
field.appendChild(option);
|
|
}
|
|
|
|
x.appendChild(field);
|
|
feature.appendChild(x);
|
|
|
|
si.appendChild(feature);
|
|
iq.appendChild(si);
|
|
|
|
d->streamTypes = streamTypes;
|
|
d->size = size;
|
|
d->iq = iq;
|
|
}
|
|
|
|
TQ_LLONG JT_FT::rangeOffset() const
|
|
{
|
|
return d->rangeOffset;
|
|
}
|
|
|
|
TQ_LLONG JT_FT::rangeLength() const
|
|
{
|
|
return d->rangeLength;
|
|
}
|
|
|
|
TQString JT_FT::streamType() const
|
|
{
|
|
return d->streamType;
|
|
}
|
|
|
|
void JT_FT::onGo()
|
|
{
|
|
send(d->iq);
|
|
}
|
|
|
|
bool JT_FT::take(const TQDomElement &x)
|
|
{
|
|
if(!iqVerify(x, d->to, id()))
|
|
return false;
|
|
|
|
if(x.attribute("type") == "result") {
|
|
TQDomElement si = firstChildElement(x);
|
|
if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
|
|
setError(900, "");
|
|
return true;
|
|
}
|
|
|
|
TQString id = si.attribute("id");
|
|
|
|
TQ_LLONG range_offset = 0;
|
|
TQ_LLONG range_length = 0;
|
|
|
|
TQDomElement file = si.elementsByTagName("file").item(0).toElement();
|
|
if(!file.isNull()) {
|
|
TQDomElement range = file.elementsByTagName("range").item(0).toElement();
|
|
if(!range.isNull()) {
|
|
int x;
|
|
bool ok;
|
|
if(range.hasAttribute("offset")) {
|
|
x = range.attribute("offset").toLongLong(&ok);
|
|
if(!ok || x < 0) {
|
|
setError(900, "");
|
|
return true;
|
|
}
|
|
range_offset = x;
|
|
}
|
|
if(range.hasAttribute("length")) {
|
|
x = range.attribute("length").toLongLong(&ok);
|
|
if(!ok || x < 0) {
|
|
setError(900, "");
|
|
return true;
|
|
}
|
|
range_length = x;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(range_offset > d->size || (range_length > (d->size - range_offset))) {
|
|
setError(900, "");
|
|
return true;
|
|
}
|
|
|
|
TQString streamtype;
|
|
TQDomElement feature = si.elementsByTagName("feature").item(0).toElement();
|
|
if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
|
|
TQDomElement x = feature.elementsByTagName("x").item(0).toElement();
|
|
if(!x.isNull() && x.attribute("type") == "submit") {
|
|
TQDomElement field = x.elementsByTagName("field").item(0).toElement();
|
|
if(!field.isNull() && field.attribute("var") == "stream-method") {
|
|
TQDomElement value = field.elementsByTagName("value").item(0).toElement();
|
|
if(!value.isNull())
|
|
streamtype = value.text();
|
|
}
|
|
}
|
|
}
|
|
|
|
// must be one of the offered streamtypes
|
|
bool found = false;
|
|
for(TQStringList::ConstIterator it = d->streamTypes.begin(); it != d->streamTypes.end(); ++it) {
|
|
if((*it) == streamtype) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found)
|
|
return true;
|
|
|
|
d->rangeOffset = range_offset;
|
|
d->rangeLength = range_length;
|
|
d->streamType = streamtype;
|
|
setSuccess();
|
|
}
|
|
else {
|
|
setError(x);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// JT_PushFT
|
|
//----------------------------------------------------------------------------
|
|
JT_PushFT::JT_PushFT(Task *parent)
|
|
:Task(parent)
|
|
{
|
|
}
|
|
|
|
JT_PushFT::~JT_PushFT()
|
|
{
|
|
}
|
|
|
|
void JT_PushFT::respondSuccess(const Jid &to, const TQString &id, TQ_LLONG rangeOffset, TQ_LLONG rangeLength, const TQString &streamType)
|
|
{
|
|
TQDomElement iq = createIQ(doc(), "result", to.full(), id);
|
|
TQDomElement si = doc()->createElement("si");
|
|
si.setAttribute("xmlns", "http://jabber.org/protocol/si");
|
|
|
|
if(rangeOffset != 0 || rangeLength != 0) {
|
|
TQDomElement file = doc()->createElement("file");
|
|
file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
|
|
TQDomElement range = doc()->createElement("range");
|
|
if(rangeOffset > 0)
|
|
range.setAttribute("offset", TQString::number(rangeOffset));
|
|
if(rangeLength > 0)
|
|
range.setAttribute("length", TQString::number(rangeLength));
|
|
file.appendChild(range);
|
|
si.appendChild(file);
|
|
}
|
|
|
|
TQDomElement feature = doc()->createElement("feature");
|
|
feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
|
|
TQDomElement x = doc()->createElement("x");
|
|
x.setAttribute("xmlns", "jabber:x:data");
|
|
x.setAttribute("type", "submit");
|
|
|
|
TQDomElement field = doc()->createElement("field");
|
|
field.setAttribute("var", "stream-method");
|
|
TQDomElement value = doc()->createElement("value");
|
|
value.appendChild(doc()->createTextNode(streamType));
|
|
field.appendChild(value);
|
|
|
|
x.appendChild(field);
|
|
feature.appendChild(x);
|
|
|
|
si.appendChild(feature);
|
|
iq.appendChild(si);
|
|
send(iq);
|
|
}
|
|
|
|
void JT_PushFT::respondError(const Jid &to, const TQString &id, int code, const TQString &str)
|
|
{
|
|
TQDomElement iq = createIQ(doc(), "error", to.full(), id);
|
|
TQDomElement err = textTag(doc(), "error", str);
|
|
err.setAttribute("code", TQString::number(code));
|
|
iq.appendChild(err);
|
|
send(iq);
|
|
}
|
|
|
|
bool JT_PushFT::take(const TQDomElement &e)
|
|
{
|
|
// must be an iq-set tag
|
|
if(e.tagName() != "iq")
|
|
return false;
|
|
if(e.attribute("type") != "set")
|
|
return false;
|
|
|
|
TQDomElement si = firstChildElement(e);
|
|
if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si")
|
|
return false;
|
|
if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer")
|
|
return false;
|
|
|
|
Jid from(e.attribute("from"));
|
|
TQString id = si.attribute("id");
|
|
|
|
TQDomElement file = si.elementsByTagName("file").item(0).toElement();
|
|
if(file.isNull())
|
|
return true;
|
|
|
|
TQString fname = file.attribute("name");
|
|
if(fname.isEmpty()) {
|
|
respondError(from, id, 400, "Bad file name");
|
|
return true;
|
|
}
|
|
|
|
// ensure kosher
|
|
{
|
|
TQFileInfo fi(fname);
|
|
fname = fi.fileName();
|
|
}
|
|
|
|
bool ok;
|
|
TQ_LLONG size = file.attribute("size").toLongLong(&ok);
|
|
if(!ok || size < 0) {
|
|
respondError(from, id, 400, "Bad file size");
|
|
return true;
|
|
}
|
|
|
|
TQString desc;
|
|
TQDomElement de = file.elementsByTagName("desc").item(0).toElement();
|
|
if(!de.isNull())
|
|
desc = de.text();
|
|
|
|
bool rangeSupported = false;
|
|
TQDomElement range = file.elementsByTagName("range").item(0).toElement();
|
|
if(!range.isNull())
|
|
rangeSupported = true;
|
|
|
|
TQStringList streamTypes;
|
|
TQDomElement feature = si.elementsByTagName("feature").item(0).toElement();
|
|
if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
|
|
TQDomElement x = feature.elementsByTagName("x").item(0).toElement();
|
|
if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
|
|
TQDomElement field = x.elementsByTagName("field").item(0).toElement();
|
|
if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
|
|
TQDomNodeList nl = field.elementsByTagName("option");
|
|
for(uint n = 0; n < nl.count(); ++n) {
|
|
TQDomElement e = nl.item(n).toElement();
|
|
TQDomElement value = e.elementsByTagName("value").item(0).toElement();
|
|
if(!value.isNull())
|
|
streamTypes += value.text();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FTRequest r;
|
|
r.from = from;
|
|
r.iq_id = e.attribute("id");
|
|
r.id = id;
|
|
r.fname = fname;
|
|
r.size = size;
|
|
r.desc = desc;
|
|
r.rangeSupported = rangeSupported;
|
|
r.streamTypes = streamTypes;
|
|
|
|
incoming(r);
|
|
return true;
|
|
}
|