/*
    Copyright (c) 2009 Jonathan Armond <jon.armond@gmail.com>
    Copyright (c) 2010 Volker Krause <vkrause@kde.org>
    Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
    Author: Kevin Krammer, krake@kdab.com
    Copyright (C) 2011 Kevin Krammer, kevin.krammer@gmx.at

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "kmailmigrator.h"


// avoid metatype.h from interfering
#define POP3_METATYPE_H

#include "imapsettings.h"
#include "pop3settings.h"
#include "mboxsettings.h"
#include "maildirsettings.h"
#include "mixedmaildirsettings.h"
#include "mixedmaildirstore.h"
#include "emptyresourcecleaner.h"
#include "imapcacheadapter.h"
#include "imapcachecollectionmigrator.h"
#include "localfolderscollectionmigrator.h"

#include "collectionannotationsattribute.h"

#include <Mailtransport/Transport>

#include <akonadi/agentmanager.h>
#include <akonadi/agentinstance.h>
#include <akonadi/agentinstancecreatejob.h>
#include <akonadi/attributefactory.h>
#include <akonadi/tag.h>
#include <akonadi/tagcreatejob.h>
#include <akonadi/tagattribute.h>
using Akonadi::AgentManager;
using Akonadi::AgentInstance;
using Akonadi::AgentInstanceCreateJob;

#include <KIO/CopyJob>

#include <KConfig>
#include <KConfigGroup>
#include <KDateTime>
#include <KDebug>
#include <KStandardDirs>
#include <KSharedConfig>
#include <kwallet.h>
#include <kstringhandler.h>
#include <KLocalizedString>

Q_DECLARE_METATYPE(QList<int>);

using KWallet::Wallet;

using namespace KMail;

/* Enums for use over DBus. Can't include settings.h for each resource because
   all have same class name. If resource interfaces are changed, these will need
   changing too. */
enum MboxLocking { Procmail, MuttDotLock, MuttDotLockPrivileged, MboxNone };

#define SPECIALCOLLECTIONS_LOCK_SERVICE QLatin1String( "org.kde.pim.SpecialCollections" )

static MixedMaildirStore *createStoreFromBasePath( const QString &basePath )
{
  MixedMaildirStore *store = 0;
  const QFileInfo baseDir( basePath );
  if ( baseDir.exists() && baseDir.isDir() ) {
    store = new MixedMaildirStore;
    store->setPath( baseDir.absoluteFilePath() );
  }

  return store;
}

static void backupConfig( const QString &name, const QDir &backupDir )
{
  const QString configFile = KStandardDirs::locate( "config", name );
  if ( !configFile.isEmpty() ) {
    QFile::copy( configFile, QFileInfo( backupDir, name ).absoluteFilePath() );
  }
}

KMailMigrator::KMailMigrator() :
  KMigratorBase(),
  mWallet( 0 ),
  mDImapCache( 0 ),
  mImapCache( 0 ),
  mForwardResourceNotifications( false ),
  mImapCacheAdapter( 0 )
{
  Akonadi::AttributeFactory::registerAttribute<Akonadi::CollectionAnnotationsAttribute>();
  connect( AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance)),
           this, SLOT(instanceStatusChanged(Akonadi::AgentInstance)) );
  connect( AgentManager::self(), SIGNAL(instanceProgressChanged(Akonadi::AgentInstance)),
           this, SLOT(instanceProgressChanged(Akonadi::AgentInstance)) );
}

KMailMigrator::~KMailMigrator()
{
  delete mWallet;
  delete mDImapCache;
  delete mImapCache;
}

void KMailMigrator::migrate()
{
  emit message( Info, i18n( "Beginning KMail migration..." ) );

  // copy config files to backup locations
  const KDateTime now = KDateTime::currentLocalDateTime();
  const QDir backupDir( KGlobal::dirs()->saveLocation( "appdata", now.toString() ) );

  // copy current configs to backup location
  // strickly speaking neither kmailrc not mailtransports are changed by the migrator
  // but copy them anyway as the belong conceptually to the KMail1 -> KMail2 migration
  backupConfig( QLatin1String("kmailrc"), backupDir );
  backupConfig( QLatin1String("mailtransports"), backupDir );
  backupConfig( QLatin1String("emailidentities"), backupDir );
  backupConfig( QLatin1String("kcmkmailsummaryrc"), backupDir );
  backupConfig( QLatin1String("templatesconfigurationrc"), backupDir );

  KSharedConfigPtr oldConfig = KSharedConfig::openConfig( QLatin1String( "kmailrc" ) );
  mConfig = KSharedConfig::openConfig( QLatin1String( "kmail2rc" ) );

  const QFileInfo migratorConfigInfo( KStandardDirs::locateLocal( "config", KGlobal::config()->name() ) );

  const QString &newKMailCfgFile = KStandardDirs::locateLocal( "config", QLatin1String( "kmail2rc" ) );

  // copy old config into new config, if
  bool copy = false;
  // there is no migrator config (no migration happened yet or full rerun)
  copy = copy || !migratorConfigInfo.exists();
  // new config does not exist yet
  copy = copy || !QFileInfo( newKMailCfgFile ).exists();
  // new config is empty
  copy = copy || mConfig->groupList().isEmpty();
  // new config does not contain any account groups
  copy = copy || mConfig->groupList().filter( QRegExp( QLatin1String("Account \\d+") ) ).isEmpty();

  if ( copy ) {
    kDebug() << "Copying content from kmailrc";
    oldConfig->copyTo( newKMailCfgFile, mConfig.data() );
  } else {
    kDebug() << "Ignoring kmailrc, just using contents from kmail2rc";
  }

  mEmailIdentityConfig = KSharedConfig::openConfig( QLatin1String( "emailidentities" ) );

  mKcmKmailSummaryConfig = KSharedConfig::openConfig( QLatin1String( "kcmkmailsummaryrc" ) );

  mTemplatesConfig = KSharedConfig::openConfig( QLatin1String( "templatesconfigurationrc" ) );

  deleteOldGroup();
  migrateTags();
  migrateRCFiles();
  migrateNotifyFile();

  // copy autosave files if there are any
  const QString autoSaveDir = KGlobal::dirs()->saveLocation( "data", QLatin1String("kmail/autosave"), false );
  if ( !autoSaveDir.isEmpty() ) {
    const QString destDir = KGlobal::dirs()->saveLocation( "data", QLatin1String("kmail2") );
    KIO::CopyJob *job = KIO::copy( KUrl::fromPath( autoSaveDir ),
                                   KUrl::fromPath( destDir ),
                                   KIO::HideProgressInfo );
    job->setAutoSkip( true );
    job->setWriteIntoExistingDirectories( true );
    connect( job, SIGNAL(result(KJob*)), this, SLOT(autoSaveCopyResult(KJob*)) );
  }

  mAccounts = mConfig->groupList().filter( QRegExp( QLatin1String("Account \\d+") ) );

  mIt = mAccounts.begin();
  migrateNext();
}

void KMailMigrator::autoSaveCopyResult( KJob *job )
{
  if ( job->error() ) {
    kDebug( KDE_DEFAULT_DEBUG_AREA ) << "error=" << job->error()
      << "text=" << job->errorString();
  }
}

void KMailMigrator::deleteOldGroup()
{
  deleteOldGroup( QLatin1String("GroupwareFolderInfo") );
  deleteOldGroup( QLatin1String("Groupware") );
  deleteOldGroup( QLatin1String("IMAP Resource") );
  deleteOldGroup( QLatin1String("Folder_local_root") );
  deleteOldGroup( QLatin1String("Folder_search") );
}

void KMailMigrator::deleteOldGroup( const QString& name ) {
  if ( mConfig->hasGroup( name ) ) {
    KConfigGroup groupName( mConfig, name );
    groupName.deleteGroup();
  }
}

void KMailMigrator::migrateNotifyFile()
{
    const QString notifyFile = KStandardDirs::locate( "config", QLatin1String("kmail.notifyrc") );
    if ( !notifyFile.isEmpty() ) {
        QFile::copy( notifyFile, KStandardDirs::locateLocal( "config", QLatin1String("kmail2.notifyrc" )) );
    }

}

void KMailMigrator::migrateTags()
{
  KConfigGroup tagMigrationConfig( KGlobal::config(), QLatin1String( "MessageTags" ) );
  const QStringList migratedTags = tagMigrationConfig.readEntry( "MigratedTags", QStringList() );

  const QStringList tagGroups = mConfig->groupList().filter( QRegExp( QLatin1String("MessageTag #\\d+") ) );

  QSet<QString> groupNames = tagGroups.toSet();
  groupNames.subtract( migratedTags.toSet() );

  kDebug() << "Tag Groups:" << tagGroups << "MigratedTags:" << migratedTags
           << "Unmigrated Tags:" << groupNames;

  QStringList newlyMigratedTags;
  Q_FOREACH ( const QString &groupName, groupNames ) {
    const KConfigGroup group( mConfig, groupName );

    const QString label = group.readEntry( QLatin1String( "Label" ), QString() );
    if ( label.isEmpty() ) {
      continue;
    }
    const QString name = group.readEntry( QLatin1String( "Name" ), QString() );
    if ( name.isEmpty() ) {
      continue;
    }

    const QColor textColor = group.readEntry( QLatin1String( "text-color" ), QColor() );
    const QColor backgroundColor = group.readEntry( QLatin1String( "background-color" ), QColor() );
    const bool hasFont = group.hasKey( QLatin1String( "text-font" ) );
    const QFont textFont = group.readEntry( QLatin1String( "text-font" ), QFont() );

    const bool inToolbar = group.readEntry( QLatin1String( "toolbar" ), false );
    const QString iconName = group.readEntry( QLatin1String( "icon" ), QString::fromLatin1( "mail-tagged" ) );

    const QString shortcut = group.readEntry( QLatin1String( "shortcut" ), QString() );

    Akonadi::Tag tag(label);
    Akonadi::TagAttribute *attr = tag.attribute<Akonadi::TagAttribute>(Akonadi::AttributeEntity::AddIfMissing);
    attr->setDisplayName(name);
    attr->setIconName( iconName );
    attr->setInToolbar( inToolbar );
    if (hasFont) {
        attr->setFont( textFont.toString() );
    }
    attr->setBackgroundColor( backgroundColor );
    attr->setTextColor( textColor );
    attr->setShortcut( shortcut );

    Akonadi::TagCreateJob *createJob = new Akonadi::TagCreateJob(tag);
    createJob->setMergeIfExisting(true);

    kDebug() << "Tag: label=" << label << "name=" << name
             << "textColor=" << textColor << "backgroundColor=" << backgroundColor
             << "hasFont=" << hasFont << "font=" << textFont
             << "icon=" << iconName << "inToolbar=" << inToolbar
             << "shortcut=" << shortcut;

    newlyMigratedTags << groupName;
  }

  //Cleanup migrated group
  Q_FOREACH ( const QString &groupName, newlyMigratedTags ) {
    deleteOldGroup( groupName );
  }

  if ( !newlyMigratedTags.isEmpty() ) {
    tagMigrationConfig.writeEntry( "MigratedTags", migratedTags + newlyMigratedTags );
    tagMigrationConfig.sync();
  }
}

void KMailMigrator::migrateRCFiles()
{
  const QDir sourceDir( KStandardDirs::locateLocal( "data", QLatin1String("kmail") ) );
  const QDir targetDir( KStandardDirs::locateLocal( "data", QLatin1String("kmail2") ) );
  KStandardDirs::makeDir( targetDir.absolutePath() );

  const QFileInfoList files = sourceDir.entryInfoList( QStringList() << QLatin1String( "*.rc" ),
                                                       QDir::Files | QDir::Readable );
  Q_FOREACH ( const QFileInfo &fileInfo, files ) {
    QFile file( fileInfo.absoluteFilePath() );
    file.copy( QFileInfo( targetDir, fileInfo.fileName() ).absoluteFilePath() );
  }
}

void KMailMigrator::migrateNext()
{
  while ( mIt != mAccounts.end() ) {
    mCurrentAccount = *mIt;
    const KConfigGroup group( mConfig, mCurrentAccount );

    const QString name = group.readEntry( "Name" );
    const QString idString = group.readEntry( "Id" );
    if ( migrationState( idString ) == None ) {
      ++mIt;
      if ( !migrateCurrentAccount() ) {
        emit message( Error, i18n( "No backend for '%1' available.", name ) );
        migrateNext();
      }
      return;
    }

    if ( migrationState( idString ) == Complete )
      emit message( Skip, i18n( "'%1' has been already migrated.", name ) );

    ++mIt;
  }
  if ( mIt == mAccounts.end() ) {
    if ( mImapCacheAdapter != 0 ) {
      emit message( Info, i18n( "Enabling access to the disconnected IMAP cache of the previous KMail version" ) );
      emit status( QString() );
      mImapCacheAdapter->start();
    } else {
      migrateLocalFolders();
    }
  }
}

void KMailMigrator::migrateLocalFolders()
{
  if ( migrationState( QLatin1String("LocalFolders") ) == Complete ) {
    emit message( Skip, i18n( "Local folders have already been migrated." ) );
    emit status( QString() );
    migrationDone();
    return;
  }

  KConfigGroup cfgGroup( mConfig, "General" );
  cfgGroup.deleteEntry( "QuotaUnit" );
  cfgGroup.deleteEntry( "default-mailbox-format" );
  mConfig->sync();
  const QString localMaildirDefaultPath = KStandardDirs::locateLocal( "data", QLatin1String( "kmail/mail" ) );
  mLocalMaildirPath = cfgGroup.readPathEntry( "folders", localMaildirDefaultPath );
  const QFileInfo fileInfo( mLocalMaildirPath );
  if ( !fileInfo.exists() || !fileInfo.isDir() ) {
    emit status( QString() );
    migrationDone();
  } else {
    kDebug() << mLocalMaildirPath;

    emit message( Info, i18nc( "@info:status", "Migrating local folders in '%1'...", mLocalMaildirPath ) );

    // show status/progress info of resources in our dialog
    mForwardResourceNotifications = true;

    createAgentInstance( QLatin1String("akonadi_mixedmaildir_resource"), this,
                         SLOT(localMaildirCreated(KJob*)) );
  }
}

void KMailMigrator::migrationDone()
{
  emit message( Success, i18n( "Migration successfully completed." ) );

  migrateInstanceTrashFolder();

  mConfig->sync();
  kDebug() << "Deleting" << mFailedInstances.count() << "failed resource instances";
  Q_FOREACH ( const AgentInstance &instance, mFailedInstances ) {
    if ( instance.isValid() ) {
      AgentManager::self()->removeInstance( instance );
    }
  }
  cleanupConfigFile();
  migrateConfigurationDialogRestriction();
  deleteLater();
}

OrgKdeAkonadiImapSettingsInterface* KMailMigrator::createImapSettingsInterface( const Akonadi::AgentInstance& instance )
{
  OrgKdeAkonadiImapSettingsInterface *iface = new OrgKdeAkonadiImapSettingsInterface(
    QLatin1String("org.freedesktop.Akonadi.Resource.") + instance.identifier(),
    QLatin1String("/Settings"), QDBusConnection::sessionBus(), this );
  if ( !iface->isValid() ) {
    migrationFailed( i18n( "Failed to obtain D-Bus interface for remote configuration." ), instance );
    delete iface;
    return 0;
  }
  return iface;
}

OrgKdeAkonadiPOP3SettingsInterface* KMailMigrator::createPop3SettingsInterface( const Akonadi::AgentInstance& instance )
{
  OrgKdeAkonadiPOP3SettingsInterface *iface = new OrgKdeAkonadiPOP3SettingsInterface(
    QLatin1String("org.freedesktop.Akonadi.Resource.") + instance.identifier(),
    QLatin1String("/Settings"), QDBusConnection::sessionBus(), this );
  if ( !iface->isValid() ) {
    migrationFailed( i18n( "Failed to obtain D-Bus interface for remote configuration." ), instance );
    delete iface;
    return 0;
  }
  return iface;
}

void KMailMigrator::migrateConfigurationDialogRestriction()
{
  if ( mConfig->hasGroup( "ConfigurationDialogRestrictions" ) ) {
    KSharedConfigPtr resourcebaseconfig = KSharedConfig::openConfig( QLatin1String( "resourcebaserc" ) );
    KConfigGroup oldGroup = mConfig->group( "ConfigurationDialogRestrictions" );
    KConfigGroup newGroup = resourcebaseconfig->group( "ConfigurationDialogRestrictions" );
    oldGroup.copyTo( &newGroup );
    oldGroup.deleteGroup();
  }

}

void KMailMigrator::cleanupConfigFile()
{
  mIt = mAccounts.begin();
  AccountIterator end( mAccounts.end() ) ;

  while ( mIt != end ) {
    const QString accountName = *mIt;
    deleteOldGroup( accountName );
    ++mIt;
  }

  deleteOldGroup( QLatin1String("FavoriteFolderView") );

  if ( mConfig->hasGroup( "Internal" ) ) {
    KConfigGroup cfgGroup( mConfig, "General" );
    cfgGroup.deleteEntry( "MsgDictSizeHint" );
  }
}

void KMailMigrator::migrateInstanceTrashFolder()
{
  mIt = mAccounts.begin();
  while ( mIt != mAccounts.end() ) {
    const QString accountName = *mIt;
    const KConfigGroup group( mConfig, accountName );
    if ( mAccountInstance.contains( accountName ) ) {
      AccountConfig accountConf = mAccountInstance.value( accountName );
      Akonadi::AgentInstance instance = accountConf.instance;
      if ( accountConf.imapAccount ) { //Imap
        OrgKdeAkonadiImapSettingsInterface *iface = createImapSettingsInterface( instance );
        if ( iface ) {
          const qint64 value = group.readEntry( "trash", -1 );
          if ( value != -1 ) {
            iface->setTrashCollection( value );
            // make sure the config is saved
            iface->writeConfig();
            instance.reconfigure();
          }
        }
      } else { //Pop3
        OrgKdeAkonadiPOP3SettingsInterface *iface = createPop3SettingsInterface( instance );
        if ( iface ) {
          const qint64 value = group.readEntry( "Folder", -1 );
          if ( value != -1 ) {
            iface->setTargetCollection( value );
            // make sure the config is saved
            iface->writeConfig();
            instance.reconfigure();
          }
        }
      }
    }
    ++mIt;
  }
}

void KMailMigrator::migrationFailed( const QString &errorMsg,
                                     bool doMigrateNext,
                                     const AgentInstance &instance )
{
  const KConfigGroup group( mConfig, mCurrentAccount );
  emit message( Error, i18n( "Migration of '%1' to Akonadi resource failed: %2",
                             group.readEntry( "Name" ), errorMsg ) );

  if ( instance.isValid() ) {
    mFailedInstances << instance;
  }

  mCurrentAccount.clear();
  mCurrentInstance = AgentInstance();

  if ( doMigrateNext ) {
    migrateNext();
  }
}

void KMailMigrator::migrationCompleted( const AgentInstance &instance, bool doMigrateNext )
{
  const KConfigGroup group( mConfig, mCurrentAccount );
  const QString accountId = group.readEntry( QLatin1String( "Id" ) );

  // check if the account is used in filters
  const QStringList filterGroups = mConfig->groupList().filter( QRegExp( QLatin1String("Filter #\\d+") ) );
  //kDebug( KDE_DEFAULT_DEBUG_AREA ) << "filterGroups=" << filterGroups;
  Q_FOREACH ( const QString &groupName, filterGroups ) {
    KConfigGroup filterGroup( mConfig, groupName );

    QStringList accountsSet = filterGroup.readEntry( QLatin1String( "accounts-set" ), QStringList() );
    //if ( !accountsSet.isEmpty() ) {
    //  kDebug( KDE_DEFAULT_DEBUG_AREA ) << "accountsSet=" << accountsSet;
    //}
    const int index = accountsSet.indexOf( accountId );
    if ( index != -1 ) {
      kDebug( KDE_DEFAULT_DEBUG_AREA ) << "replacing account id" << accountId
                                       << "in filter" << groupName
                                       << "with resource id" << instance.identifier();
      accountsSet.replace( index, instance.identifier() );
      filterGroup.writeEntry( QLatin1String( "accounts-set" ), accountsSet );
    }
  }

  setMigrationState( group.readEntry( "Id" ), Complete, instance.identifier(),
                     group.readEntry( "Type" ).toLower() );
  emit message( Success, i18n( "Migration of '%1' succeeded.", group.readEntry( "Name" ) ) );
  mCurrentAccount.clear();
  mCurrentInstance = AgentInstance();

  if ( doMigrateNext ) {
    migrateNext();
  }
}

void KMailMigrator::connectCollectionMigrator( AbstractCollectionMigrator *migrator )
{
  connect( migrator, SIGNAL(message(int,QString)),
           SLOT (collectionMigratorMessage(int,QString)) );
  connect( migrator, SIGNAL(status(QString)), SIGNAL (status(QString)) );
  connect( migrator, SIGNAL(progress(int)), SIGNAL (progress(int)) );
  connect( migrator, SIGNAL(progress(int,int,int)), SIGNAL (progress(int,int,int)) );
  connect( migrator, SIGNAL(migrationFinished(Akonadi::AgentInstance,QString)),
           this, SLOT(collectionMigratorFinished()) );
  connect( migrator, SIGNAL(status(QString)),
           SLOT (collectionMigratorEmittedNotification()) );
  connect( migrator, SIGNAL(progress(int)),
           SLOT (collectionMigratorEmittedNotification()) );
  connect( migrator, SIGNAL(progress(int,int,int)),
           SLOT (collectionMigratorEmittedNotification()) );
}

void KMailMigrator::migratePassword( const QString &idString, const AgentInstance &instance,
                                     const QString &newFolder, const QString& passwordFromFilePassword )
{
  QString password;
  if ( mWallet == 0 ) {
    mWallet = Wallet::openWallet( Wallet::NetworkWallet(), 0 );
  }
  if ( mWallet && mWallet->isOpen() ) {
    if ( !passwordFromFilePassword.isEmpty() )
      password = passwordFromFilePassword;

    if ( password.isEmpty() &&  mWallet->hasFolder( QLatin1String("kmail") ) ) {
      mWallet->setFolder( QLatin1String("kmail") );
      mWallet->readPassword( QLatin1String("account-") + idString, password );
    }

    if ( !password.isEmpty() ) {
      if ( !mWallet->hasFolder( newFolder ) )
        mWallet->createFolder( newFolder );
      mWallet->setFolder( newFolder );

      mWallet->writePassword( instance.identifier() + QLatin1String("rc") , password );
    }
  }
}

bool KMailMigrator::migrateCurrentAccount()
{
  if ( mCurrentAccount.isEmpty() )
    return false;
  const KConfigGroup group( mConfig, mCurrentAccount );

  emit message( Info, i18n( "Trying to migrate '%1' to resource...", group.readEntry( "Name" ) ) );

  // show status/progress info of resources in our dialog
  mForwardResourceNotifications = true;

  const QString type = group.readEntry( "Type" ).toLower();
  if ( type == QLatin1String( "imap" ) ) {
    createAgentInstance( QLatin1String("akonadi_imap_resource"), this,
                         SLOT(imapAccountCreated(KJob*)) );
  } else if ( type == QLatin1String( "dimap" ) ) {
    createAgentInstance( QLatin1String("akonadi_imap_resource"), this,
                         SLOT(imapDisconnectedAccountCreated(KJob*)) );

  } else if ( type == QLatin1String( "pop" ) ) {
    createAgentInstance( QLatin1String("akonadi_pop3_resource"), this,
                         SLOT(pop3AccountCreated(KJob*)) );
  } else if ( type == QLatin1String( "maildir" ) ) {
    createAgentInstance( QLatin1String("akonadi_maildir_resource"), this,
                         SLOT(maildirAccountCreated(KJob*)) );
  } else if ( type == QLatin1String( "local" ) ) {
    createAgentInstance( QLatin1String("akonadi_mbox_resource"), this,
                         SLOT(mboxAccountCreated(KJob*)) );
  } else {
    return false;
  }

  return true;
}

void KMailMigrator::imapDisconnectedAccountCreated( KJob * job )
{
  if ( job->error() ) {
    migrationFailed( i18n( "Failed to create resource: %1", job->errorText() ) );
    return;
  }
  emit message( Info, i18n( "Created disconnected imap resource" ) );
  migrateImapAccount( job, true );
}

void KMailMigrator::imapAccountCreated( KJob *job )
{
  if ( job->error() ) {
    migrationFailed( i18n( "Failed to create resource: %1", job->errorText() ) );
    return;
  }
  emit message( Info, i18n( "Created imap resource" ) );
  migrateImapAccount( job, false );
}


void KMailMigrator::migrateImapAccount( KJob *job, bool disconnected )
{
  AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job )->instance();
  mCurrentInstance = instance;

  AccountConfig conf;
  conf.instance = instance;
  conf.imapAccount = true;
  conf.disconnectedImap = disconnected;
  mAccountInstance.insert( mCurrentAccount, conf );

  KConfigGroup config( mConfig, mCurrentAccount );

  const QString nameAccount = config.readEntry( "Name" );
  instance.setName( nameAccount );
  emit status( nameAccount );

  ImapCacheCollectionMigrator::MigrationOptions options = ImapCacheCollectionMigrator::ImportNewMessages;
  if ( disconnected ) {
    const KConfigGroup dimapConfig( KGlobal::config(), QLatin1String( "Disconnected IMAP" ) );
    if ( dimapConfig.isValid() ) {
      options = ImapCacheCollectionMigrator::ConfigOnly;

      if ( dimapConfig.readEntry( QLatin1String( "ImportNewMessages" ), false ) ) {
        options |= ImapCacheCollectionMigrator::ImportNewMessages;
      }

      if ( dimapConfig.readEntry( QLatin1String( "ImportCachedMessages" ), false ) ) {
        options |= ImapCacheCollectionMigrator::ImportCachedMessages;
      }

      if ( dimapConfig.readEntry( QLatin1String( "RemoveDeletedMessages" ), false ) ) {
        options |= ImapCacheCollectionMigrator::RemoveDeletedMessages;
      }
    }
  }

  MixedMaildirStore *store = 0;
  if ( disconnected ) {
#if 0
    if ( mDeleteCacheAfterImport ) {
      options |= ImapCacheCollectionMigrator::DeleteImportedMessages;
    }
#endif
    if ( mDImapCache == 0 ) {
      mDImapCache = createStoreFromBasePath( KStandardDirs::locateLocal( "data", QLatin1String( "kmail/dimap" ) ) );
    }
    store = mDImapCache;
  } else {
    if ( mImapCache == 0 ) {
      mImapCache = createStoreFromBasePath( KStandardDirs::locateLocal( "data", QLatin1String( "kmail/imap" ) ) );
    }
    store = mImapCache;
  }

  ImapCacheCollectionMigrator *collectionMigrator = new ImapCacheCollectionMigrator( instance, nameAccount, store, this );
  QString topLevelFolder = config.readEntry( "Folder" );
  if ( topLevelFolder.isEmpty() ) {
    topLevelFolder = config.readEntry( "Id" );
  }

  const QString topLevelRemoteId =
    QLatin1String("imap://") + config.readEntry( "login" ) + QLatin1Char('@') + config.readEntry( "host" ) + QLatin1Char('/');

  collectionMigrator->setTopLevelFolder( topLevelFolder, nameAccount, topLevelRemoteId );
  collectionMigrator->setMigrationOptions( options );

  kDebug() << "Starting IMAP collection migration: options="
           << collectionMigrator->migrationOptions();
  collectionMigrator->setKMailConfig( mConfig );
  collectionMigrator->setEmailIdentityConfig( mEmailIdentityConfig );
  collectionMigrator->setKcmKmailSummaryConfig( mKcmKmailSummaryConfig );
  collectionMigrator->setTemplatesConfig( mTemplatesConfig );

  if ( config.readEntry( "locally-subscribed-folders", false ) ) {
    collectionMigrator->setUnsubscribedImapFolders( config.readEntry( "locallyUnsubscribedFolders", QStringList() ) );
  }

  if ( disconnected && store ) {
    if ( mImapCacheAdapter == 0 ) {
      mImapCacheAdapter = new ImapCacheAdapter( store, this );
      connect( mImapCacheAdapter, SIGNAL(finished(int,QString)),
               SLOT(imapCacheAdaptionFinished(int,QString)) );
    }
    mImapCacheAdapter->addAccount( topLevelFolder, nameAccount );
  }

  connect( collectionMigrator, SIGNAL(migrationFinished(Akonadi::AgentInstance,QString)),
           SLOT(imapFoldersMigrationFinished(Akonadi::AgentInstance,QString)) );

  connectCollectionMigrator( collectionMigrator );

  collectionMigrator->startMigration();
}

void KMailMigrator::pop3AccountCreated( KJob *job )
{
  if ( job->error() ) {
    migrationFailed( i18n( "Failed to create resource: %1", job->errorText() ) );
    return;
  }
  emit message( Info, i18n( "Created pop3 resource" ) );

  AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job )->instance();
  mCurrentInstance = instance;
  KConfigGroup config( mConfig, mCurrentAccount );

  OrgKdeAkonadiPOP3SettingsInterface *iface = createPop3SettingsInterface( instance );
  if ( !iface ) {
    return;
  }

  AccountConfig conf;
  conf.instance = instance;
  conf.imapAccount = false;
  mAccountInstance.insert( mCurrentAccount, conf );

  iface->setHost( config.readEntry( "host", QString() ) );
  iface->setPort( config.readEntry( "port", 110u ) );
  iface->setLogin( config.readEntry( "login", QString() ) );
  if ( config.readEntry( "use-ssl", true ) )
    iface->setUseSSL( true );
  if ( config.readEntry( "use-tls", true ) )
    iface->setUseTLS( true );
  if ( config.readEntry( "pipelining", false ) )
    iface->setPipelining( true );
  if ( config.readEntry( "leave-on-server", true ) ) {
    iface->setLeaveOnServer( true );
    iface->setLeaveOnServerDays( config.readEntry( "leave-on-server-days", -1 ) );
    iface->setLeaveOnServerCount( config.readEntry( "leave-on-server-count", -1 ) );
    iface->setLeaveOnServerSize( config.readEntry( "leave-on-server-size", -1 ) );
  }
  if ( config.readEntry( "filter-on-server", false ) ) {
    iface->setFilterOnServer( true );
    iface->setFilterCheckSize( config.readEntry( "filter-os-check-size" ).toUInt() );
  }
  const int checkInterval = config.readEntry( "check-interval", 0 );
  if ( checkInterval == 0 )
    iface->setIntervalCheckEnabled( false );
  else {
    iface->setIntervalCheckEnabled( true );
    iface->setIntervalCheckInterval( checkInterval );
  }

  // check-exclude in Account section means that this account should not be included
  // in manual checks. In KMail UI this is called "Include in manual checks"
  KConfigGroup resourceGroup( mConfig, QString::fromLatin1( "Resource %1" ).arg( instance.identifier() ) );
  resourceGroup.writeEntry( "IncludeInManualChecks", !config.readEntry( "check-exclude", false ) );
  const KConfigGroup generalGroup( mConfig, "General" );
  resourceGroup.writeEntry( "CheckOnStartup", generalGroup.readEntry( "checkmail-startup", false ) );
  resourceGroup.writeEntry( "OfflineOnShutdown", true );

  // Akonadi kmail uses enums for storing auth options
  // so we have to convert from the old string representations
  typedef MailTransport::Transport::EnumAuthenticationType AuthType;
  QString authOpt = config.readEntry( "auth" );
  if ( authOpt.contains( QLatin1String("PLAIN"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::PLAIN );
  else if ( authOpt.contains( QLatin1String("CRAM"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::CRAM_MD5 );
  else if ( authOpt.contains( QLatin1String("DIGEST"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::DIGEST_MD5);
  else if ( authOpt.contains( QLatin1String("GSSAPI"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::GSSAPI );
  else if ( authOpt.contains( QLatin1String("NTLM"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::NTLM);
  else if ( authOpt.contains( QLatin1String("APOP"), Qt::CaseInsensitive ) )
    iface->setAuthenticationMethod( AuthType::APOP );
  else
    iface->setAuthenticationMethod( AuthType::CLEAR );
  iface->setPrecommand( config.readPathEntry( "precommand", QString() ) );

  QString encpasswd;

  if ( config.readEntry( "store-passwd", false ) ) {
    encpasswd = config.readEntry( "pass" );
    if ( encpasswd.isEmpty() ) {
      encpasswd = config.readEntry( "passwd" );
      if ( !encpasswd.isEmpty() )
        encpasswd = importPassword( encpasswd );
    }

    if ( !encpasswd.isEmpty() ) {
      encpasswd = KStringHandler::obscure( encpasswd );
    }
    config.deleteEntry( "store-passwd" );
    config.deleteEntry( "passwd" );
  }
  migratePassword( config.readEntry( "Id" ), instance, QLatin1String("pop3"), encpasswd );

  // POP3 filter from files in kmail appdata
  const QString popFilterFileName = QString::fromLatin1( "kmail/%1:@%2:%3" )
                                      .arg( config.readEntry( "login", QString() ) )
                                      .arg( config.readEntry( "host", QString() ) )
                                      .arg( config.readEntry( "port", 110u ) );

  const KConfig popFilterConfig( popFilterFileName, KConfig::SimpleConfig, "data" );
  const KConfigGroup popFilterGroup( &popFilterConfig, QString() );
  iface->setDownloadLater( popFilterGroup.readEntry( "downloadLater", QStringList() ) );
  iface->setSeenUidList( popFilterGroup.readEntry( "seenUidList", QStringList() ) );
  iface->setSeenUidTimeList( popFilterGroup.readEntry( "seenUidTimeList", QList<int>() ) );

  // make sure the config is saved
  iface->writeConfig();

  //Info: there is trash item in config which is default and we can't configure it => don't look at it in pop account.
  config.deleteEntry( "trash" );
  config.deleteEntry( "use-default-identity" );
  const QString nameAccount = config.readEntry( "Name" );
  instance.setName( nameAccount );
  emit status( nameAccount );
  instance.reconfigure();
  config.sync();
  migrationCompleted( instance );
}

void KMailMigrator::mboxAccountCreated( KJob *job )
{
  if ( job->error() ) {
    migrationFailed( i18n( "Failed to create resource: %1", job->errorText() ) );
    return;
  }
  emit message( Info, i18n( "Created mbox resource" ) );

  AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job )->instance();
  mCurrentInstance = instance;
  KConfigGroup config( mConfig, mCurrentAccount );

  OrgKdeAkonadiMboxSettingsInterface *iface = new OrgKdeAkonadiMboxSettingsInterface(
    QLatin1String("org.freedesktop.Akonadi.Resource.") + instance.identifier(),
    QLatin1String("/Settings"), QDBusConnection::sessionBus(), this );

  if ( !iface->isValid() ) {
    migrationFailed( i18n( "Failed to obtain D-Bus interface for remote configuration." ), instance );
    delete iface;
    return;
  }

  iface->setPath( config.readEntry( "Location" ) );
  const QString lockType = config.readEntry( "LockType" ).toLower();
  if ( lockType == QLatin1String( "procmail_locktype" ) ) {
    iface->setLockfileMethod( Procmail );
    iface->setLockfile( config.readEntry( "ProcmailLockFile" ) );
  } else if ( lockType == QLatin1String( "mutt_dotlock" ) )
    iface->setLockfileMethod( MuttDotLock );
  else if ( lockType == QLatin1String( "mutt_dotlock_privileged" ) )
    iface->setLockfileMethod( MuttDotLockPrivileged );
  else if ( lockType == QLatin1String( "none" ) )
    iface->setLockfileMethod( MboxNone );

  // make sure the config is saved
  iface->writeConfig();

  // check-exclude in Account section means that this account should not be included
  // in manual checks. In KMail UI this is called "Include in manual checks"
  KConfigGroup resourceGroup( mConfig, QString::fromLatin1( "Resource %1" ).arg( instance.identifier() ) );
  resourceGroup.writeEntry( "IncludeInManualChecks", !config.readEntry( "check-exclude", false ) );
  const KConfigGroup generalGroup( mConfig, "General" );
  resourceGroup.writeEntry( "CheckOnStartup", generalGroup.readEntry( "checkmail-startup", false ) );
  resourceGroup.writeEntry( "OfflineOnShutdown", false );

  //Info: there is trash item in config which is default and we can't configure it => don't look at it in pop account.
  config.deleteEntry( "trash" );
  config.deleteEntry( "identity-id" );
  config.deleteEntry( "use-default-identity" );
  //We can't specify folder in akonadi
  config.deleteEntry( "Folder" );

  //TODO check-interval for the moment mbox doesn't support it

  const QString nameAccount = config.readEntry( "Name" );
  instance.setName( nameAccount );
  emit status( nameAccount );
  instance.reconfigure();
  config.sync();
  migrationCompleted( instance );
}

void KMailMigrator::maildirAccountCreated( KJob *job )
{
  if ( job->error() ) {
    migrationFailed( i18n( "Failed to create resource: %1", job->errorText() ) );
    return;
  }
  emit message( Info, i18n( "Created maildir resource" ) );

  AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job )->instance();
  mCurrentInstance = instance;
  KConfigGroup config( mConfig, mCurrentAccount );

  OrgKdeAkonadiMaildirSettingsInterface *iface = new OrgKdeAkonadiMaildirSettingsInterface(
    QLatin1String("org.freedesktop.Akonadi.Resource.") + instance.identifier(),
    QLatin1String("/Settings"), QDBusConnection::sessionBus(), this );

  if ( !iface->isValid() ) {
    migrationFailed( i18n( "Failed to obtain D-Bus interface for remote configuration." ), instance );
    delete iface;
    return;
  }

  iface->setPath( config.readEntry( "Location" ) );

  // make sure the config is saved
  iface->writeConfig();

  // check-exclude in Account section means that this account should not be included
  // in manual checks. In KMail UI this is called "Include in manual checks"
  KConfigGroup resourceGroup( mConfig, QString::fromLatin1( "Resource %1" ).arg( instance.identifier() ) );
  resourceGroup.writeEntry( "IncludeInManualChecks", !config.readEntry( "check-exclude", false ) );
  const KConfigGroup generalGroup( mConfig, "General" );
  resourceGroup.writeEntry( "CheckOnStartup", generalGroup.readEntry( "checkmail-startup", false ) );
  resourceGroup.writeEntry( "OfflineOnShutdown", false );

  //Info: there is trash item in config which is default and we can't configure it => don't look at it in pop account.
  config.deleteEntry( "trash" );
  config.deleteEntry( "identity-id" );
  config.deleteEntry( "use-default-identity" );
  //Now in akonadi we can't specify a folder where we put email, it's a specific top root
  config.deleteEntry( "Folder" );

  //TODO: check-interval for the moment maildir doesn't support check-interval.

  const QString nameAccount = config.readEntry( "Name" );
  instance.setName( nameAccount );
  emit status( nameAccount );
  instance.reconfigure();
  config.sync();
  migrationCompleted( instance );
}

void KMailMigrator::localMaildirCreated( KJob *job )
{
  if ( job->error() ) {
    emit message( Error, i18n( "Failed to create resource for local folders: %1", job->errorText() ) );
    deleteLater();
    return;
  }
  emit message( Info, i18n( "Created local maildir resource." ) );

  AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job )->instance();
  mCurrentInstance = instance;

  // do not include KMail folders in manual checks by default
  KConfigGroup resourceGroup( mConfig, QString::fromLatin1( "Resource %1" ).arg( instance.identifier() ) );
  resourceGroup.writeEntry( "IncludeInManualChecks", false );
  // do not synchronize on startup
  resourceGroup.writeEntry( "CheckOnStartup", false );
  resourceGroup.writeEntry( "OfflineOnShutdown", false );

  const bool specialCollectionsLock =
    QDBusConnection::sessionBus().registerService( SPECIALCOLLECTIONS_LOCK_SERVICE );

  KConfig specialMailCollectionsConfig( QLatin1String( "specialmailcollectionsrc" ) );
  KConfigGroup specialMailCollectionsGroup = specialMailCollectionsConfig.group( QLatin1String( "SpecialCollections" ) );

  QString defaultInstanceName;
  QString defaultResourceId = specialMailCollectionsGroup.readEntry( QLatin1String( "DefaultResourceId" ) );
  if ( defaultResourceId.isEmpty() ) {
    kDebug() << "No resource configured for special mail collections, using the migrated"
             << instance.identifier();
    defaultResourceId = instance.identifier();
  } else {
    const AgentInstance defaultInstance = AgentManager::self()->instance( defaultResourceId );
    if ( !defaultInstance.isValid() ) {
      kDebug() << "Resource" << defaultResourceId
               << " configured for special mail collections does not exist, using the migrated"
               << instance.identifier();
      defaultResourceId = instance.identifier();
    } else {
      defaultInstanceName = defaultInstance.name();
    }
  }

  const QString instanceName = i18n( "KMail Folders" );

  if ( defaultInstanceName.isEmpty() && specialCollectionsLock ) {
    specialMailCollectionsGroup.writeEntry( QLatin1String( "DefaultResourceId" ), defaultResourceId );
    specialMailCollectionsGroup.sync();

    emit message( Info,
                  i18nc( "@info:status resource that will provide folder such as outbox, sent",
                         "Using '%1' for default outbox, sent mail, trash, etc.",
                         instanceName ) );
  } else {
    emit message( Info,
                  i18nc( "@info:status resource that will provide folder such as outbox, sent",
                         "Keeping '%1' for default outbox, sent mail, trash, etc.",
                         defaultInstanceName ) );
  }

  if ( specialCollectionsLock ) {
    QDBusConnection::sessionBus().unregisterService( SPECIALCOLLECTIONS_LOCK_SERVICE );
  }

  instance.setName( instanceName );
  emit status( instanceName );

  MixedMaildirStore *store = createStoreFromBasePath( mLocalMaildirPath );

  LocalFoldersCollectionMigrator *collectionMigrator = new LocalFoldersCollectionMigrator( instance, instanceName, store, this );
  collectionMigrator->setTopLevelFolder( QString(), instanceName );
  collectionMigrator->setKMailConfig( mConfig );
  collectionMigrator->setEmailIdentityConfig( mEmailIdentityConfig );
  collectionMigrator->setKcmKmailSummaryConfig( mKcmKmailSummaryConfig );
  collectionMigrator->setTemplatesConfig( mTemplatesConfig );

  connect( collectionMigrator, SIGNAL(migrationFinished(Akonadi::AgentInstance,QString)),
           SLOT(localFoldersMigrationFinished(Akonadi::AgentInstance,QString)) );
  connectCollectionMigrator( collectionMigrator );

  collectionMigrator->startMigration();
}

void KMailMigrator::localFoldersMigrationFinished( const AgentInstance &instance, const QString &error )
{
  OrgKdeAkonadiMixedMaildirSettingsInterface *iface = new OrgKdeAkonadiMixedMaildirSettingsInterface(
    QLatin1String("org.freedesktop.Akonadi.Resource.") + instance.identifier(),
    QLatin1String("/Settings"), QDBusConnection::sessionBus(), this );

  if ( !iface->isValid() ) {
    migrationFailed( i18n("Failed to obtain D-Bus interface for remote configuration."), instance );
    delete iface;
    return;
  }

  iface->setPath( mLocalMaildirPath );

  // make sure the config is saved
  iface->writeConfig();

  AgentInstance resource = instance;
  resource.reconfigure();

  if ( error.isEmpty() ) {
    KConfigGroup config( KGlobal::config(), QLatin1String( "SpecialMailCollections" ) );
    if ( config.readEntry( QLatin1String( "TakeOverIfDefaultIsTotallyEmpty" ), false ) ) {
      KConfig specialMailCollectionsConfig( QLatin1String( "specialmailcollectionsrc" ) );
      KConfigGroup specialMailCollectionsGroup = specialMailCollectionsConfig.group( QLatin1String( "SpecialCollections" ) );

      QString defaultResourceId = specialMailCollectionsGroup.readEntry( QLatin1String( "DefaultResourceId" ) );

      AgentInstance defaultResource = AgentManager::self()->instance( defaultResourceId );
      if ( defaultResource.isValid() && defaultResourceId != instance.identifier() ) {
        kDebug() << "Attempting take over of special mail collections role from"
                 << defaultResourceId;

        EmptyResourceCleaner *cleaner = new EmptyResourceCleaner( defaultResource, this );
        cleaner->setCleaningOptions( EmptyResourceCleaner::CheckOnly );
        connect( cleaner, SIGNAL(cleanupFinished(Akonadi::AgentInstance)),
                 SLOT(specialColDefaultResourceCheckFinished(Akonadi::AgentInstance)) );
        return;
      }
    }

    mCurrentInstance = AgentInstance();

    setMigrationState( QLatin1String("LocalFolders"), Complete, instance.identifier(), QLatin1String("LocalFolders") );
    emit message( Success, i18n( "Local folders migrated successfully." ) );
    emit status( QString() );
    migrationDone();

  } else {
    mCurrentInstance = AgentInstance();

    migrationFailed( error, instance );
  }
}

void KMailMigrator::imapFoldersMigrationFinished( const AgentInstance &instance, const QString &error )
{
  kDebug() << "imapMigrationFinished: instance=" << instance.identifier()
           << "error=" << error;

  OrgKdeAkonadiImapSettingsInterface *iface = createImapSettingsInterface( instance );
  if ( !iface ) {
    return;
  }

  const bool disconnected = mAccountInstance[ mCurrentAccount ].disconnectedImap;

  KConfigGroup config( mConfig, mCurrentAccount );
  const QString nameAccount = config.readEntry( "Name" );

  iface->setImapServer( config.readEntry( "host" ) );
  iface->setImapPort( config.readEntry( "port", 143 ) );
  iface->setUserName( config.readEntry( "login" ) );
  if ( config.readEntry( "use-ssl" ).toLower() == QLatin1String("true") )
    iface->setSafety( QLatin1String("SSL") );
  else if ( config.readEntry( "use-tls" ).toLower() == QLatin1String("true") )
    iface->setSafety( QLatin1String("STARTTLS") );
  else
    iface->setSafety( QLatin1String("NONE") );
  const QString authentication = config.readEntry( "auth" ).toUpper();
  if ( authentication == QLatin1String( "LOGIN" ) )
    iface->setAuthentication(  MailTransport::Transport::EnumAuthenticationType::LOGIN );
  else if ( authentication == QLatin1String( "PLAIN" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::PLAIN );
  else if ( authentication == QLatin1String( "CRAM-MD5" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::CRAM_MD5 );
  else if ( authentication == QLatin1String( "DIGEST-MD5" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5 );
  else if ( authentication == QLatin1String( "NTLM" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::NTLM );
  else if ( authentication == QLatin1String( "GSSAPI" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::GSSAPI );
  else if ( authentication == QLatin1String( "ANONYMOUS" ) )
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::ANONYMOUS );
  else {
    iface->setAuthentication( MailTransport::Transport::EnumAuthenticationType::CLEAR );
  }
  if ( config.readEntry( "subscribed-folders" ).toLower() == QLatin1String("true") )
    iface->setSubscriptionEnabled( true );

  // skip interval checking so it doesn't interfere with cache importing
  iface->setIntervalCheckTime( -1 ); //exclude

  // check-exclude in Account section means that this account should not be included
  // in manual checks. In KMail UI this is called "Include in manual checks"
  KConfigGroup resourceGroup( mConfig, QString::fromLatin1( "Resource %1" ).arg( instance.identifier() ) );
  resourceGroup.writeEntry( "IncludeInManualChecks", !config.readEntry( "check-exclude", false ) );
  const KConfigGroup generalGroup( mConfig, "General" );
  resourceGroup.writeEntry( "CheckOnStartup", generalGroup.readEntry( "checkmail-startup", false ) );
  resourceGroup.writeEntry( "OfflineOnShutdown", true );

  iface->setSieveSupport( config.readEntry( "sieve-support", false ) );
  iface->setSieveReuseConfig( config.readEntry( "sieve-reuse-config", true ) );
  iface->setSievePort( config.readEntry( "sieve-port", 2000 ) );
  iface->setSieveAlternateUrl( config.readEntry( "sieve-alternate-url" ) );
  iface->setSieveVacationFilename( config.readEntry( "sieve-vacation-filename", "kmail-vacation.siv" ) );
  iface->setDisconnectedModeEnabled( disconnected );
  if ( !disconnected ) {
    iface->setAutomaticExpungeEnabled( config.readEntry( "auto-expunge", true ) );
  }
  const bool useDefaultIdentity = config.readEntry( "use-default-identity", true );
  iface->setUseDefaultIdentity( useDefaultIdentity );
  if ( !useDefaultIdentity )
    iface->setAccountIdentity( config.readEntry( "identity-id" ).toInt() );

  QString encpasswd ;
  if ( config.readEntry( "store-passwd", false ) ) {
    encpasswd = config.readEntry( "pass" );
    if ( encpasswd.isEmpty() ) {
      encpasswd = config.readEntry( "passwd" );
      if ( !encpasswd.isEmpty() )
        encpasswd = importPassword( encpasswd );
    }

    if ( !encpasswd.isEmpty() ) {
      encpasswd = KStringHandler::obscure( encpasswd );
    }
    config.deleteEntry( "store-passwd" );
    config.deleteEntry( "passwd" );
  }
  migratePassword( config.readEntry( "Id" ), instance, QLatin1String("imap"), encpasswd );

  // enable interval checking in case this had been configured
  const int checkInterval = config.readEntry( "check-interval", 0 );
  if ( checkInterval != 0 ) {
    iface->setIntervalCheckEnabled( true );
    iface->setIntervalCheckTime( checkInterval );
  }

  // make sure the config is saved
  iface->writeConfig();

  AgentInstance resource = instance;
  resource.reconfigure();

  config.deleteEntry( "locally-subscribed-folders" );
  config.deleteEntry( "locallyUnsubscribedFolders" );

  config.deleteEntry( "capabilities" );

  if ( error.isEmpty() ) {
    migrationCompleted( instance );
  } else {
    migrationFailed( error, true, instance );
  }
}

void KMailMigrator::collectionMigratorMessage( int type, const QString &msg )
{
  switch ( type ) {
    case Success:
      emit message( Success, msg );
      break;

    case Skip:
      emit message( Skip, msg );
      break;

    case Warning:
      emit message( Warning, msg );
      break;

    case Info:
      emit message( Info, msg );
      break;

    case Error:
      emit message( Error, msg );
      break;

    default:
      kError() << "Unknown message type" << type << "msg=" << msg;
      break;
  }
}

void KMailMigrator::collectionMigratorFinished()
{
  emit status( QString() );
}

void KMailMigrator::collectionMigratorEmittedNotification()
{
  mForwardResourceNotifications = false;
}

void KMailMigrator::instanceStatusChanged( const AgentInstance &instance )
{
  if ( !mForwardResourceNotifications ) {
    return;
  }

  if ( instance.identifier() != mCurrentInstance.identifier() ) {
    return;
  }

  if ( instance.status() == AgentInstance::Idle ) {
    emit status( QString() );
  } else {
    emit status( instance.statusMessage() );
  }
}

void KMailMigrator::instanceProgressChanged( const AgentInstance &instance )
{
  if ( !mForwardResourceNotifications ) {
    return;
  }

  if ( instance.identifier() != mCurrentInstance.identifier() ) {
    return;
  }

  emit progress( 0, 100, instance.progress() );
}

void KMailMigrator::imapCacheAdaptionFinished( int messageType, const QString &messageText )
{
  emit message( (KMigratorBase::MessageType)messageType, messageText );

  mImapCacheAdapter = 0;

  migrateNext();
}

void KMailMigrator::specialColDefaultResourceCheckFinished( const AgentInstance &instance )
{
  KConfig specialMailCollectionsConfig( QLatin1String( "specialmailcollectionsrc" ) );
  KConfigGroup specialMailCollectionsGroup = specialMailCollectionsConfig.group( QLatin1String( "SpecialCollections" ) );

  const EmptyResourceCleaner *cleaner = qobject_cast<const EmptyResourceCleaner*>( QObject::sender() );

  const bool specialCollectionsLock =
    QDBusConnection::sessionBus().registerService( SPECIALCOLLECTIONS_LOCK_SERVICE );

  const QString localFoldersIdentifier = mCurrentInstance.identifier();
  if ( cleaner->isResourceDeletable() && specialCollectionsLock ) {
    specialMailCollectionsGroup.writeEntry( QLatin1String( "DefaultResourceId" ), localFoldersIdentifier );
    specialMailCollectionsGroup.sync();
    AgentManager::self()->removeInstance( instance );

    kDebug() << "Former special mail collection resource" << instance.identifier()
             << "successfully delete, now using" << specialMailCollectionsGroup.readEntry( QLatin1String( "DefaultResourceId" ) );
  } else {
    kDebug() << "Former special mail collection resource" << instance.identifier()
             << "still valid";
  }

  if ( specialCollectionsLock ) {
    QDBusConnection::sessionBus().unregisterService( SPECIALCOLLECTIONS_LOCK_SERVICE );
  }

  mCurrentInstance = AgentInstance();

  setMigrationState( QLatin1String("LocalFolders"), Complete, localFoldersIdentifier, QLatin1String("LocalFolders") );
  emit message( Success, i18n( "Local folders migrated successfully." ) );
  emit status( QString() );
  migrationDone();
}

QString KMailMigrator::importPassword(const QString &aStr)
{
  unsigned int i, val;
  unsigned int len = aStr.length();
  QByteArray result;
  result.resize( len );

  for ( i = 0; i < len; i++ ) {
    val = aStr[i].toLatin1() - ' ';
    val = ( 255-' ') - val;
    result[i] = (char)( val + ' ');
  }
  result[i] = '\0';

  return KStringHandler::obscure( QString::fromLatin1(result) );
}


