853 lines
26 KiB
C++
853 lines
26 KiB
C++
/***************************************************************************
|
|
filetransfer.cpp - description
|
|
-------------------
|
|
begin : Mon Mar 24 2003
|
|
copyright : (C) 2003 by Mike K. Bennett
|
|
(C) 2005 by Diederik van der Boor
|
|
email : mkb137b@hotmail.com
|
|
vdboor --at-- codingdomain.com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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 "filetransfer.h"
|
|
|
|
#include "../../utils/kmessconfig.h"
|
|
#include "../../currentaccount.h"
|
|
#include "../../kmessdebug.h"
|
|
#include "../extra/msnftpconnection.h"
|
|
#include "../mimemessage.h"
|
|
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
|
|
#include <KLocalizedString>
|
|
|
|
// It wouldn't hurt if these GUI specific features are removed here.
|
|
// That would make this class GUI-independant and only emit signals for possible GUI actions.
|
|
#include <QFileDialog>
|
|
#include <KMessageBox>
|
|
#include "../../dialogs/transferwindow.h"
|
|
|
|
|
|
#ifdef KMESSDEBUG_FILETRANSFER
|
|
#define KMESSDEBUG_FILETRANSFER_GENERAL
|
|
#endif
|
|
|
|
|
|
|
|
// The constructor without filename (sufficient for incoming sessions)
|
|
FileTransfer::FileTransfer(const QString &contactHandle)
|
|
: MimeApplication(contactHandle)
|
|
, connectionEstablished_(false)
|
|
, connectivity_('?')
|
|
, file_(0)
|
|
, msnFtpConnection_(0)
|
|
, transferID_(-1)
|
|
{
|
|
setApplicationType( ChatMessage::TYPE_APPLICATION_FILE );
|
|
}
|
|
|
|
// The constructor with filename to start a session
|
|
FileTransfer::FileTransfer(const QString &contactHandle, const QString &filename)
|
|
: MimeApplication(contactHandle)
|
|
, connectionEstablished_(false)
|
|
, connectivity_('?')
|
|
, file_(0)
|
|
, filePath_(filename)
|
|
, msnFtpConnection_(0)
|
|
, transferID_(-1)
|
|
{
|
|
setApplicationType( ChatMessage::TYPE_APPLICATION_FILE );
|
|
}
|
|
|
|
|
|
// The destructor
|
|
FileTransfer::~FileTransfer()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
|
|
// Displays the cancel state in the transfer window
|
|
TransferWindow::getInstance()->failTransfer( transferID_ );
|
|
|
|
// Stop the ftp connection
|
|
delete msnFtpConnection_;
|
|
|
|
// Close the file nicely
|
|
if(file_ != 0)
|
|
{
|
|
delete file_;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Connect signals of the MsnFtpConnection object
|
|
void FileTransfer::connectMsnFtpConnection()
|
|
{
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( msnFtpConnection_ != 0 );
|
|
#endif
|
|
|
|
connect(msnFtpConnection_, SIGNAL( connectionEstablished() ) , // The direct connection was established
|
|
this, SLOT( slotMsnFtpConnectionEstablished() ) );
|
|
connect(msnFtpConnection_, SIGNAL( statusMessage(QString,int) ) , // Display a message in the dialog/chat window
|
|
this, SLOT( slotMsnFtpStatusMessage(QString,int) ) );
|
|
connect(msnFtpConnection_, SIGNAL( statusMessage(KLocalizedString,int) ) , // Display a message in the dialog/chat window
|
|
this, SLOT( slotMsnFtpStatusMessage(KLocalizedString,int) ) );
|
|
connect(msnFtpConnection_, SIGNAL( transferComplete() ) , // Signal that the transfer is complete
|
|
this, SLOT( slotMsnFtpTransferComplete() ) );
|
|
connect(msnFtpConnection_, SIGNAL( transferFailed() ) , // Signal that the transfer failed (and clean up)
|
|
this, SLOT( slotMsnFtpTransferFailed() ) );
|
|
connect(msnFtpConnection_, SIGNAL( transferProgess(unsigned long) ) , // Signal that the transfer made progress
|
|
this, SLOT( slotMsnFtpTransferProgess(unsigned long) ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* The contact cancelled the session
|
|
*/
|
|
void FileTransfer::contactAborted(const QString &message)
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
Q_UNUSED( message );
|
|
|
|
// Displays the cancel state in the transfer window
|
|
TransferWindow::getInstance()->failTransfer( transferID_, i18n("Cancelled") );
|
|
|
|
// Also display it in chat
|
|
modifyOfferMessage();
|
|
|
|
if( message.isEmpty() )
|
|
{
|
|
showEventMessage( getContactAbortMessage(), ChatMessage::CONTENT_APP_CANCELED, true );
|
|
}
|
|
else
|
|
{
|
|
showEventMessage( message, ChatMessage::CONTENT_APP_CANCELED, true );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Step one of a contact-started chat: the contact invites the user
|
|
void FileTransfer::contactStarted1_ContactInvitesUser(const MimeMessage& message)
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
|
|
// Get the file name and size from the message
|
|
suggestedFileName_ = message.getValue("Application-File");
|
|
fileSize_ = message.getValue("Application-FileSize").toInt();
|
|
connectivity_ = '?';
|
|
|
|
// Get the optional fields.
|
|
if( message.hasField("Connectivity") )
|
|
{
|
|
QString connectivity( message.getValue("Connectivity") );
|
|
if( connectivity.size() > 0 )
|
|
{
|
|
connectivity_ = connectivity.at(0).toLatin1();
|
|
}
|
|
}
|
|
|
|
// Send the message to the chat window.
|
|
offerAcceptOrReject( i18n( "The contact wants to send you a file: "%1" (%2).",
|
|
"<span class=\"filename invitationFilename\">" + suggestedFileName_ + "</span>",
|
|
toReadableBytes( fileSize_ ) ) );
|
|
}
|
|
|
|
|
|
|
|
// Step two of a contact-started chat: the user accepts
|
|
void FileTransfer::contactStarted2_UserAccepts()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "Starting.";
|
|
#endif
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( file_ == 0 );
|
|
KMESS_ASSERT( filePath_.isEmpty() );
|
|
#endif
|
|
|
|
QFileInfo fileInfo;
|
|
|
|
// Open the options and get the default downloads directory
|
|
KConfigGroup group = KMessConfig::instance()->getGlobalConfig( "General" );
|
|
QString startFolder( group.readEntry( "receivedFilesDir", QDir::homePath() ) );
|
|
|
|
if( group.readEntry( "useReceivedFilesDir", false ) && ! startFolder.isEmpty() )
|
|
{
|
|
int index = -1;
|
|
|
|
do
|
|
{
|
|
// The ++ ensures that at the next iteration a suffix is appended to the name
|
|
if( index++ < 0 )
|
|
{
|
|
filePath_ = startFolder + "/" + suggestedFileName_;
|
|
}
|
|
else
|
|
{
|
|
fileInfo.setFile( suggestedFileName_ );
|
|
filePath_ = startFolder + "/" + fileInfo.baseName() + "(" + QString::number( index ) + ")." + fileInfo.completeSuffix();
|
|
}
|
|
}
|
|
while( QFile::exists( filePath_ ) );
|
|
|
|
fileInfo.setFile( filePath_ );
|
|
fileName_ = fileInfo.fileName();
|
|
}
|
|
else
|
|
{
|
|
// Ask the user to specify where to put the file
|
|
QString recentFolderTag( ":filedownload" );
|
|
|
|
// Open a file dialog so that the user can save the file to a particular directory and name.
|
|
bool hasFile = false;
|
|
while( ! hasFile )
|
|
{
|
|
// Set an initial path to the file
|
|
const QString startPath = QDir( startFolder ).filePath( suggestedFileName_ );
|
|
|
|
delayDeletion( true );
|
|
|
|
// Ask the user for a file.
|
|
filePath_ = QFileDialog::getSaveFileName( nullptr, QString(), startPath );
|
|
|
|
delayDeletion( false );
|
|
if( isClosing() )
|
|
{
|
|
endApplication();
|
|
return;
|
|
}
|
|
|
|
if( filePath_.isNull() )
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_P2P
|
|
kmDebug() << "User cancelled in file save dialog";
|
|
#endif
|
|
|
|
// Dialog cancelled, cancel afterall
|
|
modifyOfferMessage();
|
|
userRejected();
|
|
return;
|
|
}
|
|
|
|
hasFile = true;
|
|
fileInfo.setFile( filePath_ );
|
|
fileName_ = fileInfo.fileName();
|
|
|
|
// Check if the selected file exists and if the user wants to overwrite it.
|
|
// The while loop is for the prompt to keep appearing if the user
|
|
// chooses the same filename but does not want to overwrite the file.
|
|
if( QFile::exists(filePath_) )
|
|
{
|
|
if( KMessageBox::warningContinueCancel( 0,
|
|
i18n("The file "%1" already exists.\nDo you want to overwrite it?", fileName_ ),
|
|
i18n("Overwrite File"), KGuiItem( i18n("Over&write") ) ) == KMessageBox::Cancel )
|
|
{
|
|
// User does not want to overwrite
|
|
hasFile = false;
|
|
startFolder = fileInfo.path();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we try to open the file
|
|
file_ = new QFile(filePath_);
|
|
bool success = (file_ != 0) && file_->open(QIODevice::WriteOnly);
|
|
|
|
if( ! success )
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_P2P
|
|
kmDebug() << "Cancelling session";
|
|
#endif
|
|
|
|
// Notify the user, even if debug mode is not enabled.
|
|
kmWarning() << "Unable to open file: " << filePath_ << "!";
|
|
|
|
// Close the file (also causes gotData() to fail)
|
|
delete file_;
|
|
file_ = 0;
|
|
|
|
// Tell the user about it
|
|
modifyOfferMessage();
|
|
showEventMessage( i18n("The transfer of the file "%1" failed. Could not save the file.",
|
|
"<span class=\"filename failedFilename\">" + fileName_ + "</span>"),
|
|
ChatMessage::CONTENT_APP_FAILED, false
|
|
);
|
|
|
|
slotMsnFtpStatusMessage( i18n( "The transfer of the file "%1" failed. Could not open the destination file." ),
|
|
MsnFtpConnection::STATUS_CHAT );
|
|
|
|
// Send 500 Internal Error back if we failed (this is still the accept stage)
|
|
sendCancelMessage( CANCEL_FAILED );
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef KMESSDEBUG_FILETRANSFER_P2P
|
|
kmDebug() << "Sending accept message";
|
|
#endif
|
|
|
|
// All passed. Send the accept message
|
|
MimeMessage message;
|
|
message.addField( "Invitation-Command", "ACCEPT" );
|
|
message.addField( "Invitation-Cookie", getCookie() );
|
|
message.addField( "Launch-Application", "FALSE" );
|
|
message.addField( "Request-Data", "IP-Address:" );
|
|
|
|
sendMessage( message );
|
|
|
|
// Notify the user
|
|
modifyOfferMessage();
|
|
showEventMessage( i18n("Transfer accepted."), ChatMessage::CONTENT_APP_STARTED, false );
|
|
}
|
|
|
|
|
|
|
|
// Step three of a contact-started chat: the contact confirms the accept
|
|
void FileTransfer::contactStarted3_ContactConfirmsAccept(const MimeMessage& message)
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( ! fileName_.isEmpty() );
|
|
KMESS_ASSERT( ! suggestedFileName_.isEmpty() );
|
|
KMESS_ASSERT( file_ != 0 );
|
|
KMESS_ASSERT( msnFtpConnection_ == 0 );
|
|
#endif
|
|
|
|
QString ip;
|
|
QString ipInternal;
|
|
quint16 port;
|
|
QString authCookie;
|
|
|
|
// Pull the IP, port, and authorization info from the message
|
|
ip = message.getValue( "IP-Address" );
|
|
ipInternal = message.getValue( "IP-Address-Internal" ); // As of MSN5
|
|
port = (quint16)message.getValue( "Port" ).toUInt();
|
|
authCookie = message.getValue( "AuthCookie" );
|
|
|
|
/*
|
|
* This is an attempt to get file transfers
|
|
* between two home systems working.. (both behind the same NAT router)
|
|
* I didn't discover any reference when I started this.
|
|
*
|
|
* TODO: Implement MSN5-compatible transfers using http://www.hypothetic.org/docs/msn/phorum/read.php?f=1&i=3435&t=3372#reply_3435
|
|
* A full implementation also requires UPnP handling.
|
|
*
|
|
* When receiving an invite with a "Connectivity: N" field,
|
|
* and the client it's not being translated by a NAT, set a "Sender-Connect: TRUE" field,
|
|
* and include addressing information for both internal and external addresses, and start a server.
|
|
* Otherwise, start a client.
|
|
*
|
|
* Presumably, a client can try three connections when attempting to connect to a server:
|
|
* - To IP-Address on Port
|
|
* - To IP-Address on PortX
|
|
* - To IP-Address-Internal on PortX-Internal.
|
|
* Using IP-Address on Port and IP-Address-Internal on PortX-Internal should be sufficient.
|
|
*/
|
|
|
|
QString externalIp( getExternalIp() );
|
|
if(ip == externalIp)
|
|
{
|
|
// The other client must be at the same network..!
|
|
// Try to connect internally.
|
|
|
|
// Just to be sure the values are set...
|
|
if(! ipInternal.isEmpty())
|
|
{
|
|
ip = ipInternal;
|
|
//port = internPort;
|
|
port = 6891; // Only works if there is one transfer, but it's better then nothing.
|
|
}
|
|
// else: the file transfer fails anyway
|
|
}
|
|
|
|
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "IP: " << ip << " Port: " << port << " AuthCookie: " << authCookie;
|
|
#endif
|
|
|
|
// Initialize the MSNFTP connection
|
|
msnFtpConnection_ = new MsnFtpConnection( CurrentAccount::instance()->getHandle(), authCookie );
|
|
connectMsnFtpConnection();
|
|
|
|
// Initialize the progess dialog
|
|
initializeProgressDialog(true);
|
|
slotMsnFtpStatusMessage( i18n( "Connecting to %1, port %2", ip, QString::number( port ) ), MsnFtpConnection::STATUS_DIALOG );
|
|
|
|
// Start the transfer
|
|
msnFtpConnection_->retrieveFile(file_, ip, port);
|
|
}
|
|
|
|
|
|
|
|
// Return the application's GUID
|
|
QString FileTransfer::getAppId()
|
|
{
|
|
return "{5D3E02AB-6190-11d3-BBBB-00C04F795683}";
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Return a cancel message to display.
|
|
*/
|
|
QString FileTransfer::getContactAbortMessage() const
|
|
{
|
|
// Application::getUserAbortMessage() returns "The contact cancelled the session".
|
|
return i18n("The contact has cancelled the transfer of the file "%1".", ( fileName_.isEmpty() ? suggestedFileName_ : fileName_ ) );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Return a cancel message to display.
|
|
*/
|
|
QString FileTransfer::getUserAbortMessage() const
|
|
{
|
|
// Application::getUserAbortMessage() returns "You have cancelled the session".
|
|
return i18n( "You have cancelled the transfer of the file "%1".", ( fileName_.isEmpty() ? suggestedFileName_ : fileName_ ) );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Return a cancel message to display.
|
|
*/
|
|
QString FileTransfer::getUserRejectMessage() const
|
|
{
|
|
// Application::getUserAbortMessage() returns "You have cancelled the session".
|
|
return i18n( "You have rejected the transfer of the file "%1".", ( fileName_.isEmpty() ? suggestedFileName_ : fileName_ ) );
|
|
}
|
|
|
|
|
|
|
|
// Create and initilize the progress dialog.
|
|
void FileTransfer::initializeProgressDialog(bool incoming)
|
|
{
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( fileSize_ != 0 );
|
|
KMESS_ASSERT( fileName_.length() > 0 );
|
|
#endif
|
|
|
|
// Create a new entry in the tranfer window
|
|
TransferWindow *transferWindow = TransferWindow::getInstance();
|
|
transferID_ = transferWindow->addEntry( fileName_, fileSize_, incoming );
|
|
|
|
// Connect the dialog so that if the user closes it, it's deleted.
|
|
connect( transferWindow, SIGNAL( cancelTransfer(int) ) ,
|
|
this, SLOT( slotCancelTransfer(int) ) );
|
|
}
|
|
|
|
|
|
|
|
// Cancelled the file transfer from the TransferWindow
|
|
void FileTransfer::slotCancelTransfer( int transferID )
|
|
{
|
|
// Check if the cancelled transfer is ours
|
|
if( transferID_ != transferID )
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
|
|
// Call userAborted() which cleans up this object correctly
|
|
userAborted();
|
|
}
|
|
|
|
|
|
|
|
// The direct connection was established
|
|
void FileTransfer::slotMsnFtpConnectionEstablished()
|
|
{
|
|
// Update the status dialog
|
|
slotMsnFtpStatusMessage( i18n("Connection established"), MsnFtpConnection::STATUS_DIALOG );
|
|
connectionEstablished_ = true;
|
|
}
|
|
|
|
|
|
|
|
// Display a status message (as a raw localization string)
|
|
void FileTransfer::slotMsnFtpStatusMessage( KLocalizedString message, int statusType )
|
|
{
|
|
// Parse the %1 placeholder for file name
|
|
switch( statusType )
|
|
{
|
|
case MsnFtpConnection::STATUS_DIALOG:
|
|
case MsnFtpConnection::STATUS_DIALOG_FAILED:
|
|
message = message.subs( fileName_ );
|
|
break;
|
|
|
|
// A message to display in the chat window
|
|
case MsnFtpConnection::STATUS_CHAT_FAILED:
|
|
message = message.subs( "<span class=\"filename failedFilename\">" + fileName_ + "</span>" );
|
|
break;
|
|
case MsnFtpConnection::STATUS_CHAT:
|
|
default:
|
|
message = message.subs( "<span class=\"filename\">" + fileName_ + "</span>" );
|
|
break;
|
|
}
|
|
|
|
// Call the real handler with the completed localization string
|
|
slotMsnFtpStatusMessage( message.toString(), statusType );
|
|
}
|
|
|
|
|
|
|
|
// Display a status message
|
|
void FileTransfer::slotMsnFtpStatusMessage(QString message, int statusType)
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "Status message (type=" << statusType
|
|
<< " message=" << message << ").";
|
|
#endif
|
|
|
|
switch(statusType)
|
|
{
|
|
// A message for the transfer window
|
|
case MsnFtpConnection::STATUS_DIALOG:
|
|
{
|
|
TransferWindow::getInstance()->setStatusMessage( transferID_, message );
|
|
break;
|
|
}
|
|
case MsnFtpConnection::STATUS_DIALOG_FAILED:
|
|
{
|
|
TransferWindow::getInstance()->failTransfer( transferID_, message );
|
|
break;
|
|
}
|
|
|
|
// A message to display in the chat window
|
|
case MsnFtpConnection::STATUS_CHAT_FAILED:
|
|
{
|
|
showEventMessage( message, ChatMessage::CONTENT_APP_FAILED, ! isUserStartedApp() );
|
|
break;
|
|
}
|
|
case MsnFtpConnection::STATUS_CHAT:
|
|
default:
|
|
{
|
|
showEventMessage( message, ChatMessage::CONTENT_APP_FAILED, ! isUserStartedApp() );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Signal that the transfer was succesful
|
|
void FileTransfer::slotMsnFtpTransferComplete()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "Transfer successful.";
|
|
#endif
|
|
|
|
// Displays the success in the transfer window
|
|
TransferWindow::getInstance()->finishTransfer( transferID_ );
|
|
|
|
// Send an event to the chat
|
|
QString fileURL( "<span class=\"filename completedFilename\">"
|
|
"<a href=\"file:" + filePath_ + "\">" + fileName_ + "</a>"
|
|
"</span>" );
|
|
|
|
// Show an appropriate message
|
|
QString message;
|
|
if( isUserStartedApp() )
|
|
{
|
|
message = i18n( "Successfully sent the file "%1".", fileURL );
|
|
}
|
|
else
|
|
{
|
|
message = i18n( "Successfully received the file "%1".", fileURL );
|
|
}
|
|
showEventMessage( message, ChatMessage::CONTENT_APP_ENDED, ! isUserStartedApp() );
|
|
}
|
|
|
|
|
|
|
|
// Signal that the transfer failed
|
|
void FileTransfer::slotMsnFtpTransferFailed()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "Transfer failed.";
|
|
#endif
|
|
|
|
// If MsnFtpConnection did not emit a statusMessage(.. STATUS_DIALOG_FAILED) signal
|
|
// the dialog will be terminated with a default message here.
|
|
TransferWindow::getInstance()->failTransfer( transferID_ );
|
|
|
|
|
|
// If the connection attempt failed, send a cancel message.
|
|
// In other situations, the remote client will notice the MSNFTP connection closed.
|
|
if( ! connectionEstablished_ )
|
|
{
|
|
sendCancelMessage(CANCEL_FAILED); // calls endApplication() already
|
|
return;
|
|
}
|
|
|
|
// Terminate this application
|
|
endApplication();
|
|
}
|
|
|
|
|
|
|
|
// Update the progress dialog with the number of bytes transferred.
|
|
void FileTransfer::slotMsnFtpTransferProgess(unsigned long bytesReceived)
|
|
{
|
|
// Display the progress update in the transfer window
|
|
TransferWindow::getInstance()->updateProgress( transferID_, bytesReceived );
|
|
}
|
|
|
|
|
|
|
|
// Convert a string to some more readable form
|
|
QString FileTransfer::toReadableBytes( ulong bytes )
|
|
{
|
|
QString format;
|
|
if(bytes > 1048576)
|
|
{
|
|
// Using '%.2f' instead of '%.1f' removes the ".0" part, but it's less pretty.
|
|
format = QString::number( (double) bytes / 1048576.0, 'f', 1 );
|
|
return i18n( "%1 MB", format );
|
|
}
|
|
else if(bytes > 1024)
|
|
{
|
|
format = QString::number( (double) bytes / 1024.0, 'f', 1 );
|
|
return i18n( "%1 kB", format );
|
|
}
|
|
else
|
|
{
|
|
return i18n( "%1 bytes", bytes );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// The user cancelled the session
|
|
void FileTransfer::userAborted()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
|
|
// Display the cancel state in the transfer window
|
|
TransferWindow::getInstance()->failTransfer( transferID_, i18n("Cancelled") );
|
|
|
|
// If there is no MSNFTP connection, the standard behavour is exactly what we need.
|
|
if(msnFtpConnection_ == 0)
|
|
{
|
|
MimeApplication::userAborted();
|
|
return;
|
|
}
|
|
|
|
|
|
// We have a MsnFtpConnection listening
|
|
// Then teardown the active transfer
|
|
if(msnFtpConnection_->isConnected())
|
|
{
|
|
// It is already sending the file
|
|
// Initiate connection teardown
|
|
|
|
// Disconnect signals
|
|
// We are not interested in status messages, they are displayed here already
|
|
// slotWriteData() calls slotMsnFtpTransferFailed() which deletes this object, we do this here already
|
|
disconnect(msnFtpConnection_, SIGNAL(statusMessage(QString,int)), this, SLOT(slotMsnFtpStatusMessage(QString,int)));
|
|
disconnect(msnFtpConnection_, SIGNAL(transferFailed()), this, SLOT(slotMsnFtpTransferFailed()));
|
|
|
|
// Tell MsnFtpConnection to stop transferring. This also signals the other client we cancelled
|
|
// delete after all signals are processed (since we detached slotMsnFtpTransferFailed())
|
|
msnFtpConnection_->cancelTransfer(true);
|
|
msnFtpConnection_->deleteLater();
|
|
msnFtpConnection_ = 0;
|
|
|
|
// we don't have to send a cancel message anymore.
|
|
// MsnFtpConnection already does the same thing.
|
|
|
|
// Finally delete this object
|
|
endApplication();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// No transfer, but close the connection ASAP
|
|
msnFtpConnection_->closeConnection();
|
|
msnFtpConnection_->deleteLater();
|
|
msnFtpConnection_ = 0;
|
|
|
|
// The transfer was not initiated.
|
|
// Send the cancel-session message.
|
|
MimeApplication::userAborted();
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Step one of a user-started chat: the user invites the contact
|
|
void FileTransfer::userStarted1_UserInvitesContact()
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( ! filePath_.isEmpty() );
|
|
#endif
|
|
QString sizeString;
|
|
MimeMessage message;
|
|
|
|
file_ = new QFile(filePath_);
|
|
bool success = (file_ != 0) && file_->open(QIODevice::ReadOnly);
|
|
|
|
// Stop if the file can't be openend.
|
|
if( ! success )
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_P2P
|
|
kmDebug() << "Cancelling session";
|
|
#endif
|
|
|
|
// Notify the user, even if debug mode is not enabled.
|
|
kmWarning() << "Unable to open file: " << filePath_ << "!";
|
|
|
|
// Close the file
|
|
delete file_;
|
|
file_ = 0;
|
|
|
|
// Tell the user about it
|
|
if( ! QFile::exists(filePath_) )
|
|
{
|
|
showEventMessage( i18n( "The transfer of the file "%1" failed. The file does not exist.",
|
|
"<span class=\"filename failedFilename\">" + filePath_ + "</span>"), ChatMessage::CONTENT_APP_CANCELED, false );
|
|
}
|
|
else
|
|
{
|
|
showEventMessage( i18n( "The transfer of the file "%1" failed. The file could not be read.",
|
|
"<span class=\"filename failedFilename\">" + filePath_ + "</span>"), ChatMessage::CONTENT_APP_CANCELED, false );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Set the short file name
|
|
QFileInfo fileInfo( filePath_ );
|
|
fileName_ = fileInfo.fileName();
|
|
|
|
// Read the file parameters
|
|
fileSize_ = (uint)file_->size();
|
|
sizeString = QString::number(fileSize_);
|
|
|
|
// Create the invitation message
|
|
// connectivity_ = 'U'; // Or 'N' if the port is mapped with NAT
|
|
message.addField( "Application-Name", "File Transfer" );
|
|
message.addField( "Application-GUID", getAppId() );
|
|
message.addField( "Invitation-Command", "INVITE" );
|
|
message.addField( "Invitation-Cookie", getCookie() );
|
|
message.addField( "Application-File", fileName_ );
|
|
message.addField( "Application-FileSize", sizeString );
|
|
// message.addField( "Connectivity", connectivity_ );
|
|
|
|
sendMessage( message );
|
|
|
|
// Give the user the option of cancelling the transfer
|
|
offerCancel( i18n( "Sending file "%1" (%2).",
|
|
"<span class=\"filename invitationFilename\">" + fileName_ + "</span>",
|
|
toReadableBytes( fileSize_ ) ) );
|
|
}
|
|
|
|
|
|
|
|
// Step two of a user-started chat: the contact accepts
|
|
void FileTransfer::userStarted2_ContactAccepts(const MimeMessage& /*message*/)
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug();
|
|
#endif
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( msnFtpConnection_ == 0 );
|
|
#endif
|
|
|
|
QString html;
|
|
QString portString;
|
|
QString authCookie;
|
|
|
|
// Remove the accept links
|
|
modifyOfferMessage();
|
|
|
|
// Create a message showing that the transfer was accepted.
|
|
showEventMessage( i18n( "The contact has accepted the transfer of the file "%1".", fileName_ ),
|
|
ChatMessage::CONTENT_APP_STARTED,
|
|
true );
|
|
|
|
// Get the authorisation cookie to use
|
|
authCookie = generateCookie();
|
|
|
|
// Initialize the msnftp connection class
|
|
msnFtpConnection_ = new MsnFtpConnection( getContactHandle(), authCookie );
|
|
connectMsnFtpConnection();
|
|
|
|
// Get the IP,port to use
|
|
portString = QString::number( msnFtpConnection_->getLocalServerPort() );
|
|
|
|
// Initialize the progress dialog
|
|
initializeProgressDialog();
|
|
|
|
// Set the first status message.
|
|
slotMsnFtpStatusMessage( i18n("Negotiating options to connect"), MsnFtpConnection::STATUS_DIALOG );
|
|
|
|
// Create the invitation message
|
|
// TODO: PortX-Internal and PortX should be another port.
|
|
// We should be listening to port 11178 as well for internal transfers
|
|
MimeMessage response;
|
|
response.addField( "Invitation-Command", "ACCEPT" );
|
|
response.addField( "Invitation-Cookie", getCookie() );
|
|
response.addField( "IP-Address", getExternalIp() );
|
|
// response.addField( "IP-Address-Internal", getLocalIp() );
|
|
// response.addField( "PortX-Internal", portString );
|
|
response.addField( "Port", portString );
|
|
// response.addField( "PortX", portString );
|
|
response.addField( "Launch-Application", "FALSE" );
|
|
response.addField( "AuthCookie", authCookie );
|
|
|
|
sendMessage( response );
|
|
}
|
|
|
|
|
|
|
|
// Connect to the port and start listening for the transfer.
|
|
void FileTransfer::userStarted3_UserPrepares()
|
|
{
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( msnFtpConnection_ != 0 );
|
|
KMESS_ASSERT( file_ != 0 );
|
|
#endif
|
|
|
|
if(file_ != 0)
|
|
{
|
|
msnFtpConnection_->sendFile(file_);
|
|
}
|
|
else
|
|
{
|
|
#ifdef KMESSDEBUG_FILETRANSFER_GENERAL
|
|
kmDebug() << "Pointer file_ is null!";
|
|
#endif
|
|
slotMsnFtpTransferFailed();
|
|
}
|
|
}
|