/*  -*- mode: C++; c-file-style: "gnu" -*-
 *
 *  This file is part of KMail, the KDE mail client.
 *  Copyright (c) 2002-2004  Bo Thorsen <bo@sonofthor.dk>
 *                2002-2003  Steffen Hansen <hansen@kde.org>
 *                2002-2003  Zack Rusin <zack@kde.org>
 *
 *  KMail is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License, version 2, as
 *  published by the Free Software Foundation.
 *
 *  KMail is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the TQt library by Trolltech AS, Norway (or with modified versions
 *  of TQt that use the same license as TQt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  TQt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "cachedimapjob.h"
#include "imapaccountbase.h"

#include "kmfoldermgr.h"
#include "kmfolder.h"
#include "kmfoldercachedimap.h"
#include "kmailicalifaceimpl.h"
#include "kmacctcachedimap.h"
#include "kmmsgdict.h"
#include "maildirjob.h"
#include "scalix.h"
#include "util.h"

#include <kio/scheduler.h>
#include <kio/job.h>

#include <klocale.h>
#include <kdebug.h>


namespace KMail {

// Get messages
CachedImapJob::CachedImapJob( const TQValueList<MsgForDownload>& msgs,
                              JobType type, KMFolderCachedImap* folder )
  : FolderJob( type ), mFolder( folder ), mMsgsForDownload( msgs ),
    mTotalBytes(0), mMsg(0), mParentFolder( 0 )
{
  TQValueList<MsgForDownload>::ConstIterator it = msgs.begin();
  for ( ; it != msgs.end() ; ++it )
    mTotalBytes += (*it).size;
}

// Put messages
CachedImapJob::CachedImapJob( const TQPtrList<KMMessage>& msgs, JobType type,
                              KMFolderCachedImap* folder )
  : FolderJob( msgs, TQString(), type, folder?folder->folder():0 ), mFolder( folder ),
    mTotalBytes( msgs.count() ), // we abuse it as "total number of messages"
    mMsg( 0 ), mParentFolder( 0 )
{
}

CachedImapJob::CachedImapJob( const TQValueList<unsigned long>& msgs,
			      JobType type, KMFolderCachedImap* folder )
  : FolderJob( TQPtrList<KMMessage>(), TQString(), type, folder?folder->folder():0 ),
    mFolder( folder ), mSerNumMsgList( msgs ), mTotalBytes( msgs.count() ), mMsg( 0 ),
    mParentFolder ( 0 )
{
}

// Add sub folders
CachedImapJob::CachedImapJob( const TQValueList<KMFolderCachedImap*>& fList,
                              JobType type, KMFolderCachedImap* folder )
  : FolderJob( type ), mFolder( folder ), mFolderList( fList ), mMsg( 0 ),
    mParentFolder ( 0 )
{
}

// Rename folder
CachedImapJob::CachedImapJob( const TQString& string1, JobType type,
                              KMFolderCachedImap* folder )
  : FolderJob( type ), mFolder(folder), mMsg( 0 ), mString( string1 ),
    mParentFolder ( 0 )
{
  assert( folder );
  assert( type != tDeleteMessage ); // moved to another ctor
}

// Delete folders or messages
CachedImapJob::CachedImapJob( const TQStringList& foldersOrMsgs, JobType type,
                              KMFolderCachedImap* folder )
  : FolderJob( type ), mFolder( folder ), mFoldersOrMessages( foldersOrMsgs ),
    mMsg( 0 ), mParentFolder( 0 )
{
  assert( folder );
}

// Other jobs (list messages,expunge folder, check uid validity)
CachedImapJob::CachedImapJob( JobType type, KMFolderCachedImap* folder )
  : FolderJob( type ), mFolder( folder ), mMsg( 0 ), mParentFolder ( 0 )
{
  assert( folder );
}

CachedImapJob::~CachedImapJob()
{
  mAccount->mJobList.remove(this);
}

void CachedImapJob::execute()
{
  mSentBytes = 0;

  if( !mFolder ) {
    if( !mMsgList.isEmpty() ) {
      mFolder = static_cast<KMFolderCachedImap*>(mMsgList.first()->storage());
    }
  }
  assert( mFolder );
  mAccount = mFolder->account();
  assert( mAccount != 0 );
  if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
    // No connection to the IMAP server
    kdDebug(5006) << "mAccount->makeConnection() failed" << endl;
    mPassiveDestructor = true;
    delete this;
    return;
  } else
    mPassiveDestructor = false;

  // All necessary conditions have been met. Register this job
  mAccount->mJobList.append(this);

  /**
   * The Scalix server requires to send him a custom X-SCALIX-ID command
   * to switch it into a special mode.
   *
   * This should be done once after the login and before the first command.
   */
  if ( mAccount->groupwareType() == KMAcctCachedImap::GroupwareScalix ) {
    if ( !mAccount->sentCustomLoginCommand() ) {
      TQByteArray packedArgs;
      TQDataStream stream( packedArgs, IO_WriteOnly );

      const TQString command = TQString( "X-SCALIX-ID " );
      const TQString argument = TQString( "(\"name\" \"Evolution\" \"version\" \"2.10.0\")" );

      stream << (int) 'X' << 'N' << command << argument;

      const KURL url = mAccount->getUrl();

      ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
      jd.items << mFolder->label(); // for the err msg
      KIO::SimpleJob *simpleJob = KIO::special( url.url(), packedArgs, false );
      KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
      mAccount->insertJob(simpleJob, jd);

      mAccount->setSentCustomLoginCommand( true );
    }
  }

  switch( mType ) {
  case tGetMessage:       slotGetNextMessage();     break;
  case tPutMessage:       slotPutNextMessage();     break;
  case tDeleteMessage:    slotDeleteNextMessages(); break;
  case tExpungeFolder:    expungeFolder();          break;
  case tAddSubfolders:    slotAddNextSubfolder();   break;
  case tDeleteFolders:    slotDeleteNextFolder();   break;
  case tCheckUidValidity: checkUidValidity();       break;
  case tRenameFolder:     renameFolder(mString);    break;
  case tListMessages:     listMessages();           break;
  default:
    assert( 0 );
  }
}

void CachedImapJob::listMessages()
{
  KURL url = mAccount->getUrl();
  url.setPath( mFolder->imapPath() + ";UID=1:*;SECTION=FLAGS RFC822.SIZE");

  KIO::SimpleJob *job = KIO::get(url, false, false);
  KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  jd.cancellable = true;
  mAccount->insertJob( job, jd );
  connect( job, TQT_SIGNAL( result(KIO::Job *) ),
           this, TQT_SLOT( slotListMessagesResult( KIO::Job* ) ) );
  // send the data directly for KMFolderCachedImap
  connect( job, TQT_SIGNAL( data( KIO::Job*, const TQByteArray& ) ),
           mFolder, TQT_SLOT( slotGetMessagesData( KIO::Job* , const TQByteArray& ) ) );
}

void CachedImapJob::slotDeleteNextMessages( KIO::Job* job )
{
  if (job) {
    KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
    if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
      delete this;
      return;
    }

    if( job->error() ) {
      mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
      delete this;
      return;
    }
    mAccount->removeJob(it);
  }

  if( mFoldersOrMessages.isEmpty() ) {
    // No more messages to delete
    delete this;
    return;
  }

  TQString uids = mFoldersOrMessages.front(); mFoldersOrMessages.pop_front();

  KURL url = mAccount->getUrl();
  url.setPath( mFolder->imapPath() +
               TQString::tqfromLatin1(";UID=%1").tqarg(uids) );

  KIO::SimpleJob *simpleJob = KIO::file_delete( url, false );
  KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  mAccount->insertJob( simpleJob, jd );
  connect( simpleJob, TQT_SIGNAL( result(KIO::Job *) ),
           this, TQT_SLOT( slotDeleteNextMessages(KIO::Job *) ) );
}

void CachedImapJob::expungeFolder()
{
  KURL url = mAccount->getUrl();
  // Special URL that means EXPUNGE
  url.setPath( mFolder->imapPath() + TQString::tqfromLatin1(";UID=*") );

  KIO::SimpleJob *job = KIO::file_delete( url, false );
  KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  mAccount->insertJob( job, jd );
  connect( job, TQT_SIGNAL( result(KIO::Job *) ),
           this, TQT_SLOT( slotExpungeResult(KIO::Job *) ) );
}

void CachedImapJob::slotExpungeResult( KIO::Job * job )
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }

  if (job->error()) {
    mErrorCode = job->error();
    mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
  }
  else
    mAccount->removeJob(it);

  delete this;
}

void CachedImapJob::slotGetNextMessage(KIO::Job * job)
{
  if (job) {
    KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
    if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
      delete this;
      return;
    }

    if (job->error()) {
      mErrorCode = job->error();
      mAccount->handleJobError( job, i18n( "Error while retrieving message on the server: " ) + '\n' );
      delete this;
      return;
    }

    ulong size = 0;
    if ((*it).data.size() > 0) {
      ulong uid = mMsg->UID();
      size = mMsg->msgSizeServer();

      // Convert CR/LF to LF.
      size_t dataSize = (*it).data.size();
      dataSize = Util::crlf2lf( (*it).data.data(), dataSize ); // always <=
      (*it).data.resize( dataSize );

      mMsg->setComplete( true );
      mMsg->fromByteArray( (*it).data );
      mMsg->setUID(uid);
      mMsg->setMsgSizeServer(size);
      mMsg->setTransferInProgress( false );
      int index = 0;
      mFolder->addMsgInternal( mMsg, true, &index );

      if ( kmkernel->iCalIface().isResourceFolder( mFolder->folder() ) ) {
        mFolder->setqStatus( index, KMMsgStatusRead, false );
      }

      emit messageRetrieved( mMsg );
      if ( index > 0 ) mFolder->unGetMsg( index );
    } else {
      emit messageRetrieved( 0 );
    }
    mMsg = 0;

    mSentBytes += size;
    emit progress( mSentBytes, mTotalBytes );
    mAccount->removeJob(it);
  } else
    mFolder->quiet( true );

  if( mMsgsForDownload.isEmpty() ) {
    mFolder->quiet( false );
    delete this;
    return;
  }

  MsgForDownload mfd = mMsgsForDownload.front(); mMsgsForDownload.pop_front();

  mMsg = new KMMessage;
  mMsg->setUID(mfd.uid);
  mMsg->setMsgSizeServer(mfd.size);
  if( mfd.flags > 0 )
    KMFolderImap::flagsTotqStatus(mMsg, mfd.flags, true, GlobalSettings::allowLocalFlags() ? mFolder->permanentFlags() : INT_MAX);
  KURL url = mAccount->getUrl();
  url.setPath(mFolder->imapPath() + TQString(";UID=%1;SECTION=BODY.PEEK[]").tqarg(mfd.uid));

  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  jd.cancellable = true;
  mMsg->setTransferInProgress(true);
  KIO::SimpleJob *simpleJob = KIO::get(url, false, false);
  KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
  mAccount->insertJob(simpleJob, jd);
  connect(simpleJob, TQT_SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)),
          this, TQT_SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
  connect(simpleJob, TQT_SIGNAL(result(KIO::Job *)),
          this, TQT_SLOT(slotGetNextMessage(KIO::Job *)));
  connect(simpleJob, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
          mFolder, TQT_SLOT(slotSimpleData(KIO::Job *, const TQByteArray &)));
}

void CachedImapJob::slotProcessedSize(KIO::Job *, KIO::filesize_t processed)
{
  emit progress( mSentBytes + processed, mTotalBytes );
}

void CachedImapJob::slotPutNextMessage()
{
  mMsg = 0;

  // First try the message list
  if( !mMsgList.isEmpty() ) {
    mMsg = mMsgList.first();
    mMsgList.removeFirst();
  }

  // Now try the serial number list
  while( mMsg == 0 && !mSerNumMsgList.isEmpty() ) {
    unsigned long serNum = mSerNumMsgList.first();
    mSerNumMsgList.pop_front();

    // Find the message with this serial number
    int i = 0;
    KMFolder* aFolder = 0;
    KMMsgDict::instance()->getLocation( serNum, &aFolder, &i );
    if( mFolder->folder() != aFolder )
      // This message was moved or something
      continue;
    mMsg = mFolder->getMsg( i );
  }

  if( !mMsg ) {
    // No message found for upload
    delete this;
    return;
  }

  KURL url = mAccount->getUrl();
  TQString flags = KMFolderImap::statusToFlags( mMsg->status(), mFolder->permanentFlags() );
  url.setPath( mFolder->imapPath() + ";SECTION=" + flags );

  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );

  mMsg->setUID( 0 ); // for the index
  TQCString cstr(mMsg->asString());
  int a = cstr.find("\nX-UID: ");
  int b = cstr.find('\n', a);
  if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a);
  TQCString mData(cstr.length() + cstr.contains('\n'));
  unsigned int i = 0;
  for( char *ch = cstr.data(); *ch; ch++ ) {
    if ( *ch == '\n' ) {
      mData.tqat(i) = '\r';
      i++;
    }
    mData.tqat(i) = *ch; i++;
  }
  jd.data = mData;
  jd.msgList.append( mMsg );

  mMsg->setTransferInProgress(true);
  KIO::SimpleJob *simpleJob = KIO::put(url, 0, false, false, false);
  KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
  mAccount->insertJob(simpleJob, jd);
  connect( simpleJob, TQT_SIGNAL( result(KIO::Job *) ),
           TQT_SLOT( slotPutMessageResult(KIO::Job *) ) );
  connect( simpleJob, TQT_SIGNAL( dataReq(KIO::Job *, TQByteArray &) ),
           TQT_SLOT( slotPutMessageDataReq(KIO::Job *, TQByteArray &) ) );
  connect( simpleJob, TQT_SIGNAL( data(KIO::Job *, const TQByteArray &) ),
           mFolder, TQT_SLOT( slotSimpleData(KIO::Job *, const TQByteArray &) ) );
  connect( simpleJob, TQT_SIGNAL(infoMessage(KIO::Job *, const TQString &)),
             TQT_SLOT(slotPutMessageInfoData(KIO::Job *, const TQString &)) );

}

//-----------------------------------------------------------------------------
// TODO: port to KIO::StoredTransferJob once it's ok to require kdelibs-3.3
void CachedImapJob::slotPutMessageDataReq(KIO::Job *job, TQByteArray &data)
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }
  if ((*it).data.size() - (*it).offset > 0x8000) {
    data.duplicate((*it).data.data() + (*it).offset, 0x8000);
    (*it).offset += 0x8000;
  } else if ((*it).data.size() - (*it).offset > 0) {
    data.duplicate((*it).data.data() + (*it).offset,
                   (*it).data.size() - (*it).offset);
    (*it).offset = (*it).data.size();
  } else
    data.resize(0);
}

//----------------------------------------------------------------------------
void CachedImapJob::slotPutMessageInfoData( KIO::Job *job, const TQString &data )
{
  KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( mDestFolder->storage() );
  if ( imapFolder ) {
    KMAcctCachedImap *account = imapFolder->account();
    ImapAccountBase::JobIterator it = account->findJob( job );
    if ( it == account->jobsEnd() ) {
      return;
    }

    if ( data.find( "UID" ) != -1 && mMsg ) {
      int uid = ( data.right( data.length() - 4 ) ).toInt();
      kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
      mMsg->setUID( uid );
    }
  }
}


//-----------------------------------------------------------------------------
void CachedImapJob::slotPutMessageResult(KIO::Job *job)
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }

  if ( job->error() ) {
    bool cont = mAccount->handlePutError( job, *it, mFolder->folder() );
    if ( !cont ) {
      delete this;
    } else {
      mMsg = 0;
      slotPutNextMessage();
    }
    return;
  }

  emit messageStored( mMsg );

  // we abuse those fields, the unit is the number of messages, here
  ++mSentBytes;
  emit progress( mSentBytes, mTotalBytes );

  int i;
  if( ( i = mFolder->find(mMsg) ) != -1 ) {
     /*
      * If we have aquired a uid during upload the server supports the uidnext
      * extension and there is no need to redownload this mail, we already have
      * it. Otherwise remove it, it will be redownloaded.
      */
     if ( mMsg->UID() == 0 ) {
        mFolder->removeMsg(i);
     } else {
        // When removing+readding, no point in telling the imap resources about it
        bool b = kmkernel->iCalIface().isResourceQuiet();
        kmkernel->iCalIface().setResourceQuiet( true );

        mFolder->takeTemporarily( i );
        mFolder->addMsgKeepUID( mMsg );
        mMsg->setTransferInProgress( false );

        kmkernel->iCalIface().setResourceQuiet( b );
     }
  }
  mMsg = NULL;
  mAccount->removeJob( it );
  slotPutNextMessage();
}


void CachedImapJob::slotAddNextSubfolder( KIO::Job * job )
{
  if (job) {
    KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
    if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
      delete this;
      return;
    }

    // make copy of setting, to reset it before potentially destroying 'it'
    bool silentUpload = static_cast<KMFolderCachedImap*>((*it).parent->storage())->silentUpload();
    static_cast<KMFolderCachedImap*>((*it).parent->storage())->setSilentUpload( false );

    if ( job->error() && !silentUpload ) {
      TQString myError = "<p><b>" + i18n("Error while uploading folder")
        + "</b></p><p>" + i18n("Could not make the folder <b>%1</b> on the server.").tqarg((*it).items[0])
        + "</p><p>" + i18n("This could be because you do not have permission to do this, or because the folder is already present on the server; the error message from the server communication is here:") + "</p>";
      mAccount->handleJobError( job, myError );
    }

    if( job->error() ) {
      delete this;
      return;
    } else {
      KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( (*it).current->storage() );
      KMFolderCachedImap* parentStorage = static_cast<KMFolderCachedImap*>( (*it).parent->storage() );
      Q_ASSERT( storage );
      Q_ASSERT( parentStorage );
      if ( storage->imapPath().isEmpty() ) {
        TQString path = mAccount->createImapPath( parentStorage->imapPath(), storage->folder()->name() );
        if ( !storage->imapPathForCreation().isEmpty() )
          path = storage->imapPathForCreation();
        storage->setImapPath( path );
        storage->writeConfig();
      }
    }
    mAccount->removeJob( it );
  }

  if (mFolderList.isEmpty()) {
    // No more folders to add
    delete this;
    return;
  }

  KMFolderCachedImap *folder = mFolderList.front();
  mFolderList.pop_front();
  KURL url = mAccount->getUrl();
  TQString path = mAccount->createImapPath( mFolder->imapPath(),
      folder->folder()->name() );
  if ( !folder->imapPathForCreation().isEmpty() ) {
    // the folder knows it's namespace
    path = folder->imapPathForCreation();
  }
  url.setPath( path );

  if ( mAccount->groupwareType() != KMAcctCachedImap::GroupwareScalix ) {
    // Associate the jobData with the parent folder, not with the child
    // This is necessary in case of an error while creating the subfolder,
    // so that folderComplete is called on the parent (and the sync resetted).
    ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
    jd.items << folder->label(); // for the err msg
    jd.current = folder->folder();
    KIO::SimpleJob *simpleJob = KIO::mkdir(url);
    KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
    mAccount->insertJob(simpleJob, jd);
    connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
             this, TQT_SLOT(slotAddNextSubfolder(KIO::Job *)) );
  } else {
    TQByteArray packedArgs;
    TQDataStream stream( packedArgs, IO_WriteOnly );

    const TQString command = TQString( "X-CREATE-SPECIAL" );
    const TQString argument = TQString( "%1 %2" ).tqarg( Scalix::Utils::contentsTypeToScalixId( folder->contentsType() ) )
                                               .tqarg( path );

    stream << (int) 'X' << 'N' << command << argument;

    ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
    jd.items << folder->label(); // for the err msg
    jd.current = folder->folder();
    KIO::SimpleJob *simpleJob = KIO::special( url.url(), packedArgs, false );
    KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
    mAccount->insertJob(simpleJob, jd);
    connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
             this, TQT_SLOT(slotAddNextSubfolder(KIO::Job *)) );
  }
}


void CachedImapJob::slotDeleteNextFolder( KIO::Job *job )
{
  if (job) {
    KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
    if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
      delete this;
      return;
    }

    mAccount->removeDeletedFolder( (*it).path );

    if( job->error() ) {
      mAccount->handleJobError( job, i18n( "Error while deleting folder %1 on the server: " ).tqarg( (*it).path ) + '\n' );
      delete this;
      return;
    }
    mAccount->removeJob(it);
  }

  if( mFoldersOrMessages.isEmpty() ) {
    // No more folders to delete
    delete this;
    return;
  }

  TQString folderPath = mFoldersOrMessages.front();
  mFoldersOrMessages.pop_front();
  KURL url = mAccount->getUrl();
  url.setPath(folderPath);
  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  jd.path = url.path();
  KIO::SimpleJob *simpleJob = KIO::file_delete(url, false);
  KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
  mAccount->insertJob(simpleJob, jd);
  connect( simpleJob, TQT_SIGNAL( result(KIO::Job *) ),
           TQT_SLOT( slotDeleteNextFolder(KIO::Job *) ) );
}

void CachedImapJob::checkUidValidity()
{
  KURL url = mAccount->getUrl();
  url.setPath( mFolder->imapPath() + ";UID=0:0" );

  ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
  jd.cancellable = true;

  KIO::SimpleJob *job = KIO::get( url, false, false );
  KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
  mAccount->insertJob( job, jd );
  connect( job, TQT_SIGNAL(result(KIO::Job *)),
           TQT_SLOT(slotCheckUidValidityResult(KIO::Job *)) );
  connect( job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
           mFolder, TQT_SLOT(slotSimpleData(KIO::Job *, const TQByteArray &)));
}

void CachedImapJob::slotCheckUidValidityResult(KIO::Job * job)
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }

  if( job->error() ) {
    mErrorCode = job->error();
    mAccount->handleJobError( job, i18n( "Error while reading folder %1 on the server: " ).tqarg( (*it).parent->label() ) + '\n' );
    delete this;
    return;
  }

  // Check the uidValidity
  TQCString cstr((*it).data.data(), (*it).data.size() + 1);
  int a = cstr.find("X-uidValidity: ");
  if (a < 0) {
    // Something is seriously rotten here!
    // TODO: Tell the user that he has a problem
    kdDebug(5006) << "No uidvalidity available for folder "
                  << mFolder->name() << endl;
  }
  else {
    int b = cstr.find("\r\n", a);
    if ( (b - a - 15) >= 0 ) {
      TQString uidv = cstr.mid(a + 15, b - a - 15);
      // kdDebug(5006) << "New uidv = " << uidv << ", old uidv = "
      //               << mFolder->uidValidity() << endl;
      if( !mFolder->uidValidity().isEmpty() && mFolder->uidValidity() != uidv ) {
        // kdDebug(5006) << "Expunging the mailbox " << mFolder->name()
        //               << "!" << endl;
        mFolder->expunge();
        mFolder->setLastUid( 0 );
        mFolder->clearUidMap();
      }
    } else
      kdDebug(5006) << "No uidvalidity available for folder "
                    << mFolder->name() << endl;
  }

  a = cstr.find( "X-PermanentFlags: " );
  if ( a < 0 ) {
    kdDebug(5006) << "no PERMANENTFLAGS response? assumming custom flags are not available" << endl;
  } else {
    int b = cstr.find( "\r\n", a );
    if ( (b - a - 18) >= 0 ) {
      int flags = cstr.mid( a + 18, b - a - 18 ).toInt();
      emit permanentFlags( flags );
    } else {
      kdDebug(5006) << "PERMANENTFLAGS response broken, assumming custom flags are not available" << endl;
    }
  }

  mAccount->removeJob(it);
  delete this;
}


void CachedImapJob::renameFolder( const TQString &newName )
{
  mNewName = newName;

  // Set the source URL
  KURL urlSrc = mAccount->getUrl();
  mOldImapPath = mFolder->imapPath();
  urlSrc.setPath( mOldImapPath );

  // Set the destination URL - this is a bit trickier
  KURL urlDst = mAccount->getUrl();
  mNewImapPath = mFolder->imapPath();
  // Destination url = old imappath - oldname + new name
  mNewImapPath.truncate( mNewImapPath.length() - mFolder->folder()->name().length() - 1);
  mNewImapPath += newName + '/';
  urlDst.setPath( mNewImapPath );

  ImapAccountBase::jobData jd( newName, mFolder->folder() );
  jd.path = mNewImapPath;

  KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, false );
  KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
  mAccount->insertJob( simpleJob, jd );
  connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
           TQT_SLOT(slotRenameFolderResult(KIO::Job *)) );
}

static void renameChildFolders( KMFolderDir* dir, const TQString& oldPath,
                                const TQString& newPath )
{
  if( dir ) {
    KMFolderNode *node = dir->first();
    while( node ) {
      if( !node->isDir() ) {
        KMFolderCachedImap* imapFolder =
          static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
        if ( !imapFolder->imapPath().isEmpty() )
          // Only rename folders that have been accepted by the server
          if( imapFolder->imapPath().find( oldPath ) == 0 ) {
            TQString p = imapFolder->imapPath();
            p = p.mid( oldPath.length() );
            p.prepend( newPath );
            imapFolder->setImapPath( p );
            renameChildFolders( imapFolder->folder()->child(), oldPath, newPath );
          }
      }
      node = dir->next();
    }
  }
}

void CachedImapJob::revertLabelChange()
{
  TQMap<TQString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
  Q_ASSERT( renit != mAccount->renamedFolders().end() );
  if ( renit != mAccount->renamedFolders().end() ) {
    mFolder->folder()->setLabel( (*renit).mOldLabel );
    mAccount->removeRenamedFolder( mFolder->imapPath() );
    kmkernel->dimapFolderMgr()->contentsChanged();
  }
}

void CachedImapJob::renameOnDisk()
{
  TQString oldName = mFolder->name();
  TQString oldPath = mFolder->imapPath();
  mAccount->removeRenamedFolder( oldPath );
  mFolder->setImapPath( mNewImapPath );
  mFolder->FolderStorage::rename( mNewName );

  if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
  TQString newPath = mFolder->imapPath();
  if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
  renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
  kmkernel->dimapFolderMgr()->contentsChanged();
}

void CachedImapJob::slotSubscribtionChange1Failed( const TQString &errorMessage )
{
  KMessageBox::sorry( 0, i18n( "Error while trying to subscribe to the renamed folder %1.\n"
                               "Renaming itself was successful, but the renamed folder might disappear "
                               "from the folder list after the next sync since it is unsubscribed on the server.\n"
                               "You can try to manually subscribe to the folder yourself.\n\n"
                               "%2" )
      .tqarg( mFolder->label() ).tqarg( errorMessage ) );
  delete this;
}

void CachedImapJob::slotSubscribtionChange2Failed( const TQString &errorMessage )
{
  kdWarning(5006) << k_funcinfo << errorMessage << endl;
  // Ignore this error, not something user-visible anyway
  delete this;
}

void CachedImapJob::slotSubscribtionChange1Done( const TQString&, bool )
{
  disconnect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
              this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
  connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
           this, TQT_SLOT( slotSubscribtionChange2Done( const TQString&, bool ) ) );
  disconnect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
              this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
  connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
           this, TQT_SLOT( slotSubscribtionChange2Failed( const TQString& ) ) );

  mAccount->changeSubscription( false, mOldImapPath, true /* quiet */ );
}

void CachedImapJob::slotSubscribtionChange2Done( const TQString&, bool )
{
  // Finally done with everything!
  delete this;
}

void CachedImapJob::slotRenameFolderResult( KIO::Job *job )
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }

  if( job->error() ) {
    revertLabelChange();
    const TQString errorMessage = i18n( "Error while trying to rename folder %1" ).tqarg( mFolder->label() );
    mAccount->handleJobError( job, errorMessage );
    delete this;
  } else {

    mAccount->removeJob( it );
    renameOnDisk();

    // Okay, the folder seems to be renamed on the server and on disk.
    // Now unsubscribe from the old folder name and subscribe to the new folder name,
    // so that the folder doesn't suddenly disappear after renaming it
    connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
             this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
    connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
             this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
    mAccount->changeSubscription( true, mNewImapPath, true /* quiet */ );
  }
}

void CachedImapJob::slotListMessagesResult( KIO::Job * job )
{
  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
    delete this;
    return;
  }

  if (job->error()) {
    mErrorCode = job->error();
    mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
  }
  else
    mAccount->removeJob(it);

  delete this;
}

//-----------------------------------------------------------------------------
void CachedImapJob::setParentFolder( const KMFolderCachedImap* parent )
{
  mParentFolder = const_cast<KMFolderCachedImap*>( parent );
}

}

#include "cachedimapjob.moc"