From 95d3dfef904b3a90b64c8b28f93b3f4c04701048 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Wed, 24 Feb 2010 10:33:02 +0000 Subject: replace QXmppIbbTransfer* by QXmppTransferManager --- source/QXmppClient.cpp | 22 +- source/QXmppClient.h | 5 +- source/QXmppIbbTransferJob.cpp | 229 -------------- source/QXmppIbbTransferJob.h | 66 ---- source/QXmppIbbTransferManager.cpp | 95 ------ source/QXmppIbbTransferManager.h | 61 ---- source/QXmppInformationRequestResult.cpp | 8 + source/QXmppStream.cpp | 116 ++++---- source/QXmppStream.h | 10 +- source/QXmppTransferManager.cpp | 496 +++++++++++++++++++++++++++++++ source/QXmppTransferManager.h | 125 ++++++++ source/source.pro | 6 +- 12 files changed, 719 insertions(+), 520 deletions(-) delete mode 100644 source/QXmppIbbTransferJob.cpp delete mode 100644 source/QXmppIbbTransferJob.h delete mode 100644 source/QXmppIbbTransferManager.cpp delete mode 100644 source/QXmppIbbTransferManager.h create mode 100644 source/QXmppTransferManager.cpp create mode 100644 source/QXmppTransferManager.h (limited to 'source') diff --git a/source/QXmppClient.cpp b/source/QXmppClient.cpp index c5f67402..4070cdda 100644 --- a/source/QXmppClient.cpp +++ b/source/QXmppClient.cpp @@ -27,7 +27,6 @@ #include "QXmppRoster.h" #include "QXmppMessage.h" #include "QXmppReconnectionManager.h" -#include "QXmppIbbTransferManager.h" #include "QXmppInvokable.h" #include "QXmppRpcIq.h" #include "QXmppRemoteMethod.h" @@ -39,7 +38,7 @@ QXmppClient::QXmppClient(QObject *parent) : QObject(parent), m_stream(0), m_clientPrecence(QXmppPresence::Available), - m_reconnectionManager(0), m_ibbTransferManager(0) + m_reconnectionManager(0) { m_stream = new QXmppStream(this); @@ -73,8 +72,6 @@ QXmppClient::QXmppClient(QObject *parent) check = setReconnectionManager(new QXmppReconnectionManager(this)); Q_ASSERT(check); - - m_ibbTransferManager = new QXmppIbbTransferManager(this); } /// Destructor, destroys the QXmppClient object. @@ -448,11 +445,6 @@ void QXmppClient::invokeInterfaceMethod( const QXmppRpcInvokeIq &iq ) m_stream->sendPacket( errorIq ); } -QXmppIbbTransferManager* QXmppClient::getIbbTransferManager() const -{ - return m_ibbTransferManager; -} - QXmppRemoteMethodResult QXmppClient::callRemoteMethod( const QString &jid, const QString &interface, const QVariant &arg1, @@ -496,3 +488,15 @@ QXmppArchiveManager& QXmppClient::getArchiveManager() { return m_stream->getArchiveManager(); } + +/// Returns the reference to QXmppTransferManager, implementation of: +/// +/// * XEP-0047: In-Band Bytestreams +/// * XEP-0095: Stream Initiation +/// * XEP-0096: SI File Transfer +/// + +QXmppTransferManager& QXmppClient::getTransferManager() +{ + return m_stream->getTransferManager(); +} diff --git a/source/QXmppClient.h b/source/QXmppClient.h index 1692e6fb..57077222 100644 --- a/source/QXmppClient.h +++ b/source/QXmppClient.h @@ -58,10 +58,10 @@ class QXmppVCardManager; class QXmppInvokable; class QXmppRpcInvokeIq; class QXmppRemoteMethod; -class QXmppIbbTransferManager; class QXmppRemoteMethodResult; class QXmppArchiveManager; class QXmppDiscoveryIq; +class QXmppTransferManager; class QXmppClient : public QObject { @@ -112,8 +112,8 @@ public: bool setReconnectionManager(QXmppReconnectionManager*); const QXmppPresence& getClientPresence() const; QXmppVCardManager& getVCardManager(); - QXmppIbbTransferManager* getIbbTransferManager() const; QXmppArchiveManager& getArchiveManager(); + QXmppTransferManager& getTransferManager(); signals: @@ -207,7 +207,6 @@ private: QXmppPresence m_clientPrecence; ///< Stores the current presence of connected client QXmppReconnectionManager* m_reconnectionManager; ///< Pointer to the reconnection manager QHash m_interfaces; - QXmppIbbTransferManager* m_ibbTransferManager; ///< Pointer to the ibb transfer manager }; #endif // QXMPPCLIENT_H diff --git a/source/QXmppIbbTransferJob.cpp b/source/QXmppIbbTransferJob.cpp deleted file mode 100644 index f6af11e6..00000000 --- a/source/QXmppIbbTransferJob.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "QXmppIbbTransferJob.h" - -#include -#include -#include "QXmppIbbIqs.h" -#include "QXmppClient.h" -#include "QXmppUtils.h" - -QXmppIbbTransferJob::QXmppIbbTransferJob( QXmppClient *parent ) - : QObject (parent ), m_client(parent), m_io(0), m_blockSize(4096), - m_streamBlockSize(0), m_sequence(0), m_state(Idle) -{ - m_localJid = parent->getConfiguration().getJid(); - m_id = generateStanzaHash(); - m_sid = generateStanzaHash(); -} - -QXmppIbbTransferJob::~QXmppIbbTransferJob() -{ -} - -QString QXmppIbbTransferJob::getSid() const -{ - return m_sid; -} - -void QXmppIbbTransferJob::setSid( const QString &sid ) -{ - m_sid = sid; -} - -void QXmppIbbTransferJob::setRemoteJid( const QString &jid ) -{ - m_remoteJid = jid; -} - -QString QXmppIbbTransferJob::getRemoteJid( ) const -{ - return m_remoteJid; -} - -QString QXmppIbbTransferJob::getId() const -{ - return m_id; -} - -void QXmppIbbTransferJob::setId( const QString &id ) -{ - m_id = id; -} - -void QXmppIbbTransferJob::requestTransfer( ) -{ - m_state = Requesting; - QXmppIbbOpenIq request; - request.setBlockSize(m_blockSize); - request.setTo( m_remoteJid ); - request.setFrom( m_localJid ); - request.setSid( m_sid ); - request.generateAndSetNextId(); - m_id = request.getId(); - - m_client->sendPacket( request ); - -} - -void QXmppIbbTransferJob::acceptTransfer( ) -{ - if( m_state != Pending ) - { - return; - } - - m_state = TransferringIn; - QXmppIbbAckIq ack; - ack.setTo( m_remoteJid ); - ack.setFrom( m_localJid ); - ack.setId( m_id ); - m_client->sendPacket( ack ); - emit transferStarted(m_sid); -} - -void QXmppIbbTransferJob::cancelTransfer( ) -{ - QXmppIbbErrorIq cancel; - cancel.setId(m_id); - cancel.setErrorType( QXmppIbbErrorIq::Cancel ); - m_client->sendPacket( cancel ); -} - -void QXmppIbbTransferJob::setIoDevice( QIODevice *io ) -{ - m_io = io; -} - -void QXmppIbbTransferJob::setBlockSize( long size) -{ - m_blockSize = size; -} - -void QXmppIbbTransferJob::gotAck() -{ - if( m_state == Requesting ) - { - // start transfer - m_state = TransferringOut; - sendNextBlock(); - } - else if ( m_state == TransferringOut ) - { - // send next packet - sendNextBlock(); - } - else if ( m_state == Idle ) - { - emit readyForTeardown(m_sid); - } - else - { - - } -} - -void QXmppIbbTransferJob::gotOpen( const QXmppIbbOpenIq &open ) -{ - m_sid = open.getSid(); - m_id = open.getId(); - m_remoteJid = open.getFrom(); - if( open.getBlockSize() > m_blockSize ) - { - // cancel - m_state = Idle; - QXmppIbbErrorIq modifyError; - modifyError.setId(m_id); - modifyError.setErrorType( QXmppIbbErrorIq::Modify ); - m_client->sendPacket( modifyError ); - emit readyForTeardown(m_sid); - } - else - { - m_streamBlockSize = open.getBlockSize(); - m_state = Pending; - emit transferRequested( m_sid , m_remoteJid ); - } -} - -void QXmppIbbTransferJob::gotClose( const QXmppIbbCloseIq &close ) -{ - m_state = Idle; - QXmppIbbAckIq ack; - ack.setTo( m_remoteJid ); - ack.setFrom( m_localJid ); - ack.setId( m_id ); - m_client->sendPacket( ack ); - emit transferFinished(m_sid, "Closed"); - emit readyForTeardown(m_sid); -} - -void QXmppIbbTransferJob::gotError( const QXmppIbbErrorIq &err ) -{ - m_state = Idle; - emit transferCanceled(m_sid,err.getError().getConditionStr()); - emit readyForTeardown(m_sid); -} - -void QXmppIbbTransferJob::gotData( const QXmppIbbDataIq &data ) -{ - if( m_io && - (data.getSequence() == 0 || data.getSequence() > m_sequence) ) - { - m_io->write( data.getPayload() ); - m_sequence = data.getSequence(); - QXmppIbbAckIq ack; - ack.setId(m_id); - ack.setTo(m_remoteJid); - ack.setFrom(m_localJid); - m_client->sendPacket( ack ); - } - else - { - QXmppIbbErrorIq error; - error.setId(m_id); - error.setTo(m_remoteJid); - error.setFrom(m_localJid); - error.setErrorType(QXmppIbbErrorIq::Cancel); - m_client->sendPacket( error ); - } -} - -void QXmppIbbTransferJob::sendNextBlock() -{ - - if( m_io == 0 || !m_io->isReadable() ) - { - QXmppIbbErrorIq error; - error.setId(m_id); - error.setTo(m_remoteJid); - error.setFrom(m_localJid); - error.setErrorType(QXmppIbbErrorIq::Cancel); - m_client->sendPacket( error ); - } - else if( m_io->atEnd() || !m_io->isOpen() ) - { - QXmppIbbCloseIq close; - close.setId(m_id); - close.setTo(m_remoteJid); - close.setFrom(m_localJid); - close.setSid(m_sid); - m_client->sendPacket( close ); - m_state = Idle; - emit transferFinished(m_sid, "Send finished"); - } - else - { - //FIXME: work better with sockets and other stream devices. - QByteArray buffer = m_io->read( m_blockSize ); - - m_sequence++; - QXmppIbbDataIq sendData; - sendData.setId(m_id); - sendData.setTo(m_remoteJid); - sendData.setFrom(m_localJid); - sendData.setSid(m_sid); - sendData.setSequence(m_sequence); - sendData.setPayload( buffer ); - - m_client->sendPacket( sendData ); - } -} diff --git a/source/QXmppIbbTransferJob.h b/source/QXmppIbbTransferJob.h deleted file mode 100644 index a3bf1140..00000000 --- a/source/QXmppIbbTransferJob.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef QXMPPIBBTRANSFERJOB_H -#define QXMPPIBBTRANSFERJOB_H - -#include -class QIODevice; -class QXmppIbbOpenIq; -class QXmppIbbCloseIq; -class QXmppIbbErrorIq; -class QXmppIbbDataIq; -class QXmppClient; - -class QXmppIbbTransferJob : public QObject -{ - Q_OBJECT -public: - - QXmppIbbTransferJob(QXmppClient *parent = 0); - ~QXmppIbbTransferJob(); - QString getSid() const; - void setSid( const QString &sid ); - QString getRemoteJid( ) const; - void setRemoteJid( const QString &jid ); - QString getId() const; - void setId( const QString &id ); - -// Used by the client -public slots: - void requestTransfer( ); - void acceptTransfer( ); - void cancelTransfer( ); - -signals: - void transferRequested( const QString &sid, const QString &remoteJid ); - void transferStarted( const QString &sid ); - void transferFinished( const QString &sid, const QString &reason); - void transferCanceled( const QString &sid, const QString &reason ); - void readyForTeardown( const QString &sid ); - -public: - void setIoDevice( QIODevice *io ); - void setBlockSize( long size); - - // Used by the stream. - void gotAck(); - void gotOpen( const QXmppIbbOpenIq &open ); - void gotClose( const QXmppIbbCloseIq &close ); - void gotError( const QXmppIbbErrorIq &err ); - void gotData( const QXmppIbbDataIq &data ); - -private: - enum TransferState { Idle, Requesting, Pending, TransferringIn, TransferringOut }; - void sendNextBlock(); - - QXmppClient *m_client; - QIODevice *m_io; - long m_blockSize; - qint64 m_streamBlockSize; - quint16 m_sequence; - QString m_sid; - QString m_id; - QString m_localJid; - QString m_remoteJid; - TransferState m_state; -}; - -#endif // QXMPPIBBTRANSFERJOB_H diff --git a/source/QXmppIbbTransferManager.cpp b/source/QXmppIbbTransferManager.cpp deleted file mode 100644 index e9437b5b..00000000 --- a/source/QXmppIbbTransferManager.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "QXmppIbbTransferManager.h" -#include "QXmppClient.h" - -QXmppIbbTransferManager::QXmppIbbTransferManager(QXmppClient* client): - m_client(client) -{ -} - -void QXmppIbbTransferManager::teardownIbbTransferManager( const QString &sid ) -{ - QString id = getIbbTransferJobBySid(sid)->getId(); - delete m_activeTransfers[id]; - m_activeTransfers.remove(id); -} - -void QXmppIbbTransferManager::addIbbTransferManager( QXmppIbbTransferJob *mgr ) -{ - m_activeTransfers[mgr->getId()] = mgr; - connect( mgr, SIGNAL(transferCanceled(QString,QString)), - this, SIGNAL(byteStreamCanceled(QString,QString))); - connect( mgr, SIGNAL(transferFinished(QString,QString)), - this, SIGNAL(byteStreamClosed(QString,QString))); - connect( mgr, SIGNAL(transferRequested(QString,QString)), - this, SIGNAL(byteStreamRequestReceived(QString,QString))); - connect( mgr, SIGNAL(transferStarted(QString)), - this, SIGNAL(byteStreamOpened(QString))); - connect( mgr, SIGNAL(readyForTeardown(QString)), - this, SLOT(teardownIbbTransferManager(QString))); -} - -QXmppIbbTransferJob *QXmppIbbTransferManager::getIbbTransferJob( const QString &id ) -{ - QXmppIbbTransferJob *mgr = m_activeTransfers[id]; - if( mgr == 0 ) - { - mgr = new QXmppIbbTransferJob(m_client); - mgr->setId(id); - addIbbTransferManager(mgr); - } - return mgr; -} - -bool QXmppIbbTransferManager::isIbbTransferJobId( const QString &id ) -{ - return m_activeTransfers.keys().contains(id); -} - -QXmppIbbTransferJob *QXmppIbbTransferManager::getIbbTransferJobBySid( const QString &sid ) -{ - foreach( QXmppIbbTransferJob *mgr, m_activeTransfers ) - { - if ( mgr->getSid() == sid ) - return mgr; - } - - return 0; -} - -void QXmppIbbTransferManager::sendByteStreamRequest( const QString &sid, const QString &bareRemoteJid, QIODevice *io) -{ - - QXmppIbbTransferJob *mgr = new QXmppIbbTransferJob(m_client); - mgr->setSid( sid ); - mgr->setRemoteJid( bareRemoteJid ); //FIXME: make like send message - mgr->setIoDevice( io ); - mgr->requestTransfer(); - addIbbTransferManager( mgr ); - -} - -void QXmppIbbTransferManager::acceptByteStreamRequest( const QString &sid, QIODevice *io ) -{ - QXmppIbbTransferJob *mgr = getIbbTransferJobBySid(sid); - if( mgr == 0 ) - return; - - mgr->setIoDevice(io); - mgr->acceptTransfer(); -} - -void QXmppIbbTransferManager::rejectByteStreamRequest( const QString &sid ) -{ - QXmppIbbTransferJob *mgr = getIbbTransferJobBySid(sid); - if( mgr == 0 ) - return; - mgr->cancelTransfer(); -} - -void QXmppIbbTransferManager::cancelByteStreamRequest( const QString &sid ) -{ - QXmppIbbTransferJob *mgr = getIbbTransferJobBySid(sid); - if( mgr == 0 ) - return; - mgr->cancelTransfer(); -} diff --git a/source/QXmppIbbTransferManager.h b/source/QXmppIbbTransferManager.h deleted file mode 100644 index 014b20cf..00000000 --- a/source/QXmppIbbTransferManager.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef QXMPPIBBTRANSFERMANAGER_H -#define QXMPPIBBTRANSFERMANAGER_H - -#include -#include -#include "QXmppIbbTransferJob.h" - -class QXmppClient; - -class QXmppIbbTransferManager : public QObject -{ - Q_OBJECT -public: - QXmppIbbTransferManager(QXmppClient* client); - -signals: - /// Notifies that a XMPP bytestream request has been received. The - /// program must either reply with an acceptByteStreamRequest(...) or - /// a rejectByteStreamRequest(...) depending on the desired response. - void byteStreamRequestReceived( const QString &sid, const QString &remoteJid ); - /// Notifies that a XMPP bytestream has been closed. - void byteStreamClosed( const QString &sid , const QString &reason ); - /// Notifies that a XMPP bytestream was canceled by the remote peer. - /// The reason is given for the cancelation. - void byteStreamCanceled( const QString &sid , const QString &reason ); - /// Notifes that the XMPP bytestream has been opened and the transfer - /// has started. - void byteStreamOpened( const QString &sid ); - -public: - QXmppIbbTransferJob *getIbbTransferJob( const QString &id ); - bool isIbbTransferJobId( const QString &id ); - -public slots: - /// Send a request to open a bytestream to a specific jid. Once the - /// stream is opened then the data is read from the QIODevice. The - /// QIODevice MUST be be open and ready for reading otherwise the - /// transfer will fail. When there are no more bytes available to - /// send from the QIODevice then the bytestream is closed. - void sendByteStreamRequest( const QString &sid, const QString &bareJid, QIODevice *io); - /// Accept a bytestream with a specific sid. Data from the remote - /// peer is then written to the QIODevice. Therefor the QIODevice must - /// be open and ready for writing before this method is called. - void acceptByteStreamRequest( const QString &sid, QIODevice *io ); - /// Rejects a bytestream from a specific sid. - void rejectByteStreamRequest( const QString &sid ); - /// Cacels a currenly connected bytestream with a specific sid. - void cancelByteStreamRequest( const QString &sid ); - -private slots: - void addIbbTransferManager( QXmppIbbTransferJob *mgr ); - void teardownIbbTransferManager( const QString &sid ); - -private: - QXmppIbbTransferJob *getIbbTransferJobBySid( const QString &sid ); - - QXmppClient* m_client; - QHash m_activeTransfers; -}; - -#endif // QXMPPIBBTRANSFERMANAGER_H diff --git a/source/QXmppInformationRequestResult.cpp b/source/QXmppInformationRequestResult.cpp index 072bc75f..964d6cfc 100644 --- a/source/QXmppInformationRequestResult.cpp +++ b/source/QXmppInformationRequestResult.cpp @@ -35,5 +35,13 @@ void QXmppInformationRequestResult::toXmlElementFromChild(QXmlStreamWriter *writ writer->writeAttribute("var", ns_chat_states); writer->writeEndElement(); + writer->writeStartElement("feature"); + writer->writeAttribute("var", ns_stream_initiation); + writer->writeEndElement(); + + writer->writeStartElement("feature"); + writer->writeAttribute("var", ns_stream_initiation_file_transfer); + writer->writeEndElement(); + writer->writeEndElement(); } diff --git a/source/QXmppStream.cpp b/source/QXmppStream.cpp index d01b4130..05967a47 100644 --- a/source/QXmppStream.cpp +++ b/source/QXmppStream.cpp @@ -39,13 +39,12 @@ #include "QXmppInformationRequestResult.h" #include "QXmppIbbIqs.h" #include "QXmppRpcIq.h" -#include "QXmppIbbTransferManager.h" #include "QXmppArchiveIq.h" #include "QXmppDiscoveryIq.h" #include "QXmppPingIq.h" #include "QXmppLogger.h" -#include "QXmppUtils.h" - +#include "QXmppStreamInitiationIq.h" +#include "QXmppTransferManager.h" #include #include @@ -58,8 +57,10 @@ static const QByteArray streamRootElementEnd = ""; QXmppStream::QXmppStream(QXmppClient* client) : QObject(client), m_client(client), m_roster(this), - m_sessionAvaliable(false), m_vCardManager(m_client), + m_sessionAvaliable(false), m_archiveManager(m_client), + m_transferManager(m_client), + m_vCardManager(m_client), m_authStep(0) { bool check = QObject::connect(&m_socket, SIGNAL(hostFound()), @@ -121,6 +122,28 @@ QXmppStream::QXmppStream(QXmppClient* client) check = QObject::connect(this, SIGNAL(archivePrefIqReceived(const QXmppArchivePrefIq&)), &m_archiveManager, SLOT(archivePrefIqReceived(const QXmppArchivePrefIq&))); Q_ASSERT(check); + + // XEP-0047: In-Band Bytestreams + check = QObject::connect(this, SIGNAL(iqReceived(const QXmppIq&)), + &m_transferManager, SLOT(iqReceived(const QXmppIq&))); + Q_ASSERT(check); + + check = QObject::connect(this, SIGNAL(ibbCloseIqReceived(const QXmppIbbCloseIq&)), + &m_transferManager, SLOT(ibbCloseIqReceived(const QXmppIbbCloseIq&))); + Q_ASSERT(check); + + check = QObject::connect(this, SIGNAL(ibbDataIqReceived(const QXmppIbbDataIq&)), + &m_transferManager, SLOT(ibbDataIqReceived(const QXmppIbbDataIq&))); + Q_ASSERT(check); + + check = QObject::connect(this, SIGNAL(ibbOpenIqReceived(const QXmppIbbOpenIq&)), + &m_transferManager, SLOT(ibbOpenIqReceived(const QXmppIbbOpenIq&))); + Q_ASSERT(check); + + // XEP-0095: Stream Initiation + check = QObject::connect(this, SIGNAL(streamInitiationIqReceived(const QXmppStreamInitiationIq&)), + &m_transferManager, SLOT(streamInitiationIqReceived(const QXmppStreamInitiationIq&))); + Q_ASSERT(check); } QXmppStream::~QXmppStream() @@ -433,53 +456,7 @@ void QXmppStream::parser(const QByteArray& data) QDomElement elemen = nodeRecv.firstChildElement("error"); QXmppStanza::Error error = QXmppStanza::parseError(elemen); - if( QXmppIbbOpenIq::isIbbOpenIq( nodeRecv ) ) - { - QXmppIbbOpenIq openIqPacket; - openIqPacket.parse( nodeRecv ); - - QXmppIbbTransferJob *mgr = m_client->getIbbTransferManager()-> - getIbbTransferJob(openIqPacket.getId()); - mgr->gotOpen( openIqPacket ); - } - else if( QXmppIbbErrorIq::isIbbErrorIq( nodeRecv ) && - m_client->getIbbTransferManager()->isIbbTransferJobId( id )) - { - QXmppIbbErrorIq errorIqPacket; - errorIqPacket.parse(nodeRecv); - - QXmppIbbTransferJob *mgr = m_client->getIbbTransferManager()-> - getIbbTransferJob(errorIqPacket.getId()); - mgr->gotError( errorIqPacket ); - } - else if( QXmppIbbAckIq::isIbbAckIq( nodeRecv ) && - m_client->getIbbTransferManager()->isIbbTransferJobId( id )) - { - QXmppIbbAckIq ackIqPacket; - ackIqPacket.parse(nodeRecv); - - QXmppIbbTransferJob *mgr = m_client->getIbbTransferManager()->getIbbTransferJob(ackIqPacket.getId()); - mgr->gotAck(); - } - else if( QXmppIbbDataIq::isIbbDataIq( nodeRecv ) && - m_client->getIbbTransferManager()->isIbbTransferJobId( id )) - { - QXmppIbbDataIq dataIqPacket; - dataIqPacket.parse(nodeRecv); - - QXmppIbbTransferJob *mgr = m_client->getIbbTransferManager()->getIbbTransferJob(dataIqPacket.getId()); - mgr->gotData(dataIqPacket); - } - else if( QXmppIbbCloseIq::isIbbCloseIq( nodeRecv ) && - m_client->getIbbTransferManager()->isIbbTransferJobId( id )) - { - QXmppIbbCloseIq closeIqPacket; - closeIqPacket.parse(nodeRecv); - - QXmppIbbTransferJob *mgr = m_client->getIbbTransferManager()->getIbbTransferJob(closeIqPacket.getId()); - mgr->gotClose(closeIqPacket); - } - else if( QXmppRpcInvokeIq::isRpcInvokeIq( nodeRecv ) ) + if( QXmppRpcInvokeIq::isRpcInvokeIq( nodeRecv ) ) { QXmppRpcInvokeIq rpcIqPacket; rpcIqPacket.parse(nodeRecv); @@ -625,7 +602,37 @@ void QXmppStream::parser(const QByteArray& data) sendNonSASLAuth(plainText); } } - // XEP-0136 message archiving + // XEP-0047 In-Band Bytestreams + else if(QXmppIbbCloseIq::isIbbCloseIq(nodeRecv)) + { + QXmppIbbCloseIq ibbCloseIq; + ibbCloseIq.parse(nodeRecv); + emit ibbCloseIqReceived(ibbCloseIq); + iqPacket = ibbCloseIq; + } + else if(QXmppIbbDataIq::isIbbDataIq(nodeRecv)) + { + QXmppIbbDataIq ibbDataIq; + ibbDataIq.parse(nodeRecv); + emit ibbDataIqReceived(ibbDataIq); + iqPacket = ibbDataIq; + } + else if(QXmppIbbOpenIq::isIbbOpenIq(nodeRecv)) + { + QXmppIbbOpenIq ibbOpenIq; + ibbOpenIq.parse(nodeRecv); + emit ibbOpenIqReceived(ibbOpenIq); + iqPacket = ibbOpenIq; + } + // XEP-0095: Stream Initiation + else if(QXmppStreamInitiationIq::isStreamInitiationIq(nodeRecv)) + { + QXmppStreamInitiationIq siIq; + siIq.parse(nodeRecv); + emit streamInitiationIqReceived(siIq); + iqPacket = siIq; + } + // XEP-0136: Message Archiving else if(QXmppArchiveChatIq::isArchiveChatIq(nodeRecv)) { QXmppArchiveChatIq archiveIq; @@ -1075,3 +1082,8 @@ QXmppArchiveManager& QXmppStream::getArchiveManager() { return m_archiveManager; } + +QXmppTransferManager& QXmppStream::getTransferManager() +{ + return m_transferManager; +} diff --git a/source/QXmppStream.h b/source/QXmppStream.h index 2890d38e..8de103b3 100644 --- a/source/QXmppStream.h +++ b/source/QXmppStream.h @@ -32,6 +32,7 @@ #include "QXmppStanza.h" #include "QXmppVCardManager.h" #include "QXmppArchiveManager.h" +#include "QXmppTransferManager.h" class QDomElement; @@ -64,6 +65,7 @@ public: void disconnect(); QXmppArchiveManager& getArchiveManager(); QXmppRoster& getRoster(); + QXmppTransferManager& getTransferManager(); QXmppVCardManager& getVCardManager(); void sendPacket(const QXmppPacket&); @@ -101,6 +103,11 @@ signals: void discoveryIqReceived(const QXmppDiscoveryIq&); + void ibbCloseIqReceived(const QXmppIbbCloseIq&); + void ibbDataIqReceived(const QXmppIbbDataIq&); + void ibbOpenIqReceived(const QXmppIbbOpenIq&); + void streamInitiationIqReceived(const QXmppStreamInitiationIq&); + private slots: void socketHostFound(); void socketReadReady(); @@ -126,8 +133,9 @@ private: QXmppClient::StreamError m_xmppStreamError; // m_xmppStanzaError; - QXmppVCardManager m_vCardManager; QXmppArchiveManager m_archiveManager; + QXmppTransferManager m_transferManager; + QXmppVCardManager m_vCardManager; int m_authStep; QXmppConfiguration& getConfiguration(); diff --git a/source/QXmppTransferManager.cpp b/source/QXmppTransferManager.cpp new file mode 100644 index 00000000..511933b8 --- /dev/null +++ b/source/QXmppTransferManager.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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 +#include +#include + +#include "QXmppClient.h" +#include "QXmppConstants.h" +#include "QXmppIbbIqs.h" +#include "QXmppStreamInitiationIq.h" +#include "QXmppTransferManager.h" +#include "QXmppUtils.h" + +QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferManager *manager) + : QObject(manager), + m_done(0), + m_error(NoError), + m_iodevice(0), + m_jid(jid), + m_fileSize(0), + m_ibbSequence(0) +{ +} + +void QXmppTransferJob::accept(QIODevice *iodevice) +{ + if (!m_iodevice) + m_iodevice = iodevice; +} + +QXmppTransferJob::Error QXmppTransferJob::error() const +{ + return m_error; +} + +QString QXmppTransferJob::jid() const +{ + return m_jid; +} + +QDateTime QXmppTransferJob::fileDate() const +{ + return m_fileDate; +} + +QString QXmppTransferJob::fileHash() const +{ + return m_fileHash; +} + +QString QXmppTransferJob::fileName() const +{ + return m_fileName; +} + +int QXmppTransferJob::fileSize() const +{ + return m_fileSize; +} + +void QXmppTransferJob::terminate(QXmppTransferJob::Error cause) +{ + // close IO device + m_iodevice->close(); + + // emit signal + m_error = cause; + if (cause == NoError) + emit finished(); + else + emit error(m_error); +} + +QXmppTransferManager::QXmppTransferManager(QXmppClient *client) + : m_client(client), m_ibbBlockSize(4096) +{ +} + +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.getFrom()); + response.setId(iq.getId()); + + QXmppTransferJob *job = getJobBySid(iq.getFrom(), iq.getSid()); + if (!job) + { + // the job is unknown, cancel it + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + return; + } + + // acknowledge the packet + response.setType(QXmppIq::Result); + m_client->sendPacket(response); + + // terminate the transfer + if (job->fileSize() && job->m_done != job->fileSize()) + job->terminate(QXmppTransferJob::FileCorruptError); + else + job->terminate(QXmppTransferJob::NoError); +} + +void QXmppTransferManager::ibbDataIqReceived(const QXmppIbbDataIq &iq) +{ + QXmppIq response; + response.setTo(iq.getFrom()); + response.setId(iq.getId()); + + QXmppTransferJob *job = getJobBySid(iq.getFrom(), iq.getSid()); + if (!job) + { + // the job is unknown, cancel it + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + return; + } + + if (iq.getSequence() != 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_client->sendPacket(response); + return; + } + + // write data + const QByteArray data = iq.getPayload(); + job->m_iodevice->write(data); + job->m_done += data.size(); + job->m_ibbSequence++; + job->progress(job->m_done, job->fileSize()); + + // acknowledge the packet + response.setType(QXmppIq::Result); + m_client->sendPacket(response); +} + +void QXmppTransferManager::ibbOpenIqReceived(const QXmppIbbOpenIq &iq) +{ + QXmppIq response; + response.setTo(iq.getFrom()); + response.setId(iq.getId()); + + QXmppTransferJob *job = getJobBySid(iq.getFrom(), iq.getSid()); + if (!job) + { + // the job is unknown, cancel it + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + return; + } + + if (iq.getBlockSize() > 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_client->sendPacket(response); + return; + } + + // accept transfer + response.setType(QXmppIq::Result); + m_client->sendPacket(response); +} + +void QXmppTransferManager::iqReceived(const QXmppIq &iq) +{ + QXmppTransferJob *job = getJobByRequestId(iq.getFrom(), iq.getId()); + if (!job) + return; + + // if the IO device is closed, do nothing + if (!job->m_iodevice->isOpen()) + return; + + if (iq.getType() == QXmppIq::Result) + { + const QByteArray buffer = job->m_iodevice->read(m_ibbBlockSize); + + 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); + m_client->sendPacket(dataIq); + + job->m_done += buffer.size(); + job->m_requestId = dataIq.getId(); + job->m_ibbSequence++; + job->progress(job->m_done, job->fileSize()); + } else { + // close the bytestream + QXmppIbbCloseIq closeIq; + closeIq.setTo(job->m_jid); + closeIq.setSid(job->m_sid); + m_client->sendPacket(closeIq); + + job->m_requestId = closeIq.getId(); + job->terminate(QXmppTransferJob::NoError); + } + } + else if (iq.getType() == QXmppIq::Error) + { + // close the bytestream + QXmppIbbCloseIq closeIq; + closeIq.setTo(job->m_jid); + closeIq.setSid(job->m_sid); + m_client->sendPacket(closeIq); + + job->m_requestId = closeIq.getId(); + job->terminate(QXmppTransferJob::ProtocolError); + + } +} + +QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QString &fileName) +{ + // open file + QFile *fileIo = new QFile(fileName, this); + fileIo->open(QIODevice::ReadOnly); + QFileInfo info(*fileIo); + + // create job + QXmppTransferJob *job = new QXmppTransferJob(jid, this); + job->m_iodevice = fileIo; + job->m_sid = generateStanzaHash(); + job->m_fileDate = info.lastModified(); + job->m_fileName = info.fileName(); + job->m_fileSize = info.size(); + m_jobs.append(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("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); + + QXmppElement option; + option.setTagName("option"); + field.appendChild(option); + + QXmppElement value; + value.setTagName("value"); + value.setValue(ns_ibb); + option.appendChild(value); + items.append(feature); + + 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.getId(); + m_client->sendPacket(request); + + return job; +} + +void QXmppTransferManager::streamInitiationIqReceived(const QXmppStreamInitiationIq &iq) +{ + if (iq.getType() == QXmppIq::Result) + streamInitiationResultReceived(iq); + else if (iq.getType() == QXmppIq::Set) + streamInitiationSetReceived(iq); +} + +void QXmppTransferManager::streamInitiationResultReceived(const QXmppStreamInitiationIq &iq) +{ + QXmppTransferJob *job = getJobByRequestId(iq.getFrom(), iq.getId()); + if (!job) + return; + + int method = 0; + foreach (const QXmppElement &item, iq.getSiItems()) + { + 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) + method = QXmppTransferJob::InBandByteStream; +/* + else if (field.firstChildElement("value").value() == ns_bytestreams) + method = QXmppTransferJob::SocksByteStream; +*/ + } + field = field.nextSiblingElement("field"); + } + } + } + + if (method == QXmppTransferJob::InBandByteStream) + { + QXmppIbbOpenIq openIq; + openIq.setTo(job->m_jid); + openIq.setSid(job->m_sid); + openIq.setBlockSize(m_ibbBlockSize); + m_client->sendPacket(openIq); + } +} + +void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiationIq &iq) +{ + QXmppStreamInitiationIq response; + response.setTo(iq.getFrom()); + response.setId(iq.getId()); + + // check we support the profile + if (iq.getProfile() != QXmppStreamInitiationIq::FileTransfer) + { + // FIXME : we should add: + // + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest); + error.setCode(400); + + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + return; + } + + // check the stream type + QXmppTransferJob *job = new QXmppTransferJob(iq.getFrom(), this); + job->m_methods = 0; + job->m_sid = iq.getSiId(); + job->m_mimeType = iq.getMimeType(); + foreach (const QXmppElement &item, iq.getSiItems()) + { + 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) + job->m_methods = job->m_methods | QXmppTransferJob::InBandByteStream; +/* + else if (option.firstChildElement("value").value() == ns_bytestreams) + job->m_methods = job->m_methods | QXmppTransferJob::SocksByteStream; +*/ + option = option.nextSiblingElement("option"); + } + } + field = field.nextSiblingElement("field"); + } + } + else if (item.tagName() == "file" && item.attribute("xmlns") == ns_stream_initiation_file_transfer) + { + job->m_fileDate = datetimeFromString(item.attribute("date")); + job->m_fileHash = item.attribute("hash"); + job->m_fileName = item.attribute("name"); + job->m_fileSize = item.attribute("size").toInt(); + } + } + + if (!job->m_methods) + { + // FIXME : we should add: + // + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest); + error.setCode(400); + + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + + delete job; + return; + } + + // allow user to accept or decline the job + emit fileReceived(job); + if (!job->m_iodevice) + { + QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden); + error.setCode(403); + + response.setType(QXmppIq::Error); + response.setError(error); + m_client->sendPacket(response); + + delete job; + return; + } + + // the job was accepted + m_jobs.append(job); + + QXmppElement value; + value.setTagName("value"); + value.setValue(ns_ibb); + + 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(iq.getProfile()); + response.setSiItems(feature); + + m_client->sendPacket(response); +} + diff --git a/source/QXmppTransferManager.h b/source/QXmppTransferManager.h new file mode 100644 index 00000000..bfd781b3 --- /dev/null +++ b/source/QXmppTransferManager.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 Bolloré telecom + * + * 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. + * + */ + +#ifndef QXMPPTRANSFERMANAGER_H +#define QXMPPTRANSFERMANAGER_H + +#include + +#include "QXmppIq.h" + +class QXmppClient; +class QXmppIbbCloseIq; +class QXmppIbbDataIq; +class QXmppIbbOpenIq; +class QXmppStreamInitiationIq; + +class QXmppTransferManager; + +class QXmppTransferJob : public QObject +{ + Q_OBJECT + +public: + enum Error + { + NoError = 0, + FileCorruptError, + ProtocolError, + }; + + enum Method + { + InBandByteStream = 1, + SocksByteStream = 2, + }; + + void accept(QIODevice *output); + + QXmppTransferJob::Error error() const; + QString jid() const; + + // XEP-0096 : File transfer + QDateTime fileDate() const; + QString fileHash() const; + QString fileName() const; + int fileSize() const; + +signals: + void error(QXmppTransferJob::Error error); + void finished(); + void progress(qint64 done, qint64 total); + +private: + QXmppTransferJob(const QString &jid, QXmppTransferManager *manager); + void terminate(QXmppTransferJob::Error error); + + int m_done; + QXmppTransferJob::Error m_error; + QIODevice *m_iodevice; + QString m_jid; + QString m_sid; + int m_methods; + QString m_mimeType; + + QDateTime m_fileDate; + QString m_fileHash; + QString m_fileName; + int m_fileSize; + + QString m_requestId; + int m_ibbSequence; + friend class QXmppTransferManager; +}; + +class QXmppTransferManager : public QObject +{ + Q_OBJECT + +public: + QXmppTransferManager(QXmppClient* client); + QXmppTransferJob *sendFile(const QString &jid, const QString &fileName); + +signals: + void fileReceived(QXmppTransferJob *offer); + +private slots: + void ibbCloseIqReceived(const QXmppIbbCloseIq&); + void ibbDataIqReceived(const QXmppIbbDataIq&); + void ibbOpenIqReceived(const QXmppIbbOpenIq&); + void iqReceived(const QXmppIq&); + void streamInitiationIqReceived(const QXmppStreamInitiationIq&); + +private: + QXmppTransferJob *getJobByRequestId(const QString &jid, const QString &id); + QXmppTransferJob *getJobBySid(const QString &jid, const QString &sid); + void streamInitiationResultReceived(const QXmppStreamInitiationIq&); + void streamInitiationSetReceived(const QXmppStreamInitiationIq&); + + // reference to client object (no ownership) + QXmppClient* m_client; + QList m_jobs; + int m_ibbBlockSize; +}; + +#endif diff --git a/source/source.pro b/source/source.pro index 46b70925..7d1368a8 100644 --- a/source/source.pro +++ b/source/source.pro @@ -33,6 +33,7 @@ HEADERS += QXmppUtils.h \ QXmppStanza.h \ QXmppStream.h \ QXmppStreamInitiationIq.h \ + QXmppTransferManager.h \ QXmppLogger.h \ QXmppReconnectionManager.h \ QXmppVCardManager.h \ @@ -40,8 +41,6 @@ HEADERS += QXmppUtils.h \ QXmppNonSASLAuth.h \ QXmppInformationRequestResult.h \ QXmppIbbIqs.h \ - QXmppIbbTransferJob.h \ - QXmppIbbTransferManager.h \ xmlrpc.h \ QXmppInvokable.h \ QXmppRpcIq.h \ @@ -68,6 +67,7 @@ SOURCES += QXmppUtils.cpp \ QXmppStanza.cpp \ QXmppStream.cpp \ QXmppStreamInitiationIq.cpp \ + QXmppTransferManager.cpp \ QXmppLogger.cpp \ QXmppReconnectionManager.cpp \ QXmppVCardManager.cpp \ @@ -75,8 +75,6 @@ SOURCES += QXmppUtils.cpp \ QXmppNonSASLAuth.cpp \ QXmppInformationRequestResult.cpp \ QXmppIbbIqs.cpp \ - QXmppIbbTransferJob.cpp \ - QXmppIbbTransferManager.cpp \ xmlrpc.cpp \ QXmppInvokable.cpp \ QXmppRpcIq.cpp \ -- cgit v1.2.3