You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
konversation/konversation/src/dcctransferrecv.cpp

800 lines
26 KiB

/*
receive a file on DCC protocol
begin: Mit Aug 7 2002
copyright: (C) 2002 by Dario Abatianni
email: eisfuchs@tigress.com
*/
// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
/*
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 "dcctransferrecv.h"
#include "dcccommon.h"
#include "channel.h"
#include "dcctransfermanager.h"
#include "konversationapplication.h"
#include "connectionmanager.h"
#include "server.h"
#include <kdebug.h>
#include <tdefileitem.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kserversocket.h>
#include <kstandarddirs.h>
#include <kstreamsocket.h>
#include <kdirselectdialog.h>
#include <kuser.h>
#include <tdeio/job.h>
#include <tdeio/jobclasses.h>
#include <tdeio/netaccess.h>
class DccResumeDialog;
/*
*flow chart*
DccTransferRecv()
start() : called from DccTransferPanel when user pushes the accept button
| \
| requestResume() : called when user chooses to resume in DccResumeDialog. it emits the signal ResumeRequest()
|
| startResume() : called by "Server"
| |
connectToSender()
connectionSuccess() : called by recvSocket
*/
DccTransferRecv::DccTransferRecv(TQObject* parent)
: DccTransfer( DccTransfer::Receive, parent )
{
kdDebug() << "DccTransferRecv::DccTransferRecv()" << endl;
m_serverSocket = 0;
m_recvSocket = 0;
m_writeCacheHandler = 0;
m_connectionTimer = new TQTimer( this );
connect( m_connectionTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( connectionTimeout() ) );
//timer hasn't started yet. qtimer will be deleted automatically when 'this' object is deleted
}
DccTransferRecv::~DccTransferRecv()
{
cleanUp();
}
TQString DccTransferRecv::getTypeText() const
{
return i18n( "Receive" );
}
TQPixmap DccTransferRecv::getTypeIcon() const
{
return TDEGlobal::iconLoader()->loadIcon( "go-down", TDEIcon::Small );
}
void DccTransferRecv::cleanUp()
{
kdDebug() << "DccTransferRecv::cleanUp()" << endl;
stopConnectionTimer();
finishTransferLogger();
if ( m_serverSocket )
{
m_serverSocket->close();
m_serverSocket = 0;
}
if ( m_recvSocket )
{
m_recvSocket->close();
m_recvSocket = 0; // the instance will be deleted automatically by its parent
}
if ( m_writeCacheHandler )
{
m_writeCacheHandler->closeNow();
m_writeCacheHandler->deleteLater();
m_writeCacheHandler = 0;
}
}
// just for convenience
void DccTransferRecv::failed( const TQString& errorMessage )
{
setStatus( Failed, errorMessage );
cleanUp();
emit done( this );
}
void DccTransferRecv::setPartnerIp( const TQString& ip )
{
if ( getStatus() == Configuring )
m_partnerIp = ip;
}
void DccTransferRecv::setPartnerPort( const TQString& port )
{
if ( getStatus() == Configuring )
m_partnerPort = port;
}
void DccTransferRecv::setFileSize( unsigned long fileSize )
{
if ( getStatus() == Configuring )
m_fileSize = fileSize;
}
void DccTransferRecv::setFileName( const TQString& fileName )
{
if ( getStatus() == Configuring )
m_fileName = fileName;
}
void DccTransferRecv::setFileURL( const KURL& url )
{
if ( getStatus() == Configuring || getStatus() == Queued )
m_fileURL = url;
}
void DccTransferRecv::setReverse( bool reverse, const TQString& reverseToken )
{
if ( getStatus() == Configuring )
{
m_reverse = reverse;
if ( reverse )
{
m_partnerPort = TQString::number( 0 );
m_reverseToken = reverseToken;
}
}
}
bool DccTransferRecv::queue()
{
kdDebug() << "DccTransferRecv::queue()" << endl;
if ( getStatus() != Configuring )
return false;
if ( m_partnerIp.isEmpty() || m_partnerPort.isEmpty() )
return false;
if (!kapp->authorize("allow_downloading"))
{
//note we have this after the initialisations so that item looks okay
//Do not have the rights to send the file. Shouldn't have gotten this far anyway
failed(i18n("The admin has restricted the right to receive files"));
return false;
}
// check if the sender IP is valid
if ( m_partnerIp == "0.0.0.0" )
{
failed( i18n( "Invalid sender address (%1)" ).arg( m_partnerIp ) );
return false;
}
// TODO: should we support it?
if ( m_fileSize == 0 )
{
failed( i18n( "Unsupported negotiation (filesize=0)" ) );
return false;
}
if ( m_fileName.isEmpty() )
{
m_fileName = "unnamed_file";
}
if ( m_fileURL.isEmpty() )
{
// determine default incoming file URL
// set default folder
if ( !Preferences::dccPath().isEmpty() )
m_fileURL = KURL( Preferences::dccPath() );
else
m_fileURL.setPath( KUser( KUser::UseRealUserID ).homeDir() ); // default folder is *not* specified
// add a slash if there is none
m_fileURL.adjustPath( 1 );
// Append folder with partner's name if wanted
if ( Preferences::dccCreateFolder() )
m_fileURL.addPath( m_partnerNick + '/' );
// Just incase anyone tries to do anything nasty
TQString fileNameSanitized = sanitizeFileName( m_fileName );
// Append partner's name to file name if wanted
if ( Preferences::dccAddPartner() )
m_fileURL.addPath( m_partnerNick + '.' + fileNameSanitized );
else
m_fileURL.addPath( fileNameSanitized );
}
return DccTransfer::queue();
}
void DccTransferRecv::abort() // public slot
{
kdDebug() << "DccTransferRecv::abort()" << endl;
if(m_writeCacheHandler)
{
m_writeCacheHandler->write( true ); // flush
}
setStatus( Aborted );
cleanUp();
emit done( this );
}
void DccTransferRecv::start() // public slot
{
kdDebug() << "DccTransferRecv::start() [BEGIN]" << endl;
if ( getStatus() != Queued )
return;
setStatus( Preparing );
prepareLocalKio( false, false );
kdDebug() << "DccTransferRecv::start() [END]" << endl;
}
void DccTransferRecv::prepareLocalKio( bool overwrite, bool resume, TDEIO::fileoffset_t startPosition /* = 0 */ )
{
kdDebug() << "DccTransferRecv::prepareLocalKio()" << endl
<< "DccTransferRecv::prepareLocalKio(): URL: " << m_fileURL << endl
<< "DccTransferRecv::prepareLocalKio(): Overwrite: " << overwrite << endl
<< "DccTransferRecv::prepareLocalKio(): Resume: " << resume << " (Position: " << TQString::number( startPosition ) << ")" << endl;
m_resumed = resume;
m_transferringPosition = startPosition;
if ( !createDirs( m_fileURL.upURL() ) )
{
askAndPrepareLocalKio( i18n( "<b>Cannot create the folder.</b><br>"
"Folder: %1<br>" )
.arg( m_fileURL.upURL().prettyURL() ),
DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
DccResumeDialog::RA_Rename );
return;
}
TDEIO::TransferJob* transferJob = TDEIO::put( m_fileURL, -1, overwrite, m_resumed, false );
if ( !transferJob )
{
kdDebug() << "DccTransferRecv::prepareLocalKio(): TDEIO::put() returned NULL. what happened?" << endl;
failed( i18n( "Could not create a TDEIO instance" ) );
return;
}
connect( transferJob, TQ_SIGNAL( canResume( TDEIO::Job*, TDEIO::filesize_t ) ), this, TQ_SLOT( slotLocalCanResume( TDEIO::Job*, TDEIO::filesize_t ) ) );
connect( transferJob, TQ_SIGNAL( result( TDEIO::Job* ) ), this, TQ_SLOT( slotLocalGotResult( TDEIO::Job* ) ) );
connect( transferJob, TQ_SIGNAL( dataReq( TDEIO::Job*, TQByteArray& ) ), this, TQ_SLOT( slotLocalReady( TDEIO::Job* ) ) );
}
void DccTransferRecv::askAndPrepareLocalKio( const TQString& message, int enabledActions, DccResumeDialog::ReceiveAction defaultAction, TDEIO::fileoffset_t startPosition )
{
switch ( DccResumeDialog::ask( this, message, enabledActions, defaultAction ) )
{
case DccResumeDialog::RA_Resume:
prepareLocalKio( false, true, startPosition );
break;
case DccResumeDialog::RA_Overwrite:
prepareLocalKio( true, false );
break;
case DccResumeDialog::RA_Rename:
prepareLocalKio( false, false );
break;
case DccResumeDialog::RA_Cancel:
default:
setStatus( Queued );
}
}
bool DccTransferRecv::createDirs( const KURL& dirURL ) const
{
KURL kurl( dirURL );
TQString surl = kurl.url();
//First we split directories until we reach to the top,
//since we need to create directories one by one
TQStringList dirList;
while ( surl != kurl.upURL().url() )
{
dirList.prepend( surl );
kurl = kurl.upURL();
surl = kurl.url();
}
//Now we create the directories
TQStringList::ConstIterator it;
for ( it=dirList.begin() ; it!=dirList.end() ; ++it )
if ( !TDEIO::NetAccess::exists( *it, true, NULL ) )
if ( !TDEIO::NetAccess::mkdir( *it, NULL, -1 ) )
return false;
return true;
}
void DccTransferRecv::slotLocalCanResume( TDEIO::Job* job, TDEIO::filesize_t size )
{
kdDebug() << "DccTransferRecv::slotLocalCanResume() [BEGIN]" << endl
<< "DccTransferRecv::slotLocalCanResume(): size: " << TQString::number( size ) << endl;
if ( size != 0 )
{
TDEIO::TransferJob* transferJob = static_cast<TDEIO::TransferJob*>( job );
disconnect( transferJob, 0, 0, 0 );
transferJob->kill();
if ( KonversationApplication::instance()->getDccTransferManager()->isLocalFileInWritingProcess( m_fileURL ) )
{
askAndPrepareLocalKio( i18n( "<b>The file is used by another transfer.</b><br>"
"%1<br>" )
.arg( m_fileURL.prettyURL() ),
DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
DccResumeDialog::RA_Rename );
}
else if ( Preferences::dccAutoResume() )
{
prepareLocalKio( false, true, size );
}
else
{
askAndPrepareLocalKio( i18n( "<b>A partial file exists.</b><br>"
"%1<br>"
"Size of the partial file: %2 bytes<br>" )
.arg( m_fileURL.prettyURL() )
.arg( TDEGlobal::locale()->formatNumber( size, 0 ) ),
DccResumeDialog::RA_Resume | DccResumeDialog::RA_Overwrite | DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
DccResumeDialog::RA_Resume,
size );
}
}
kdDebug() << "DccTransferRecv::slotLocalCanResume() [END]" << endl;
}
void DccTransferRecv::slotLocalGotResult( TDEIO::Job* job )
{
kdDebug() << "DccTransferRecv::slotLocalGotResult() [BEGIN]" << endl;
TDEIO::TransferJob* transferJob = static_cast<TDEIO::TransferJob*>( job );
disconnect( transferJob, 0, 0, 0 );
switch ( transferJob->error() )
{
case 0: // no error
kdDebug() << "DccTransferRecv::slotLocalGotResult(): job->error() returned 0." << endl
<< "DccTransferRecv::slotLocalGotResult(): Why was I called in spite of no error?" << endl;
break;
case TDEIO::ERR_FILE_ALREADY_EXIST:
askAndPrepareLocalKio( i18n( "<b>The file already exists.</b><br>"
"%1<br>" )
.arg( m_fileURL.prettyURL() ),
DccResumeDialog::RA_Overwrite | DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
DccResumeDialog::RA_Overwrite );
break;
default:
askAndPrepareLocalKio( i18n( "<b>Could not open the file.<br>"
"Error: %1</b><br>"
"%2<br>" )
.arg( transferJob->error() )
.arg( m_fileURL.prettyURL() ),
DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
DccResumeDialog::RA_Rename );
}
kdDebug() << "DccTransferRecv::slotLocalGotResult() [END]" << endl;
}
void DccTransferRecv::slotLocalReady( TDEIO::Job* job )
{
kdDebug() << "DccTransferRecv::slotLocalReady()" << endl;
TDEIO::TransferJob* transferJob = static_cast<TDEIO::TransferJob*>( job );
disconnect( transferJob, 0, 0, 0 ); // WriteCacheHandler will control the job after this
m_writeCacheHandler = new DccTransferRecvWriteCacheHandler( transferJob );
connect( m_writeCacheHandler, TQ_SIGNAL( done() ), this, TQ_SLOT( slotLocalWriteDone() ) );
connect( m_writeCacheHandler, TQ_SIGNAL( gotError( const TQString& ) ), this, TQ_SLOT( slotLocalGotWriteError( const TQString& ) ) );
if ( !m_resumed )
connectWithSender();
else
requestResume();
}
void DccTransferRecv::connectWithSender()
{
if ( m_reverse )
{
if ( !startListeningForSender() )
return;
Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId );
if ( !server )
{
failed( i18n( "Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server." ) );
}
m_ownIp = DccCommon::getOwnIp( server );
m_ownPort = TQString::number( DccCommon::getServerSocketPort( m_serverSocket ) );
setStatus( WaitingRemote, i18n( "Waiting for connection" ) );
server->dccReverseSendAck( m_partnerNick, m_fileName, DccCommon::textIpToNumericalIp( m_ownIp ), m_ownPort, m_fileSize, m_reverseToken );
//FIXME: add connection timer here
}
else
{
connectToSendServer();
}
}
void DccTransferRecv::requestResume()
{
kdDebug() << "DccTransferRecv::requestResume()" << endl;
setStatus( WaitingRemote, i18n( "Waiting for remote host's acceptance" ) );
startConnectionTimer( 30 );
kdDebug() << "DccTransferRecv::requestResume(): requesting resume for " << m_partnerNick << " file " << m_fileName << " partner " << m_partnerPort << endl;
//TODO m_filename could have been sanitized - will this effect this?
Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId );
if ( !server )
{
kdDebug() << "DccTransferSend::start(): could not retrieve the instance of Server. Connection id: " << m_connectionId << endl;
failed( i18n( "Could not send DCC RECV resume request to the partner via the IRC server." ) );
return;
}
server->dccResumeGetRequest( m_partnerNick, m_fileName, m_partnerPort, m_transferringPosition );
}
// public slot
void DccTransferRecv::startResume( unsigned long position )
{
kdDebug() << "DccTransferRecv::startResume(): position: " << position << endl;
stopConnectionTimer();
if ( (unsigned long)m_transferringPosition != position )
{
kdDebug() << "DccTransferRecv::startResume(): remote responsed an unexpected position" << endl
<< "DccTransferRecv::startResume(): expected: " << TQString::number( m_transferringPosition ) << endl
<< "DccTransferRecv::startResume(): remote response: " << position << endl;
failed( i18n( "Unexpected response from remote host" ) );
return;
}
connectWithSender();
}
void DccTransferRecv::connectToSendServer()
{
kdDebug() << "DccTransferRecv::connectToSendServer()" << endl;
// connect to sender
setStatus( Connecting );
m_recvSocket = new KNetwork::KStreamSocket( m_partnerIp, m_partnerPort, this);
m_recvSocket->setBlocking( false ); // asynchronous mode
m_recvSocket->setFamily( KNetwork::KResolver::InetFamily );
m_recvSocket->setResolutionEnabled( false );
m_recvSocket->setTimeout( 30000 );
m_recvSocket->enableRead( false );
m_recvSocket->enableWrite( false );
connect( m_recvSocket, TQ_SIGNAL( connected( const KResolverEntry& ) ), this, TQ_SLOT( startReceiving() ) );
connect( m_recvSocket, TQ_SIGNAL( gotError( int ) ), this, TQ_SLOT( connectionFailed( int ) ) );
kdDebug() << "DccTransferRecv::connectToServer(): attempting to connect to " << m_partnerIp << ":" << m_partnerPort << endl;
m_recvSocket->connect();
}
bool DccTransferRecv::startListeningForSender()
{
// Set up server socket
TQString failedReason;
if ( Preferences::dccSpecificSendPorts() )
m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason, Preferences::dccSendPortsFirst(), Preferences::dccSendPortsLast() );
else
m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason );
if ( !m_serverSocket )
{
failed( failedReason );
return false;
}
connect( m_serverSocket, TQ_SIGNAL( readyAccept() ), this, TQ_SLOT( slotServerSocketReadyAccept() ) );
connect( m_serverSocket, TQ_SIGNAL( gotError( int ) ), this, TQ_SLOT( slotServerSocketGotError( int ) ) );
return true;
}
void DccTransferRecv::slotServerSocketReadyAccept()
{
//stopConnectionTimer()
m_recvSocket = static_cast<KNetwork::KStreamSocket*>( m_serverSocket->accept() );
if ( !m_recvSocket )
{
failed( i18n( "Could not accept the connection. (Socket Error)" ) );
return;
}
connect( m_recvSocket, TQ_SIGNAL( gotError( int ) ), this, TQ_SLOT( connectionFailed( int ) ) );
// we don't need ServerSocket anymore
m_serverSocket->close();
startReceiving();
}
void DccTransferRecv::slotServerSocketGotError( int /* errorCode*/ )
{
failed( i18n( "Socket error: %1" ).arg( m_serverSocket->errorString() ) );
}
void DccTransferRecv::startReceiving()
{
kdDebug() << "DccTransferRecv::startReceiving()" << endl;
m_recvSocket->setBlocking( false ); // asynchronous mode
connect( m_recvSocket, TQ_SIGNAL( readyRead() ), this, TQ_SLOT( readData() ) );
connect( m_recvSocket, TQ_SIGNAL( readyWrite() ), this, TQ_SLOT( sendAck() ) );
connect( m_recvSocket, TQ_SIGNAL( closed() ), this, TQ_SLOT( slotSocketClosed() ) );
setStatus( Transferring );
m_transferStartPosition = m_transferringPosition;
m_recvSocket->enableRead( true );
m_recvSocket->enableWrite( false );
startTransferLogger(); // initialize CPS counter, ETA counter, etc...
}
// slot
void DccTransferRecv::connectionFailed( int errorCode )
{
kdDebug() << "DccTransferRecv::connectionFailed(): code = " << errorCode << ", string = " << m_recvSocket->TDESocketBase::errorString() << endl;
failed( i18n( "Connection failure: %1" ).arg( m_recvSocket->TDESocketBase::errorString() ) );
}
void DccTransferRecv::readData() // slot
{
//kdDebug() << "readData()" << endl;
int actual = m_recvSocket->readBlock( m_buffer, m_bufferSize );
if ( actual > 0 )
{
//actual is the size we read in, and is guaranteed to be less than m_bufferSize
m_transferringPosition += actual;
m_writeCacheHandler->append( m_buffer, actual );
m_writeCacheHandler->write( false );
m_recvSocket->enableWrite( true );
}
}
void DccTransferRecv::sendAck() // slot
{
//kdDebug() << "sendAck()" << endl;
TDEIO::fileoffset_t pos = intel( m_transferringPosition );
m_recvSocket->enableWrite( false );
m_recvSocket->writeBlock( (char*)&pos, 4 );
if ( m_transferringPosition == (TDEIO::fileoffset_t)m_fileSize )
{
kdDebug() << "DccTransferRecv::sendAck(): Sent final ACK." << endl;
m_recvSocket->enableRead( false );
disconnect( m_recvSocket, 0, 0, 0 );
finishTransferLogger();
m_writeCacheHandler->close(); // WriteCacheHandler will send the signal done()
}
else if ( m_transferringPosition > (TDEIO::fileoffset_t)m_fileSize )
{
kdDebug() << "DccTransferRecv::sendAck(): the remote host sent larger data than expected: " << TQString::number( m_transferringPosition ) << endl;
failed( i18n( "Transferring error" ) );
}
}
void DccTransferRecv::slotLocalWriteDone() // <-WriteCacheHandler::done()
{
kdDebug() << "DccTransferRecv::slotLocalWriteDone()" << endl;
setStatus( Done );
cleanUp();
emit done( this );
}
// <- WriteCacheHandler::gotError()
void DccTransferRecv::slotLocalGotWriteError( const TQString& errorString )
{
kdDebug() << "DccTransferRecv::slotLocalGotWriteError()" << endl;
failed( i18n( "TDEIO error: %1" ).arg( errorString ) );
}
void DccTransferRecv::startConnectionTimer( int sec )
{
stopConnectionTimer();
kdDebug() << "DccTransferRecv::startConnectionTimer()" << endl;
m_connectionTimer->start( sec*1000, true );
}
void DccTransferRecv::stopConnectionTimer()
{
if ( m_connectionTimer->isActive() )
{
m_connectionTimer->stop();
kdDebug() << "DccTransferRecv::stopConnectionTimer(): stop" << endl;
}
}
void DccTransferRecv::connectionTimeout() // slot
{
kdDebug() << "DccTransferRecv::connectionTimeout()" << endl;
failed( i18n( "Timed out" ) );
}
void DccTransferRecv::slotSocketClosed()
{
finishTransferLogger();
if ( getStatus() == Transferring )
failed( i18n( "Remote user disconnected" ) );
}
// WriteCacheHandler
DccTransferRecvWriteCacheHandler::DccTransferRecvWriteCacheHandler( TDEIO::TransferJob* transferJob )
: m_transferJob( transferJob )
{
m_writeReady = true;
m_cacheStream = 0;
connect( this, TQ_SIGNAL( dataFinished() ), m_transferJob, TQ_SLOT( slotFinished() ) );
connect( m_transferJob, TQ_SIGNAL( dataReq( TDEIO::Job*, TQByteArray& ) ), this, TQ_SLOT( slotKIODataReq( TDEIO::Job*, TQByteArray& ) ) );
connect( m_transferJob, TQ_SIGNAL( result( TDEIO::Job* ) ), this, TQ_SLOT( slotKIOResult( TDEIO::Job* ) ) );
m_transferJob->setAsyncDataEnabled( m_writeAsyncMode = true );
}
DccTransferRecvWriteCacheHandler::~DccTransferRecvWriteCacheHandler()
{
closeNow();
}
// public
void DccTransferRecvWriteCacheHandler::append( char* data, int size )
{
// sendAsyncData() and dataReq() cost a lot of time, so we should pack some caches.
// 1meg
static const unsigned int maxWritePacketSize = 1 * 1024 * 1024;
if ( m_cacheList.isEmpty() || m_cacheList.back().size() + size > maxWritePacketSize )
{
m_cacheList.append( TQByteArray() );
delete m_cacheStream;
m_cacheStream = new TQDataStream( m_cacheList.back(), IO_WriteOnly );
}
m_cacheStream->writeRawBytes( data, size );
}
// public
bool DccTransferRecvWriteCacheHandler::write( bool force )
{
// force == false: return without doing anything when the whole cache size is smaller than maxWritePacketSize
if ( m_cacheList.isEmpty() || !m_transferJob || !m_writeReady || !m_writeAsyncMode )
return false;
if ( !force && m_cacheList.count() < 2 )
return false;
// do write
m_writeReady = false;
m_transferJob->sendAsyncData( m_cacheList.front() );
//kdDebug() << "DTRWriteCacheHandler::write(): wrote " << m_cacheList.front().size() << " bytes." << endl;
m_cacheList.pop_front();
return true;
}
void DccTransferRecvWriteCacheHandler::close() // public
{
kdDebug() << "DTRWriteCacheHandler::close()" << endl;
write( true ); // write once if tdeio is ready to write
m_transferJob->setAsyncDataEnabled( m_writeAsyncMode = false );
kdDebug() << "DTRWriteCacheHandler::close(): switched to synchronized mode." << endl;
kdDebug() << "DTRWriteCacheHandler::close(): flushing... (remaining caches: " << m_cacheList.count() << ")" << endl;
}
void DccTransferRecvWriteCacheHandler::closeNow() // public
{
write( true ); // flush
if ( m_transferJob )
{
m_transferJob->kill();
m_transferJob = 0;
}
m_cacheList.clear();
delete m_cacheStream;
m_cacheStream = 0;
}
void DccTransferRecvWriteCacheHandler::slotKIODataReq( TDEIO::Job*, TQByteArray& data )
{
// We are in writeAsyncMode if there is more data to be read in from dcc
if ( m_writeAsyncMode )
m_writeReady = true;
else
{
// No more data left to read from incoming dcctransfer
if ( !m_cacheList.isEmpty() )
{
// once we write everything in cache, the file is complete.
// This function will be called once more after this last data is written.
data = m_cacheList.front();
kdDebug() << "DccTransferRecvWriteCacheHandler::slotKIODataReq(): will write " << m_cacheList.front().size() << " bytes." << endl;
m_cacheList.pop_front();
}
else
{
// finally, no data left to write or read.
kdDebug() << "DTRWriteCacheHandler::slotKIODataReq(): flushing done." << endl;
m_transferJob = 0;
emit done(); // -> DccTransferRecv::slotLocalWriteDone()
}
}
}
void DccTransferRecvWriteCacheHandler::slotKIOResult( TDEIO::Job* job )
{
Q_ASSERT( m_transferJob );
disconnect( m_transferJob, 0, 0, 0 );
m_transferJob = 0;
if ( job->error() )
{
TQString errorString = job->errorString();
closeNow();
emit gotError( errorString ); // -> DccTransferRecv::slotLocalGotWriteError()
}
}
#include "dcctransferrecv.moc"