/* kirctransfer.cpp - IRC transfer. Copyright (c) 2003-2004 by Michel Hermier Kopete (c) 2003-2004 by the Kopete developers ************************************************************************* * * * 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 #include #include #include #include #include "kirctransfer.h" using namespace KIRC; Transfer::Transfer( Engine *engine, TQString nick,// TQString nick_peer_adress Type type, TQObject *parent, const char *name ) : TQObject( parent, name ), m_engine(engine), m_nick(nick), m_type(type), m_socket(0), m_initiated(false), m_file(0), m_fileName(TQString()), 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, TQString nick,// TQString nick_peer_adress Transfer::Type type, TQString fileName, TQ_UINT32 fileSize, // put this in a TQVariant ? TQObject *parent, const char *name ) : TQObject( 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, TQString nick,// TQString nick_peer_adress TQHostAddress hostAdress, TQ_UINT16 port, // put this in a TQVariant ? Transfer::Type type, TQString fileName, TQ_UINT32 fileSize, // put this in a TQVariant ? TQObject *parent, const char *name ) : TQObject( 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, TQString nick,// TQString nick_peer_adress Transfer::Type type, TQVariant properties, TQObject *parent, const char *name ) : TQObject( 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, TQT_SIGNAL(complete()), this, TQT_SLOT(closeSocket())); connect(this, TQT_SIGNAL(abort(TQString)), this, TQT_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() { TQTimer *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, TQT_SIGNAL(complete()), this, TQT_SLOT(closeSocket())); connect(this, TQT_SIGNAL(abort(TQString)), this, TQT_SLOT(closeSocket())); // connect(m_socket, TQT_SIGNAL(connectionClosed()), // this, TQT_SLOT(slotConnectionClosed())); // connect(m_socket, TQT_SIGNAL(delayedCloseFinished()), // this, TQT_SLOT(slotConnectionClosed())); connect(m_socket, TQT_SIGNAL(error(int)), // FIXME: connection failed: No such signal KExtendedSocket::error(int) this, TQT_SLOT(slotError(int))); switch( m_type ) { case Chat: kdDebug(14121) << k_funcinfo << "Stting up a chat." << endl; connect(m_socket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(readyReadFileIncoming())); break; case FileIncoming: kdDebug(14121) << k_funcinfo << "Stting up an incoming file transfer." << endl; m_file.open(IO_WriteOnly); connect(m_socket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(readyReadFileIncoming())); break; case FileOutgoing: kdDebug(14121) << k_funcinfo << "Stting up an outgoing file transfer." << endl; m_file.open(IO_ReadOnly); connect(m_socket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(readyReadFileOutgoing())); // timer = new TQTimer(this); // connect(timer, TQT_SIGNAL(timeout()), // this, TQT_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 TQTimer(this); connect(timer, TQT_SIGNAL(timeout()), this, TQT_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(TQString msg) { emit abort(msg); } void Transfer::setCodec( TQTextCodec *codec ) { switch( m_type ) { case Chat: m_socket_textStream.setCodec( codec ); break; default: // operation not permitted on this type. break; } } void Transfer::writeLine( const TQString &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() ) { TQString 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; TQ_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) { TQ_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(TQ_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"