aboutsummaryrefslogtreecommitdiff
path: root/source/QXmppTransferManager.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-03-16 12:22:56 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-03-16 12:22:56 +0000
commit4755c74230c37787e113f222e47fb8886c5f6bd7 (patch)
tree174e66e972927ff1f8b97ca2848de27910fada6e /source/QXmppTransferManager.cpp
parent7dd4908d6d0429bd208da03a891ce127096fbf92 (diff)
downloadqxmpp-4755c74230c37787e113f222e47fb8886c5f6bd7.tar.gz
make it possible to send arbitrary QIODevice using QXmppTransferManager
Diffstat (limited to 'source/QXmppTransferManager.cpp')
-rw-r--r--source/QXmppTransferManager.cpp341
1 files changed, 218 insertions, 123 deletions
diff --git a/source/QXmppTransferManager.cpp b/source/QXmppTransferManager.cpp
index c3ab05cd..0142a539 100644
--- a/source/QXmppTransferManager.cpp
+++ b/source/QXmppTransferManager.cpp
@@ -31,6 +31,7 @@
#include "QXmppClient.h"
#include "QXmppConstants.h"
#include "QXmppIbbIq.h"
+#include "QXmppLogger.h"
#include "QXmppSocks.h"
#include "QXmppStreamInitiationIq.h"
#include "QXmppTransferManager.h"
@@ -47,6 +48,58 @@ static QString streamHash(const QString &sid, const QString &initiatorJid, const
return hash.result().toHex();
}
+QXmppTransferFileInfo::QXmppTransferFileInfo()
+ : m_size(0)
+{
+}
+
+QDateTime QXmppTransferFileInfo::date() const
+{
+ return m_date;
+}
+
+void QXmppTransferFileInfo::setDate(const QDateTime &date)
+{
+ m_date = date;
+}
+
+QByteArray QXmppTransferFileInfo::hash() const
+{
+ return m_hash;
+}
+
+void QXmppTransferFileInfo::setHash(const QByteArray &hash)
+{
+ m_hash = hash;
+}
+
+QString QXmppTransferFileInfo::name() const
+{
+ return m_name;
+}
+
+void QXmppTransferFileInfo::setName(const QString &name)
+{
+ m_name = name;
+}
+
+qint64 QXmppTransferFileInfo::size() const
+{
+ return m_size;
+}
+
+void QXmppTransferFileInfo::setSize(qint64 size)
+{
+ m_size = size;
+}
+
+bool QXmppTransferFileInfo::operator==(const QXmppTransferFileInfo &other) const
+{
+ return other.m_size == m_size &&
+ other.m_hash == m_hash &&
+ other.m_name == m_name;
+}
+
QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferJob::Direction direction, QObject *parent)
: QObject(parent),
m_blockSize(16384),
@@ -58,7 +111,6 @@ QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferJob::Directi
m_jid(jid),
m_method(NoMethod),
m_state(OfferState),
- m_fileSize(0),
m_ibbSequence(0),
m_socksSocket(0)
{
@@ -86,8 +138,8 @@ void QXmppTransferJob::accept(QIODevice *iodevice)
void QXmppTransferJob::checkData()
{
- if ((m_fileSize && m_done != m_fileSize) ||
- (!m_fileHash.isEmpty() && m_hash.result() != m_fileHash))
+ if ((m_fileInfo.size() && m_done != m_fileInfo.size()) ||
+ (!m_fileInfo.hash().isEmpty() && m_hash.result() != m_fileInfo.hash()))
terminate(QXmppTransferJob::FileCorruptError);
else
terminate(QXmppTransferJob::NoError);
@@ -133,24 +185,29 @@ QString QXmppTransferJob::jid() const
return m_jid;
}
+QXmppTransferFileInfo QXmppTransferJob::fileInfo() const
+{
+ return m_fileInfo;
+}
+
QDateTime QXmppTransferJob::fileDate() const
{
- return m_fileDate;
+ return m_fileInfo.date();
}
QByteArray QXmppTransferJob::fileHash() const
{
- return m_fileHash;
+ return m_fileInfo.hash();
}
QString QXmppTransferJob::fileName() const
{
- return m_fileName;
+ return m_fileInfo.name();
}
qint64 QXmppTransferJob::fileSize() const
{
- return m_fileSize;
+ return m_fileInfo.size();
}
QXmppTransferJob::Method QXmppTransferJob::method() const
@@ -172,6 +229,71 @@ void QXmppTransferJob::setState(QXmppTransferJob::State state)
}
}
+void QXmppTransferJob::disconnected()
+{
+ if (m_state == QXmppTransferJob::FinishedState)
+ return;
+
+ // terminate transfer
+ if (m_direction == QXmppTransferJob::IncomingDirection)
+ {
+ checkData();
+ } else {
+ if (fileSize() && m_done != fileSize())
+ terminate(QXmppTransferJob::ProtocolError);
+ else
+ terminate(QXmppTransferJob::NoError);
+ }
+}
+
+void QXmppTransferJob::receiveData()
+{
+ if (m_state != QXmppTransferJob::TransferState)
+ return;
+
+ // receive data block
+ if (m_direction == QXmppTransferJob::IncomingDirection)
+ {
+ writeData(m_socksSocket->readAll());
+
+ // if we have received all the data, stop here
+ if (fileSize() && m_done >= fileSize())
+ checkData();
+ }
+}
+
+void QXmppTransferJob::sendData()
+{
+ if (m_state != QXmppTransferJob::TransferState)
+ return;
+
+ // don't saturate the outgoing socket
+ if (m_socksSocket->bytesToWrite() > 2 * m_blockSize)
+ return;
+
+ // check whether we have written the whole file
+ if (m_fileInfo.size() && m_done >= m_fileInfo.size())
+ {
+ if (!m_socksSocket->bytesToWrite())
+ terminate(QXmppTransferJob::NoError);
+ return;
+ }
+
+ char buffer[m_blockSize];
+ qint64 length = m_iodevice->read(buffer, m_blockSize);
+ if (length < 0)
+ {
+ terminate(QXmppTransferJob::FileAccessError);
+ return;
+ }
+ if (length > 0)
+ {
+ m_socksSocket->write(buffer, length);
+ m_done += length;
+ emit progress(m_done, fileSize());
+ }
+}
+
void QXmppTransferJob::slotTerminated()
{
emit stateChanged(m_state);
@@ -210,9 +332,9 @@ bool QXmppTransferJob::writeData(const QByteArray &data)
if (written < 0)
return false;
m_done += written;
- if (!m_fileHash.isEmpty())
+ if (!m_fileInfo.hash().isEmpty())
m_hash.addData(data);
- progress(m_done, m_fileSize);
+ progress(m_done, m_fileInfo.size());
return true;
}
@@ -285,9 +407,11 @@ void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq)
if (iq.streamHostUsed() == job->m_socksProxy.jid())
{
const QXmppByteStreamIq::StreamHost streamHost = job->m_socksProxy;
- qDebug() << "Connecting to proxy" << streamHost.jid();
- qDebug() << " host:" << streamHost.host().toString();
- qDebug() << " port:" << streamHost.port();
+ m_client->logger()->log(QXmppLogger::InformationMessage,
+ QString("Connecting to proxy: %1 (%2:%3)").arg(
+ streamHost.jid(),
+ streamHost.host().toString(),
+ QString::number(streamHost.port())));
// connect to proxy
const QString hostName = streamHash(job->m_sid,
@@ -299,14 +423,17 @@ void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq)
// FIXME : this should probably be made asynchronous as it blocks XMPP packet handling
if (!socksClient->waitForReady(socksTimeout))
{
- qWarning() << "Failed to connect to" << streamHost.host().toString() << streamHost.port() << ":" << socksClient->errorString();
+ m_client->logger()->log(QXmppLogger::WarningMessage,
+ QString("Failed to connect to proxy: %1 (%2:%3)").arg(
+ streamHost.jid(),
+ streamHost.host().toString(),
+ QString::number(streamHost.port())));
delete socksClient;
job->terminate(QXmppTransferJob::ProtocolError);
return;
}
job->m_socksSocket = socksClient;
- connect(job->m_socksSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(socksSocketDataSent()));
- connect(job->m_socksSocket, SIGNAL(disconnected()), this, SLOT(socksSocketDisconnected()));
+ connect(job->m_socksSocket, SIGNAL(disconnected()), job, SLOT(disconnected()));
// activate stream
QXmppByteStreamIq streamIq;
@@ -328,9 +455,10 @@ void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq)
return;
}
job->setState(QXmppTransferJob::TransferState);
- connect(job->m_socksSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(socksSocketDataSent()));
- connect(job->m_socksSocket, SIGNAL(disconnected()), this, SLOT(socksSocketDisconnected()));
- socksServerSendData(job);
+ connect(job->m_socksSocket, SIGNAL(disconnected()), job, SLOT(disconnected()));
+ connect(job->m_socksSocket, SIGNAL(bytesWritten(qint64)), job, SLOT(sendData()));
+ connect(job->m_iodevice, SIGNAL(readyRead()), job, SLOT(sendData()));
+ job->sendData();
}
/// Handle a bytestream set, i.e. an invitation from the remote party to connect
@@ -359,9 +487,11 @@ void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq)
// try connecting to the offered stream hosts
foreach (const QXmppByteStreamIq::StreamHost &streamHost, iq.streamHosts())
{
- qDebug() << "Connecting to streamhost" << streamHost.jid();
- qDebug() << " host:" << streamHost.host().toString();
- qDebug() << " port:" << streamHost.port();
+ m_client->logger()->log(QXmppLogger::InformationMessage,
+ QString("Connecting to streamhost: %1 (%2:%3)").arg(
+ streamHost.jid(),
+ streamHost.host().toString(),
+ QString::number(streamHost.port())));
const QString hostName = streamHash(job->m_sid,
job->m_jid,
@@ -375,8 +505,8 @@ void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq)
{
job->setState(QXmppTransferJob::TransferState);
job->m_socksSocket = socksClient;
- connect(job->m_socksSocket, SIGNAL(readyRead()), this, SLOT(socksSocketDataReceived()));
- connect(job->m_socksSocket, SIGNAL(disconnected()), this, SLOT(socksSocketDisconnected()));
+ connect(job->m_socksSocket, SIGNAL(readyRead()), job, SLOT(receiveData()));
+ connect(job->m_socksSocket, SIGNAL(disconnected()), job, SLOT(disconnected()));
QXmppByteStreamIq ackIq;
ackIq.setId(iq.id());
@@ -387,7 +517,11 @@ void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq)
m_client->sendPacket(ackIq);
return;
} else {
- qWarning() << "Failed to connect to" << streamHost.host().toString() << streamHost.port() << ":" << socksClient->errorString();
+ m_client->logger()->log(QXmppLogger::WarningMessage,
+ QString("Failed to connect to streamhost: %1 (%2:%3)").arg(
+ streamHost.jid(),
+ streamHost.host().toString(),
+ QString::number(streamHost.port())));
delete socksClient;
}
}
@@ -418,14 +552,6 @@ QXmppTransferJob* QXmppTransferManager::getJobBySid(const QString &jid, const QS
return 0;
}
-QXmppTransferJob* QXmppTransferManager::getJobBySocksSocket(QTcpSocket *socksSocket)
-{
- foreach (QXmppTransferJob *job, m_jobs)
- if (job->m_socksSocket == socksSocket)
- return job;
- return 0;
-}
-
void QXmppTransferManager::ibbCloseIqReceived(const QXmppIbbCloseIq &iq)
{
QXmppIq response;
@@ -597,7 +723,9 @@ void QXmppTransferManager::iqReceived(const QXmppIq &iq)
{
// proxy stream activated, start sending data
job->setState(QXmppTransferJob::TransferState);
- socksServerSendData(job);
+ connect(job->m_socksSocket, SIGNAL(bytesWritten(qint64)), job, SLOT(sendData()));
+ connect(job->m_iodevice, SIGNAL(readyRead()), job, SLOT(sendData()));
+ job->sendData();
} else if (iq.type() == QXmppIq::Error) {
// proxy stream not activated, terminate
qWarning("Could not activate SOCKS5 proxy bytestream");
@@ -626,6 +754,11 @@ void QXmppTransferManager::iqReceived(const QXmppIq &iq)
}
}
+void QXmppTransferManager::jobDestroyed(QObject *object)
+{
+ m_jobs.removeAll(static_cast<QXmppTransferJob*>(object));
+}
+
void QXmppTransferManager::jobError(QXmppTransferJob::Error error)
{
QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
@@ -716,40 +849,62 @@ QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QStri
{
QFileInfo info(fileName);
- // create job
- QXmppTransferJob *job = new QXmppTransferJob(jid, QXmppTransferJob::OutgoingDirection, this);
- job->m_sid = generateStanzaHash();
- job->m_fileDate = info.lastModified();
- job->m_fileName = info.fileName();
- job->m_fileSize = info.size();
-
- // check we support some methods
- if (m_supportedMethods == QXmppTransferJob::NoMethod)
- {
- job->terminate(QXmppTransferJob::ProtocolError);
- return job;
- }
+ QXmppTransferFileInfo fileInfo;
+ fileInfo.setDate(info.lastModified());
+ fileInfo.setName(info.fileName());
+ fileInfo.setSize(info.size());
// open file
- job->m_iodevice = new QFile(fileName, job);
- if (!job->m_iodevice->open(QIODevice::ReadOnly))
+ QIODevice *device = new QFile(fileName);
+ if (!device->open(QIODevice::ReadOnly))
{
- job->terminate(QXmppTransferJob::FileAccessError);
- return job;
+ delete device;
+ device = 0;
}
// hash file
- if (!job->m_iodevice->isSequential())
+ if (device && !device->isSequential())
{
+ QCryptographicHash hash(QCryptographicHash::Md5);
QByteArray buffer;
- while (job->m_iodevice->bytesAvailable())
+ while (device->bytesAvailable())
{
- buffer = job->m_iodevice->read(16384);
- job->m_hash.addData(buffer);
+ buffer = device->read(16384);
+ hash.addData(buffer);
}
- job->m_iodevice->reset();
- job->m_fileHash = job->m_hash.result();
- job->m_hash.reset();
+ device->reset();
+ fileInfo.setHash(hash.result());
+ }
+
+ // create job
+ return sendFile(jid, device, fileInfo);
+}
+
+/// 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)
+{
+ QXmppTransferJob *job = new QXmppTransferJob(jid, QXmppTransferJob::OutgoingDirection, this);
+ job->m_sid = generateStanzaHash();
+ job->m_fileInfo = fileInfo;
+ job->m_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 (m_supportedMethods == QXmppTransferJob::NoMethod)
+ {
+ job->terminate(QXmppTransferJob::ProtocolError);
+ return job;
}
// prepare negotiation
@@ -808,6 +963,7 @@ QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QStri
// start job
m_jobs.append(job);
+ connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)));
connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(jobError(QXmppTransferJob::Error)));
QXmppStreamInitiationIq request;
@@ -837,68 +993,6 @@ void QXmppTransferManager::socksServerConnected(QTcpSocket *socket, const QStrin
socket->close();
}
-void QXmppTransferManager::socksSocketDataReceived()
-{
- QTcpSocket *socksSocket = qobject_cast<QTcpSocket*>(sender());
- QXmppTransferJob *job = getJobBySocksSocket(socksSocket);
- if (!job || job->state() != QXmppTransferJob::TransferState)
- return;
-
- // receive data block
- if (job->direction() == QXmppTransferJob::IncomingDirection)
- {
- job->writeData(job->m_socksSocket->readAll());
-
- // if we have received all the data, stop here
- if (job->fileSize() && job->m_done >= job->fileSize())
- job->checkData();
- }
-}
-
-void QXmppTransferManager::socksSocketDataSent()
-{
- QTcpSocket *socksSocket = qobject_cast<QTcpSocket*>(sender());
- QXmppTransferJob *job = getJobBySocksSocket(socksSocket);
- if (!job || job->state() != QXmppTransferJob::TransferState)
- return;
-
- // send next data block
- if (job->direction() == QXmppTransferJob::OutgoingDirection)
- socksServerSendData(job);
-}
-
-void QXmppTransferManager::socksSocketDisconnected()
-{
- QTcpSocket *socksSocket = qobject_cast<QTcpSocket*>(sender());
- QXmppTransferJob *job = getJobBySocksSocket(socksSocket);
- if (!job || job->state() == QXmppTransferJob::FinishedState)
- return;
-
- // terminate transfer
- if (job->direction() == QXmppTransferJob::IncomingDirection)
- {
- job->checkData();
- } else {
- if (job->fileSize() && job->m_done != job->fileSize())
- job->terminate(QXmppTransferJob::ProtocolError);
- else
- job->terminate(QXmppTransferJob::NoError);
- }
-}
-
-void QXmppTransferManager::socksServerSendData(QXmppTransferJob *job)
-{
- const QByteArray buffer = job->m_iodevice->read(job->m_blockSize);
- if (buffer.size())
- {
- job->m_socksSocket->write(buffer);
- job->m_done += buffer.size();
- job->progress(job->m_done, job->fileSize());
- } else {
- job->terminate(QXmppTransferJob::NoError);
- }
-}
-
void QXmppTransferManager::socksServerSendOffer(QXmppTransferJob *job)
{
const QString ownJid = m_client->getConfiguration().jid();
@@ -1086,10 +1180,10 @@ void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiati
}
else if (item.tagName() == "file" && item.attribute("xmlns") == ns_stream_initiation_file_transfer)
{
- job->m_fileDate = datetimeFromString(item.attribute("date"));
- job->m_fileHash = QByteArray::fromHex(item.attribute("hash").toAscii());
- job->m_fileName = item.attribute("name");
- job->m_fileSize = item.attribute("size").toInt();
+ job->m_fileInfo.setDate(datetimeFromString(item.attribute("date")));
+ job->m_fileInfo.setHash(QByteArray::fromHex(item.attribute("hash").toAscii()));
+ job->m_fileInfo.setName(item.attribute("name"));
+ job->m_fileInfo.setSize(item.attribute("size").toInt());
}
}
@@ -1116,6 +1210,7 @@ void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiati
// register job
m_jobs.append(job);
+ connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)));
connect(job, SIGNAL(stateChanged(QXmppTransferJob::State)), this, SLOT(jobStateChanged(QXmppTransferJob::State)));
// allow user to accept or decline the job