initial commit

This commit is contained in:
mikhail "synzr" 2025-12-25 01:37:49 +05:00
commit 9d20827c46
2469 changed files with 470994 additions and 0 deletions

View 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;
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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;
}

View 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