diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-02-24 10:33:02 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-02-24 10:33:02 +0000 |
| commit | 95d3dfef904b3a90b64c8b28f93b3f4c04701048 (patch) | |
| tree | 99fa0ba9138ad6de46464f921c121f955f690b99 | |
| parent | eaa17977f41d1fa3568540824bf33fd725c40afa (diff) | |
| download | qxmpp-95d3dfef904b3a90b64c8b28f93b3f4c04701048.tar.gz | |
replace QXmppIbbTransfer* by QXmppTransferManager
| -rw-r--r-- | source/QXmppClient.cpp | 22 | ||||
| -rw-r--r-- | source/QXmppClient.h | 5 | ||||
| -rw-r--r-- | source/QXmppIbbTransferJob.cpp | 229 | ||||
| -rw-r--r-- | source/QXmppIbbTransferJob.h | 66 | ||||
| -rw-r--r-- | source/QXmppIbbTransferManager.cpp | 95 | ||||
| -rw-r--r-- | source/QXmppIbbTransferManager.h | 61 | ||||
| -rw-r--r-- | source/QXmppInformationRequestResult.cpp | 8 | ||||
| -rw-r--r-- | source/QXmppStream.cpp | 116 | ||||
| -rw-r--r-- | source/QXmppStream.h | 10 | ||||
| -rw-r--r-- | source/QXmppTransferManager.cpp | 496 | ||||
| -rw-r--r-- | source/QXmppTransferManager.h | 125 | ||||
| -rw-r--r-- | source/source.pro | 6 |
12 files changed, 719 insertions, 520 deletions
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<QString,QXmppInvokable *> 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 <QIODevice>
-#include <QUuid>
-#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 <QObject>
-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 <QObject>
-#include <QHash>
-#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<QString, QXmppIbbTransferJob*> 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 <QDomDocument> #include <QStringList> @@ -58,8 +57,10 @@ static const QByteArray streamRootElementEnd = "</stream:stream>"; 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 <QDomElement> +#include <QFile> +#include <QFileInfo> + +#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: + // <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_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: + // <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_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 <QDateTime> + +#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<QXmppTransferJob*> 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 \ |
