2 kopetetransfermanager.cpp
4 Copyright (c) 2002-2003 by Nick Betcher <nbetcher@kde.org>
5 Copyright (c) 2002-2003 by Richard Smith <kopete@metafoo.co.uk>
6 Copyright (c) 2008 by Roman Jarosz <kedgedev@centrum.cz>
8 Kopete (c) 2002-2008 by the Kopete developers <kopete-devel@kde.org>
10 *************************************************************************
12 * This library is free software; you can redistribute it and/or *
13 * modify it under the terms of the GNU Lesser General Public *
14 * License as published by the Free Software Foundation; either *
15 * version 2 of the License, or (at your option) any later version. *
17 *************************************************************************
20 #include <QtCore/QTimerEvent>
21 #include <QtGui/QTextDocument>
25 #include <kfiledialog.h>
26 #include <kfileitem.h>
27 #include <kmessagebox.h>
28 #include <kio/jobuidelegate.h>
29 #include <kuiserverjobtracker.h>
31 #include "kopetemetacontact.h"
32 #include "kopetecontact.h"
33 #include "kopetemessage.h"
34 #include "kopetechatsession.h"
35 #include "kopeteuiglobal.h"
37 #include "kopetetransfermanager.h"
39 /***************************
40 * Kopete::FileTransferInfo *
41 ***************************/
43 Kopete::FileTransferInfo::FileTransferInfo()
48 mSaveToDirectory
= false;
51 Kopete::FileTransferInfo::FileTransferInfo( Kopete::Contact
*contact
, const QStringList
& files
, const unsigned long size
, const QString
&recipient
, KopeteTransferDirection di
, const unsigned int id
, QString internalId
, const QPixmap
&preview
, bool saveToDirectory
)
57 mRecipient
= recipient
;
61 mSaveToDirectory
= saveToDirectory
;
64 /***************************
66 ***************************/
68 static const int TransferRateWindowLength
= 10;
69 static const int TransferRateTimerDelay
= 1000;
71 class Kopete::Transfer::Private
74 Private( const Kopete::FileTransferInfo
&ftInfo
)
75 : info( ftInfo
), transferRateTimer( 0 )
77 memset( transferRate
, 0, sizeof(transferRate
) );
80 FileTransferInfo info
;
83 //if ft has one file then localUrl is file otherwise it's directory
86 int transferRate
[TransferRateWindowLength
];
87 int transferRateTimer
;
90 Kopete::Transfer::Transfer( const Kopete::FileTransferInfo
&kfti
, const QString
&localFile
, bool showProgressInfo
)
91 : KIO::Job(), d( new Private(kfti
) )
93 this->setUiDelegate(new KIO::JobUiDelegate());
95 KIO::getJobTracker()->registerJob(this);
97 KUrl targ
; targ
.setPath( localFile
);
99 init( targ
, showProgressInfo
);
102 Kopete::Transfer::Transfer( const Kopete::FileTransferInfo
&kfti
, const Kopete::Contact
*contact
, bool showProgressInfo
)
103 : KIO::Job(), d( new Private(kfti
) )
105 this->setUiDelegate(new KIO::JobUiDelegate());
107 KIO::getJobTracker()->registerJob(this);
109 // TODO: use mInfo.url().fileName() after move to protocol-aware filetransfers
110 KUrl targ
; targ
.setPath( d
->info
.file() );
111 init( displayURL( contact
, targ
.fileName() ), showProgressInfo
);
114 void Kopete::Transfer::init( const KUrl
&target
, bool showProgressInfo
)
117 setTotalAmount( KJob::Files
, d
->info
.files().count() );
118 setTotalAmount( KJob::Bytes
, d
->info
.size() );
120 if( showProgressInfo
)
121 emitCopying( sourceURL(), destinationURL() );
123 connect( this, SIGNAL( result( KJob
* ) ), SLOT( slotResultEmitted() ) );
125 ui()->setAutoErrorHandlingEnabled( true );
128 Kopete::Transfer::~Transfer()
130 stopTransferRateTimer();
135 const Kopete::FileTransferInfo
&Kopete::Transfer::info() const
140 KUrl
Kopete::Transfer::displayURL( const Kopete::Contact
*contact
, const QString
&file
)
143 url
.setProtocol( QString::fromLatin1("kopete") );
147 host
= QString::fromLatin1("unknown origin");
148 else if( contact
->metaContact() )
149 host
= contact
->metaContact()->displayName();
151 host
= contact
->contactId();
154 // url.setPath( contact->protocol()->displayName() );
156 url
.setFileName( file
);
160 void Kopete::Transfer::slotNextFile( const QString
&sourceFile
, const QString
&destinationFile
)
165 kDebug() << "source: " << sourceFile
<< " destination: " << destinationFile
;
166 if( d
->info
.direction() == Kopete::FileTransferInfo::Incoming
)
168 KUrl
url( sourceFile
);
169 src
= displayURL( d
->info
.contact(), url
.fileName() );
170 dest
.setPath( destinationFile
);
174 src
.setPath( sourceFile
);
175 KUrl
url( destinationFile
);
176 dest
= displayURL( d
->info
.contact(), url
.fileName() );
179 setProcessedAmount( KJob::Files
, processedAmount(KJob::Files
) + 1 );
180 emit
description(this, i18n("Copying"),
181 qMakePair(i18n("Source"), src
.prettyUrl()),
182 qMakePair(i18n("Destination"), dest
.prettyUrl()));
185 // TODO: add possibility of network file transfers;
186 // call mInfo->url() not file()
187 KUrl
Kopete::Transfer::sourceURL()
189 if( d
->info
.direction() == Kopete::FileTransferInfo::Incoming
)
190 return displayURL( d
->info
.contact(), d
->info
.file() );
193 KUrl url
; url
.setPath( d
->info
.file() );
198 KUrl
Kopete::Transfer::destinationURL()
203 void Kopete::Transfer::emitCopying(const KUrl
&src
, const KUrl
&dest
)
205 emit
description(this, i18n("Copying"),
206 qMakePair(i18n("Source"), src
.prettyUrl()),
207 qMakePair(i18n("Destination"), dest
.prettyUrl()));
210 void Kopete::Transfer::slotProcessed( unsigned int bytes
)
212 if ( !d
->transferRateTimer
)
213 d
->transferRateTimer
= startTimer( TransferRateTimerDelay
);
215 d
->transferRate
[0] += (bytes
- processedAmount(KJob::Bytes
));
217 setProcessedAmount( KJob::Bytes
, bytes
);
218 emitPercent( bytes
, d
->info
.size() );
221 void Kopete::Transfer::timerEvent( QTimerEvent
*event
)
223 if ( event
->timerId() != d
->transferRateTimer
)
225 KIO::Job::timerEvent( event
);
229 // Calculate average transferRate
230 qint64 bytesPerSecond
= 0;
231 for ( int i
= 0; i
< TransferRateWindowLength
; ++i
)
232 bytesPerSecond
+= d
->transferRate
[i
];
234 bytesPerSecond
/= qint64( TransferRateWindowLength
);
236 for ( int i
= TransferRateWindowLength
- 2; i
>= 0; --i
)
237 d
->transferRate
[i
+ 1] = d
->transferRate
[i
];
239 d
->transferRate
[0] = 0;
240 emitSpeed( bytesPerSecond
);
242 // Stop the timer if there is no activity.
243 if ( bytesPerSecond
== 0 )
244 stopTransferRateTimer();
247 void Kopete::Transfer::slotComplete()
249 stopTransferRateTimer();
250 showHtmlMessage( i18n("File transfer %1 completed.", fileForMessage() ) );
254 void Kopete::Transfer::slotError( int error
, const QString
&errorText
)
256 stopTransferRateTimer();
258 setErrorText(errorText
);
260 showHtmlMessage( i18n("File transfer %1 failed.", fileForMessage() ) );
264 void Kopete::Transfer::slotResultEmitted()
266 if( error() == KIO::ERR_USER_CANCELED
)
268 stopTransferRateTimer();
269 showHtmlMessage( i18n("You cancelled file transfer %1", fileForMessage() ) );
270 emit
transferCanceled();
274 void Kopete::Transfer::slotCancelled()
276 stopTransferRateTimer();
277 showHtmlMessage( i18n("File transfer %1 cancelled.", fileForMessage() ) );
279 //slotError( KIO::ERR_ABORTED, i18n("File transfer cancelled.") );
282 bool Kopete::Transfer::showMessage( QString text
) const
284 Kopete::ChatSession
*cs
= d
->info
.contact()->manager();
289 msg
.setPlainBody( text
);
290 cs
->appendMessage( msg
);
294 bool Kopete::Transfer::showHtmlMessage( QString text
) const
296 Kopete::ChatSession
*cs
= d
->info
.contact()->manager();
301 msg
.setHtmlBody( text
);
302 cs
->appendMessage( msg
);
306 QString
Kopete::Transfer::fileForMessage() const
308 if( d
->info
.direction() == Kopete::FileTransferInfo::Incoming
)
309 return QString( "<a href=\"%1\">%2</a>" ).arg( d
->localUrl
.url(), Qt::escape( d
->localUrl
.path() ) );
311 return Qt::escape( d
->info
.file() );
314 void Kopete::Transfer::stopTransferRateTimer()
316 if ( d
->transferRateTimer
)
318 killTimer( d
->transferRateTimer
);
319 d
->transferRateTimer
= 0;
323 /***************************
324 * Kopete::TransferManager *
325 ***************************/
327 Kopete::TransferManager
* Kopete::TransferManager::transferManager()
329 static TransferManager
s(0L);
333 Kopete::TransferManager::TransferManager( QObject
*parent
) : QObject( parent
)
337 Kopete::Transfer
* Kopete::TransferManager::addTransfer( Kopete::Contact
*contact
, const QString
& file
, const unsigned long size
, const QString
&recipient
, Kopete::FileTransferInfo::KopeteTransferDirection di
)
339 return addTransfer( contact
, QStringList(file
), size
, recipient
, di
);
342 Kopete::Transfer
* Kopete::TransferManager::addTransfer( Kopete::Contact
*contact
, const QStringList
& files
, const unsigned long size
, const QString
&recipient
, Kopete::FileTransferInfo::KopeteTransferDirection di
)
344 // Use message id to make file transfer id unique because we already use it for incoming file transfer.
345 uint id
= Kopete::Message::nextId();
346 Kopete::FileTransferInfo
info(contact
, files
, size
, recipient
, di
, id
);
347 Kopete::Transfer
*trans
= new Kopete::Transfer(info
, contact
);
348 connect(trans
, SIGNAL(result(KJob
*)), this, SLOT(slotComplete(KJob
*)));
349 mTransfersMap
.insert(id
, trans
);
353 unsigned int Kopete::TransferManager::askIncomingTransfer( Kopete::Contact
*contact
, const QString
& file
, const unsigned long size
, const QString
& description
, QString internalId
, const QPixmap
&preview
)
355 return askIncomingTransfer( contact
, QStringList( file
), size
, description
, internalId
, preview
);
358 unsigned int Kopete::TransferManager::askIncomingTransfer( Kopete::Contact
*contact
, const QStringList
& files
, const unsigned long size
, const QString
& description
, QString internalId
, const QPixmap
&preview
)
360 Kopete::ChatSession
*cs
= contact
->manager( Kopete::Contact::CanCreate
);
364 const QString dn
= contact
->metaContact() ? contact
->metaContact()->displayName() : contact
->contactId();
367 foreach ( const QString
&file
, files
)
369 QString trimmedFile
= file
.trimmed();
370 if ( !trimmedFile
.isEmpty() )
371 msgFileName
+= trimmedFile
+ ", ";
374 // Remove ", " from end
375 if ( msgFileName
.size() >= 2 )
376 msgFileName
= msgFileName
.left( msgFileName
.size() - 2 );
378 Kopete::Message
msg( contact
, cs
->myself() );
379 msg
.setType( Kopete::Message::TypeFileTransferRequest
);
380 msg
.setDirection( Kopete::Message::Inbound
);
381 msg
.setPlainBody( description
);
382 msg
.setFileName( msgFileName
);
383 msg
.setFileSize( size
);
384 msg
.setFilePreview( preview
);
386 Kopete::FileTransferInfo
info( contact
, files
, size
, dn
, Kopete::FileTransferInfo::Incoming
,
387 msg
.id(), internalId
, preview
, (files
.count() > 1) );
388 mTransferRequestInfoMap
.insert( msg
.id(), info
);
390 cs
->appendMessage( msg
);
395 void Kopete::TransferManager::saveIncomingTransfer( unsigned int id
)
397 Kopete::FileTransferInfo info
= mTransferRequestInfoMap
.value( id
);
398 if ( !info
.isValid() )
401 KConfigGroup
cg( KGlobal::config(), "File Transfer" );
402 const QString defaultPath
= cg
.readEntry( "defaultPath", QDir::homePath() );
403 KUrl url
= defaultPath
+ QLatin1String( "/" ) + info
.file();
405 if ( info
.saveToDirectory() )
406 url
= getSaveDir( url
);
408 url
= getSaveFile( url
);
410 if ( !url
.isValid() )
412 emit
askIncomingDone( id
);
413 emit
refused( info
);
414 mTransferRequestInfoMap
.remove( id
);
418 const QString directory
= ( info
.saveToDirectory() ) ? url
.path() : url
.directory();
419 if( !directory
.isEmpty() )
420 cg
.writeEntry( "defaultPath", directory
);
422 Kopete::Transfer
*trans
= new Kopete::Transfer( info
, url
.path() );
423 connect( trans
, SIGNAL(result(KJob
*)), this, SLOT(slotComplete(KJob
*)) );
424 mTransfersMap
.insert( info
.transferId(), trans
);
425 emit
askIncomingDone( id
);
426 emit
accepted( trans
, url
.path() );
427 mTransferRequestInfoMap
.remove( id
);
430 void Kopete::TransferManager::cancelIncomingTransfer( unsigned int id
)
432 Kopete::FileTransferInfo info
= mTransferRequestInfoMap
.value( id
);
433 if ( !info
.isValid() )
436 emit
askIncomingDone( id
);
437 emit
refused( info
);
438 mTransferRequestInfoMap
.remove( id
);
441 void Kopete::TransferManager::slotComplete(KJob
*job
)
443 Kopete::Transfer
*transfer
=dynamic_cast<Kopete::Transfer
*>(job
);
449 for( QMap
<unsigned, Kopete::Transfer
*>::Iterator it
= mTransfersMap
.begin();
450 it
!= mTransfersMap
.end(); ++it
)
452 if( it
.value() == transfer
)
454 removeTransfer(it
.key());
460 void Kopete::TransferManager::sendFile( const KUrl
&file
, const QString
&fname
, unsigned long sz
,
461 bool mustBeLocal
, QObject
*sendTo
, const char *slot
)
465 unsigned int size
= 0;
467 //If the file location is null, then get it from a file open dialog
469 url
= KFileDialog::getOpenUrl( KUrl(), QString::fromLatin1("*"), 0l, i18n( "Kopete File Transfer" ));
476 if( filename
.isEmpty() )
477 filename
= url
.fileName();
481 KFileItem
finfo(KFileItem::Unknown
, KFileItem::Unknown
, url
);
482 size
= (unsigned long)finfo
.size();
487 if( mustBeLocal
&& !url
.isLocalFile() )
489 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry
,
490 i18n( "Sorry, sending files which are not stored locally is not yet supported by this protocol.\n"
491 "Please copy this file to your computer and try again." ) );
495 connect( this, SIGNAL(sendFile(const KUrl
&, const QString
&, unsigned int)), sendTo
, slot
);
496 emit
sendFile( url
, filename
, size
);
497 disconnect( this, SIGNAL(sendFile(const KUrl
&, const QString
&, unsigned int)), sendTo
, slot
);
502 void Kopete::TransferManager::removeTransfer( unsigned int id
)
504 mTransfersMap
.remove(id
);
505 //we don't need to delete the job, the job get deleted itself
508 KUrl
Kopete::TransferManager::getSaveFile( const KUrl
& startDir
) const
513 url
= KFileDialog::getSaveUrl( url
, QLatin1String( "*" ), 0, i18n( "File Transfer" ) );
514 if ( !url
.isValid() )
517 if ( !url
.isLocalFile() )
519 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "You must provide a valid local filename" ) );
523 QFileInfo
fileInfo( url
.path() );
524 if ( fileInfo
.exists() )
526 if ( !fileInfo
.isWritable() )
528 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "You do not have permission to write to selected file" ) );
532 int ret
= KMessageBox::warningContinueCancel( 0, i18n( "The file '%1' already exists.\nDo you want to overwrite it ?", url
.path() ),
533 i18n( "Overwrite File" ), KStandardGuiItem::save() );
534 if ( ret
== KMessageBox::Cancel
)
539 QFileInfo
dirInfo( url
.directory() );
540 if ( !dirInfo
.isDir() || !dirInfo
.exists() )
542 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "The directory %1 does not exist", dirInfo
.fileName() ) );
545 else if ( !dirInfo
.isWritable() )
547 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "You do not have permission to write to selected directory" ) );
557 KUrl
Kopete::TransferManager::getSaveDir( const KUrl
& startDir
) const
562 url
= KFileDialog::getExistingDirectoryUrl( url
, 0, i18n( "File Transfer" ) );
563 if ( !url
.isValid() )
566 if ( !url
.isLocalFile() )
568 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "You must provide a valid local directory" ) );
572 QFileInfo
dirInfo( url
.path() );
573 if ( !dirInfo
.isDir() || !dirInfo
.exists() )
575 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "The directory %1 does not exist", dirInfo
.filePath() ) );
578 else if ( !dirInfo
.isWritable() )
580 KMessageBox::messageBox( 0, KMessageBox::Sorry
, i18n( "You do not have permission to write to selected directory" ) );
588 #include "kopetetransfermanager.moc"