diff options
Diffstat (limited to 'kopete/protocols/irc/libkirc/kirctransfer.cpp')
-rw-r--r-- | kopete/protocols/irc/libkirc/kirctransfer.cpp | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/kopete/protocols/irc/libkirc/kirctransfer.cpp b/kopete/protocols/irc/libkirc/kirctransfer.cpp new file mode 100644 index 00000000..2466d6a9 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransfer.cpp @@ -0,0 +1,365 @@ +/* + kirctransfer.cpp - IRC transfer. + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> +#include <kextsock.h> +#include <klocale.h> + +#include <qfile.h> +#include <qtimer.h> + +#include "kirctransfer.h" + +using namespace KIRC; + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Type type, + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(QString::null), m_fileSize(0), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ +} + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, // put this in a QVariant ? + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ +} + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + QHostAddress hostAdress, Q_UINT16 port, // put this in a QVariant ? + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, // put this in a QVariant ? + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ + setSocket(new KExtendedSocket(hostAdress.toString(), port)); +} +/* +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Transfer::Type type, QVariant properties, + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(properties[socket]), + m_initiated(false), + m_file(0), m_fileName(properties[fileName]), m_fileSize(properties[fileSize]), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ + if(!properites["socket"].isNull()) + setSocket(properites["socket"]); + else if(!properites["hostAddress"].isNull() && !properites["hostPort"].isNull()) + setSocket(new KExtendedSocket(properites["hostAddress"], properites["hostPort"])); + + connect(this, SIGNAL(complete()), + this, SLOT(closeSocket())); + + connect(this, SIGNAL(abort(QString)), + this, SLOT(closeSocket())); +} +*/ +Transfer::~Transfer() +{ + closeSocket(); + // m_file is automatically closed on destroy. +} + +Transfer::Status Transfer::status() const +{ + if(m_socket) + { +// return (Transfer::Status)m_socket->socketStatus(); + return Connected; + } + return Error_NoSocket; +} + +void Transfer::slotError( int error ) +{ + // Connection in progress.. This is a signal fired wrong + if (m_socket->socketStatus () != KExtendedSocket::connecting) + { + abort(KExtendedSocket::strError(m_socket->status(), m_socket->systemError())); +// closeSocket(); + } +} + +bool Transfer::initiate() +{ + QTimer *timer = 0; + + if(m_initiated) + { + kdDebug(14121) << k_funcinfo << "Transfer allready initiated" << endl; + return false; + } + + if(!m_socket) + { + kdDebug(14121) << k_funcinfo << "Socket not set" << endl; + return false; + } + + m_initiated = true; + + m_file.setName(m_fileName); + + connect(this, SIGNAL(complete()), + this, SLOT(closeSocket())); + connect(this, SIGNAL(abort(QString)), + this, SLOT(closeSocket())); + +// connect(m_socket, SIGNAL(connectionClosed()), +// this, SLOT(slotConnectionClosed())); +// connect(m_socket, SIGNAL(delayedCloseFinished()), +// this, SLOT(slotConnectionClosed())); + connect(m_socket, SIGNAL(error(int)), // FIXME: connection failed: No such signal KExtendedSocket::error(int) + this, SLOT(slotError(int))); + + switch( m_type ) + { + case Chat: + kdDebug(14121) << k_funcinfo << "Stting up a chat." << endl; + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileIncoming())); + break; + case FileIncoming: + kdDebug(14121) << k_funcinfo << "Stting up an incoming file transfer." << endl; + m_file.open(IO_WriteOnly); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileIncoming())); + break; + case FileOutgoing: + kdDebug(14121) << k_funcinfo << "Stting up an outgoing file transfer." << endl; + m_file.open(IO_ReadOnly); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileOutgoing())); +// timer = new QTimer(this); +// connect(timer, SIGNAL(timeout()), +// this, SLOT(writeFileOutgoing())); +// timer->start(1000, false); + writeFileOutgoing(); // send a first packet. + break; + default: + kdDebug(14121) << k_funcinfo << "Closing transfer: Unknown extra initiation for type:" << m_type << endl; + m_socket->close(); + return false; + break; + } + +// if(status()==Idle) + if(m_socket->status()==KExtendedSocket::nothing) + m_socket->connect(); + + m_socket->enableRead(true); + m_socket->enableWrite(true); + + m_socketDataStream.setDevice(m_socket); + + // I wonder if calling this is really necessary + // As far as I understand, buffer (socket buffer at least) should be flushed while event-looping. + // But I'm not really sure of this, so I force the flush. + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), + this, SLOT(flush())); + timer->start(1000, FALSE); // flush the streams at every seconds + + return true; +} + +bool Transfer::setSocket( KExtendedSocket *socket ) +{ + if (!m_socket) + { + m_socket = socket; + return true; + } + else + kdDebug(14121) << k_funcinfo << "Socket allready set" << endl; + return false; +} + +void Transfer::closeSocket() +{ + if(m_socket) + { + m_socket->close(); +// m_socket->reset(); + m_socket->deleteLater(); + } + m_socket = 0; +} + +/* + * This slot ensure that all the stream are flushed. + * This slot is called periodically internaly. + */ + void Transfer::flush() +{ + /* + * Enure the incoming file content in case of a crash. + */ + if(m_file.isOpen() && m_file.isWritable()) + m_file.flush(); + + /* + * Ensure that non interactive streams outputs (i.e file transfer acknowledge by example) + * are sent (Don't stay in a local buffer). + */ + if(m_socket && status() == Connected) + m_socket->flush(); +} + +void Transfer::userAbort(QString msg) +{ + emit abort(msg); +} + +void Transfer::setCodec( QTextCodec *codec ) +{ + switch( m_type ) + { + case Chat: + m_socket_textStream.setCodec( codec ); + break; + default: +// operation not permitted on this type. + break; + } +} + +void Transfer::writeLine( const QString &line ) +{ + switch( m_type ) + { + case Chat: +// m_socket.flush(); + break; + default: +// operation not permitted on this type. + break; + } +} + +void Transfer::readyReadLine() +{ + if( m_socket->canReadLine() ) + { + QString msg = m_socket_textStream.readLine(); + emit readLine(msg); + } +} + +void Transfer::readyReadFileIncoming() +{ + kdDebug(14121) << k_funcinfo << endl; + + m_bufferLength = m_socket->readBlock(m_buffer, sizeof(m_buffer)); + + if(m_bufferLength > 0) + { + int written = m_file.writeBlock(m_buffer, m_bufferLength); + if(m_bufferLength == written) + { + m_fileSizeCur += written; + m_fileSizeAck = m_fileSizeCur; + m_socketDataStream << m_fileSizeAck; + checkFileTransferEnd(m_fileSizeAck); + return; + } + else + // Something bad happened while writting. + abort(m_file.errorString()); + } + else if(m_bufferLength == -1) + abort("Error while reading socket."); +} + +void Transfer::readyReadFileOutgoing() +{ + kdDebug(14121) << k_funcinfo << "Available bytes:" << m_socket->bytesAvailable() << endl; + + bool hadData = false; + Q_UINT32 fileSizeAck = 0; + +// if (m_socket->bytesAvailable() >= sizeof(fileSizeAck)) // BUGGY: bytesAvailable() that allways return 0 on unbuffered sockets. + { + m_socketDataStream >> fileSizeAck; + hadData = true; + } + + if (hadData) + { + checkFileTransferEnd(fileSizeAck); + writeFileOutgoing(); + } +} + +void Transfer::writeFileOutgoing() +{ + kdDebug(14121) << k_funcinfo << endl; + + if (m_fileSizeAck < m_fileSize) + { + m_bufferLength = m_file.readBlock(m_buffer, sizeof(m_buffer)); + if (m_bufferLength > 0) + { + Q_UINT32 read = m_socket->writeBlock(m_buffer, m_bufferLength); // should check written == read + +// if(read != m_buffer_length) +// buffer is not cleared still + + m_fileSizeCur += read; +// m_socket->flush(); // Should think on using this + emit fileSizeCurrent( m_fileSizeCur ); + } + else if(m_bufferLength == -1) + abort("Error while reading file."); + } +} + +void Transfer::checkFileTransferEnd(Q_UINT32 fileSizeAck) +{ + kdDebug(14121) << k_funcinfo << "Acknowledged:" << fileSizeAck << endl; + + m_fileSizeAck = fileSizeAck; + emit fileSizeAcknowledge(m_fileSizeAck); + + if(m_fileSizeAck > m_fileSize) + abort(i18n("Acknowledge size is greater than the expected file size")); + + if(m_fileSizeAck == m_fileSize) + emit complete(); +} + +#include "kirctransfer.moc" |