/***************************************************************************
                          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 <KLocale>

// 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 <KFileDialog>
#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: &quot;%1&quot; (%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
      KUrl startDir = KFileDialog::getStartUrl( startFolder, recentFolderTag );
      startDir.addPath( suggestedFileName_ );

      delayDeletion( true );

      // Ask the user for a file.
      filePath_ = KFileDialog::getSaveFileName( startDir.url() );

      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 &quot;%1&quot; 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 &quot;%1&quot; failed. Could not save the file.",
                           "<span class=\"filename failedFilename\">" + fileName_ + "</span>"),
                            ChatMessage::CONTENT_APP_FAILED, false
                    );

    slotMsnFtpStatusMessage( i18n( "The transfer of the file &quot;%1&quot; 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;
  int     portXInternal;
  int     portX;
  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
  portXInternal = message.getValue( "PortX-Internal"      ).toInt();  // As of MSN5
  port          = (quint16)message.getValue( "Port"       ).toUInt();
  portX         = message.getValue( "PortX"               ).toInt();  // As of MSN5
  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 &quot;%1&quot;.", ( 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 &quot;%1&quot;.", ( 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 &quot;%1&quot;.", ( 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 &quot;%1&quot;.", fileURL );
  }
  else
  {
    message = i18n( "Successfully received the file &quot;%1&quot;.", 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.sprintf("%.1f", (double) bytes / 1048576.0);
    return i18n( "%1 MB", format );
  }
  else if(bytes > 1024)
  {
    format.sprintf("%.1f", (double) bytes / 1024.0);
    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 &quot;%1&quot; 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 &quot;%1&quot; 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 &quot;%1&quot; (%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 &quot;%1&quot;.", 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();
  }
}


#include "filetransfer.moc"
