1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684 |
- /*
- * Copyright (C) 2008-2012 The QXmpp developers
- *
- * Author:
- * Jeremy Lainé
- *
- * Source:
- * http://code.google.com/p/qxmpp
- *
- * This file is a part of QXmpp library.
- *
- * 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.
- *
- */
- #include <QCryptographicHash>
- #include <QDomElement>
- #include <QFile>
- #include <QFileInfo>
- #include <QHash>
- #include <QHostAddress>
- #include <QNetworkInterface>
- #include <QTime>
- #include <QTimer>
- #include <QUrl>
- #include "QXmppByteStreamIq.h"
- #include "QXmppClient.h"
- #include "QXmppConstants.h"
- #include "QXmppIbbIq.h"
- #include "QXmppSocks.h"
- #include "QXmppStreamInitiationIq_p.h"
- #include "QXmppStun.h"
- #include "QXmppTransferManager.h"
- #include "QXmppTransferManager_p.h"
- #include "QXmppUtils.h"
- // time to try to connect to a SOCKS host (7 seconds)
- const int socksTimeout = 7000;
- static QString streamHash(const QString &sid, const QString &initiatorJid, const QString &targetJid)
- {
- QCryptographicHash hash(QCryptographicHash::Sha1);
- QString str = sid + initiatorJid + targetJid;
- hash.addData(str.toAscii());
- return hash.result().toHex();
- }
- class QXmppTransferFileInfoPrivate : public QSharedData
- {
- public:
- QXmppTransferFileInfoPrivate();
- QDateTime date;
- QByteArray hash;
- QString name;
- QString description;
- qint64 size;
- };
- QXmppTransferFileInfoPrivate::QXmppTransferFileInfoPrivate()
- : size(0)
- {
- }
- QXmppTransferFileInfo::QXmppTransferFileInfo()
- : d(new QXmppTransferFileInfoPrivate)
- {
- }
- QXmppTransferFileInfo::QXmppTransferFileInfo(const QXmppTransferFileInfo &other)
- : d(other.d)
- {
- }
- QXmppTransferFileInfo::~QXmppTransferFileInfo()
- {
- }
- QDateTime QXmppTransferFileInfo::date() const
- {
- return d->date;
- }
- void QXmppTransferFileInfo::setDate(const QDateTime &date)
- {
- d->date = date;
- }
- QByteArray QXmppTransferFileInfo::hash() const
- {
- return d->hash;
- }
- void QXmppTransferFileInfo::setHash(const QByteArray &hash)
- {
- d->hash = hash;
- }
- QString QXmppTransferFileInfo::name() const
- {
- return d->name;
- }
- void QXmppTransferFileInfo::setName(const QString &name)
- {
- d->name = name;
- }
- QString QXmppTransferFileInfo::description() const
- {
- return d->description;
- }
- void QXmppTransferFileInfo::setDescription(const QString &description)
- {
- d->description = description;
- }
- qint64 QXmppTransferFileInfo::size() const
- {
- return d->size;
- }
- void QXmppTransferFileInfo::setSize(qint64 size)
- {
- d->size = size;
- }
- bool QXmppTransferFileInfo::isNull() const
- {
- return d->date.isNull()
- && d->description.isEmpty()
- && d->hash.isEmpty()
- && d->name.isEmpty()
- && d->size == 0;
- }
- QXmppTransferFileInfo& QXmppTransferFileInfo::operator=(const QXmppTransferFileInfo &other)
- {
- d = other.d;
- return *this;
- }
- bool QXmppTransferFileInfo::operator==(const QXmppTransferFileInfo &other) const
- {
- return other.d->size == d->size &&
- other.d->hash == d->hash &&
- other.d->name == d->name;
- }
- void QXmppTransferFileInfo::parse(const QDomElement &element)
- {
- d->date = QXmppUtils::datetimeFromString(element.attribute("date"));
- d->hash = QByteArray::fromHex(element.attribute("hash").toAscii());
- d->name = element.attribute("name");
- d->size = element.attribute("size").toLongLong();
- d->description = element.firstChildElement("desc").text();
- }
- void QXmppTransferFileInfo::toXml(QXmlStreamWriter *writer) const
- {
- writer->writeStartElement("file");
- writer->writeAttribute("xmlns", ns_stream_initiation_file_transfer);
- if (d->date.isValid())
- writer->writeAttribute("date", QXmppUtils::datetimeToString(d->date));
- if (!d->hash.isEmpty())
- writer->writeAttribute("hash", d->hash.toHex());
- if (!d->name.isEmpty())
- writer->writeAttribute("name", d->name);
- if (d->size > 0)
- writer->writeAttribute("size", QString::number(d->size));
- if (!d->description.isEmpty())
- writer->writeTextElement("desc", d->description);
- writer->writeEndElement();
- }
- class QXmppTransferJobPrivate
- {
- public:
- QXmppTransferJobPrivate();
- int blockSize;
- QXmppClient *client;
- QXmppTransferJob::Direction direction;
- qint64 done;
- QXmppTransferJob::Error error;
- QCryptographicHash hash;
- QIODevice *iodevice;
- QString offerId;
- QString jid;
- QUrl localFileUrl;
- QString sid;
- QXmppTransferJob::Method method;
- QString mimeType;
- QString requestId;
- QXmppTransferJob::State state;
- QTime transferStart;
- // file meta-data
- QXmppTransferFileInfo fileInfo;
- // for in-band bytestreams
- int ibbSequence;
- // for socks5 bytestreams
- QTcpSocket *socksSocket;
- QXmppByteStreamIq::StreamHost socksProxy;
- };
- QXmppTransferJobPrivate::QXmppTransferJobPrivate()
- : blockSize(16384),
- client(0),
- done(0),
- error(QXmppTransferJob::NoError),
- hash(QCryptographicHash::Md5),
- iodevice(0),
- method(QXmppTransferJob::NoMethod),
- state(QXmppTransferJob::OfferState),
- ibbSequence(0),
- socksSocket(0)
- {
- }
- QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferJob::Direction direction, QXmppClient *client, QObject *parent)
- : QXmppLoggable(parent),
- d(new QXmppTransferJobPrivate)
- {
- d->client = client;
- d->direction = direction;
- d->jid = jid;
- }
- QXmppTransferJob::~QXmppTransferJob()
- {
- delete d;
- }
- /// Call this method if you wish to abort on ongoing transfer job.
- ///
- void QXmppTransferJob::abort()
- {
- terminate(AbortError);
- }
- /// Call this method if you wish to accept an incoming transfer job.
- ///
- void QXmppTransferJob::accept(const QString &filePath)
- {
- if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice)
- {
- QFile *file = new QFile(filePath, this);
- if (!file->open(QIODevice::WriteOnly))
- {
- warning(QString("Could not write to %1").arg(filePath));
- abort();
- return;
- }
- d->iodevice = file;
- setLocalFileUrl(QUrl::fromLocalFile(filePath));
- setState(QXmppTransferJob::StartState);
- }
- }
- /// Call this method if you wish to accept an incoming transfer job.
- ///
- void QXmppTransferJob::accept(QIODevice *iodevice)
- {
- if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice)
- {
- d->iodevice = iodevice;
- setState(QXmppTransferJob::StartState);
- }
- }
- /// Returns the job's transfer direction.
- ///
- QXmppTransferJob::Direction QXmppTransferJob::direction() const
- {
- return d->direction;
- }
- /// Returns the last error that was encountered.
- ///
- QXmppTransferJob::Error QXmppTransferJob::error() const
- {
- return d->error;
- }
- /// Returns the remote party's JID.
- ///
- QString QXmppTransferJob::jid() const
- {
- return d->jid;
- }
- /// Returns the local file URL.
- ///
- QUrl QXmppTransferJob::localFileUrl() const
- {
- return d->localFileUrl;
- }
- /// Sets the local file URL.
- ///
- /// \note You do not need to call this method if you called accept()
- /// with a file path.
- void QXmppTransferJob::setLocalFileUrl(const QUrl &localFileUrl)
- {
- if (localFileUrl != d->localFileUrl) {
- d->localFileUrl = localFileUrl;
- emit localFileUrlChanged(localFileUrl);
- }
- }
- /// Returns meta-data about the file being transferred.
- ///
- QXmppTransferFileInfo QXmppTransferJob::fileInfo() const
- {
- return d->fileInfo;
- }
- /// \cond
- QDateTime QXmppTransferJob::fileDate() const
- {
- return d->fileInfo.date();
- }
- QByteArray QXmppTransferJob::fileHash() const
- {
- return d->fileInfo.hash();
- }
- QString QXmppTransferJob::fileName() const
- {
- return d->fileInfo.name();
- }
- qint64 QXmppTransferJob::fileSize() const
- {
- return d->fileInfo.size();
- }
- /// \endcond
- /// Returns the job's transfer method.
- ///
- QXmppTransferJob::Method QXmppTransferJob::method() const
- {
- return d->method;
- }
- /// Returns the job's session identifier.
- ///
- QString QXmppTransferJob::sid() const
- {
- return d->sid;
- }
- /// Returns the job's transfer speed in bytes per second.
- ///
- /// If the transfer has not started yet or is already finished, returns 0.
- ///
- qint64 QXmppTransferJob::speed() const
- {
- qint64 elapsed = d->transferStart.elapsed();
- if (d->state != QXmppTransferJob::TransferState || !elapsed)
- return 0;
- return (d->done * 1000.0) / elapsed;
- }
- /// Returns the job's state.
- ///
- QXmppTransferJob::State QXmppTransferJob::state() const
- {
- return d->state;
- }
- void QXmppTransferJob::setState(QXmppTransferJob::State state)
- {
- if (d->state != state)
- {
- d->state = state;
- if (d->state == QXmppTransferJob::TransferState)
- d->transferStart.start();
- emit stateChanged(d->state);
- }
- }
- void QXmppTransferJob::_q_terminated()
- {
- emit stateChanged(d->state);
- if (d->error != NoError)
- emit error(d->error);
- emit finished();
- }
- void QXmppTransferJob::terminate(QXmppTransferJob::Error cause)
- {
- if (d->state == FinishedState)
- return;
- // change state
- d->error = cause;
- d->state = FinishedState;
- // close IO device
- if (d->iodevice)
- d->iodevice->close();
- // close socket
- if (d->socksSocket)
- {
- d->socksSocket->flush();
- d->socksSocket->close();
- }
- // emit signals later
- QTimer::singleShot(0, this, SLOT(_q_terminated()));
- }
- /// \cond
- QXmppTransferIncomingJob::QXmppTransferIncomingJob(const QString& jid, QXmppClient* client, QObject* parent)
- : QXmppTransferJob(jid, IncomingDirection, client, parent)
- , m_candidateClient(0)
- , m_candidateTimer(0)
- {
- }
- void QXmppTransferIncomingJob::checkData()
- {
- if ((d->fileInfo.size() && d->done != d->fileInfo.size()) ||
- (!d->fileInfo.hash().isEmpty() && d->hash.result() != d->fileInfo.hash()))
- terminate(QXmppTransferJob::FileCorruptError);
- else
- terminate(QXmppTransferJob::NoError);
- }
- void QXmppTransferIncomingJob::connectToNextHost()
- {
- bool check;
- Q_UNUSED(check);
- if (m_streamCandidates.isEmpty()) {
- // could not connect to any stream host
- QXmppByteStreamIq response;
- response.setId(m_streamOfferId);
- response.setTo(m_streamOfferFrom);
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
- error.setCode(404);
- response.setType(QXmppIq::Error);
- response.setError(error);
- d->client->sendPacket(response);
- terminate(QXmppTransferJob::ProtocolError);
- return;
- }
- // try next host
- m_candidateHost = m_streamCandidates.takeFirst();
- info(QString("Connecting to streamhost: %1 (%2 %3)").arg(
- m_candidateHost.jid(),
- m_candidateHost.host(),
- QString::number(m_candidateHost.port())));
- const QString hostName = streamHash(d->sid,
- d->jid,
- d->client->configuration().jid());
- // try to connect to stream host
- m_candidateClient = new QXmppSocksClient(m_candidateHost.host(), m_candidateHost.port(), this);
- m_candidateTimer = new QTimer(this);
- check = connect(m_candidateClient, SIGNAL(disconnected()),
- this, SLOT(_q_candidateDisconnected()));
- Q_ASSERT(check);
- check = connect(m_candidateClient, SIGNAL(ready()),
- this, SLOT(_q_candidateReady()));
- Q_ASSERT(check);
- check = connect(m_candidateTimer, SIGNAL(timeout()),
- this, SLOT(_q_candidateDisconnected()));
- Q_ASSERT(check);
- m_candidateTimer->setSingleShot(true);
- m_candidateTimer->start(socksTimeout);
- m_candidateClient->connectToHost(hostName, 0);
- }
- void QXmppTransferIncomingJob::connectToHosts(const QXmppByteStreamIq &iq)
- {
- bool check;
- Q_UNUSED(check);
- m_streamCandidates = iq.streamHosts();
- m_streamOfferId = iq.id();
- m_streamOfferFrom = iq.from();
- connectToNextHost();
- }
- bool QXmppTransferIncomingJob::writeData(const QByteArray &data)
- {
- const qint64 written = d->iodevice->write(data);
- if (written < 0)
- return false;
- d->done += written;
- if (!d->fileInfo.hash().isEmpty())
- d->hash.addData(data);
- progress(d->done, d->fileInfo.size());
- return true;
- }
- void QXmppTransferIncomingJob::_q_candidateReady()
- {
- bool check;
- Q_UNUSED(check);
- if (!m_candidateClient)
- return;
- info(QString("Connected to streamhost: %1 (%2 %3)").arg(
- m_candidateHost.jid(),
- m_candidateHost.host(),
- QString::number(m_candidateHost.port())));
- setState(QXmppTransferJob::TransferState);
- d->socksSocket = m_candidateClient;
- m_candidateClient = 0;
- m_candidateTimer->deleteLater();
- m_candidateTimer = 0;
- check = connect(d->socksSocket, SIGNAL(readyRead()),
- this, SLOT(_q_receiveData()));
- Q_ASSERT(check);
- check = connect(d->socksSocket, SIGNAL(disconnected()),
- this, SLOT(_q_disconnected()));
- Q_ASSERT(check);
- QXmppByteStreamIq ackIq;
- ackIq.setId(m_streamOfferId);
- ackIq.setTo(m_streamOfferFrom);
- ackIq.setType(QXmppIq::Result);
- ackIq.setSid(d->sid);
- ackIq.setStreamHostUsed(m_candidateHost.jid());
- d->client->sendPacket(ackIq);
- }
- void QXmppTransferIncomingJob::_q_candidateDisconnected()
- {
- if (!m_candidateClient)
- return;
- warning(QString("Failed to connect to streamhost: %1 (%2 %3)").arg(
- m_candidateHost.jid(),
- m_candidateHost.host(),
- QString::number(m_candidateHost.port())));
- m_candidateClient->deleteLater();
- m_candidateClient = 0;
- m_candidateTimer->deleteLater();
- m_candidateTimer = 0;
- // try next host
- connectToNextHost();
- }
- void QXmppTransferIncomingJob::_q_disconnected()
- {
- if (d->state == QXmppTransferJob::FinishedState)
- return;
- checkData();
- }
- void QXmppTransferIncomingJob::_q_receiveData()
- {
- if (d->state != QXmppTransferJob::TransferState)
- return;
- // receive data block
- if (d->direction == QXmppTransferJob::IncomingDirection)
- {
- writeData(d->socksSocket->readAll());
- // if we have received all the data, stop here
- if (fileSize() && d->done >= fileSize())
- checkData();
- }
- }
- QXmppTransferOutgoingJob::QXmppTransferOutgoingJob(const QString& jid, QXmppClient* client, QObject* parent)
- : QXmppTransferJob(jid, OutgoingDirection, client, parent)
- {
- }
- void QXmppTransferOutgoingJob::connectToProxy()
- {
- bool check;
- Q_UNUSED(check);
- info(QString("Connecting to proxy: %1 (%2 %3)").arg(
- d->socksProxy.jid(),
- d->socksProxy.host(),
- QString::number(d->socksProxy.port())));
- const QString hostName = streamHash(d->sid,
- d->client->configuration().jid(),
- d->jid);
- QXmppSocksClient *socksClient = new QXmppSocksClient(d->socksProxy.host(), d->socksProxy.port(), this);
- check = connect(socksClient, SIGNAL(disconnected()),
- this, SLOT(_q_disconnected()));
- Q_ASSERT(check);
- check = connect(socksClient, SIGNAL(ready()),
- this, SLOT(_q_proxyReady()));
- Q_ASSERT(check);
- d->socksSocket = socksClient;
- socksClient->connectToHost(hostName, 0);
- }
- void QXmppTransferOutgoingJob::startSending()
- {
- bool check;
- Q_UNUSED(check);
- setState(QXmppTransferJob::TransferState);
- check = connect(d->socksSocket, SIGNAL(bytesWritten(qint64)),
- this, SLOT(_q_sendData()));
- Q_ASSERT(check);
- check = connect(d->iodevice, SIGNAL(readyRead()),
- this, SLOT(_q_sendData()));
- Q_ASSERT(check);
- _q_sendData();
- }
- void QXmppTransferOutgoingJob::_q_disconnected()
- {
- if (d->state == QXmppTransferJob::FinishedState)
- return;
- if (fileSize() && d->done != fileSize())
- terminate(QXmppTransferJob::ProtocolError);
- else
- terminate(QXmppTransferJob::NoError);
- }
- void QXmppTransferOutgoingJob::_q_proxyReady()
- {
- // activate stream
- QXmppByteStreamIq streamIq;
- streamIq.setType(QXmppIq::Set);
- streamIq.setFrom(d->client->configuration().jid());
- streamIq.setTo(d->socksProxy.jid());
- streamIq.setSid(d->sid);
- streamIq.setActivate(d->jid);
- d->requestId = streamIq.id();
- d->client->sendPacket(streamIq);
- }
- void QXmppTransferOutgoingJob::_q_sendData()
- {
- if (d->state != QXmppTransferJob::TransferState)
- return;
- // don't saturate the outgoing socket
- if (d->socksSocket->bytesToWrite() > 2 * d->blockSize)
- return;
- // check whether we have written the whole file
- if (d->fileInfo.size() && d->done >= d->fileInfo.size())
- {
- if (!d->socksSocket->bytesToWrite())
- terminate(QXmppTransferJob::NoError);
- return;
- }
- char *buffer = new char[d->blockSize];
- qint64 length = d->iodevice->read(buffer, d->blockSize);
- if (length < 0)
- {
- delete [] buffer;
- terminate(QXmppTransferJob::FileAccessError);
- return;
- }
- if (length > 0)
- {
- d->socksSocket->write(buffer, length);
- delete [] buffer;
- d->done += length;
- emit progress(d->done, fileSize());
- }
- }
- /// \endcond
- class QXmppTransferManagerPrivate
- {
- public:
- QXmppTransferManagerPrivate(QXmppTransferManager *qq);
- QXmppTransferIncomingJob *getIncomingJobByRequestId(const QString &jid, const QString &id);
- QXmppTransferIncomingJob *getIncomingJobBySid(const QString &jid, const QString &sid);
- QXmppTransferOutgoingJob *getOutgoingJobByRequestId(const QString &jid, const QString &id);
- int ibbBlockSize;
- QList<QXmppTransferJob*> jobs;
- QString proxy;
- bool proxyOnly;
- QXmppSocksServer *socksServer;
- QXmppTransferJob::Methods supportedMethods;
- private:
- QXmppTransferJob *getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id);
- QXmppTransferManager *q;
- };
- QXmppTransferManagerPrivate::QXmppTransferManagerPrivate(QXmppTransferManager *qq)
- : ibbBlockSize(4096)
- , proxyOnly(false)
- , socksServer(0)
- , supportedMethods(QXmppTransferJob::AnyMethod)
- , q(qq)
- {
- }
- QXmppTransferJob* QXmppTransferManagerPrivate::getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id)
- {
- foreach (QXmppTransferJob *job, jobs)
- if (job->d->direction == direction &&
- job->d->jid == jid &&
- job->d->requestId == id)
- return job;
- return 0;
- }
- QXmppTransferIncomingJob *QXmppTransferManagerPrivate::getIncomingJobByRequestId(const QString &jid, const QString &id)
- {
- return static_cast<QXmppTransferIncomingJob*>(getJobByRequestId(QXmppTransferJob::IncomingDirection, jid, id));
- }
- QXmppTransferIncomingJob* QXmppTransferManagerPrivate::getIncomingJobBySid(const QString &jid, const QString &sid)
- {
- foreach (QXmppTransferJob *job, jobs)
- if (job->d->direction == QXmppTransferJob::IncomingDirection &&
- job->d->jid == jid &&
- job->d->sid == sid)
- return static_cast<QXmppTransferIncomingJob*>(job);
- return 0;
- }
- QXmppTransferOutgoingJob *QXmppTransferManagerPrivate::getOutgoingJobByRequestId(const QString &jid, const QString &id)
- {
- return static_cast<QXmppTransferOutgoingJob*>(getJobByRequestId(QXmppTransferJob::OutgoingDirection, jid, id));
- }
- /// Constructs a QXmppTransferManager to handle incoming and outgoing
- /// file transfers.
- QXmppTransferManager::QXmppTransferManager()
- {
- bool check;
- Q_UNUSED(check);
- d = new QXmppTransferManagerPrivate(this);
- // start SOCKS server
- d->socksServer = new QXmppSocksServer(this);
- check = connect(d->socksServer, SIGNAL(newConnection(QTcpSocket*,QString,quint16)),
- this, SLOT(_q_socksServerConnected(QTcpSocket*,QString,quint16)));
- Q_ASSERT(check);
- if (!d->socksServer->listen()) {
- qWarning("QXmppSocksServer could not start listening");
- }
- }
- QXmppTransferManager::~QXmppTransferManager()
- {
- delete d;
- }
- void QXmppTransferManager::byteStreamIqReceived(const QXmppByteStreamIq &iq)
- {
- // handle IQ from proxy
- foreach (QXmppTransferJob *job, d->jobs)
- {
- if (job->d->socksProxy.jid() == iq.from() && job->d->requestId == iq.id())
- {
- if (iq.type() == QXmppIq::Result && iq.streamHosts().size() > 0)
- {
- job->d->socksProxy = iq.streamHosts().first();
- socksServerSendOffer(job);
- return;
- }
- }
- }
- if (iq.type() == QXmppIq::Result)
- byteStreamResultReceived(iq);
- else if (iq.type() == QXmppIq::Set)
- byteStreamSetReceived(iq);
- }
- /// Handle a response to a bystream set, i.e. after we informed the remote party
- /// that we connected to a stream host.
- void QXmppTransferManager::byteStreamResponseReceived(const QXmppIq &iq)
- {
- QXmppTransferJob *job = d->getIncomingJobByRequestId(iq.from(), iq.id());
- if (!job ||
- job->method() != QXmppTransferJob::SocksMethod ||
- job->state() != QXmppTransferJob::StartState)
- return;
- if (iq.type() == QXmppIq::Error)
- job->terminate(QXmppTransferJob::ProtocolError);
- }
- /// Handle a bytestream result, i.e. after the remote party has connected to
- /// a stream host.
- void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq)
- {
- bool check;
- Q_UNUSED(check);
- QXmppTransferOutgoingJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
- if (!job ||
- job->method() != QXmppTransferJob::SocksMethod ||
- job->state() != QXmppTransferJob::StartState)
- return;
- // check the stream host
- if (iq.streamHostUsed() == job->d->socksProxy.jid())
- {
- job->connectToProxy();
- return;
- }
- // direction connection, start sending data
- if (!job->d->socksSocket)
- {
- warning("Client says they connected to our SOCKS server, but they did not");
- job->terminate(QXmppTransferJob::ProtocolError);
- return;
- }
- check = connect(job->d->socksSocket, SIGNAL(disconnected()),
- job, SLOT(_q_disconnected()));
- Q_ASSERT(check);
- job->startSending();
- }
- /// Handle a bytestream set, i.e. an invitation from the remote party to connect
- /// to a stream host.
- void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq)
- {
- bool check;
- Q_UNUSED(check);
- QXmppIq response;
- response.setId(iq.id());
- response.setTo(iq.from());
- QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
- if (!job ||
- job->method() != QXmppTransferJob::SocksMethod ||
- job->state() != QXmppTransferJob::StartState)
- {
- // the stream is unknown
- QXmppStanza::Error error(QXmppStanza::Error::Auth, QXmppStanza::Error::NotAcceptable);
- error.setCode(406);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- job->connectToHosts(iq);
- }
- /// \cond
- QStringList QXmppTransferManager::discoveryFeatures() const
- {
- return QStringList()
- << ns_ibb // XEP-0047: In-Band Bytestreams
- << ns_bytestreams // XEP-0065: SOCKS5 Bytestreams
- << ns_stream_initiation // XEP-0095: Stream Initiation
- << ns_stream_initiation_file_transfer; // XEP-0096: SI File Transfer
- }
- bool QXmppTransferManager::handleStanza(const QDomElement &element)
- {
- if (element.tagName() != "iq")
- return false;
- // XEP-0047 In-Band Bytestreams
- if(QXmppIbbCloseIq::isIbbCloseIq(element))
- {
- QXmppIbbCloseIq ibbCloseIq;
- ibbCloseIq.parse(element);
- ibbCloseIqReceived(ibbCloseIq);
- return true;
- }
- else if(QXmppIbbDataIq::isIbbDataIq(element))
- {
- QXmppIbbDataIq ibbDataIq;
- ibbDataIq.parse(element);
- ibbDataIqReceived(ibbDataIq);
- return true;
- }
- else if(QXmppIbbOpenIq::isIbbOpenIq(element))
- {
- QXmppIbbOpenIq ibbOpenIq;
- ibbOpenIq.parse(element);
- ibbOpenIqReceived(ibbOpenIq);
- return true;
- }
- // XEP-0065: SOCKS5 Bytestreams
- else if(QXmppByteStreamIq::isByteStreamIq(element))
- {
- QXmppByteStreamIq byteStreamIq;
- byteStreamIq.parse(element);
- byteStreamIqReceived(byteStreamIq);
- return true;
- }
- // XEP-0095: Stream Initiation
- else if(QXmppStreamInitiationIq::isStreamInitiationIq(element))
- {
- QXmppStreamInitiationIq siIq;
- siIq.parse(element);
- streamInitiationIqReceived(siIq);
- return true;
- }
- return false;
- }
- void QXmppTransferManager::setClient(QXmppClient *client)
- {
- bool check;
- Q_UNUSED(check);
- QXmppClientExtension::setClient(client);
- // XEP-0047: In-Band Bytestreams
- check = connect(client, SIGNAL(iqReceived(QXmppIq)),
- this, SLOT(_q_iqReceived(QXmppIq)));
- Q_ASSERT(check);
- }
- /// \endcond
- void QXmppTransferManager::ibbCloseIqReceived(const QXmppIbbCloseIq &iq)
- {
- QXmppIq response;
- response.setTo(iq.from());
- response.setId(iq.id());
- QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
- if (!job ||
- job->method() != QXmppTransferJob::InBandMethod)
- {
- // the job is unknown, cancel it
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- // acknowledge the packet
- response.setType(QXmppIq::Result);
- client()->sendPacket(response);
- // check received data
- job->checkData();
- }
- void QXmppTransferManager::ibbDataIqReceived(const QXmppIbbDataIq &iq)
- {
- QXmppIq response;
- response.setTo(iq.from());
- response.setId(iq.id());
- QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
- if (!job ||
- job->method() != QXmppTransferJob::InBandMethod ||
- job->state() != QXmppTransferJob::TransferState)
- {
- // the job is unknown, cancel it
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- if (iq.sequence() != job->d->ibbSequence)
- {
- // the packet is out of sequence
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::UnexpectedRequest);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- // write data
- job->writeData(iq.payload());
- job->d->ibbSequence++;
- // acknowledge the packet
- response.setType(QXmppIq::Result);
- client()->sendPacket(response);
- }
- void QXmppTransferManager::ibbOpenIqReceived(const QXmppIbbOpenIq &iq)
- {
- QXmppIq response;
- response.setTo(iq.from());
- response.setId(iq.id());
- QXmppTransferJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
- if (!job ||
- job->method() != QXmppTransferJob::InBandMethod)
- {
- // the job is unknown, cancel it
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- if (iq.blockSize() > d->ibbBlockSize)
- {
- // we prefer a smaller block size
- QXmppStanza::Error error(QXmppStanza::Error::Modify, QXmppStanza::Error::ResourceConstraint);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- job->d->blockSize = iq.blockSize();
- job->setState(QXmppTransferJob::TransferState);
- // accept transfer
- response.setType(QXmppIq::Result);
- client()->sendPacket(response);
- }
- void QXmppTransferManager::ibbResponseReceived(const QXmppIq &iq)
- {
- QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
- if (!job ||
- job->method() != QXmppTransferJob::InBandMethod ||
- job->state() == QXmppTransferJob::FinishedState)
- return;
- // if the IO device is closed, do nothing
- if (!job->d->iodevice->isOpen())
- return;
- if (iq.type() == QXmppIq::Result)
- {
- const QByteArray buffer = job->d->iodevice->read(job->d->blockSize);
- job->setState(QXmppTransferJob::TransferState);
- if (buffer.size())
- {
- // send next data block
- QXmppIbbDataIq dataIq;
- dataIq.setTo(job->d->jid);
- dataIq.setSid(job->d->sid);
- dataIq.setSequence(job->d->ibbSequence++);
- dataIq.setPayload(buffer);
- job->d->requestId = dataIq.id();
- client()->sendPacket(dataIq);
- job->d->done += buffer.size();
- job->progress(job->d->done, job->fileSize());
- } else {
- // close the bytestream
- QXmppIbbCloseIq closeIq;
- closeIq.setTo(job->d->jid);
- closeIq.setSid(job->d->sid);
- job->d->requestId = closeIq.id();
- client()->sendPacket(closeIq);
- job->terminate(QXmppTransferJob::NoError);
- }
- }
- else if (iq.type() == QXmppIq::Error)
- {
- // close the bytestream
- QXmppIbbCloseIq closeIq;
- closeIq.setTo(job->d->jid);
- closeIq.setSid(job->d->sid);
- job->d->requestId = closeIq.id();
- client()->sendPacket(closeIq);
- job->terminate(QXmppTransferJob::ProtocolError);
- }
- }
- void QXmppTransferManager::_q_iqReceived(const QXmppIq &iq)
- {
- bool check;
- Q_UNUSED(check);
- foreach (QXmppTransferJob *ptr, d->jobs)
- {
- // handle IQ from proxy
- if (ptr->direction() == QXmppTransferJob::OutgoingDirection && ptr->d->socksProxy.jid() == iq.from() && ptr->d->requestId == iq.id())
- {
- QXmppTransferOutgoingJob *job = static_cast<QXmppTransferOutgoingJob*>(ptr);
- if (job->d->socksSocket)
- {
- // proxy connection activation result
- if (iq.type() == QXmppIq::Result)
- {
- // proxy stream activated, start sending data
- job->startSending();
- } else if (iq.type() == QXmppIq::Error) {
- // proxy stream not activated, terminate
- warning("Could not activate SOCKS5 proxy bytestream");
- job->terminate(QXmppTransferJob::ProtocolError);
- }
- } else {
- // we could not get host/port from proxy, procede without a proxy
- if (iq.type() == QXmppIq::Error)
- socksServerSendOffer(job);
- }
- return;
- }
- // handle IQ from peer
- else if (ptr->d->jid == iq.from() && ptr->d->requestId == iq.id())
- {
- QXmppTransferJob *job = ptr;
- if (job->direction() == QXmppTransferJob::OutgoingDirection &&
- job->method() == QXmppTransferJob::InBandMethod)
- {
- ibbResponseReceived(iq);
- return;
- }
- else if (job->direction() == QXmppTransferJob::IncomingDirection &&
- job->method() == QXmppTransferJob::SocksMethod)
- {
- byteStreamResponseReceived(iq);
- return;
- }
- else if (job->direction() == QXmppTransferJob::OutgoingDirection &&
- iq.type() == QXmppIq::Error)
- {
- // remote party cancelled stream initiation
- job->terminate(QXmppTransferJob::AbortError);
- return;
- }
- }
- }
- }
- void QXmppTransferManager::_q_jobDestroyed(QObject *object)
- {
- d->jobs.removeAll(static_cast<QXmppTransferJob*>(object));
- }
- void QXmppTransferManager::_q_jobError(QXmppTransferJob::Error error)
- {
- QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
- if (!job || !d->jobs.contains(job))
- return;
- if (job->direction() == QXmppTransferJob::OutgoingDirection &&
- job->method() == QXmppTransferJob::InBandMethod &&
- error == QXmppTransferJob::AbortError)
- {
- // close the bytestream
- QXmppIbbCloseIq closeIq;
- closeIq.setTo(job->d->jid);
- closeIq.setSid(job->d->sid);
- job->d->requestId = closeIq.id();
- client()->sendPacket(closeIq);
- }
- }
- void QXmppTransferManager::_q_jobFinished()
- {
- QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
- if (!job || !d->jobs.contains(job))
- return;
- emit jobFinished(job);
- }
- void QXmppTransferManager::_q_jobStateChanged(QXmppTransferJob::State state)
- {
- bool check;
- Q_UNUSED(check);
- QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
- if (!job || !d->jobs.contains(job))
- return;
- if (job->direction() != QXmppTransferJob::IncomingDirection)
- return;
- // disconnect from the signal
- disconnect(job, SIGNAL(stateChanged(QXmppTransferJob::State)),
- this, SLOT(_q_jobStateChanged(QXmppTransferJob::State)));
- // the job was refused by the local party
- if (state != QXmppTransferJob::StartState || !job->d->iodevice || !job->d->iodevice->isWritable())
- {
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden);
- error.setCode(403);
- QXmppIq response;
- response.setTo(job->jid());
- response.setId(job->d->offerId);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- job->terminate(QXmppTransferJob::AbortError);
- return;
- }
- // the job was accepted by the local party
- check = connect(job, SIGNAL(error(QXmppTransferJob::Error)),
- this, SLOT(_q_jobError(QXmppTransferJob::Error)));
- Q_ASSERT(check);
- QXmppDataForm form;
- form.setType(QXmppDataForm::Submit);
- QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField);
- methodField.setKey("stream-method");
- if (job->method() == QXmppTransferJob::InBandMethod)
- methodField.setValue(ns_ibb);
- else if (job->method() == QXmppTransferJob::SocksMethod)
- methodField.setValue(ns_bytestreams);
- form.setFields(QList<QXmppDataForm::Field>() << methodField);
- QXmppStreamInitiationIq response;
- response.setTo(job->jid());
- response.setId(job->d->offerId);
- response.setType(QXmppIq::Result);
- response.setProfile(QXmppStreamInitiationIq::FileTransfer);
- response.setFeatureForm(form);
- client()->sendPacket(response);
- // notify user
- emit jobStarted(job);
- }
- /// Send file to a remote party.
- ///
- /// The remote party will be given the choice to accept or refuse the transfer.
- ///
- QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QString &filePath, const QString &description)
- {
- if (jid.isEmpty()) {
- warning("Refusing to send file to an empty jid");
- return 0;
- }
- QFileInfo info(filePath);
- QXmppTransferFileInfo fileInfo;
- fileInfo.setDate(info.lastModified());
- fileInfo.setName(info.fileName());
- fileInfo.setSize(info.size());
- fileInfo.setDescription(description);
- // open file
- QIODevice *device = new QFile(filePath);
- if (!device->open(QIODevice::ReadOnly))
- {
- warning(QString("Could not read from %1").arg(filePath));
- delete device;
- device = 0;
- }
- // hash file
- if (device && !device->isSequential())
- {
- QCryptographicHash hash(QCryptographicHash::Md5);
- QByteArray buffer;
- while (device->bytesAvailable())
- {
- buffer = device->read(16384);
- hash.addData(buffer);
- }
- device->reset();
- fileInfo.setHash(hash.result());
- }
- // create job
- QXmppTransferJob *job = sendFile(jid, device, fileInfo);
- job->setLocalFileUrl(filePath);
- return job;
- }
- /// Send file to a remote party.
- ///
- /// The remote party will be given the choice to accept or refuse the transfer.
- ///
- QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, QIODevice *device, const QXmppTransferFileInfo &fileInfo, const QString &sid)
- {
- bool check;
- Q_UNUSED(check);
- if (jid.isEmpty()) {
- warning("Refusing to send file to an empty jid");
- return 0;
- }
- QXmppTransferOutgoingJob *job = new QXmppTransferOutgoingJob(jid, client(), this);
- if (sid.isEmpty())
- job->d->sid = QXmppUtils::generateStanzaHash();
- else
- job->d->sid = sid;
- job->d->fileInfo = fileInfo;
- job->d->iodevice = device;
- if (device)
- device->setParent(job);
- // check file is open
- if (!device || !device->isReadable())
- {
- job->terminate(QXmppTransferJob::FileAccessError);
- return job;
- }
- // check we support some methods
- if (!d->supportedMethods)
- {
- job->terminate(QXmppTransferJob::ProtocolError);
- return job;
- }
- // collect supported stream methods
- QXmppDataForm form;
- form.setType(QXmppDataForm::Form);
- QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField);
- methodField.setKey("stream-method");
- if (d->supportedMethods & QXmppTransferJob::InBandMethod)
- methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_ibb)));
- if (d->supportedMethods & QXmppTransferJob::SocksMethod)
- methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_bytestreams)));
- form.setFields(QList<QXmppDataForm::Field>() << methodField);
- // start job
- d->jobs.append(job);
- check = connect(job, SIGNAL(destroyed(QObject*)),
- this, SLOT(_q_jobDestroyed(QObject*)));
- Q_ASSERT(check);
- check = connect(job, SIGNAL(error(QXmppTransferJob::Error)),
- this, SLOT(_q_jobError(QXmppTransferJob::Error)));
- Q_ASSERT(check);
- check = connect(job, SIGNAL(finished()),
- this, SLOT(_q_jobFinished()));
- Q_ASSERT(check);
- QXmppStreamInitiationIq request;
- request.setType(QXmppIq::Set);
- request.setTo(jid);
- request.setProfile(QXmppStreamInitiationIq::FileTransfer);
- request.setFileInfo(job->d->fileInfo);
- request.setFeatureForm(form);
- request.setSiId(job->d->sid);
- job->d->requestId = request.id();
- client()->sendPacket(request);
- // notify user
- emit jobStarted(job);
- return job;
- }
- void QXmppTransferManager::_q_socksServerConnected(QTcpSocket *socket, const QString &hostName, quint16 port)
- {
- const QString ownJid = client()->configuration().jid();
- foreach (QXmppTransferJob *job, d->jobs)
- {
- if (hostName == streamHash(job->d->sid, ownJid, job->jid()) && port == 0)
- {
- job->d->socksSocket = socket;
- return;
- }
- }
- warning("QXmppSocksServer got a connection for a unknown stream");
- socket->close();
- }
- void QXmppTransferManager::socksServerSendOffer(QXmppTransferJob *job)
- {
- const QString ownJid = client()->configuration().jid();
- QList<QXmppByteStreamIq::StreamHost> streamHosts;
- // discover local IPs
- if (!d->proxyOnly) {
- foreach (const QHostAddress &address, QXmppIceComponent::discoverAddresses()) {
- QXmppByteStreamIq::StreamHost streamHost;
- streamHost.setJid(ownJid);
- streamHost.setHost(address.toString());
- streamHost.setPort(d->socksServer->serverPort());
- streamHosts.append(streamHost);
- }
- }
- // add proxy
- if (!job->d->socksProxy.jid().isEmpty())
- streamHosts.append(job->d->socksProxy);
- // check we have some stream hosts
- if (!streamHosts.size())
- {
- warning("Could not determine local stream hosts");
- job->terminate(QXmppTransferJob::ProtocolError);
- return;
- }
- // send offer
- QXmppByteStreamIq streamIq;
- streamIq.setType(QXmppIq::Set);
- streamIq.setTo(job->d->jid);
- streamIq.setSid(job->d->sid);
- streamIq.setStreamHosts(streamHosts);
- job->d->requestId = streamIq.id();
- client()->sendPacket(streamIq);
- }
- void QXmppTransferManager::streamInitiationIqReceived(const QXmppStreamInitiationIq &iq)
- {
- if (iq.type() == QXmppIq::Result)
- streamInitiationResultReceived(iq);
- else if (iq.type() == QXmppIq::Set)
- streamInitiationSetReceived(iq);
- }
- // The remote party has accepted an outgoing transfer.
- void QXmppTransferManager::streamInitiationResultReceived(const QXmppStreamInitiationIq &iq)
- {
- QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
- if (!job ||
- job->state() != QXmppTransferJob::OfferState)
- return;
- foreach (const QXmppDataForm::Field &field, iq.featureForm().fields()) {
- if (field.key() == "stream-method") {
- if ((field.value().toString() == ns_ibb) &&
- (d->supportedMethods & QXmppTransferJob::InBandMethod))
- job->d->method = QXmppTransferJob::InBandMethod;
- else if ((field.value().toString() == ns_bytestreams) &&
- (d->supportedMethods & QXmppTransferJob::SocksMethod))
- job->d->method = QXmppTransferJob::SocksMethod;
- }
- }
- // remote party accepted stream initiation
- job->setState(QXmppTransferJob::StartState);
- if (job->method() == QXmppTransferJob::InBandMethod)
- {
- // lower block size for IBB
- job->d->blockSize = d->ibbBlockSize;
- QXmppIbbOpenIq openIq;
- openIq.setTo(job->d->jid);
- openIq.setSid(job->d->sid);
- openIq.setBlockSize(job->d->blockSize);
- job->d->requestId = openIq.id();
- client()->sendPacket(openIq);
- } else if (job->method() == QXmppTransferJob::SocksMethod) {
- if (!d->proxy.isEmpty())
- {
- job->d->socksProxy.setJid(d->proxy);
- // query proxy
- QXmppByteStreamIq streamIq;
- streamIq.setType(QXmppIq::Get);
- streamIq.setTo(job->d->socksProxy.jid());
- streamIq.setSid(job->d->sid);
- job->d->requestId = streamIq.id();
- client()->sendPacket(streamIq);
- } else {
- socksServerSendOffer(job);
- }
- } else {
- warning("QXmppTransferManager received an unsupported method");
- job->terminate(QXmppTransferJob::ProtocolError);
- }
- }
- void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiationIq &iq)
- {
- bool check;
- Q_UNUSED(check);
- QXmppIq response;
- response.setTo(iq.from());
- response.setId(iq.id());
- // check we support the profile
- if (iq.profile() != QXmppStreamInitiationIq::FileTransfer)
- {
- // FIXME : we should add:
- // <bad-profile xmlns='http://jabber.org/protocol/si'/>
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest);
- error.setCode(400);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- // check there is a receiver connected to the fileReceived() signal
- if (!receivers(SIGNAL(fileReceived(QXmppTransferJob*))))
- {
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden);
- error.setCode(403);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- return;
- }
- // check the stream type
- QXmppTransferIncomingJob *job = new QXmppTransferIncomingJob(iq.from(), client(), this);
- int offeredMethods = QXmppTransferJob::NoMethod;
- job->d->offerId = iq.id();
- job->d->sid = iq.siId();
- job->d->mimeType = iq.mimeType();
- job->d->fileInfo = iq.fileInfo();
- foreach (const QXmppDataForm::Field &field, iq.featureForm().fields()) {
- if (field.key() == "stream-method") {
- QPair<QString, QString> option;
- foreach (option, field.options()) {
- if (option.second == ns_ibb)
- offeredMethods = offeredMethods | QXmppTransferJob::InBandMethod;
- else if (option.second == ns_bytestreams)
- offeredMethods = offeredMethods | QXmppTransferJob::SocksMethod;
- }
- }
- }
- // select a method supported by both parties
- int sharedMethods = (offeredMethods & d->supportedMethods);
- if (sharedMethods & QXmppTransferJob::SocksMethod)
- job->d->method = QXmppTransferJob::SocksMethod;
- else if (sharedMethods & QXmppTransferJob::InBandMethod)
- job->d->method = QXmppTransferJob::InBandMethod;
- else
- {
- // FIXME : we should add:
- // <no-valid-streams xmlns='http://jabber.org/protocol/si'/>
- QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest);
- error.setCode(400);
- response.setType(QXmppIq::Error);
- response.setError(error);
- client()->sendPacket(response);
- delete job;
- return;
- }
- // register job
- d->jobs.append(job);
- check = connect(job, SIGNAL(destroyed(QObject*)),
- this, SLOT(_q_jobDestroyed(QObject*)));
- Q_ASSERT(check);
- check = connect(job, SIGNAL(finished()),
- this, SLOT(_q_jobFinished()));
- Q_ASSERT(check);
- check = connect(job, SIGNAL(stateChanged(QXmppTransferJob::State)),
- this, SLOT(_q_jobStateChanged(QXmppTransferJob::State)));
- Q_ASSERT(check);
- // allow user to accept or decline the job
- emit fileReceived(job);
- }
- /// Return the JID of the bytestream proxy to use for
- /// outgoing transfers.
- ///
- QString QXmppTransferManager::proxy() const
- {
- return d->proxy;
- }
- /// Set the JID of the SOCKS5 bytestream proxy to use for
- /// outgoing transfers.
- ///
- /// If you set a proxy, when you send a file the proxy will
- /// be offered to the recipient in addition to your own IP
- /// addresses.
- ///
- void QXmppTransferManager::setProxy(const QString &proxyJid)
- {
- d->proxy = proxyJid;
- }
- /// Return whether the proxy will systematically be used for
- /// outgoing SOCKS5 bytestream transfers.
- ///
- bool QXmppTransferManager::proxyOnly() const
- {
- return d->proxyOnly;
- }
- /// Set whether the proxy should systematically be used for
- /// outgoing SOCKS5 bytestream transfers.
- ///
- /// \note If you set this to true and do not provide a proxy
- /// using setProxy(), your outgoing transfers will fail!
- ///
- void QXmppTransferManager::setProxyOnly(bool proxyOnly)
- {
- d->proxyOnly = proxyOnly;
- }
- /// Return the supported stream methods.
- ///
- /// The methods are a combination of zero or more QXmppTransferJob::Method.
- ///
- QXmppTransferJob::Methods QXmppTransferManager::supportedMethods() const
- {
- return d->supportedMethods;
- }
- /// Set the supported stream methods. This allows you to selectively
- /// enable or disable stream methods (In-Band or SOCKS5 bytestreams).
- ///
- /// The methods argument is a combination of zero or more
- /// QXmppTransferJob::Method.
- ///
- void QXmppTransferManager::setSupportedMethods(QXmppTransferJob::Methods methods)
- {
- d->supportedMethods = methods;
- }
|