diff options
Diffstat (limited to 'kmail/cachedimapjob.cpp')
-rw-r--r-- | kmail/cachedimapjob.cpp | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/kmail/cachedimapjob.cpp b/kmail/cachedimapjob.cpp new file mode 100644 index 000000000..8c6339612 --- /dev/null +++ b/kmail/cachedimapjob.cpp @@ -0,0 +1,841 @@ +/* -*- 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 Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), 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 + * Qt. 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 QValueList<MsgForDownload>& msgs, + JobType type, KMFolderCachedImap* folder ) + : FolderJob( type ), mFolder( folder ), mMsgsForDownload( msgs ), + mTotalBytes(0), mMsg(0), mParentFolder( 0 ) +{ + QValueList<MsgForDownload>::ConstIterator it = msgs.begin(); + for ( ; it != msgs.end() ; ++it ) + mTotalBytes += (*it).size; +} + +// Put messages +CachedImapJob::CachedImapJob( const QPtrList<KMMessage>& msgs, JobType type, + KMFolderCachedImap* folder ) + : FolderJob( msgs, QString::null, 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 QValueList<unsigned long>& msgs, + JobType type, KMFolderCachedImap* folder ) + : FolderJob( QPtrList<KMMessage>(), QString::null, type, folder?folder->folder():0 ), + mFolder( folder ), mSerNumMsgList( msgs ), mTotalBytes( msgs.count() ), mMsg( 0 ), + mParentFolder ( 0 ) +{ +} + +// Add sub folders +CachedImapJob::CachedImapJob( const QValueList<KMFolderCachedImap*>& fList, + JobType type, KMFolderCachedImap* folder ) + : FolderJob( type ), mFolder( folder ), mFolderList( fList ), mMsg( 0 ), + mParentFolder ( 0 ) +{ +} + +// Rename folder +CachedImapJob::CachedImapJob( const QString& 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 QStringList& 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() ) { + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + + const QString command = QString( "X-SCALIX-ID " ); + const QString argument = QString( "(\"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, SIGNAL( result(KIO::Job *) ), + this, SLOT( slotListMessagesResult( KIO::Job* ) ) ); + // send the data directly for KMFolderCachedImap + connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ), + mFolder, SLOT( slotGetMessagesData( KIO::Job* , const QByteArray& ) ) ); +} + +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; + } + + QString uids = mFoldersOrMessages.front(); mFoldersOrMessages.pop_front(); + + KURL url = mAccount->getUrl(); + url.setPath( mFolder->imapPath() + + QString::fromLatin1(";UID=%1").arg(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, SIGNAL( result(KIO::Job *) ), + this, SLOT( slotDeleteNextMessages(KIO::Job *) ) ); +} + +void CachedImapJob::expungeFolder() +{ + KURL url = mAccount->getUrl(); + // Special URL that means EXPUNGE + url.setPath( mFolder->imapPath() + QString::fromLatin1(";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, SIGNAL( result(KIO::Job *) ), + this, 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->setStatus( 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::flagsToStatus(mMsg, mfd.flags, true, GlobalSettings::allowLocalFlags() ? mFolder->permanentFlags() : INT_MAX); + KURL url = mAccount->getUrl(); + url.setPath(mFolder->imapPath() + QString(";UID=%1;SECTION=BODY.PEEK[]").arg(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, SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)), + this, SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t))); + connect(simpleJob, SIGNAL(result(KIO::Job *)), + this, SLOT(slotGetNextMessage(KIO::Job *))); + connect(simpleJob, SIGNAL(data(KIO::Job *, const QByteArray &)), + mFolder, SLOT(slotSimpleData(KIO::Job *, const QByteArray &))); +} + +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(); + QString 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 + QCString 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); + QCString mData(cstr.length() + cstr.contains('\n')); + unsigned int i = 0; + for( char *ch = cstr.data(); *ch; ch++ ) { + if ( *ch == '\n' ) { + mData.at(i) = '\r'; + i++; + } + mData.at(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, SIGNAL( result(KIO::Job *) ), + SLOT( slotPutMessageResult(KIO::Job *) ) ); + connect( simpleJob, SIGNAL( dataReq(KIO::Job *, QByteArray &) ), + SLOT( slotPutMessageDataReq(KIO::Job *, QByteArray &) ) ); + connect( simpleJob, SIGNAL( data(KIO::Job *, const QByteArray &) ), + mFolder, SLOT( slotSimpleData(KIO::Job *, const QByteArray &) ) ); + connect( simpleJob, SIGNAL(infoMessage(KIO::Job *, const QString &)), + SLOT(slotPutMessageInfoData(KIO::Job *, const QString &)) ); + +} + +//----------------------------------------------------------------------------- +// TODO: port to KIO::StoredTransferJob once it's ok to require kdelibs-3.3 +void CachedImapJob::slotPutMessageDataReq(KIO::Job *job, QByteArray &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 QString &data) +{ + KMFolderCachedImap * imapFolder = static_cast<KMFolderCachedImap*>(mDestFolder->storage()); + 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 ) { + QString myError = "<p><b>" + i18n("Error while uploading folder") + + "</b></p><p>" + i18n("Could not make the folder <b>%1</b> on the server.").arg((*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() ) { + QString 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(); + QString 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, SIGNAL(result(KIO::Job *)), + this, SLOT(slotAddNextSubfolder(KIO::Job *)) ); + } else { + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + + const QString command = QString( "X-CREATE-SPECIAL" ); + const QString argument = QString( "%1 %2" ).arg( Scalix::Utils::contentsTypeToScalixId( folder->contentsType() ) ) + .arg( 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, SIGNAL(result(KIO::Job *)), + this, 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: " ).arg( (*it).path ) + '\n' ); + delete this; + return; + } + mAccount->removeJob(it); + } + + if( mFoldersOrMessages.isEmpty() ) { + // No more folders to delete + delete this; + return; + } + + QString 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, SIGNAL( result(KIO::Job *) ), + 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, SIGNAL(result(KIO::Job *)), + SLOT(slotCheckUidValidityResult(KIO::Job *)) ); + connect( job, SIGNAL(data(KIO::Job *, const QByteArray &)), + mFolder, SLOT(slotSimpleData(KIO::Job *, const QByteArray &))); +} + +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: " ).arg( (*it).parent->label() ) + '\n' ); + delete this; + return; + } + + // Check the uidValidity + QCString 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 ) { + QString 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 QString &newName ) +{ + // Set the source URL + KURL urlSrc = mAccount->getUrl(); + urlSrc.setPath( mFolder->imapPath() ); + + // Set the destination URL - this is a bit trickier + KURL urlDst = mAccount->getUrl(); + QString imapPath( mFolder->imapPath() ); + // Destination url = old imappath - oldname + new name + imapPath.truncate( imapPath.length() - mFolder->folder()->name().length() - 1); + imapPath += newName + '/'; + urlDst.setPath( imapPath ); + + ImapAccountBase::jobData jd( newName, mFolder->folder() ); + jd.path = imapPath; + + KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, false ); + KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob ); + mAccount->insertJob( simpleJob, jd ); + connect( simpleJob, SIGNAL(result(KIO::Job *)), + SLOT(slotRenameFolderResult(KIO::Job *)) ); +} + +static void renameChildFolders( KMFolderDir* dir, const QString& oldPath, + const QString& 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 ) { + QString 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::slotRenameFolderResult( KIO::Job *job ) +{ + KMAcctCachedImap::JobIterator it = mAccount->findJob(job); + if ( it == mAccount->jobsEnd() ) { // Shouldn't happen + delete this; + return; + } + + + if( job->error() ) { + // Error, revert label change + QMap<QString, 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() ); + } + mAccount->handleJobError( job, i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() ) + '\n' ); + } else { + // Okay, the folder seems to be renamed on the server, + // now rename it on disk + QString oldName = mFolder->name(); + QString oldPath = mFolder->imapPath(); + mAccount->removeRenamedFolder( oldPath ); + mFolder->setImapPath( (*it).path ); + mFolder->FolderStorage::rename( (*it).url ); + + if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 ); + QString newPath = mFolder->imapPath(); + if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 ); + renameChildFolders( mFolder->folder()->child(), oldPath, newPath ); + kmkernel->dimapFolderMgr()->contentsChanged(); + + mAccount->removeJob(it); + } + delete this; +} + +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" |