diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-11 07:31:23 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-11 07:31:23 +0000 |
| commit | 40c39853816cfab113d79682c34bc76a2c79c357 (patch) | |
| tree | e4d6a184cf565cb87477339ce738299ff9787bc3 /source/QXmppTransferManager.cpp | |
| parent | 551c284e35280b7b91a939fe7352e496ffea402a (diff) | |
| download | qxmpp-40c39853816cfab113d79682c34bc76a2c79c357.tar.gz | |
rename "source" directory to "src"
Diffstat (limited to 'source/QXmppTransferManager.cpp')
| -rw-r--r-- | source/QXmppTransferManager.cpp | 1358 |
1 files changed, 0 insertions, 1358 deletions
diff --git a/source/QXmppTransferManager.cpp b/source/QXmppTransferManager.cpp deleted file mode 100644 index b64d6285..00000000 --- a/source/QXmppTransferManager.cpp +++ /dev/null @@ -1,1358 +0,0 @@ -/* - * Copyright (C) 2008-2010 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 <QDomElement> -#include <QFile> -#include <QFileInfo> -#include <QNetworkInterface> -#include <QTimer> - -#include "QXmppByteStreamIq.h" -#include "QXmppConstants.h" -#include "QXmppIbbIq.h" -#include "QXmppLogger.h" -#include "QXmppSocks.h" -#include "QXmppStream.h" -#include "QXmppStreamInitiationIq.h" -#include "QXmppTransferManager.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(); -} - -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), - m_direction(direction), - m_done(0), - m_error(NoError), - m_hash(QCryptographicHash::Md5), - m_iodevice(0), - m_jid(jid), - m_method(NoMethod), - m_state(OfferState), - m_ibbSequence(0), - m_socksSocket(0) -{ -} - -/// 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(QIODevice *iodevice) -{ - if (m_direction == IncomingDirection && m_state == OfferState && !m_iodevice) - { - m_iodevice = iodevice; - setState(QXmppTransferJob::StartState); - } -} - -void QXmppTransferJob::checkData() -{ - 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); -} - -/// Returns the job's data for a given role. -/// -/// You can associate arbitrary data with the role using setData(). - -QVariant QXmppTransferJob::data(int role) const -{ - return m_data.value(role); -} - -/// Sets the data for a given role to the given value. -/// -/// You can set any data you want for use in your application, this -/// data will not be used internally by QXmppTransferManager. - -void QXmppTransferJob::setData(int role, const QVariant &value) -{ - m_data.insert(role, value); -} - -/// Returns the job's transfer direction. -/// - -QXmppTransferJob::Direction QXmppTransferJob::direction() const -{ - return m_direction; -} - -/// Returns the last error that was encountered. -/// - -QXmppTransferJob::Error QXmppTransferJob::error() const -{ - return m_error; -} - -/// Returns the remote party's JID. -/// - -QString QXmppTransferJob::jid() const -{ - return m_jid; -} - -/// Returns meta-data about the file being transfered. -/// - -QXmppTransferFileInfo QXmppTransferJob::fileInfo() const -{ - return m_fileInfo; -} - -QDateTime QXmppTransferJob::fileDate() const -{ - return m_fileInfo.date(); -} - -QByteArray QXmppTransferJob::fileHash() const -{ - return m_fileInfo.hash(); -} - -QString QXmppTransferJob::fileName() const -{ - return m_fileInfo.name(); -} - -qint64 QXmppTransferJob::fileSize() const -{ - return m_fileInfo.size(); -} - -/// Returns the job's transfer method. -/// - -QXmppTransferJob::Method QXmppTransferJob::method() const -{ - return m_method; -} - -/// Returns the job's session identifier. -/// - -QString QXmppTransferJob::sid() const -{ - return m_sid; -} - -/// Returns the job's state. -/// - -QXmppTransferJob::State QXmppTransferJob::state() const -{ - return m_state; -} - -void QXmppTransferJob::setState(QXmppTransferJob::State state) -{ - if (m_state != state) - { - m_state = state; - emit stateChanged(m_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 = new char[m_blockSize]; - qint64 length = m_iodevice->read(buffer, m_blockSize); - if (length < 0) - { - delete [] buffer; - terminate(QXmppTransferJob::FileAccessError); - return; - } - if (length > 0) - { - m_socksSocket->write(buffer, length); - delete [] buffer; - m_done += length; - emit progress(m_done, fileSize()); - } -} - -void QXmppTransferJob::slotTerminated() -{ - emit stateChanged(m_state); - if (m_error != NoError) - emit error(m_error); - emit finished(); -} - -void QXmppTransferJob::terminate(QXmppTransferJob::Error cause) -{ - if (m_state == FinishedState) - return; - - // change state - m_error = cause; - m_state = FinishedState; - - // close IO device - if (m_iodevice) - m_iodevice->close(); - - // close socket - if (m_socksSocket) - { - m_socksSocket->flush(); - m_socksSocket->close(); - } - - // emit signals later - QTimer::singleShot(0, this, SLOT(slotTerminated())); -} - -bool QXmppTransferJob::writeData(const QByteArray &data) -{ - const qint64 written = m_iodevice->write(data); - if (written < 0) - return false; - m_done += written; - if (!m_fileInfo.hash().isEmpty()) - m_hash.addData(data); - progress(m_done, m_fileInfo.size()); - return true; -} - -QXmppTransferManager::QXmppTransferManager(QXmppStream *stream, QObject *parent) - : QObject(parent), - m_stream(stream), - m_ibbBlockSize(4096), - m_proxyOnly(false), - m_socksServer(0), - m_supportedMethods(QXmppTransferJob::AnyMethod) -{ - // Logging - bool check = connect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)), - m_stream, SIGNAL(logMessage(QXmppLogger::MessageType, QString))); - Q_ASSERT(check); - - // XEP-0047: In-Band Bytestreams - check = QObject::connect(m_stream, SIGNAL(iqReceived(const QXmppIq&)), - this, SLOT(iqReceived(const QXmppIq&))); - Q_ASSERT(check); - - check = QObject::connect(m_stream, SIGNAL(ibbCloseIqReceived(const QXmppIbbCloseIq&)), - this, SLOT(ibbCloseIqReceived(const QXmppIbbCloseIq&))); - Q_ASSERT(check); - - check = QObject::connect(m_stream, SIGNAL(ibbDataIqReceived(const QXmppIbbDataIq&)), - this, SLOT(ibbDataIqReceived(const QXmppIbbDataIq&))); - Q_ASSERT(check); - - check = QObject::connect(m_stream, SIGNAL(ibbOpenIqReceived(const QXmppIbbOpenIq&)), - this, SLOT(ibbOpenIqReceived(const QXmppIbbOpenIq&))); - Q_ASSERT(check); - - // XEP-0065: SOCKS5 Bytestreams - check = QObject::connect(m_stream, SIGNAL(byteStreamIqReceived(const QXmppByteStreamIq&)), - this, SLOT(byteStreamIqReceived(const QXmppByteStreamIq&))); - Q_ASSERT(check); - - // XEP-0095: Stream Initiation - check = QObject::connect(m_stream, SIGNAL(streamInitiationIqReceived(const QXmppStreamInitiationIq&)), - this, SLOT(streamInitiationIqReceived(const QXmppStreamInitiationIq&))); - Q_ASSERT(check); - - // start SOCKS server - m_socksServer = new QXmppSocksServer(this); - if (m_socksServer->listen()) - { - connect(m_socksServer, SIGNAL(newConnection(QTcpSocket*, const QString&, quint16)), - this, SLOT(socksServerConnected(QTcpSocket*, const QString&, quint16))); - } else { - emit logMessage(QXmppLogger::WarningMessage, "QXmppSocksServer could not start listening"); - } -} - -void QXmppTransferManager::byteStreamIqReceived(const QXmppByteStreamIq &iq) -{ - // handle IQ from proxy - foreach (QXmppTransferJob *job, m_jobs) - { - if (job->m_socksProxy.jid() == iq.from() && job->m_requestId == iq.id()) - { - if (iq.type() == QXmppIq::Result && iq.streamHosts().size() > 0) - { - job->m_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 = getJobByRequestId(iq.from(), iq.id()); - if (!job || - job->direction() != QXmppTransferJob::IncomingDirection || - 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) -{ - QXmppTransferJob *job = getJobByRequestId(iq.from(), iq.id()); - if (!job || - job->direction() != QXmppTransferJob::OutgoingDirection || - job->method() != QXmppTransferJob::SocksMethod || - job->state() != QXmppTransferJob::StartState) - return; - - // check the stream host - if (iq.streamHostUsed() == job->m_socksProxy.jid()) - { - const QXmppByteStreamIq::StreamHost streamHost = job->m_socksProxy; - emit logMessage(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, - m_stream->configuration().jid(), - job->m_jid); - - QXmppSocksClient *socksClient = new QXmppSocksClient(streamHost.host(), streamHost.port(), job); - socksClient->connectToHost(hostName, 0); - // FIXME : this should probably be made asynchronous as it blocks XMPP packet handling - if (!socksClient->waitForReady(socksTimeout)) - { - emit logMessage(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(disconnected()), job, SLOT(disconnected())); - - // activate stream - QXmppByteStreamIq streamIq; - streamIq.setType(QXmppIq::Set); - streamIq.setFrom(m_stream->configuration().jid()); - streamIq.setTo(streamHost.jid()); - streamIq.setSid(job->m_sid); - streamIq.setActivate(job->m_jid); - job->m_requestId = streamIq.id(); - m_stream->sendPacket(streamIq); - return; - } - - // direction connection, start sending data - if (!job->m_socksSocket) - { - emit logMessage(QXmppLogger::WarningMessage, "Client says they connected to our SOCKS server, but they did not"); - job->terminate(QXmppTransferJob::ProtocolError); - return; - } - job->setState(QXmppTransferJob::TransferState); - 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 -/// to a stream host. -void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq) -{ - QXmppIq response; - response.setId(iq.id()); - response.setTo(iq.from()); - - QXmppTransferJob *job = getJobBySid(iq.from(), iq.sid()); - if (!job || - job->direction() != QXmppTransferJob::IncomingDirection || - 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); - m_stream->sendPacket(response); - return; - } - - // try connecting to the offered stream hosts - foreach (const QXmppByteStreamIq::StreamHost &streamHost, iq.streamHosts()) - { - emit logMessage(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, - m_stream->configuration().jid()); - - // try to connect to stream host - QXmppSocksClient *socksClient = new QXmppSocksClient(streamHost.host(), streamHost.port(), job); - socksClient->connectToHost(hostName, 0); - // FIXME : this should probably be made asynchronous as it blocks XMPP packet handling - if (socksClient->waitForReady(socksTimeout)) - { - job->setState(QXmppTransferJob::TransferState); - job->m_socksSocket = socksClient; - connect(job->m_socksSocket, SIGNAL(readyRead()), job, SLOT(receiveData())); - connect(job->m_socksSocket, SIGNAL(disconnected()), job, SLOT(disconnected())); - - QXmppByteStreamIq ackIq; - ackIq.setId(iq.id()); - ackIq.setTo(iq.from()); - ackIq.setType(QXmppIq::Result); - ackIq.setSid(job->m_sid); - ackIq.setStreamHostUsed(streamHost.jid()); - m_stream->sendPacket(ackIq); - return; - } else { - emit logMessage(QXmppLogger::WarningMessage, - QString("Failed to connect to streamhost: %1 (%2:%3)").arg( - streamHost.jid(), - streamHost.host().toString(), - QString::number(streamHost.port()))); - delete socksClient; - } - } - - // could not connect to any stream host - QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); - error.setCode(404); - response.setType(QXmppIq::Error); - response.setError(error); - m_stream->sendPacket(response); - - job->terminate(QXmppTransferJob::ProtocolError); -} - -QXmppTransferJob* QXmppTransferManager::getJobByRequestId(const QString &jid, const QString &id) -{ - foreach (QXmppTransferJob *job, m_jobs) - if (job->m_jid == jid && job->m_requestId == id) - return job; - return 0; -} - -QXmppTransferJob* QXmppTransferManager::getJobBySid(const QString &jid, const QString &sid) -{ - foreach (QXmppTransferJob *job, m_jobs) - if (job->m_jid == jid && job->m_sid == sid) - return job; - return 0; -} - -void QXmppTransferManager::ibbCloseIqReceived(const QXmppIbbCloseIq &iq) -{ - QXmppIq response; - response.setTo(iq.from()); - response.setId(iq.id()); - - QXmppTransferJob *job = getJobBySid(iq.from(), iq.sid()); - if (!job || - job->direction() != QXmppTransferJob::IncomingDirection || - 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); - m_stream->sendPacket(response); - return; - } - - // acknowledge the packet - response.setType(QXmppIq::Result); - m_stream->sendPacket(response); - - // check received data - job->checkData(); -} - -void QXmppTransferManager::ibbDataIqReceived(const QXmppIbbDataIq &iq) -{ - QXmppIq response; - response.setTo(iq.from()); - response.setId(iq.id()); - - QXmppTransferJob *job = getJobBySid(iq.from(), iq.sid()); - if (!job || - job->direction() != QXmppTransferJob::IncomingDirection || - 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); - m_stream->sendPacket(response); - return; - } - - if (iq.sequence() != job->m_ibbSequence) - { - // the packet is out of sequence - QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::UnexpectedRequest); - response.setType(QXmppIq::Error); - response.setError(error); - m_stream->sendPacket(response); - return; - } - - // write data - job->writeData(iq.payload()); - job->m_ibbSequence++; - - // acknowledge the packet - response.setType(QXmppIq::Result); - m_stream->sendPacket(response); -} - -void QXmppTransferManager::ibbOpenIqReceived(const QXmppIbbOpenIq &iq) -{ - QXmppIq response; - response.setTo(iq.from()); - response.setId(iq.id()); - - QXmppTransferJob *job = getJobBySid(iq.from(), iq.sid()); - if (!job || - job->direction() != QXmppTransferJob::IncomingDirection || - 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); - m_stream->sendPacket(response); - return; - } - - if (iq.blockSize() > m_ibbBlockSize) - { - // we prefer a smaller block size - QXmppStanza::Error error(QXmppStanza::Error::Modify, QXmppStanza::Error::ResourceConstraint); - response.setType(QXmppIq::Error); - response.setError(error); - m_stream->sendPacket(response); - return; - } - - job->m_blockSize = iq.blockSize(); - job->setState(QXmppTransferJob::TransferState); - - // accept transfer - response.setType(QXmppIq::Result); - m_stream->sendPacket(response); -} - -void QXmppTransferManager::ibbResponseReceived(const QXmppIq &iq) -{ - QXmppTransferJob *job = getJobByRequestId(iq.from(), iq.id()); - if (!job || - job->direction() != QXmppTransferJob::OutgoingDirection || - job->method() != QXmppTransferJob::InBandMethod || - job->state() == QXmppTransferJob::FinishedState) - return; - - // if the IO device is closed, do nothing - if (!job->m_iodevice->isOpen()) - return; - - if (iq.type() == QXmppIq::Result) - { - const QByteArray buffer = job->m_iodevice->read(job->m_blockSize); - job->setState(QXmppTransferJob::TransferState); - if (buffer.size()) - { - // send next data block - QXmppIbbDataIq dataIq; - dataIq.setTo(job->m_jid); - dataIq.setSid(job->m_sid); - dataIq.setSequence(job->m_ibbSequence++); - dataIq.setPayload(buffer); - job->m_requestId = dataIq.id(); - m_stream->sendPacket(dataIq); - - job->m_done += buffer.size(); - job->progress(job->m_done, job->fileSize()); - } else { - // close the bytestream - QXmppIbbCloseIq closeIq; - closeIq.setTo(job->m_jid); - closeIq.setSid(job->m_sid); - job->m_requestId = closeIq.id(); - m_stream->sendPacket(closeIq); - - job->terminate(QXmppTransferJob::NoError); - } - } - else if (iq.type() == QXmppIq::Error) - { - // close the bytestream - QXmppIbbCloseIq closeIq; - closeIq.setTo(job->m_jid); - closeIq.setSid(job->m_sid); - job->m_requestId = closeIq.id(); - m_stream->sendPacket(closeIq); - - job->terminate(QXmppTransferJob::ProtocolError); - } -} - -void QXmppTransferManager::iqReceived(const QXmppIq &iq) -{ - // handle IQ from proxy - foreach (QXmppTransferJob *job, m_jobs) - { - if (job->m_socksProxy.jid() == iq.from() && job->m_requestId == iq.id()) - { - if (job->m_socksSocket) - { - // proxy connection activation result - if (iq.type() == QXmppIq::Result) - { - // proxy stream activated, start sending data - job->setState(QXmppTransferJob::TransferState); - 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 - emit logMessage(QXmppLogger::WarningMessage, "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; - } - } - - QXmppTransferJob *job = getJobByRequestId(iq.from(), iq.id()); - if (!job) - return; - - if (job->method() == QXmppTransferJob::InBandMethod) - ibbResponseReceived(iq); - else if (job->method() == QXmppTransferJob::SocksMethod) - byteStreamResponseReceived(iq); - else if (iq.type() == QXmppIq::Error) { - // remote party cancelled stream initiation - job->terminate(QXmppTransferJob::AbortError); - } -} - -void QXmppTransferManager::jobDestroyed(QObject *object) -{ - m_jobs.removeAll(static_cast<QXmppTransferJob*>(object)); -} - -void QXmppTransferManager::jobError(QXmppTransferJob::Error error) -{ - QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender()); - if (!job || !m_jobs.contains(job)) - return; - - if (job->direction() == QXmppTransferJob::OutgoingDirection && - job->method() == QXmppTransferJob::InBandMethod && - error == QXmppTransferJob::AbortError) - { - // close the bytestream - QXmppIbbCloseIq closeIq; - closeIq.setTo(job->m_jid); - closeIq.setSid(job->m_sid); - job->m_requestId = closeIq.id(); - m_stream->sendPacket(closeIq); - } -} - -void QXmppTransferManager::jobFinished() -{ - QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender()); - if (!job || !m_jobs.contains(job)) - return; - - emit finished(job); -} - -void QXmppTransferManager::jobStateChanged(QXmppTransferJob::State state) -{ - QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender()); - if (!job || !m_jobs.contains(job)) - return; - - if (job->direction() != QXmppTransferJob::IncomingDirection) - return; - - // disconnect from the signal - disconnect(job, SIGNAL(stateChanged(QXmppTransferJob::State)), this, SLOT(jobStateChanged(QXmppTransferJob::State))); - - QXmppStreamInitiationIq response; - response.setTo(job->jid()); - response.setId(job->m_offerId); - - // the job was refused by the local party - if (state != QXmppTransferJob::StartState || !job->m_iodevice || !job->m_iodevice->isWritable()) - { - QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden); - error.setCode(403); - - response.setType(QXmppIq::Error); - response.setError(error); - m_stream->sendPacket(response); - - job->terminate(QXmppTransferJob::AbortError); - return; - } - - // the job was accepted by the local party - connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(jobError(QXmppTransferJob::Error))); - - QXmppElement value; - value.setTagName("value"); - if (job->method() == QXmppTransferJob::InBandMethod) - value.setValue(ns_ibb); - else if (job->method() == QXmppTransferJob::SocksMethod) - value.setValue(ns_bytestreams); - - QXmppElement field; - field.setTagName("field"); - field.setAttribute("var", "stream-method"); - field.appendChild(value); - - QXmppElement x; - x.setTagName("x"); - x.setAttribute("xmlns", "jabber:x:data"); - x.setAttribute("type", "submit"); - x.appendChild(field); - - QXmppElement feature; - feature.setTagName("feature"); - feature.setAttribute("xmlns", ns_feature_negotiation); - feature.appendChild(x); - - response.setType(QXmppIq::Result); - response.setProfile(QXmppStreamInitiationIq::FileTransfer); - response.setSiItems(feature); - - m_stream->sendPacket(response); -} - -/// 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 &fileName, const QString &sid) -{ - QFileInfo info(fileName); - - QXmppTransferFileInfo fileInfo; - fileInfo.setDate(info.lastModified()); - fileInfo.setName(info.fileName()); - fileInfo.setSize(info.size()); - - // open file - QIODevice *device = new QFile(fileName); - if (!device->open(QIODevice::ReadOnly)) - { - 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 - return sendFile(jid, device, fileInfo, sid); -} - -/// 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) -{ - QXmppTransferJob *job = new QXmppTransferJob(jid, QXmppTransferJob::OutgoingDirection, this); - if (sid.isEmpty()) - job->m_sid = generateStanzaHash(); - else - job->m_sid = sid; - 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) - { - job->terminate(QXmppTransferJob::ProtocolError); - return job; - } - - // prepare negotiation - QXmppElementList items; - - QXmppElement file; - file.setTagName("file"); - file.setAttribute("xmlns", ns_stream_initiation_file_transfer); - file.setAttribute("date", datetimeToString(job->fileDate())); - file.setAttribute("hash", job->fileHash().toHex()); - file.setAttribute("name", job->fileName()); - file.setAttribute("size", QString::number(job->fileSize())); - items.append(file); - - QXmppElement feature; - feature.setTagName("feature"); - feature.setAttribute("xmlns", ns_feature_negotiation); - - QXmppElement x; - x.setTagName("x"); - x.setAttribute("xmlns", "jabber:x:data"); - x.setAttribute("type", "form"); - feature.appendChild(x); - - QXmppElement field; - field.setTagName("field"); - field.setAttribute("var", "stream-method"); - field.setAttribute("type", "list-single"); - x.appendChild(field); - - // add supported stream methods - if (m_supportedMethods & QXmppTransferJob::InBandMethod) - { - QXmppElement option; - option.setTagName("option"); - field.appendChild(option); - - QXmppElement value; - value.setTagName("value"); - value.setValue(ns_ibb); - option.appendChild(value); - } - if (m_supportedMethods & QXmppTransferJob::SocksMethod) - { - QXmppElement option; - option.setTagName("option"); - field.appendChild(option); - - QXmppElement value; - value.setTagName("value"); - value.setValue(ns_bytestreams); - option.appendChild(value); - } - - items.append(feature); - - // 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))); - connect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - - QXmppStreamInitiationIq request; - request.setType(QXmppIq::Set); - request.setTo(jid); - request.setProfile(QXmppStreamInitiationIq::FileTransfer); - request.setSiItems(items); - request.setSiId(job->m_sid); - job->m_requestId = request.id(); - m_stream->sendPacket(request); - - return job; -} - -void QXmppTransferManager::socksServerConnected(QTcpSocket *socket, const QString &hostName, quint16 port) -{ - const QString ownJid = m_stream->configuration().jid(); - foreach (QXmppTransferJob *job, m_jobs) - { - if (hostName == streamHash(job->m_sid, ownJid, job->jid()) && port == 0) - { - job->m_socksSocket = socket; - return; - } - } - emit logMessage(QXmppLogger::WarningMessage, "QXmppSocksServer got a connection for a unknown stream"); - socket->close(); -} - -void QXmppTransferManager::socksServerSendOffer(QXmppTransferJob *job) -{ - const QString ownJid = m_stream->configuration().jid(); - QList<QXmppByteStreamIq::StreamHost> streamHosts; - - // discover local IPs - if (!m_proxyOnly) - { - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) - { - if (!(interface.flags() & QNetworkInterface::IsRunning) || - interface.flags() & QNetworkInterface::IsLoopBack) - continue; - - foreach (const QNetworkAddressEntry &entry, interface.addressEntries()) - { - if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol || - entry.netmask().isNull() || - entry.netmask() == QHostAddress::Broadcast) - continue; - - QXmppByteStreamIq::StreamHost streamHost; - streamHost.setHost(entry.ip()); - streamHost.setPort(m_socksServer->serverPort()); - streamHost.setJid(ownJid); - streamHosts.append(streamHost); - } - } - } - - // add proxy - if (!job->m_socksProxy.jid().isEmpty()) - streamHosts.append(job->m_socksProxy); - - // check we have some stream hosts - if (!streamHosts.size()) - { - emit logMessage(QXmppLogger::WarningMessage, "Could not determine local stream hosts"); - job->terminate(QXmppTransferJob::ProtocolError); - return; - } - - // send offer - QXmppByteStreamIq streamIq; - streamIq.setType(QXmppIq::Set); - streamIq.setTo(job->m_jid); - streamIq.setSid(job->m_sid); - streamIq.setStreamHosts(streamHosts); - job->m_requestId = streamIq.id(); - m_stream->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 = getJobByRequestId(iq.from(), iq.id()); - if (!job || - job->direction() != QXmppTransferJob::OutgoingDirection || - job->state() != QXmppTransferJob::OfferState) - return; - - foreach (const QXmppElement &item, iq.siItems()) - { - if (item.tagName() == "feature" && item.attribute("xmlns") == ns_feature_negotiation) - { - QXmppElement field = item.firstChildElement("x").firstChildElement("field"); - while (!field.isNull()) - { - if (field.attribute("var") == "stream-method") - { - if ((field.firstChildElement("value").value() == ns_ibb) && - (m_supportedMethods & QXmppTransferJob::InBandMethod)) - job->m_method = QXmppTransferJob::InBandMethod; - else if ((field.firstChildElement("value").value() == ns_bytestreams) && - (m_supportedMethods & QXmppTransferJob::SocksMethod)) - job->m_method = QXmppTransferJob::SocksMethod; - } - field = field.nextSiblingElement("field"); - } - } - } - - // remote party accepted stream initiation - job->setState(QXmppTransferJob::StartState); - if (job->method() == QXmppTransferJob::InBandMethod) - { - // lower block size for IBB - job->m_blockSize = m_ibbBlockSize; - - QXmppIbbOpenIq openIq; - openIq.setTo(job->m_jid); - openIq.setSid(job->m_sid); - openIq.setBlockSize(job->m_blockSize); - job->m_requestId = openIq.id(); - m_stream->sendPacket(openIq); - } else if (job->method() == QXmppTransferJob::SocksMethod) { - if (!m_socksServer->isListening()) - { - emit logMessage(QXmppLogger::WarningMessage, "QXmppSocksServer is not listening"); - job->terminate(QXmppTransferJob::ProtocolError); - return; - } - if (!m_proxy.isEmpty()) - { - job->m_socksProxy.setJid(m_proxy); - - // query proxy - QXmppByteStreamIq streamIq; - streamIq.setType(QXmppIq::Get); - streamIq.setTo(job->m_socksProxy.jid()); - streamIq.setSid(job->m_sid); - job->m_requestId = streamIq.id(); - m_stream->sendPacket(streamIq); - } else { - socksServerSendOffer(job); - } - } else { - emit logMessage(QXmppLogger::WarningMessage, "QXmppTransferManager received an unsupported method"); - job->terminate(QXmppTransferJob::ProtocolError); - } -} - -void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiationIq &iq) -{ - QXmppStreamInitiationIq 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); - m_stream->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); - m_stream->sendPacket(response); - return; - } - - // check the stream type - QXmppTransferJob *job = new QXmppTransferJob(iq.from(), QXmppTransferJob::IncomingDirection, this); - int offeredMethods = QXmppTransferJob::NoMethod; - job->m_offerId = iq.id(); - job->m_sid = iq.siId(); - job->m_mimeType = iq.mimeType(); - foreach (const QXmppElement &item, iq.siItems()) - { - if (item.tagName() == "feature" && item.attribute("xmlns") == ns_feature_negotiation) - { - QXmppElement field = item.firstChildElement("x").firstChildElement("field"); - while (!field.isNull()) - { - if (field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") - { - QXmppElement option = field.firstChildElement("option"); - while (!option.isNull()) - { - if (option.firstChildElement("value").value() == ns_ibb) - offeredMethods = offeredMethods | QXmppTransferJob::InBandMethod; - else if (option.firstChildElement("value").value() == ns_bytestreams) - offeredMethods = offeredMethods | QXmppTransferJob::SocksMethod; - option = option.nextSiblingElement("option"); - } - } - field = field.nextSiblingElement("field"); - } - } - else if (item.tagName() == "file" && item.attribute("xmlns") == ns_stream_initiation_file_transfer) - { - 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").toLongLong()); - } - } - - // select a method supported by both parties - int sharedMethods = (offeredMethods & m_supportedMethods); - if (sharedMethods & QXmppTransferJob::SocksMethod) - job->m_method = QXmppTransferJob::SocksMethod; - else if (sharedMethods & QXmppTransferJob::InBandMethod) - job->m_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); - m_stream->sendPacket(response); - - delete job; - return; - } - - // register job - m_jobs.append(job); - connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*))); - connect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - connect(job, SIGNAL(stateChanged(QXmppTransferJob::State)), this, SLOT(jobStateChanged(QXmppTransferJob::State))); - - // 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 m_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) -{ - m_proxy = proxyJid; -} - -/// Return whether the proxy will systematically be used for -/// outgoing SOCKS5 bytestream transfers. -/// - -bool QXmppTransferManager::proxyOnly() const -{ - return m_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) -{ - m_proxyOnly = proxyOnly; -} - -/// Return the supported stream methods. -/// -/// The methods are a combination of zero or more QXmppTransferJob::Method. -/// - -QXmppTransferJob::Methods QXmppTransferManager::supportedMethods() const -{ - return m_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) -{ - m_supportedMethods = methods; -} |
