initial commit
This commit is contained in:
commit
9d20827c46
2469 changed files with 470994 additions and 0 deletions
782
iris-legacy/iris/jabber/filetransfer.cpp
Normal file
782
iris-legacy/iris/jabber/filetransfer.cpp
Normal file
|
|
@ -0,0 +1,782 @@
|
|||
/*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "filetransfer.h"
|
||||
|
||||
#include <qtimer.h>
|
||||
#include <q3ptrlist.h>
|
||||
#include <qpointer.h>
|
||||
#include <qfileinfo.h>
|
||||
#include "xmpp_xmlcommon.h"
|
||||
#include "s5b.h"
|
||||
|
||||
#define SENDBUFSIZE 65536
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
// firstChildElement
|
||||
//
|
||||
// Get an element's first child element
|
||||
static QDomElement firstChildElement(const QDomElement &e)
|
||||
{
|
||||
for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
|
||||
if(n.isElement())
|
||||
return n.toElement();
|
||||
}
|
||||
return QDomElement();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// FileTransfer
|
||||
//----------------------------------------------------------------------------
|
||||
class FileTransfer::Private
|
||||
{
|
||||
public:
|
||||
FileTransferManager *m;
|
||||
JT_FT *ft;
|
||||
Jid peer;
|
||||
QString fname;
|
||||
qlonglong size;
|
||||
qlonglong sent;
|
||||
QString desc;
|
||||
bool rangeSupported;
|
||||
qlonglong rangeOffset, rangeLength, length;
|
||||
QString streamType;
|
||||
bool needStream;
|
||||
QString id, iq_id;
|
||||
S5BConnection *c;
|
||||
Jid proxy;
|
||||
int state;
|
||||
bool sender;
|
||||
};
|
||||
|
||||
FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
d = new Private;
|
||||
d->m = m;
|
||||
d->ft = 0;
|
||||
d->c = 0;
|
||||
reset();
|
||||
}
|
||||
|
||||
FileTransfer::FileTransfer(const FileTransfer& other)
|
||||
: QObject(other.parent())
|
||||
{
|
||||
d = new Private;
|
||||
*d = *other.d;
|
||||
d->m = other.d->m;
|
||||
d->ft = 0;
|
||||
d->c = 0;
|
||||
reset();
|
||||
|
||||
if (d->m->isActive(&other))
|
||||
d->m->link(this);
|
||||
}
|
||||
|
||||
FileTransfer::~FileTransfer()
|
||||
{
|
||||
reset();
|
||||
delete d;
|
||||
}
|
||||
|
||||
FileTransfer *FileTransfer::copy() const
|
||||
{
|
||||
return new FileTransfer(*this);
|
||||
}
|
||||
|
||||
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 QString &fname, qlonglong size, const QString &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, SIGNAL(finished()), SLOT(ft_finished()));
|
||||
QStringList 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;
|
||||
qlonglong left = d->length - (d->sent + pending);
|
||||
int size = SENDBUFSIZE - pending;
|
||||
if((qlonglong)size > left)
|
||||
size = (int)left;
|
||||
return size;
|
||||
}
|
||||
|
||||
void FileTransfer::writeFileData(const QByteArray &a)
|
||||
{
|
||||
int pending = d->c->bytesToWrite();
|
||||
qlonglong left = d->length - (d->sent + pending);
|
||||
if(left == 0)
|
||||
return;
|
||||
|
||||
QByteArray block;
|
||||
if((qlonglong)a.size() > left) {
|
||||
block = a;
|
||||
block.resize((uint)left);
|
||||
}
|
||||
else
|
||||
block = a;
|
||||
d->c->write(block);
|
||||
}
|
||||
|
||||
Jid FileTransfer::peer() const
|
||||
{
|
||||
return d->peer;
|
||||
}
|
||||
|
||||
QString FileTransfer::fileName() const
|
||||
{
|
||||
return d->fname;
|
||||
}
|
||||
|
||||
qlonglong FileTransfer::fileSize() const
|
||||
{
|
||||
return d->size;
|
||||
}
|
||||
|
||||
QString FileTransfer::description() const
|
||||
{
|
||||
return d->desc;
|
||||
}
|
||||
|
||||
bool FileTransfer::rangeSupported() const
|
||||
{
|
||||
return d->rangeSupported;
|
||||
}
|
||||
|
||||
qlonglong FileTransfer::offset() const
|
||||
{
|
||||
return d->rangeOffset;
|
||||
}
|
||||
|
||||
qlonglong FileTransfer::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
void FileTransfer::accept(qlonglong offset, qlonglong 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, SIGNAL(connected()), SLOT(s5b_connected()));
|
||||
connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
|
||||
connect(d->c, SIGNAL(bytesWritten(int)), SLOT(s5b_bytesWritten(int)));
|
||||
connect(d->c, SIGNAL(error(int)), 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, SIGNAL(connected()), SLOT(s5b_connected()));
|
||||
connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
|
||||
connect(d->c, SIGNAL(readyRead()), SLOT(s5b_readyRead()));
|
||||
connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
|
||||
if(d->proxy.isValid())
|
||||
d->c->setProxy(d->proxy);
|
||||
accepted();
|
||||
QTimer::singleShot(0, this, SLOT(doAccept()));
|
||||
}
|
||||
|
||||
void FileTransfer::s5b_connected()
|
||||
{
|
||||
d->state = Active;
|
||||
connected();
|
||||
}
|
||||
|
||||
void FileTransfer::s5b_connectionClosed()
|
||||
{
|
||||
reset();
|
||||
error(ErrStream);
|
||||
}
|
||||
|
||||
void FileTransfer::s5b_readyRead()
|
||||
{
|
||||
QByteArray a = d->c->read();
|
||||
qlonglong need = d->length - d->sent;
|
||||
if((qlonglong)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;
|
||||
Q3PtrList<FileTransfer> list, incoming;
|
||||
JT_PushFT *pft;
|
||||
};
|
||||
|
||||
FileTransferManager::FileTransferManager(Client *client)
|
||||
:QObject(client)
|
||||
{
|
||||
d = new Private;
|
||||
d->client = client;
|
||||
|
||||
d->pft = new JT_PushFT(d->client->rootTask());
|
||||
connect(d->pft, SIGNAL(incoming(const FTRequest &)), 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;
|
||||
}
|
||||
|
||||
bool FileTransferManager::isActive(const FileTransfer *ft) const
|
||||
{
|
||||
return d->list.contains(ft) > 0;
|
||||
}
|
||||
|
||||
void FileTransferManager::pft_incoming(const FTRequest &req)
|
||||
{
|
||||
bool found = false;
|
||||
for(QStringList::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)
|
||||
{
|
||||
Q3PtrListIterator<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);
|
||||
}
|
||||
|
||||
QString 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:
|
||||
QDomElement iq;
|
||||
Jid to;
|
||||
qlonglong size, rangeOffset, rangeLength;
|
||||
QString streamType;
|
||||
QStringList 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 QString &_id, const QString &fname, qlonglong size, const QString &desc, const QStringList &streamTypes)
|
||||
{
|
||||
QDomElement iq;
|
||||
d->to = to;
|
||||
iq = createIQ(doc(), "set", to.full(), id());
|
||||
QDomElement 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");
|
||||
|
||||
QDomElement file = doc()->createElement("file");
|
||||
file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
|
||||
file.setAttribute("name", fname);
|
||||
file.setAttribute("size", QString::number(size));
|
||||
if(!desc.isEmpty()) {
|
||||
QDomElement de = doc()->createElement("desc");
|
||||
de.appendChild(doc()->createTextNode(desc));
|
||||
file.appendChild(de);
|
||||
}
|
||||
QDomElement range = doc()->createElement("range");
|
||||
file.appendChild(range);
|
||||
si.appendChild(file);
|
||||
|
||||
QDomElement feature = doc()->createElement("feature");
|
||||
feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
|
||||
QDomElement x = doc()->createElement("x");
|
||||
x.setAttribute("xmlns", "jabber:x:data");
|
||||
x.setAttribute("type", "form");
|
||||
|
||||
QDomElement field = doc()->createElement("field");
|
||||
field.setAttribute("var", "stream-method");
|
||||
field.setAttribute("type", "list-single");
|
||||
for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
|
||||
QDomElement option = doc()->createElement("option");
|
||||
QDomElement 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;
|
||||
}
|
||||
|
||||
qlonglong JT_FT::rangeOffset() const
|
||||
{
|
||||
return d->rangeOffset;
|
||||
}
|
||||
|
||||
qlonglong JT_FT::rangeLength() const
|
||||
{
|
||||
return d->rangeLength;
|
||||
}
|
||||
|
||||
QString JT_FT::streamType() const
|
||||
{
|
||||
return d->streamType;
|
||||
}
|
||||
|
||||
void JT_FT::onGo()
|
||||
{
|
||||
send(d->iq);
|
||||
}
|
||||
|
||||
bool JT_FT::take(const QDomElement &x)
|
||||
{
|
||||
if(!iqVerify(x, d->to, id()))
|
||||
return false;
|
||||
|
||||
if(x.attribute("type") == "result") {
|
||||
QDomElement si = firstChildElement(x);
|
||||
if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
|
||||
setError(900, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
QString id = si.attribute("id");
|
||||
|
||||
qlonglong range_offset = 0;
|
||||
qlonglong range_length = 0;
|
||||
|
||||
QDomElement file = si.elementsByTagName("file").item(0).toElement();
|
||||
if(!file.isNull()) {
|
||||
QDomElement 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;
|
||||
}
|
||||
|
||||
QString streamtype;
|
||||
QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
|
||||
if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
|
||||
QDomElement x = feature.elementsByTagName("x").item(0).toElement();
|
||||
if(!x.isNull() && x.attribute("type") == "submit") {
|
||||
QDomElement field = x.elementsByTagName("field").item(0).toElement();
|
||||
if(!field.isNull() && field.attribute("var") == "stream-method") {
|
||||
QDomElement value = field.elementsByTagName("value").item(0).toElement();
|
||||
if(!value.isNull())
|
||||
streamtype = value.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must be one of the offered streamtypes
|
||||
bool found = false;
|
||||
for(QStringList::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 QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType)
|
||||
{
|
||||
QDomElement iq = createIQ(doc(), "result", to.full(), id);
|
||||
QDomElement si = doc()->createElement("si");
|
||||
si.setAttribute("xmlns", "http://jabber.org/protocol/si");
|
||||
|
||||
if(rangeOffset != 0 || rangeLength != 0) {
|
||||
QDomElement file = doc()->createElement("file");
|
||||
file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
|
||||
QDomElement range = doc()->createElement("range");
|
||||
if(rangeOffset > 0)
|
||||
range.setAttribute("offset", QString::number(rangeOffset));
|
||||
if(rangeLength > 0)
|
||||
range.setAttribute("length", QString::number(rangeLength));
|
||||
file.appendChild(range);
|
||||
si.appendChild(file);
|
||||
}
|
||||
|
||||
QDomElement feature = doc()->createElement("feature");
|
||||
feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
|
||||
QDomElement x = doc()->createElement("x");
|
||||
x.setAttribute("xmlns", "jabber:x:data");
|
||||
x.setAttribute("type", "submit");
|
||||
|
||||
QDomElement field = doc()->createElement("field");
|
||||
field.setAttribute("var", "stream-method");
|
||||
QDomElement 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 QString &id, int code, const QString &str)
|
||||
{
|
||||
QDomElement iq = createIQ(doc(), "error", to.full(), id);
|
||||
QDomElement err = textTag(doc(), "error", str);
|
||||
err.setAttribute("code", QString::number(code));
|
||||
iq.appendChild(err);
|
||||
send(iq);
|
||||
}
|
||||
|
||||
bool JT_PushFT::take(const QDomElement &e)
|
||||
{
|
||||
// must be an iq-set tag
|
||||
if(e.tagName() != "iq")
|
||||
return false;
|
||||
if(e.attribute("type") != "set")
|
||||
return false;
|
||||
|
||||
QDomElement 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"));
|
||||
QString id = si.attribute("id");
|
||||
|
||||
QDomElement file = si.elementsByTagName("file").item(0).toElement();
|
||||
if(file.isNull())
|
||||
return true;
|
||||
|
||||
QString fname = file.attribute("name");
|
||||
if(fname.isEmpty()) {
|
||||
respondError(from, id, 400, "Bad file name");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ensure kosher
|
||||
{
|
||||
QFileInfo fi(fname);
|
||||
fname = fi.fileName();
|
||||
}
|
||||
|
||||
bool ok;
|
||||
qlonglong size = file.attribute("size").toLongLong(&ok);
|
||||
if(!ok || size < 0) {
|
||||
respondError(from, id, 400, "Bad file size");
|
||||
return true;
|
||||
}
|
||||
|
||||
QString desc;
|
||||
QDomElement de = file.elementsByTagName("desc").item(0).toElement();
|
||||
if(!de.isNull())
|
||||
desc = de.text();
|
||||
|
||||
bool rangeSupported = false;
|
||||
QDomElement range = file.elementsByTagName("range").item(0).toElement();
|
||||
if(!range.isNull())
|
||||
rangeSupported = true;
|
||||
|
||||
QStringList streamTypes;
|
||||
QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
|
||||
if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
|
||||
QDomElement x = feature.elementsByTagName("x").item(0).toElement();
|
||||
if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
|
||||
QDomElement field = x.elementsByTagName("field").item(0).toElement();
|
||||
if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
|
||||
QDomNodeList nl = field.elementsByTagName("option");
|
||||
for(int n = 0; n < nl.count(); ++n) {
|
||||
QDomElement e = nl.item(n).toElement();
|
||||
QDomElement 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;
|
||||
}
|
||||
183
iris-legacy/iris/jabber/filetransfer.h
Normal file
183
iris-legacy/iris/jabber/filetransfer.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* filetransfer.h - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef XMPP_FILETRANSFER_H
|
||||
#define XMPP_FILETRANSFER_H
|
||||
|
||||
#include "im.h"
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class S5BConnection;
|
||||
struct FTRequest;
|
||||
|
||||
/*class AbstractFileTransfer
|
||||
{
|
||||
public:
|
||||
// Receive
|
||||
virtual Jid peer() const = 0;
|
||||
virtual QString fileName() const = 0;
|
||||
virtual qlonglong fileSize() const = 0;
|
||||
virtual QString description() const { return ""; }
|
||||
virtual bool rangeSupported() const { return false; }
|
||||
virtual void accept(qlonglong offset=0, qlonglong length=0) = 0;
|
||||
};*/
|
||||
|
||||
class FileTransfer : public QObject /*, public AbstractFileTransfer */
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream };
|
||||
enum { Idle, Requesting, Connecting, WaitingForAccept, Active };
|
||||
~FileTransfer();
|
||||
|
||||
FileTransfer *copy() const;
|
||||
|
||||
void setProxy(const Jid &proxy);
|
||||
|
||||
// send
|
||||
void sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc);
|
||||
qlonglong offset() const;
|
||||
qlonglong length() const;
|
||||
int dataSizeNeeded() const;
|
||||
void writeFileData(const QByteArray &a);
|
||||
|
||||
// receive
|
||||
Jid peer() const;
|
||||
QString fileName() const;
|
||||
qlonglong fileSize() const;
|
||||
QString description() const;
|
||||
bool rangeSupported() const;
|
||||
void accept(qlonglong offset=0, qlonglong length=0);
|
||||
|
||||
// both
|
||||
void close(); // reject, or stop sending/receiving
|
||||
S5BConnection *s5bConnection() const; // active link
|
||||
|
||||
signals:
|
||||
void accepted(); // indicates S5BConnection has started
|
||||
void connected();
|
||||
void readyRead(const QByteArray &a);
|
||||
void bytesWritten(int);
|
||||
void error(int);
|
||||
|
||||
private slots:
|
||||
void ft_finished();
|
||||
void s5b_connected();
|
||||
void s5b_connectionClosed();
|
||||
void s5b_readyRead();
|
||||
void s5b_bytesWritten(int);
|
||||
void s5b_error(int);
|
||||
void doAccept();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
void reset();
|
||||
|
||||
friend class FileTransferManager;
|
||||
FileTransfer(FileTransferManager *, QObject *parent=0);
|
||||
FileTransfer(const FileTransfer& other);
|
||||
void man_waitForAccept(const FTRequest &req);
|
||||
void takeConnection(S5BConnection *c);
|
||||
};
|
||||
|
||||
class FileTransferManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FileTransferManager(Client *);
|
||||
~FileTransferManager();
|
||||
|
||||
bool isActive(const FileTransfer *ft) const;
|
||||
|
||||
Client *client() const;
|
||||
FileTransfer *createTransfer();
|
||||
FileTransfer *takeIncoming();
|
||||
|
||||
signals:
|
||||
void incomingReady();
|
||||
|
||||
private slots:
|
||||
void pft_incoming(const FTRequest &req);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
friend class Client;
|
||||
void s5b_incomingReady(S5BConnection *);
|
||||
|
||||
friend class FileTransfer;
|
||||
QString link(FileTransfer *);
|
||||
void con_accept(FileTransfer *);
|
||||
void con_reject(FileTransfer *);
|
||||
void unlink(FileTransfer *);
|
||||
};
|
||||
|
||||
class JT_FT : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
JT_FT(Task *parent);
|
||||
~JT_FT();
|
||||
|
||||
void request(const Jid &to, const QString &id, const QString &fname, qlonglong size, const QString &desc, const QStringList &streamTypes);
|
||||
qlonglong rangeOffset() const;
|
||||
qlonglong rangeLength() const;
|
||||
QString streamType() const;
|
||||
|
||||
void onGo();
|
||||
bool take(const QDomElement &);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
struct FTRequest
|
||||
{
|
||||
Jid from;
|
||||
QString iq_id, id;
|
||||
QString fname;
|
||||
qlonglong size;
|
||||
QString desc;
|
||||
bool rangeSupported;
|
||||
QStringList streamTypes;
|
||||
};
|
||||
class JT_PushFT : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
JT_PushFT(Task *parent);
|
||||
~JT_PushFT();
|
||||
|
||||
void respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType);
|
||||
void respondError(const Jid &to, const QString &id, int code, const QString &str);
|
||||
|
||||
bool take(const QDomElement &);
|
||||
|
||||
signals:
|
||||
void incoming(const FTRequest &req);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
2533
iris-legacy/iris/jabber/s5b.cpp
Normal file
2533
iris-legacy/iris/jabber/s5b.cpp
Normal file
File diff suppressed because it is too large
Load diff
345
iris-legacy/iris/jabber/s5b.h
Normal file
345
iris-legacy/iris/jabber/s5b.h
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* s5b.h - direct connection protocol via tcp
|
||||
* Copyright (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef XMPP_S5B_H
|
||||
#define XMPP_S5B_H
|
||||
|
||||
#include <qobject.h>
|
||||
#include <q3cstring.h>
|
||||
#include <q3ptrlist.h>
|
||||
#include <QList>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "bytestream.h"
|
||||
#include "xmpp_jid.h"
|
||||
#include "xmpp_task.h"
|
||||
|
||||
class SocksClient;
|
||||
class SocksUDP;
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class StreamHost;
|
||||
class Client;
|
||||
class S5BConnection;
|
||||
class S5BManager;
|
||||
class S5BServer;
|
||||
struct S5BRequest;
|
||||
typedef QList<StreamHost> StreamHostList;
|
||||
typedef Q3PtrList<S5BConnection> S5BConnectionList;
|
||||
typedef Q3PtrListIterator<S5BConnection> S5BConnectionListIt;
|
||||
|
||||
class S5BDatagram
|
||||
{
|
||||
public:
|
||||
S5BDatagram();
|
||||
S5BDatagram(int source, int dest, const QByteArray &data);
|
||||
|
||||
int sourcePort() const;
|
||||
int destPort() const;
|
||||
QByteArray data() const;
|
||||
|
||||
private:
|
||||
int _source, _dest;
|
||||
QByteArray _buf;
|
||||
};
|
||||
|
||||
class S5BConnection : public ByteStream
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Mode { Stream, Datagram };
|
||||
enum Error { ErrRefused, ErrConnect, ErrProxy, ErrSocket };
|
||||
enum State { Idle, Requesting, Connecting, WaitingForAccept, Active };
|
||||
~S5BConnection();
|
||||
|
||||
Jid proxy() const;
|
||||
void setProxy(const Jid &proxy);
|
||||
|
||||
void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream);
|
||||
void accept();
|
||||
void close();
|
||||
|
||||
Jid peer() const;
|
||||
QString sid() const;
|
||||
bool isRemote() const;
|
||||
Mode mode() const;
|
||||
int state() const;
|
||||
|
||||
bool isOpen() const;
|
||||
void write(const QByteArray &);
|
||||
QByteArray read(int bytes=0);
|
||||
int bytesAvailable() const;
|
||||
int bytesToWrite() const;
|
||||
|
||||
void writeDatagram(const S5BDatagram &);
|
||||
S5BDatagram readDatagram();
|
||||
int datagramsAvailable() const;
|
||||
|
||||
signals:
|
||||
void proxyQuery(); // querying proxy for streamhost information
|
||||
void proxyResult(bool b); // query success / fail
|
||||
void requesting(); // sent actual S5B request (initiator only)
|
||||
void accepted(); // target accepted (initiator only
|
||||
void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts
|
||||
void proxyConnect(); // connecting to proxy
|
||||
void waitingForActivation(); // waiting for activation (target only)
|
||||
void connected(); // connection active
|
||||
void datagramReady();
|
||||
|
||||
private slots:
|
||||
void doPending();
|
||||
|
||||
void sc_connectionClosed();
|
||||
void sc_delayedCloseFinished();
|
||||
void sc_readyRead();
|
||||
void sc_bytesWritten(int);
|
||||
void sc_error(int);
|
||||
|
||||
void su_packetReady(const QByteArray &buf);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
void reset(bool clear=false);
|
||||
void handleUDP(const QByteArray &buf);
|
||||
void sendUDP(const QByteArray &buf);
|
||||
|
||||
friend class S5BManager;
|
||||
void man_waitForAccept(const S5BRequest &r);
|
||||
void man_clientReady(SocksClient *, SocksUDP *);
|
||||
void man_udpReady(const QByteArray &buf);
|
||||
void man_failed(int);
|
||||
S5BConnection(S5BManager *, QObject *parent=0);
|
||||
};
|
||||
|
||||
class S5BManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
S5BManager(Client *);
|
||||
~S5BManager();
|
||||
|
||||
Client *client() const;
|
||||
S5BServer *server() const;
|
||||
void setServer(S5BServer *s);
|
||||
|
||||
bool isAcceptableSID(const Jid &peer, const QString &sid) const;
|
||||
QString genUniqueSID(const Jid &peer) const;
|
||||
|
||||
S5BConnection *createConnection();
|
||||
S5BConnection *takeIncoming();
|
||||
|
||||
class Item;
|
||||
class Entry;
|
||||
|
||||
signals:
|
||||
void incomingReady();
|
||||
|
||||
private slots:
|
||||
void ps_incoming(const S5BRequest &req);
|
||||
void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr);
|
||||
void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
|
||||
void item_accepted();
|
||||
void item_tryingHosts(const StreamHostList &list);
|
||||
void item_proxyConnect();
|
||||
void item_waitingForActivation();
|
||||
void item_connected();
|
||||
void item_error(int);
|
||||
void query_finished();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
S5BConnection *findIncoming(const Jid &from, const QString &sid) const;
|
||||
Entry *findEntry(S5BConnection *) const;
|
||||
Entry *findEntry(Item *) const;
|
||||
Entry *findEntryByHash(const QString &key) const;
|
||||
Entry *findEntryBySID(const Jid &peer, const QString &sid) const;
|
||||
Entry *findServerEntryByHash(const QString &key) const;
|
||||
|
||||
void entryContinue(Entry *e);
|
||||
void queryProxy(Entry *e);
|
||||
bool targetShouldOfferProxy(Entry *e);
|
||||
|
||||
friend class S5BConnection;
|
||||
void con_connect(S5BConnection *);
|
||||
void con_accept(S5BConnection *);
|
||||
void con_reject(S5BConnection *);
|
||||
void con_unlink(S5BConnection *);
|
||||
void con_sendUDP(S5BConnection *, const QByteArray &buf);
|
||||
|
||||
friend class S5BServer;
|
||||
bool srv_ownsHash(const QString &key) const;
|
||||
void srv_incomingReady(SocksClient *sc, const QString &key);
|
||||
void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data);
|
||||
void srv_unlink();
|
||||
|
||||
friend class Item;
|
||||
void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost);
|
||||
void doError(const Jid &peer, const QString &id, int, const QString &);
|
||||
void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost);
|
||||
};
|
||||
|
||||
class S5BConnector : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
S5BConnector(QObject *parent=0);
|
||||
~S5BConnector();
|
||||
|
||||
void reset();
|
||||
void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout);
|
||||
SocksClient *takeClient();
|
||||
SocksUDP *takeUDP();
|
||||
StreamHost streamHostUsed() const;
|
||||
|
||||
class Item;
|
||||
|
||||
signals:
|
||||
void result(bool);
|
||||
|
||||
private slots:
|
||||
void item_result(bool);
|
||||
void t_timeout();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
friend class S5BManager;
|
||||
void man_udpSuccess(const Jid &streamHost);
|
||||
};
|
||||
|
||||
// listens on a port for serving
|
||||
class S5BServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
S5BServer(QObject *par=0);
|
||||
~S5BServer();
|
||||
|
||||
bool isActive() const;
|
||||
bool start(int port);
|
||||
void stop();
|
||||
int port() const;
|
||||
void setHostList(const QStringList &);
|
||||
QStringList hostList() const;
|
||||
|
||||
class Item;
|
||||
|
||||
private slots:
|
||||
void ss_incomingReady();
|
||||
void ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
|
||||
void item_result(bool);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
friend class S5BManager;
|
||||
void link(S5BManager *);
|
||||
void unlink(S5BManager *);
|
||||
void unlinkAll();
|
||||
const Q3PtrList<S5BManager> & managerList() const;
|
||||
void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
|
||||
};
|
||||
|
||||
class JT_S5B : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
JT_S5B(Task *);
|
||||
~JT_S5B();
|
||||
|
||||
void request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp=false);
|
||||
void requestProxyInfo(const Jid &to);
|
||||
void requestActivation(const Jid &to, const QString &sid, const Jid &target);
|
||||
|
||||
void onGo();
|
||||
void onDisconnect();
|
||||
bool take(const QDomElement &);
|
||||
|
||||
Jid streamHostUsed() const;
|
||||
StreamHost proxyInfo() const;
|
||||
|
||||
private slots:
|
||||
void t_timeout();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
struct S5BRequest
|
||||
{
|
||||
Jid from;
|
||||
QString id, sid;
|
||||
StreamHostList hosts;
|
||||
bool fast;
|
||||
bool udp;
|
||||
};
|
||||
class JT_PushS5B : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
JT_PushS5B(Task *);
|
||||
~JT_PushS5B();
|
||||
|
||||
int priority() const;
|
||||
|
||||
void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost);
|
||||
void respondError(const Jid &to, const QString &id, int code, const QString &str);
|
||||
void sendUDPSuccess(const Jid &to, const QString &dstaddr);
|
||||
void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost);
|
||||
|
||||
bool take(const QDomElement &);
|
||||
|
||||
signals:
|
||||
void incoming(const S5BRequest &req);
|
||||
void incomingUDPSuccess(const Jid &from, const QString &dstaddr);
|
||||
void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
|
||||
};
|
||||
|
||||
class StreamHost
|
||||
{
|
||||
public:
|
||||
StreamHost();
|
||||
|
||||
const Jid & jid() const;
|
||||
const QString & host() const;
|
||||
int port() const;
|
||||
bool isProxy() const;
|
||||
void setJid(const Jid &);
|
||||
void setHost(const QString &);
|
||||
void setPort(int);
|
||||
void setIsProxy(bool);
|
||||
|
||||
private:
|
||||
Jid j;
|
||||
QString v_host;
|
||||
int v_port;
|
||||
bool proxy;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
638
iris-legacy/iris/jabber/xmpp_ibb.cpp
Normal file
638
iris-legacy/iris/jabber/xmpp_ibb.cpp
Normal file
|
|
@ -0,0 +1,638 @@
|
|||
/*
|
||||
* ibb.cpp - Inband bytestream
|
||||
* Copyright (C) 2001, 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmpp_ibb.h"
|
||||
|
||||
#include <qtimer.h>
|
||||
#include "xmpp_xmlcommon.h"
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define IBB_PACKET_SIZE 4096
|
||||
#define IBB_PACKET_DELAY 0
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
static int num_conn = 0;
|
||||
static int id_conn = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// IBBConnection
|
||||
//----------------------------------------------------------------------------
|
||||
class IBBConnection::Private
|
||||
{
|
||||
public:
|
||||
Private() {}
|
||||
|
||||
int state;
|
||||
Jid peer;
|
||||
QString sid;
|
||||
IBBManager *m;
|
||||
JT_IBB *j;
|
||||
QDomElement comment;
|
||||
QString iq_id;
|
||||
|
||||
int blockSize;
|
||||
QByteArray recvbuf, sendbuf;
|
||||
bool closePending, closing;
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
IBBConnection::IBBConnection(IBBManager *m)
|
||||
:ByteStream(m)
|
||||
{
|
||||
d = new Private;
|
||||
d->m = m;
|
||||
d->j = 0;
|
||||
reset();
|
||||
|
||||
++num_conn;
|
||||
d->id = id_conn++;
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
|
||||
d->m->client()->debug(dstr);
|
||||
}
|
||||
|
||||
void IBBConnection::reset(bool clear)
|
||||
{
|
||||
d->m->unlink(this);
|
||||
d->state = Idle;
|
||||
d->closePending = false;
|
||||
d->closing = false;
|
||||
|
||||
delete d->j;
|
||||
d->j = 0;
|
||||
|
||||
d->sendbuf.resize(0);
|
||||
if(clear)
|
||||
d->recvbuf.resize(0);
|
||||
}
|
||||
|
||||
IBBConnection::~IBBConnection()
|
||||
{
|
||||
reset(true);
|
||||
|
||||
--num_conn;
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
|
||||
{
|
||||
close();
|
||||
reset(true);
|
||||
|
||||
d->state = Requesting;
|
||||
d->peer = peer;
|
||||
d->comment = comment;
|
||||
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().latin1());
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
d->j = new JT_IBB(d->m->client()->rootTask());
|
||||
connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
|
||||
d->j->request(d->peer, comment);
|
||||
d->j->go(true);
|
||||
}
|
||||
|
||||
void IBBConnection::accept()
|
||||
{
|
||||
if(d->state != WaitingForAccept)
|
||||
return;
|
||||
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
d->m->doAccept(this, d->iq_id);
|
||||
d->state = Active;
|
||||
d->m->link(this);
|
||||
}
|
||||
|
||||
void IBBConnection::close()
|
||||
{
|
||||
if(d->state == Idle)
|
||||
return;
|
||||
|
||||
if(d->state == WaitingForAccept) {
|
||||
d->m->doReject(this, d->iq_id, 403, "Rejected");
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
if(d->state == Active) {
|
||||
// if there is data pending to be written, then pend the closing
|
||||
if(bytesToWrite() > 0) {
|
||||
d->closePending = true;
|
||||
trySend();
|
||||
return;
|
||||
}
|
||||
|
||||
// send a close packet
|
||||
JT_IBB *j = new JT_IBB(d->m->client()->rootTask());
|
||||
j->sendData(d->peer, d->sid, QByteArray(), true);
|
||||
j->go(true);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
int IBBConnection::state() const
|
||||
{
|
||||
return d->state;
|
||||
}
|
||||
|
||||
Jid IBBConnection::peer() const
|
||||
{
|
||||
return d->peer;
|
||||
}
|
||||
|
||||
QString IBBConnection::streamid() const
|
||||
{
|
||||
return d->sid;
|
||||
}
|
||||
|
||||
QDomElement IBBConnection::comment() const
|
||||
{
|
||||
return d->comment;
|
||||
}
|
||||
|
||||
bool IBBConnection::isOpen() const
|
||||
{
|
||||
if(d->state == Active)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void IBBConnection::write(const QByteArray &a)
|
||||
{
|
||||
if(d->state != Active || d->closePending || d->closing)
|
||||
return;
|
||||
|
||||
// append to the end of our send buffer
|
||||
int oldsize = d->sendbuf.size();
|
||||
d->sendbuf.resize(oldsize + a.size());
|
||||
memcpy(d->sendbuf.data() + oldsize, a.data(), a.size());
|
||||
|
||||
trySend();
|
||||
}
|
||||
|
||||
QByteArray IBBConnection::read(int)
|
||||
{
|
||||
// TODO: obey argument
|
||||
QByteArray a = d->recvbuf;
|
||||
d->recvbuf.resize(0);
|
||||
return a;
|
||||
}
|
||||
|
||||
int IBBConnection::bytesAvailable() const
|
||||
{
|
||||
return d->recvbuf.size();
|
||||
}
|
||||
|
||||
int IBBConnection::bytesToWrite() const
|
||||
{
|
||||
return d->sendbuf.size();
|
||||
}
|
||||
|
||||
void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
|
||||
{
|
||||
close();
|
||||
reset(true);
|
||||
|
||||
d->state = WaitingForAccept;
|
||||
d->peer = peer;
|
||||
d->sid = sid;
|
||||
d->comment = comment;
|
||||
d->iq_id = iq_id;
|
||||
}
|
||||
|
||||
void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
|
||||
{
|
||||
// append to the end of our recv buffer
|
||||
int oldsize = d->recvbuf.size();
|
||||
d->recvbuf.resize(oldsize + a.size());
|
||||
memcpy(d->recvbuf.data() + oldsize, a.data(), a.size());
|
||||
|
||||
readyRead();
|
||||
|
||||
if(close) {
|
||||
reset();
|
||||
connectionClosed();
|
||||
}
|
||||
}
|
||||
|
||||
void IBBConnection::ibb_finished()
|
||||
{
|
||||
JT_IBB *j = d->j;
|
||||
d->j = 0;
|
||||
|
||||
if(j->success()) {
|
||||
if(j->mode() == JT_IBB::ModeRequest) {
|
||||
d->sid = j->streamid();
|
||||
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().latin1(), d->sid.latin1());
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
d->state = Active;
|
||||
d->m->link(this);
|
||||
connected();
|
||||
}
|
||||
else {
|
||||
bytesWritten(d->blockSize);
|
||||
|
||||
if(d->closing) {
|
||||
reset();
|
||||
delayedCloseFinished();
|
||||
}
|
||||
|
||||
if(!d->sendbuf.isEmpty() || d->closePending)
|
||||
QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(j->mode() == JT_IBB::ModeRequest) {
|
||||
QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().latin1());
|
||||
d->m->client()->debug(dstr);
|
||||
|
||||
reset(true);
|
||||
error(ErrRequest);
|
||||
}
|
||||
else {
|
||||
reset(true);
|
||||
error(ErrData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IBBConnection::trySend()
|
||||
{
|
||||
// if we already have an active task, then don't do anything
|
||||
if(d->j)
|
||||
return;
|
||||
|
||||
QByteArray a;
|
||||
if(!d->sendbuf.isEmpty()) {
|
||||
// take a chunk
|
||||
if(d->sendbuf.size() < IBB_PACKET_SIZE)
|
||||
a.resize(d->sendbuf.size());
|
||||
else
|
||||
a.resize(IBB_PACKET_SIZE);
|
||||
memcpy(a.data(), d->sendbuf.data(), a.size());
|
||||
d->sendbuf.resize(d->sendbuf.size() - a.size());
|
||||
}
|
||||
|
||||
bool doClose = false;
|
||||
if(d->sendbuf.isEmpty() && d->closePending)
|
||||
doClose = true;
|
||||
|
||||
// null operation?
|
||||
if(a.isEmpty() && !doClose)
|
||||
return;
|
||||
|
||||
printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
|
||||
if(doClose)
|
||||
printf("and closing.\n");
|
||||
else
|
||||
printf("(%d bytes left)\n", d->sendbuf.size());
|
||||
|
||||
if(doClose) {
|
||||
d->closePending = false;
|
||||
d->closing = true;
|
||||
}
|
||||
|
||||
d->blockSize = a.size();
|
||||
d->j = new JT_IBB(d->m->client()->rootTask());
|
||||
connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
|
||||
d->j->sendData(d->peer, d->sid, a, doClose);
|
||||
d->j->go(true);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// IBBManager
|
||||
//----------------------------------------------------------------------------
|
||||
class IBBManager::Private
|
||||
{
|
||||
public:
|
||||
Private() {}
|
||||
|
||||
Client *client;
|
||||
IBBConnectionList activeConns;
|
||||
IBBConnectionList incomingConns;
|
||||
JT_IBB *ibb;
|
||||
};
|
||||
|
||||
IBBManager::IBBManager(Client *parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
d = new Private;
|
||||
d->client = parent;
|
||||
|
||||
d->ibb = new JT_IBB(d->client->rootTask(), true);
|
||||
connect(d->ibb, SIGNAL(incomingRequest(const Jid &, const QString &, const QDomElement &)), SLOT(ibb_incomingRequest(const Jid &, const QString &, const QDomElement &)));
|
||||
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)));
|
||||
}
|
||||
|
||||
IBBManager::~IBBManager()
|
||||
{
|
||||
d->incomingConns.setAutoDelete(true);
|
||||
d->incomingConns.clear();
|
||||
delete d->ibb;
|
||||
delete d;
|
||||
}
|
||||
|
||||
Client *IBBManager::client() const
|
||||
{
|
||||
return d->client;
|
||||
}
|
||||
|
||||
IBBConnection *IBBManager::takeIncoming()
|
||||
{
|
||||
if(d->incomingConns.isEmpty())
|
||||
return 0;
|
||||
|
||||
IBBConnection *c = d->incomingConns.getFirst();
|
||||
d->incomingConns.removeRef(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
|
||||
{
|
||||
QString sid = genUniqueKey();
|
||||
|
||||
// create a "waiting" connection
|
||||
IBBConnection *c = new IBBConnection(this);
|
||||
c->waitForAccept(from, sid, comment, id);
|
||||
d->incomingConns.append(c);
|
||||
incomingReady();
|
||||
}
|
||||
|
||||
void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
|
||||
{
|
||||
IBBConnection *c = findConnection(streamid, from);
|
||||
if(!c) {
|
||||
d->ibb->respondError(from, id, 404, "No such stream");
|
||||
}
|
||||
else {
|
||||
d->ibb->respondAck(from, id);
|
||||
c->takeIncomingData(data, close);
|
||||
}
|
||||
}
|
||||
|
||||
QString IBBManager::genKey() const
|
||||
{
|
||||
QString key = "ibb_";
|
||||
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
int word = rand() & 0xffff;
|
||||
for(int n = 0; n < 4; ++n) {
|
||||
QString s;
|
||||
s.sprintf("%x", (word >> (n * 4)) & 0xf);
|
||||
key.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
QString IBBManager::genUniqueKey() const
|
||||
{
|
||||
// get unused key
|
||||
QString key;
|
||||
while(1) {
|
||||
key = genKey();
|
||||
|
||||
if(!findConnection(key))
|
||||
break;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void IBBManager::link(IBBConnection *c)
|
||||
{
|
||||
d->activeConns.append(c);
|
||||
}
|
||||
|
||||
void IBBManager::unlink(IBBConnection *c)
|
||||
{
|
||||
d->activeConns.removeRef(c);
|
||||
}
|
||||
|
||||
IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
|
||||
{
|
||||
IBBConnectionListIt it(d->activeConns);
|
||||
for(IBBConnection *c; (c = it.current()); ++it) {
|
||||
if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IBBManager::doAccept(IBBConnection *c, const QString &id)
|
||||
{
|
||||
d->ibb->respondSuccess(c->peer(), id, c->streamid());
|
||||
}
|
||||
|
||||
void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
|
||||
{
|
||||
d->ibb->respondError(c->peer(), id, code, str);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// JT_IBB
|
||||
//----------------------------------------------------------------------------
|
||||
class JT_IBB::Private
|
||||
{
|
||||
public:
|
||||
Private() {}
|
||||
|
||||
QDomElement iq;
|
||||
int mode;
|
||||
bool serve;
|
||||
Jid to;
|
||||
QString streamid;
|
||||
};
|
||||
|
||||
JT_IBB::JT_IBB(Task *parent, bool serve)
|
||||
:Task(parent)
|
||||
{
|
||||
d = new Private;
|
||||
d->serve = serve;
|
||||
}
|
||||
|
||||
JT_IBB::~JT_IBB()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void JT_IBB::request(const Jid &to, const QDomElement &comment)
|
||||
{
|
||||
d->mode = ModeRequest;
|
||||
QDomElement iq;
|
||||
d->to = to;
|
||||
iq = createIQ(doc(), "set", to.full(), id());
|
||||
QDomElement query = doc()->createElement("query");
|
||||
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
|
||||
iq.appendChild(query);
|
||||
query.appendChild(comment);
|
||||
d->iq = iq;
|
||||
}
|
||||
|
||||
void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
|
||||
{
|
||||
d->mode = ModeSendData;
|
||||
QDomElement iq;
|
||||
d->to = to;
|
||||
iq = createIQ(doc(), "set", to.full(), id());
|
||||
QDomElement query = doc()->createElement("query");
|
||||
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
|
||||
iq.appendChild(query);
|
||||
query.appendChild(textTag(doc(), "streamid", streamid));
|
||||
if(!a.isEmpty())
|
||||
query.appendChild(textTag(doc(), "data", QCA::Base64().arrayToString(a)));
|
||||
if(close) {
|
||||
QDomElement c = doc()->createElement("close");
|
||||
query.appendChild(c);
|
||||
}
|
||||
d->iq = iq;
|
||||
}
|
||||
|
||||
void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
|
||||
{
|
||||
QDomElement iq = createIQ(doc(), "result", to.full(), id);
|
||||
QDomElement query = doc()->createElement("query");
|
||||
query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
|
||||
iq.appendChild(query);
|
||||
query.appendChild(textTag(doc(), "streamid", streamid));
|
||||
send(iq);
|
||||
}
|
||||
|
||||
void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
|
||||
{
|
||||
QDomElement iq = createIQ(doc(), "error", to.full(), id);
|
||||
QDomElement err = textTag(doc(), "error", str);
|
||||
err.setAttribute("code", QString::number(code));
|
||||
iq.appendChild(err);
|
||||
send(iq);
|
||||
}
|
||||
|
||||
void JT_IBB::respondAck(const Jid &to, const QString &id)
|
||||
{
|
||||
QDomElement iq = createIQ(doc(), "result", to.full(), id);
|
||||
send(iq);
|
||||
}
|
||||
|
||||
void JT_IBB::onGo()
|
||||
{
|
||||
send(d->iq);
|
||||
}
|
||||
|
||||
bool JT_IBB::take(const QDomElement &e)
|
||||
{
|
||||
if(d->serve) {
|
||||
// must be an iq-set tag
|
||||
if(e.tagName() != "iq" || e.attribute("type") != "set")
|
||||
return false;
|
||||
|
||||
if(queryNS(e) != "http://jabber.org/protocol/ibb")
|
||||
return false;
|
||||
|
||||
Jid from(e.attribute("from"));
|
||||
QString id = e.attribute("id");
|
||||
QDomElement q = queryTag(e);
|
||||
|
||||
bool found;
|
||||
QDomElement s = findSubTag(q, "streamid", &found);
|
||||
if(!found) {
|
||||
QDomElement comment = findSubTag(q, "comment", &found);
|
||||
incomingRequest(from, id, comment);
|
||||
}
|
||||
else {
|
||||
QString sid = tagContent(s);
|
||||
QByteArray a;
|
||||
bool close = false;
|
||||
s = findSubTag(q, "data", &found);
|
||||
if(found)
|
||||
a = QCA::Base64().stringToArray(tagContent(s)).toByteArray();
|
||||
s = findSubTag(q, "close", &found);
|
||||
if(found)
|
||||
close = true;
|
||||
|
||||
incomingData(from, sid, id, a, close);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Jid from(e.attribute("from"));
|
||||
if(e.attribute("id") != id() || !d->to.compare(from))
|
||||
return false;
|
||||
|
||||
if(e.attribute("type") == "result") {
|
||||
QDomElement q = queryTag(e);
|
||||
|
||||
// request
|
||||
if(d->mode == ModeRequest) {
|
||||
bool found;
|
||||
QDomElement s = findSubTag(q, "streamid", &found);
|
||||
if(found)
|
||||
d->streamid = tagContent(s);
|
||||
else
|
||||
d->streamid = "";
|
||||
setSuccess();
|
||||
}
|
||||
// sendData
|
||||
else {
|
||||
// thank you for the ack, kind sir
|
||||
setSuccess();
|
||||
}
|
||||
}
|
||||
else {
|
||||
setError(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QString JT_IBB::streamid() const
|
||||
{
|
||||
return d->streamid;
|
||||
}
|
||||
|
||||
Jid JT_IBB::jid() const
|
||||
{
|
||||
return d->to;
|
||||
}
|
||||
|
||||
int JT_IBB::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
145
iris-legacy/iris/jabber/xmpp_ibb.h
Normal file
145
iris-legacy/iris/jabber/xmpp_ibb.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* ibb.h - Inband bytestream
|
||||
* Copyright (C) 2001, 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JABBER_IBB_H
|
||||
#define JABBER_IBB_H
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qdom.h>
|
||||
#include <qstring.h>
|
||||
#include <q3ptrlist.h>
|
||||
#include "bytestream.h"
|
||||
#include "im.h"
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class Client;
|
||||
class IBBManager;
|
||||
|
||||
// this is an IBB connection. use it much like a qsocket
|
||||
class IBBConnection : public ByteStream
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum { ErrRequest, ErrData };
|
||||
enum { Idle, Requesting, WaitingForAccept, Active };
|
||||
IBBConnection(IBBManager *);
|
||||
~IBBConnection();
|
||||
|
||||
void connectToJid(const Jid &peer, const QDomElement &comment);
|
||||
void accept();
|
||||
void close();
|
||||
|
||||
int state() const;
|
||||
Jid peer() const;
|
||||
QString streamid() const;
|
||||
QDomElement comment() const;
|
||||
|
||||
bool isOpen() const;
|
||||
void write(const QByteArray &);
|
||||
QByteArray read(int bytes=0);
|
||||
int bytesAvailable() const;
|
||||
int bytesToWrite() const;
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
|
||||
private slots:
|
||||
void ibb_finished();
|
||||
void trySend();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
void reset(bool clear=false);
|
||||
|
||||
friend class IBBManager;
|
||||
void waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id);
|
||||
void takeIncomingData(const QByteArray &, bool close);
|
||||
};
|
||||
|
||||
typedef Q3PtrList<IBBConnection> IBBConnectionList;
|
||||
typedef Q3PtrListIterator<IBBConnection> IBBConnectionListIt;
|
||||
class IBBManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IBBManager(Client *);
|
||||
~IBBManager();
|
||||
|
||||
Client *client() const;
|
||||
|
||||
IBBConnection *takeIncoming();
|
||||
|
||||
signals:
|
||||
void incomingReady();
|
||||
|
||||
private slots:
|
||||
void ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &);
|
||||
void ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
QString genKey() const;
|
||||
|
||||
friend class IBBConnection;
|
||||
IBBConnection *findConnection(const QString &sid, const Jid &peer="") const;
|
||||
QString genUniqueKey() const;
|
||||
void link(IBBConnection *);
|
||||
void unlink(IBBConnection *);
|
||||
void doAccept(IBBConnection *c, const QString &id);
|
||||
void doReject(IBBConnection *c, const QString &id, int, const QString &);
|
||||
};
|
||||
|
||||
class JT_IBB : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum { ModeRequest, ModeSendData };
|
||||
JT_IBB(Task *, bool serve=false);
|
||||
~JT_IBB();
|
||||
|
||||
void request(const Jid &, const QDomElement &comment);
|
||||
void sendData(const Jid &, const QString &streamid, const QByteArray &data, bool close);
|
||||
void respondSuccess(const Jid &, const QString &id, const QString &streamid);
|
||||
void respondError(const Jid &, const QString &id, int code, const QString &str);
|
||||
void respondAck(const Jid &to, const QString &id);
|
||||
|
||||
void onGo();
|
||||
bool take(const QDomElement &);
|
||||
|
||||
QString streamid() const;
|
||||
Jid jid() const;
|
||||
int mode() const;
|
||||
|
||||
signals:
|
||||
void incomingRequest(const Jid &from, const QString &id, const QDomElement &);
|
||||
void incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in a new issue