You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/kmail/cachedimapjob.cpp

903 lines
31 KiB

/* -*- 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 <tdeio/scheduler.h>
#include <tdeio/job.h>
#include <tdelocale.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
TDEIO::SimpleJob *simpleJob = TDEIO::special( url.url(), packedArgs, false );
TDEIO::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");
TDEIO::SimpleJob *job = TDEIO::get(url, false, false);
TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
jd.cancellable = true;
mAccount->insertJob( job, jd );
connect( job, TQT_SIGNAL( result(TDEIO::Job *) ),
this, TQT_SLOT( slotListMessagesResult( TDEIO::Job* ) ) );
// send the data directly for KMFolderCachedImap
connect( job, TQT_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
mFolder, TQT_SLOT( slotGetMessagesData( TDEIO::Job* , const TQByteArray& ) ) );
}
void CachedImapJob::slotDeleteNextMessages( TDEIO::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::fromLatin1(";UID=%1").arg(uids) );
TDEIO::SimpleJob *simpleJob = TDEIO::file_delete( url, false );
TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
mAccount->insertJob( simpleJob, jd );
connect( simpleJob, TQT_SIGNAL( result(TDEIO::Job *) ),
this, TQT_SLOT( slotDeleteNextMessages(TDEIO::Job *) ) );
}
void CachedImapJob::expungeFolder()
{
KURL url = mAccount->getUrl();
// Special URL that means EXPUNGE
url.setPath( mFolder->imapPath() + TQString::fromLatin1(";UID=*") );
TDEIO::SimpleJob *job = TDEIO::file_delete( url, false );
TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
mAccount->insertJob( job, jd );
connect( job, TQT_SIGNAL( result(TDEIO::Job *) ),
this, TQT_SLOT( slotExpungeResult(TDEIO::Job *) ) );
}
void CachedImapJob::slotExpungeResult( TDEIO::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(TDEIO::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() + TQString(";UID=%1;SECTION=BODY.PEEK[]").arg(mfd.uid));
ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
jd.cancellable = true;
mMsg->setTransferInProgress(true);
TDEIO::SimpleJob *simpleJob = TDEIO::get(url, false, false);
TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mAccount->insertJob(simpleJob, jd);
connect(simpleJob, TQT_SIGNAL(processedSize(TDEIO::Job *, TDEIO::filesize_t)),
this, TQT_SLOT(slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t)));
connect(simpleJob, TQT_SIGNAL(result(TDEIO::Job *)),
this, TQT_SLOT(slotGetNextMessage(TDEIO::Job *)));
connect(simpleJob, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
mFolder, TQT_SLOT(slotSimpleData(TDEIO::Job *, const TQByteArray &)));
}
void CachedImapJob::slotProcessedSize(TDEIO::Job *, TDEIO::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.at(i) = '\r';
i++;
}
mData.at(i) = *ch; i++;
}
jd.data = mData;
jd.msgList.append( mMsg );
mMsg->setTransferInProgress(true);
TDEIO::SimpleJob *simpleJob = TDEIO::put(url, 0, false, false, false);
TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mAccount->insertJob(simpleJob, jd);
connect( simpleJob, TQT_SIGNAL( result(TDEIO::Job *) ),
TQT_SLOT( slotPutMessageResult(TDEIO::Job *) ) );
connect( simpleJob, TQT_SIGNAL( dataReq(TDEIO::Job *, TQByteArray &) ),
TQT_SLOT( slotPutMessageDataReq(TDEIO::Job *, TQByteArray &) ) );
connect( simpleJob, TQT_SIGNAL( data(TDEIO::Job *, const TQByteArray &) ),
mFolder, TQT_SLOT( slotSimpleData(TDEIO::Job *, const TQByteArray &) ) );
connect( simpleJob, TQT_SIGNAL(infoMessage(TDEIO::Job *, const TQString &)),
TQT_SLOT(slotPutMessageInfoData(TDEIO::Job *, const TQString &)) );
}
//-----------------------------------------------------------------------------
// TODO: port to TDEIO::StoredTransferJob once it's ok to require tdelibs-3.3
void CachedImapJob::slotPutMessageDataReq(TDEIO::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( TDEIO::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(TDEIO::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( TDEIO::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.").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() ) {
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();
TDEIO::SimpleJob *simpleJob = TDEIO::mkdir(url);
TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mAccount->insertJob(simpleJob, jd);
connect( simpleJob, TQT_SIGNAL(result(TDEIO::Job *)),
this, TQT_SLOT(slotAddNextSubfolder(TDEIO::Job *)) );
} else {
TQByteArray packedArgs;
TQDataStream stream( packedArgs, IO_WriteOnly );
const TQString command = TQString( "X-CREATE-SPECIAL" );
const TQString argument = TQString( "%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();
TDEIO::SimpleJob *simpleJob = TDEIO::special( url.url(), packedArgs, false );
TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mAccount->insertJob(simpleJob, jd);
connect( simpleJob, TQT_SIGNAL(result(TDEIO::Job *)),
this, TQT_SLOT(slotAddNextSubfolder(TDEIO::Job *)) );
}
}
void CachedImapJob::slotDeleteNextFolder( TDEIO::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;
}
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();
TDEIO::SimpleJob *simpleJob = TDEIO::file_delete(url, false);
TDEIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mAccount->insertJob(simpleJob, jd);
connect( simpleJob, TQT_SIGNAL( result(TDEIO::Job *) ),
TQT_SLOT( slotDeleteNextFolder(TDEIO::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;
TDEIO::SimpleJob *job = TDEIO::get( url, false, false );
TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
mAccount->insertJob( job, jd );
connect( job, TQT_SIGNAL(result(TDEIO::Job *)),
TQT_SLOT(slotCheckUidValidityResult(TDEIO::Job *)) );
connect( job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
mFolder, TQT_SLOT(slotSimpleData(TDEIO::Job *, const TQByteArray &)));
}
void CachedImapJob::slotCheckUidValidityResult(TDEIO::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
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;
TDEIO::SimpleJob *simpleJob = TDEIO::rename( urlSrc, urlDst, false );
TDEIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
mAccount->insertJob( simpleJob, jd );
connect( simpleJob, TQT_SIGNAL(result(TDEIO::Job *)),
TQT_SLOT(slotRenameFolderResult(TDEIO::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" )
.arg( mFolder->label() ).arg( 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( TDEIO::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" ).arg( 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( TDEIO::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"