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