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.
 
 
 

1692 lines
54 KiB

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include "kmfoldercachedimap.h"
#include "kmfolderimap.h"
#include "kmundostack.h"
#include "kmfoldermgr.h"
#include "kmmessage.h"
#include "kmacctcachedimap.h"
#include "kmacctmgr.h"
#include "imapprogressdialog.h"
#include <kapplication.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kdebug.h>
#include <kio/global.h>
#include <kio/scheduler.h>
#include <qbuffer.h>
#include <qfile.h>
#define UIDCACHE_VERSION 1
KMFolderCachedImap::KMFolderCachedImap(KMFolderDir* aParent, const QString& aName)
: KMFolderCachedImapInherited(aParent, aName), mSyncState(SYNC_STATE_INITIAL),
mContentState(imapNoInformation),
mSubfolderState(imapNoInformation), mIsSelected(FALSE), mCheckFlags(TRUE), mAccount(NULL),
mLastUid(0), mIsConnected(false), mFolderRemoved(false)
{
KConfig* config = kapp->config();
KConfigGroupSaver saver(config, "Folder-" + idString());
if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath");
if (aName == "INBOX" && mImapPath == "/INBOX/")
{
//mIsSystemFolder = TRUE;
//mLabel = i18n("inbox");
}
mNoContent = config->readBoolEntry("NoContent", FALSE);
mReadOnly = config->readBoolEntry("ReadOnly", FALSE);
connect( this, SIGNAL( listMessagesComplete() ), this, SLOT( serverSyncInternal() ) );
setUidValidity("");
mLastUid=0;
readUidCache();
prog = 0;
}
KMFolderCachedImap::~KMFolderCachedImap()
{
if( !mFolderRemoved ) {
// Only write configuration when the folder haven't been deleted
KConfig* config = kapp->config();
KConfigGroupSaver saver(config, "Folder-" + idString());
config->writeEntry("UidValidity", mUidValidity); /* unused */
config->writeEntry("lastUid", mLastUid); /* unused */
config->writeEntry("ImapPath", mImapPath);
config->writeEntry("NoContent", mNoContent);
config->writeEntry("ReadOnly", mReadOnly);
writeUidCache();
}
if (kernel->undoStack()) kernel->undoStack()->folderDestroyed(this);
}
int KMFolderCachedImap::remove()
{
mFolderRemoved = true;
return KMFolderCachedImapInherited::remove();
}
QString KMFolderCachedImap::uidCacheLocation() const
{
QString sLocation(path());
if (!sLocation.isEmpty()) sLocation += '/';
return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
}
int KMFolderCachedImap::readUidCache()
{
QFile uidcache( uidCacheLocation() );
if( uidcache.open( IO_ReadOnly ) ) {
char buf[1024];
int len = uidcache.readLine( buf, sizeof(buf) );
if( len > 0 ) {
int cacheVersion;
sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion );
if( cacheVersion == UIDCACHE_VERSION ) {
len = uidcache.readLine( buf, sizeof(buf) );
if( len > 0 ) {
setUidValidity( QString::fromLocal8Bit( buf).stripWhiteSpace() );
len = uidcache.readLine( buf, sizeof(buf) );
if( len > 0 ) {
mLastUid = QString::fromLocal8Bit( buf).stripWhiteSpace().toULong();
return 0;
}
}
}
}
}
return -1;
}
int KMFolderCachedImap::writeUidCache()
{
QFile uidcache( uidCacheLocation() );
if( uidcache.open( IO_WriteOnly ) ) {
QTextStream str( &uidcache );
str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
str << uidValidity() << endl;
str << lastUid() << endl;
uidcache.flush();
fsync( uidcache.handle() ); /* this is probably overkill */
uidcache.close();
return 0;
} else {
return errno; /* does QFile set errno? */
}
}
int KMFolderCachedImap::create(bool imap)
{
int rc = KMFolderCachedImapInherited::create(imap);
mLastUid = 0;
mUidValidity = "";
if( !rc ) return writeUidCache();
return rc;
}
void KMFolderCachedImap::reloadUidMap()
{
uidMap.clear();
uidRevMap.clear();
open();
for( int i = 0; i < count(); ++i ) {
bool unget = !isMessage(i);
bool ok;
KMMessage *msg = getMsg(i);
if( !msg ) continue;
ulong uid = msg->headerField("X-UID").toULong(&ok);
if (unget) unGetMsg(i);
if( ok ) {
uidMap.insert( uid, i );
uidRevMap.insert( i, uid );
}
}
close();
}
/* Reimplemented from KMFolderMaildir */
KMMessage* KMFolderCachedImap::take(int idx)
{
QMap<int,ulong>::Iterator it = uidRevMap.find(idx);
if( it != uidRevMap.end() ) {
uidMap.remove( it.data() );
uidRevMap.remove( idx );
}
return KMFolderCachedImapInherited::take(idx);
}
// Add a message without clearing it's X-UID field.
int KMFolderCachedImap::addMsgInternal(KMMessage* msg, int* index_return)
{
bool ok;
int idx_return;
// Add the message
ulong uid = msg->headerField("X-UID").toULong(&ok);
int rc = KMFolderCachedImapInherited::addMsg(msg, &idx_return);
if( index_return ) *index_return = idx_return;
// Put it in the uid maps
if( ok && !rc && idx_return >= 0 ) {
uidMap.insert( uid, idx_return );
uidRevMap.insert( idx_return, uid );
if( uid > mLastUid ) mLastUid = uid;
}
return rc;
}
/* Reimplemented from KMFolderMaildir */
int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
{
assert(msg);
// Strip the IMAP UID
msg->removeHeaderField( "X-UID" );
// Add it to storage
return addMsgInternal( msg, index_return );
}
/* Reimplemented from KMFolderMaildir */
void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
{
// kdDebug() << "KMFolderCachedImap::removeMsg(" << idx << ", " << imapQuiet << ")" << endl;
// Remove it from the maps
QMap<int,ulong>::Iterator it = uidRevMap.find(idx);
if( it != uidRevMap.end() ) {
ulong uid = it.data();
uidMap.remove( uid );
uidRevMap.remove( idx );
}
// Remove it from disk
KMFolderCachedImapInherited::removeMsg(idx,imapQuiet);
// TODO (Bo): Shouldn't this be "emit changed();"?
kernel->imapFolderMgr()->contentsChanged();
}
bool KMFolderCachedImap::canRemoveFolder() const {
// If this has subfolders it can't be removed
if( child() != 0 && child()->count() > 0 )
return false;
#if 0
// No special condition here, so let base class decide
return KMFolderCachedImapInherited::canRemoveFolder();
#endif
return true;
}
/* Reimplemented from KMFolderDir */
int KMFolderCachedImap::rename(const QString& aName, KMFolderDir *aParent) {
assert( aParent == 0 );
if ( aName == name() )
// Stupid user trying to rename it to it's old name :)
return 0;
if( mSyncState != SYNC_STATE_INITIAL ) {
KMessageBox::error( 0, i18n("You can't rename a folder when a sync is in progress") );
return -1;
}
if( account() == 0 ) {
QString err = i18n("You must synchronize with the server before renaming IMAP folders.");
KMessageBox::error( 0, err );
return -1;
}
new KMCachedImapJob( aName, KMCachedImapJob::tRenameFolder, this );
return 0;
}
void KMFolderCachedImap::setLastUid( ulong uid ) {
mLastUid = uid;
writeUidCache();
}
ulong KMFolderCachedImap::lastUid() {
if( mLastUid )
return mLastUid;
//reloadUidMap();
open();
if (count() > 0)
{
bool unget = !isMessage(count() - 1);
KMMessage *msg = getMsg(count() - 1);
mLastUid = msg->headerField("X-UID").toULong();
if (unget) unGetMsg(count() - 1);
}
close();
// kdDebug() << "KMFolderCachedImap::lastUid("<<name()<<") = " << mLastUid << endl;
return mLastUid;
}
KMMsgBase* KMFolderCachedImap::findByUID( ulong uid )
{
QMap<ulong,int>::Iterator it = uidMap.find( uid );
if( it != uidMap.end() ) return getMsgBase( *it );
else return NULL;
}
// This finds and sets the proper account for this folder if it has not been done
KMAcctCachedImap *KMFolderCachedImap::account()
{
if( (KMAcctCachedImap *)mAccount == 0 ) {
// Find the account
mAccount = static_cast<KMAcctCachedImap *>( kernel->acctMgr()->find( name() ) );
}
return mAccount;
}
void KMFolderCachedImap::serverSync()
{
if( mSyncState != SYNC_STATE_INITIAL ) {
if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset\nit to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ) ) == KMessageBox::Yes ) {
mSyncState = SYNC_STATE_INITIAL;
} else return;
}
assert( account() );
reloadUidMap();
// kdDebug() << "KMFolderCachedImap::serverSync(), imapPath()=" << imapPath() << ", path()="
// << path() << " name()="<< name() << endl;
// Connect to the imap progress dialog
if( mIsConnected != mAccount->isProgressDialogEnabled() ) {
if( !mIsConnected )
connect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
account()->imapProgressDialog(),
SLOT( syncState( const QString&, int, const QString& ) ) );
else
disconnect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
account()->imapProgressDialog(),
SLOT( syncState( const QString&, int, const QString& ) ) );
mIsConnected = mAccount->isProgressDialogEnabled();
}
serverSyncInternal();
}
QString KMFolderCachedImap::state2String( int state ) const
{
switch( state ) {
case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
default: return "Unknown state";
}
}
// While the server synchronization is running, mSyncState will hold
// the state that should be executed next
void KMFolderCachedImap::serverSyncInternal()
{
// kdDebug() << "KMFolderCachedImap::serverSyncInternal(), " << name()
// << " state = " << state2String(mSyncState) << endl;
// Don't let the states continue into the next one. Instead use sync(); break;
// to get the debug output
switch( mSyncState ) {
case SYNC_STATE_INITIAL:
prog = 0;
emit statusMsg( i18n("%1: Synchronizing").arg(name()) );
emit newState( name(), prog, i18n("Syncronizing"));
// emit syncState( SYNC_STATE_INITIAL, prog /*dummy value*/ );
mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
open();
serverSyncInternal();
break;
case SYNC_STATE_CHECK_UIDVALIDITY:
mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
checkUidValidity();
break;
case SYNC_STATE_CREATE_SUBFOLDERS:
mSyncState = SYNC_STATE_PUT_MESSAGES;
createNewFolders();
break;
case SYNC_STATE_PUT_MESSAGES:
mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
uploadNewMessages();
break;
case SYNC_STATE_LIST_SUBFOLDERS:
mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
prog += 10;
emit statusMsg( i18n("%1: Retrieving folderlist").arg(name()) );
emit newState( name(), prog, i18n("Retrieving folderlist"));
if( !listDirectory() ) {
mSyncState = SYNC_STATE_INITIAL;
KMessageBox::error(0, i18n("Error during listDirectory()"));
}
break;
case SYNC_STATE_LIST_SUBFOLDERS2:
mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
prog += 10;
emit newState( name(), prog, i18n("Retrieving subfolders"));
listDirectory2();
break;
case SYNC_STATE_DELETE_SUBFOLDERS:
mSyncState = SYNC_STATE_LIST_MESSAGES;
emit syncState( SYNC_STATE_DELETE_SUBFOLDERS, foldersForDeletionOnServer.count() );
prog += 10;
if( !foldersForDeletionOnServer.isEmpty() ) {
emit statusMsg( i18n("%1: Deleting folders %2 from server").arg(name())
.arg( foldersForDeletionOnServer.join(", ") ) );
emit newState( name(), prog, i18n("Deleting folders from server"));
KMCachedImapJob* job = new KMCachedImapJob( foldersForDeletionOnServer,
KMCachedImapJob::tDeleteFolders, this );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
} else {
emit newState( name(), prog, i18n("No folders to delete from server"));
serverSyncInternal();
}
break;
case SYNC_STATE_LIST_MESSAGES:
mSyncState = SYNC_STATE_DELETE_MESSAGES;
prog += 10;
emit statusMsg( i18n("%1: Retrieving messagelist").arg(name()) );
emit newState( name(), prog, i18n("Retrieving messagelist"));
// emit syncState( SYNC_STATE_LIST_MESSAGES, foldersForDeletionOnServer.count() );
listMessages();
break;
case SYNC_STATE_DELETE_MESSAGES:
mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
if( deleteMessages() ) {
// Fine, we will continue with the next state
} else {
// No messages to delete, skip to GET_MESSAGES
prog += 10;
emit newState( name(), prog, i18n("No messages to delete..."));
mSyncState = SYNC_STATE_GET_MESSAGES;
serverSyncInternal();
}
break;
case SYNC_STATE_EXPUNGE_MESSAGES:
mSyncState = SYNC_STATE_GET_MESSAGES;
{
prog += 10;
emit statusMsg( i18n("%1: Expunging deleted messages").arg(name()) );
emit newState( name(), prog, i18n("Expunging deleted messages"));
KMCachedImapJob *job = new KMCachedImapJob( QString::null,
KMCachedImapJob::tExpungeFolder, this );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
}
break;
case SYNC_STATE_GET_MESSAGES:
mSyncState = SYNC_STATE_HANDLE_INBOX;
{
prog += 10;
//emit syncState( SYNC_STATE_GET_MESSAGES, uidsForDownload.count() );
if( !uidsForDownload.isEmpty() ) {
emit statusMsg( i18n("%1: Retrieving new messages").arg(name()) );
emit newState( name(), prog, i18n("Retrieving new messages"));
KMCachedImapJob *job = new KMCachedImapJob( uidsForDownload,
KMCachedImapJob::tGetMessage,
this, flagsForDownload );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
uidsForDownload.clear();
flagsForDownload.clear();
} else {
// kdDebug() << "No new messages from server(" << imapPath() << "):" << endl;
emit newState( name(), prog, i18n("No new messages from server"));
serverSyncInternal();
}
}
break;
case SYNC_STATE_HANDLE_INBOX:
mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
#if 0
if( imapPath() == "/INBOX/" && kernel->groupware().isEnabled() ) {
// Here we need to move messages from INBOX to the "real" inbox
KMFolderNode* node = child()->hasNamedFolder( kernel->groupware().folderName( KFolderTreeItem::Inbox ) );
if( node && !node->isDir() ) {
KMFolder* inboxFolder = static_cast<KMFolder*>(node);
open();
inboxFolder->open();
mAccount->setFolder( inboxFolder );
while( count() > 0) {
KMMessage* m = getMsg(0);
inboxFolder->moveMsg(m);
mAccount->processNewMsg(m);
}
compact();
close();
inboxFolder->close();
// Delete from INBOX
if( deleteMessages() ) {
break;
}
}
}
#endif
serverSyncInternal();
break;
case SYNC_STATE_FIND_SUBFOLDERS:
{
emit newState( name(), prog, i18n("Updating cache file"));
// last state got new messages, update cache file
writeUidCache();
mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
mSubfoldersForSync.clear();
mCurrentSubfolder = 0;
if( child() ) {
KMFolderNode *node = child()->first();
while( node ) {
if( !node->isDir() ) {
// kdDebug() << "child folder " << node->name() << " is a "
// << node->className() << endl;
if ( static_cast<KMFolderCachedImap*>(node)->imapPath() != "" )
// Only sync folders that have been accepted by the server
mSubfoldersForSync << static_cast<KMFolderCachedImap*>(node);
}
node = child()->next();
}
}
}
serverSyncInternal();
break;
case SYNC_STATE_SYNC_SUBFOLDERS:
{
if( mCurrentSubfolder ) {
disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
this, SLOT( serverSyncInternal() ) );
mCurrentSubfolder = 0;
}
prog += 10;
//emit syncState( SYNC_STATE_SYNC_SUBFOLDERS, mSubfoldersForSync.count() );
emit newState( name(), prog, i18n("Synchronization done"));
if( mSubfoldersForSync.isEmpty() ) {
mSyncState = SYNC_STATE_INITIAL;
emit statusMsg( i18n("%1: Synchronization done").arg(name()) );
emit folderComplete( this, TRUE );
close();
} else {
mCurrentSubfolder = mSubfoldersForSync.front();
mSubfoldersForSync.pop_front();
connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
this, SLOT( serverSyncInternal() ) );
// kdDebug() << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
assert( mCurrentSubfolder->imapPath() != "" );
mCurrentSubfolder->setAccount( account() );
mCurrentSubfolder->serverSync();
}
}
break;
default:
kdDebug() << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
<< mSyncState << endl;
}
// kdDebug() << "KMFolderCachedImap::serverSyncInternal() done"<<endl;
}
/* find new messages (messages without a UID) */
QValueList<KMMessage*> KMFolderCachedImap::findNewMessages()
{
//kdDebug() << "KMFolderCachedImap::findNewMessages(), message count is " << count() << endl;
QValueList<KMMessage*> result;
for( int i = 0; i < count(); ++i ) {
bool unget = !isMessage(i);
KMMessage *msg = getMsg(i);
if( !msg ) continue; /* what goes on if getMsg() returns 0? */
if( msg->headerField("X-UID").isEmpty() ) {
result << msg;
} else {
if (unget) unGetMsg(i);
}
}
return result;
}
/* Upload new messages to server */
void KMFolderCachedImap::uploadNewMessages()
{
QValueList<KMMessage*> newMsgs = findNewMessages();
emit syncState( SYNC_STATE_PUT_MESSAGES, newMsgs.count() );
prog += 10;
if( !newMsgs.isEmpty() ) {
emit statusMsg( i18n("%1: Uploading messages to server").arg(name()) );
emit newState( i18n("%1").arg(name()) , prog, i18n("Uploading messages to server"));
KMCachedImapJob *job = new KMCachedImapJob( newMsgs, KMCachedImapJob::tPutMessage, this );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
} else {
emit newState( i18n("%1").arg(name()) , prog, i18n("No messages to upload to server"));
serverSyncInternal();
}
}
/* Upload new folders to server */
void KMFolderCachedImap::createNewFolders()
{
QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
//emit syncState( SYNC_STATE_CREATE_SUBFOLDERS, newFolders.count() );
prog += 10;
if( !newFolders.isEmpty() ) {
emit statusMsg( i18n("%1: Creating subfolders on server").arg(name()) );
emit newState( i18n("%1").arg(name()) , prog, i18n("Creating subfolders on server"));
connect( new KMCachedImapJob( newFolders, KMCachedImapJob::tAddSubfolders, this ),
SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
} else {
serverSyncInternal();
}
}
QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
{
QValueList<KMFolderCachedImap*> newFolders;
if( child() ) {
KMFolderNode *node = child()->first();
while( node ) {
if( !node->isDir() ) {
if( !node->isA("KMFolderCachedImap") ) {
kdDebug() << "KMFolderCachedImap::findNewFolders(): ARGH!!! " << node->name()
<< " is not an IMAP folder. It is a " << node->className() << endl;
node = child()->next();
assert(0);
}
KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(node);
if( folder->imapPath() == "" ) newFolders << folder;
}
node = child()->next();
}
}
return newFolders;
}
bool KMFolderCachedImap::deleteMessages()
{
/* Delete messages from cache that are gone from the server */
QPtrList<KMMsgBase> msgsForDeletion;
reloadUidMap();
// It is not possible to just go over all indices and remove them one by one
// because the index list can get resized under us. So use msg pointers instead
for( QMap<ulong,int>::Iterator it = uidMap.begin(); it != uidMap.end(); ++it ) {
// kdDebug() << "looking at " << it.key() << " in cache " << imapPath() << endl;
if( !uidsOnServer.contains( it.key() ) ) {
// kdDebug() << "Uid" << it.key() << "(idx="<<it.data()<< ") not present on server" << endl;
msgsForDeletion.append( mMsgList[it.data()] );
}
}
if( !msgsForDeletion.isEmpty() ) {
emit statusMsg( i18n("%1: Deleting removed messages from cache").arg(name()) );
open();
for( KMMsgBase *msg = msgsForDeletion.first(); msg; msg = msgsForDeletion.next() )
KMFolder::removeMsg( msg );
compact();
close();
// It is quite possible that the list have been resized, so we need to rebuild
// the entire uid map
reloadUidMap();
}
//emit syncState( SYNC_STATE_DELETE_MESSAGES, uidsForDeletionOnServer.count() );
prog += 10;
emit newState( i18n("%1").arg(name()) , prog, i18n("Deleting removed messages from server"));
/* Delete messages from the server that we dont have anymore */
if( !uidsForDeletionOnServer.isEmpty() ) {
emit statusMsg( i18n("%1: Deleting removed messages from server").arg(name()) );
QStringList sets = makeSets( uidsForDeletionOnServer, true );
uidsForDeletionOnServer.clear();
if( sets.count() > 1 ) {
KMessageBox::error( 0, i18n("The number of messages scheduled for deletion is too large") );
}
//kdDebug() << "Deleting " << sets.front() << " from sever folder " << imapPath() << endl;
KMCachedImapJob *job = new KMCachedImapJob( sets.front(), KMCachedImapJob::tDeleteMessage,
this );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
return true;
} else {
return false;
}
}
void KMFolderCachedImap::checkUidValidity() {
// IMAP root folders doesn't seem to have a UID validity setting.
// Also, don't try the uid validity on new folders
if( imapPath().isEmpty() || imapPath() == "/" )
// Just proceed
serverSyncInternal();
else {
prog += 10;
emit newState( i18n("%1").arg(name()) , prog, i18n("Checking folder validity"));
emit statusMsg( i18n("%1: Checking folder validity").arg(name()) );
KMCachedImapJob *job = new KMCachedImapJob( KMCachedImapJob::tCheckUidValidity, this );
connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
}
}
/* This will only list the messages in a folder.
No directory listing done*/
void KMFolderCachedImap::listMessages() {
if( imapPath() == "/" ) {
// Don't list messages on the root folder
serverSyncInternal();
return;
}
if( !mAccount->makeConnection() ) {
emit listMessagesComplete();
//emit folderComplete(this, FALSE);
return;
}
uidsOnServer.clear();
uidsForDeletionOnServer.clear();
uidsForDownload.clear();
flagsForDownload.clear();
KURL url = mAccount->getUrl();
url.setPath(imapPath() + ";UID=1:*;SECTION=ENVELOPE");
KMAcctCachedImap::jobData jd( url.url(), this );
KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
mAccount->mapJobData.insert(newJob, jd);
connect( newJob, SIGNAL( result( KIO::Job* ) ),
this, SLOT( slotGetLastMessagesResult( KIO::Job* ) ) );
connect( newJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
this, SLOT( slotGetMessagesData( KIO::Job* , const QByteArray& ) ) );
mAccount->displayProgress();
}
void KMFolderCachedImap::slotGetLastMessagesResult(KIO::Job * job)
{
getMessagesResult(job, true);
}
//-----------------------------------------------------------------------------
void KMFolderCachedImap::slotGetMessagesResult(KIO::Job * job)
{
getMessagesResult(job, false);
}
void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end())
return;
(*it).cdata += QCString(data, data.size() + 1);
int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
if (pos > 0) {
int p = (*it).cdata.find("\r\nX-uidValidity:");
if (p != -1)
setUidValidity((*it).cdata.mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
(*it).cdata.remove(0, pos);
}
pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
int flags;
while (pos >= 0) {
KMMessage *msg = new KMMessage;
msg->fromString((*it).cdata.mid(16, pos - 16));
flags = msg->headerField("X-Flags").toInt();
bool ok;
ulong uid = msg->headerField("X-UID").toULong(&ok);
if( ok ) uidsOnServer.append( uid );
if ( /*flags & 8 ||*/ uid <= lastUid()) {
// kdDebug() << "KMFolderCachedImap::slotGetMessagesData() : folder "<<name()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
/* If this message UID is not present locally, then it must
have been deleted by the user, so we delete it on the
server also.
*/
KMMsgBase *existingMessage = findByUID(uid);
if( !existingMessage ) {
// kdDebug() << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
uidsForDeletionOnServer << uid;
} else {
/* The message is OK, update flags */
existingMessage->setStatus( flagsToStatus( flags ) );
}
delete msg;
} else {
uidsForDownload << uid;
flagsForDownload << flags;
#if 0
msg->setStatus(flagsToStatus(flags));
open();
//KMFolderCachedImapInherited::addMsg(msg, NULL);
msg->setComplete( FALSE );
addMsgInternal(msg, NULL);
//sync();
//if (count() > 1) unGetMsg(count() - 1);
//mLastUid = uid;
close();
#endif
/*
QValueList<KMMessage*> msgList;
msgList << msg;
(void)new KMCachedImapJob( msgList, KMCachedImapJob::tGetMessage, this );
*/
/* if ((*it).total > 20 &&
((*it).done + 1) * 5 / (*it).total > (*it).done * 5 / (*it).total)
{
quiet(FALSE);
quiet(TRUE);
} */
}
(*it).cdata.remove(0, pos);
(*it).done++;
pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
mAccount->displayProgress();
}
}
void KMFolderCachedImap::getMessagesResult(KIO::Job * job, bool lastSet)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it =
mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end()) return;
assert(it != mAccount->mapJobData.end());
if (job->error())
{
mAccount->slotSlaveError( mAccount->slave(), job->error(),
job->errorText() );
mContentState = imapNoInformation;
emit folderComplete(this, FALSE);
} else if (lastSet) mContentState = imapFinished;
if (lastSet) quiet(FALSE);
if (mAccount->slave()) mAccount->mapJobData.remove(it);
mAccount->displayProgress();
if (!job->error() && lastSet) {
emit listMessagesComplete();
//emit folderComplete(this, TRUE);
}
}
KMMsgStatus KMFolderCachedImap::flagsToStatus(int flags, bool newMsg)
{
if (flags & 4) return KMMsgStatusFlag;
if (flags & 2) return KMMsgStatusReplied;
if (flags & 1) return KMMsgStatusOld;
return (newMsg) ? KMMsgStatusNew : KMMsgStatusUnread;
}
QCString KMFolderCachedImap::statusToFlags(KMMsgStatus status)
{
QCString flags = "";
switch (status)
{
case KMMsgStatusNew:
case KMMsgStatusUnread:
break;
case KMMsgStatusDeleted:
flags = "\\DELETED";
break;
case KMMsgStatusReplied:
flags = "\\SEEN \\ANSWERED";
break;
case KMMsgStatusFlag:
flags = "\\SEEN \\FLAGGED";
break;
default:
flags = "\\SEEN";
}
return flags;
}
void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
{
// kdDebug() << "KMFolderCachedImap::setAccount( " << aAccount->name() << " )" << endl;
assert( aAccount && aAccount->isA("KMAcctCachedImap") );
mAccount = aAccount;
if( imapPath()=="/" ) aAccount->setFolder(this);
if (!mChild) return;
#if 0
KMFolderNode* node;
for (node = mChild->first(); node; node = mChild->next())
{
if (!node->isDir())
static_cast<KMFolderCachedImap*>(node)->setAccount(aAccount);
}
#endif
}
// This synchronizes the subfolders with the server
bool KMFolderCachedImap::listDirectory() {
// kdDebug() << "KMFolderCachedImap::listDirectory " << "imapPath() = "
// << imapPath() << " mAccount->prefix() = " << mAccount->prefix() << endl;
reloadUidMap();
mSubfolderState = imapInProgress;
KURL url = mAccount->getUrl();
url.setPath(imapPath() + ";TYPE="
+ (mAccount->onlySubscribedFolders() ? "LSUB" : "LIST"));
KMAcctCachedImap::jobData jd( url.url(), this );
mSubfolderNames.clear();
mSubfolderPaths.clear();
mSubfolderMimeTypes.clear();
// kdDebug() << "listDirectory(): listing url " << url.url() << endl;
if (!mAccount->makeConnection())
return FALSE;
KIO::SimpleJob *job = KIO::listDir(url, FALSE);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
mAccount->mapJobData.insert(job, jd);
connect(job, SIGNAL(result(KIO::Job *)),
this, SLOT(slotListResult(KIO::Job *)));
connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
mAccount->displayProgress();
return TRUE;
}
void KMFolderCachedImap::slotListResult(KIO::Job * job)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end()) {
kdDebug() << "could not find job!?!?!" << endl;
serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
return;
}
if (job->error())
mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
mSubfolderState = imapFinished;
if (mAccount->slave()) mAccount->mapJobData.remove(it);
if (!job->error()) {
kernel->imapFolderMgr()->quiet(TRUE);
createChildFolder();
// Find all subfolders present on disk but not on the server
KMFolderCachedImap *folder;
KMFolderNode *node = mChild->first();
QValueList<KMFolderCachedImap*> subfolders;
while (node) {
if (!node->isDir() ) {
if( mSubfolderNames.findIndex(node->name()) == -1) {
// This subfolder isn't present on disk
kdDebug(5006) << node->name() << " isn't on the server." << endl;
folder = static_cast<KMFolderCachedImap*>(node);
if (folder->uidValidity() == "") {
// This folder doesn't have a uidValidity setting yet, so assume
// it's a new one the user made. Add it on the server
subfolders.append(folder);
node = mChild->next();
} else {
// The folder have a uidValidity setting, so it has been on the
// server before. Delete it locally.
KMFolderNode *n = mChild->next();
kernel->imapFolderMgr()->remove(static_cast<KMFolder*>(node));
node = n;
}
} else {
// the folder was on the server too
node = mChild->next();
}
} else {
// The folder was not a dir
node = mChild->next();
}
}
mAccount->displayProgress();
serverSyncInternal();
}
}
void KMFolderCachedImap::listDirectory2() {
foldersForDeletionOnServer.clear();
// Find all subfolders present on server but not on disk
for (uint i = 0; i < mSubfolderNames.count(); i++) {
KMFolderCachedImap *folder = 0;
// Find the subdir, if already present
KMFolderNode *node;
for (node = mChild->first(); node; node = mChild->next())
if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
if (!node) {
// This folder is not present here
QString uidCacheFile = path() + "/." + dotEscape(name()) + ".directory/."
+ dotEscape(mSubfolderNames[i]) + ".uidcache";
if( QFile::exists(uidCacheFile) ) {
// This is an old folder that is deleted locally. We need to schedule a deletion
// on the server. TODO: Actually do it!
// kdDebug() << "unlink( " << QFile::encodeName( uidCacheFile ) << " )" << endl;
unlink( QFile::encodeName( uidCacheFile ) );
foldersForDeletionOnServer << mSubfolderPaths[i];
} else {
// Create new folder
// kdDebug() << "Creating new KMFolderCachedImap folder with name "
// << mSubfolderNames[i] << endl;
folder = static_cast<KMFolderCachedImap*>
(mChild->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap));
if (folder) {
folder->close();
folder->setAccount(mAccount);
kernel->imapFolderMgr()->contentsChanged();
} else {
kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl;
}
}
} else {
// kdDebug() << "node " << node->name() << " is a " << node->className() << endl;
if( node->isA("KMFolderCachedImap") )
folder = static_cast<KMFolderCachedImap*>(node);
}
if (folder && folder->imapPath() == "") {
// kdDebug() << "folder("<<folder->name()<<")->imapPath()=" << folder->imapPath()
// << "\nAssigning new imapPath " << mSubfolderPaths[i] << endl;
// Write folder settings
folder->setAccount(mAccount);
folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
folder->setImapPath(mSubfolderPaths[i]);
}
}
kernel->imapFolderMgr()->quiet(FALSE);
emit listComplete(this);
serverSyncInternal();
}
//-----------------------------------------------------------------------------
void KMFolderCachedImap::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds)
{
// kdDebug() << "KMFolderCachedImap::slotListEntries("<<name()<<")" << endl;
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it =
mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end()) return;
QString name;
KURL url;
QString mimeType;
for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
udsIt != uds.end(); udsIt++)
{
// kdDebug() << "slotListEntries start" << endl;
mimeType = QString::null;
// Find the info on this subfolder
for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
eIt != (*udsIt).end(); eIt++)
{
//kdDebug() << "slotListEntries got type " << (*eIt).m_uds << " str " << (*eIt).m_str << endl;
if ((*eIt).m_uds == KIO::UDS_NAME)
name = (*eIt).m_str;
else if ((*eIt).m_uds == KIO::UDS_URL)
url = KURL((*eIt).m_str, 106); // utf-8
else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
mimeType = (*eIt).m_str;
}
// kdDebug() << "slotListEntries end. mimetype = " << mimeType
// << ", name = " << name << ", path = " << url.path() << endl;
// If this was a subfolder, add it to the list
if ((mimeType == "inode/directory" || mimeType == "message/digest"
|| mimeType == "message/directory")
&& name != ".." && (mAccount->hiddenFolders() || name.at(0) != '.'))
{
// Some servers send _lots_ of duplicates
if (mSubfolderNames.findIndex(name) == -1) {
mSubfolderNames.append(name);
mSubfolderPaths.append(url.path());
mSubfolderMimeTypes.append(mimeType);
}
}
}
}
void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end()) return;
QBuffer buff((*it).data);
buff.open(IO_WriteOnly | IO_Append);
buff.writeBlock(data.data(), data.size());
buff.close();
}
QStringList KMFolderCachedImap::makeSets(QStringList& uids, bool sort)
{
QValueList<ulong> tmp;
for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
tmp.append( (*it).toInt() );
return makeSets(tmp, sort);
}
QStringList KMFolderCachedImap::makeSets(QValueList<ulong>& uids, bool sort)
{
QStringList sets;
QString set;
if (uids.size() == 1)
{
sets.append(QString::number(uids.first()));
return sets;
}
if (sort) qHeapSort(uids);
ulong last = 0;
// needed to make a uid like 124 instead of 124:124
bool inserted = false;
/* iterate over uids and build sets like 120:122,124,126:150 */
for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
{
if (it == uids.begin() || set.isEmpty()) {
set = QString::number(*it);
inserted = true;
} else
{
if (last+1 != *it)
{
// end this range
if (inserted)
set += ',' + QString::number(*it);
else
set += ':' + QString::number(last) + ',' + QString::number(*it);
inserted = true;
if (set.length() > 100)
{
// just in case the server has a problem with longer lines..
sets.append(set);
set = "";
}
} else {
inserted = false;
}
}
last = *it;
}
// last element
if (!inserted)
set += ':' + QString::number(uids.last());
if (!set.isEmpty()) sets.append(set);
return sets;
}
KMCachedImapJob::KMCachedImapJob( const QValueList<ulong>& uids, JobType type,
KMFolderCachedImap* folder, const QValueList<int>& flags )
: QObject( 0, "cached_imap_job" ), mFolder( folder ), mUidList( uids ), mFlags( flags ),
mMsg(0)
{
init( type );
}
KMCachedImapJob::KMCachedImapJob( const QValueList<KMMessage*>& msgs, JobType type,
KMFolderCachedImap* folder )
: QObject( 0,"cached_imap_job" ), mFolder( folder ), mMsgList( msgs ), mMsg(0)
{
init( type );
}
KMCachedImapJob::KMCachedImapJob( const QValueList<KMFolderCachedImap*>& fList,
JobType type, KMFolderCachedImap* folder )
: QObject( 0, "cached_imap_job" ), mFolder( folder ), mFolderList( fList ), mMsg(0)
{
init( type );
}
KMCachedImapJob::KMCachedImapJob( const QString& uids, JobType type,
KMFolderCachedImap* folder )
: QObject( 0, "cached_imap_job" ), mFolder(folder), mMsg(0), mString(uids)
{
assert( folder );
init( type );
}
KMCachedImapJob::KMCachedImapJob( const QStringList& folderpaths, JobType type,
KMFolderCachedImap* folder )
: QObject( 0, "cached_imap_job" ), mFolder( folder ), mFolderPathList( folderpaths ), mMsg(0)
{
assert( folder );
init( type );
}
KMCachedImapJob::KMCachedImapJob( JobType type, KMFolderCachedImap* folder )
: QObject( 0, "cached_imap_job" ), mFolder( folder ), mMsg( 0 ), mJob( 0 )
{
assert( folder );
init( type );
}
KMCachedImapJob::~KMCachedImapJob()
{
mAccount->displayProgress();
if( mJob ) {
// kdDebug() << "~KMCachedImapJob(): Removing jobdata from mapJobData" << endl;
mAccount->mapJobData.remove(mJob);
}
/* // TODO(steffen): Handle transferinpro...
if ( !(*it).msgList.isEmpty() ) {
for ( KMMessage* msg = (*it).msgList.first(); msg; msg = (*it).msgList.next() )
msg->setTransferInProgress(false);
}
*/
//if( mMsg ) mMsg->setTransferInProgress(false);
mAccount->displayProgress();
// kdDebug() << "~KMCachedImapJob(): Removing this from joblist" << endl;
mAccount->mJobList.remove(this);
if( !mPassiveDestructor )
emit finished();
}
void KMCachedImapJob::init( JobType type )
{
mType = type;
if( !mFolder ) {
if( !mMsgList.isEmpty() ) {
mFolder = static_cast<KMFolderCachedImap*>(mMsgList.front()->parent());
}
}
assert( mFolder );
mAccount = mFolder->account();
assert( mAccount != 0 );
if( !mAccount->makeConnection() ) {
// No connection to the IMAP server
kdDebug() << "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);
switch( mType ) {
case tGetMessage: slotGetNextMessage(); break;
case tPutMessage: slotPutNextMessage(); break;
case tDeleteMessage: deleteMessages(mString); break;
case tExpungeFolder: expungeFolder(); break;
case tAddSubfolders: slotAddNextSubfolder(); break;
case tDeleteFolders: slotDeleteNextFolder(); break;
case tCheckUidValidity: checkUidValidity(); break;
case tRenameFolder: renameFolder(mString); break;
default:
assert( 0 );
}
}
void KMCachedImapJob::deleteMessages( const QString& uids )
{
KURL url = mAccount->getUrl();
url.setPath( mFolder->imapPath() + QString::fromLatin1(";UID=%1").arg(uids) );
KIO::SimpleJob *job = KIO::file_delete( url, FALSE );
KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
KMAcctCachedImap::jobData jd;
mAccount->mapJobData.insert( job, jd );
connect( job, SIGNAL( result(KIO::Job *) ), this, SLOT( slotDeleteResult(KIO::Job *) ) );
mAccount->displayProgress();
}
void KMCachedImapJob::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 );
KMAcctCachedImap::jobData jd( url.url() );
mAccount->mapJobData.insert( job, jd );
connect( job, SIGNAL( result(KIO::Job *) ), this, SLOT( slotDeleteResult(KIO::Job *) ) );
mAccount->displayProgress();
}
void KMCachedImapJob::slotDeleteResult( KIO::Job * job )
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if (it != mAccount->mapJobData.end())
// What?
if (mAccount->slave()) mAccount->mJobList.remove(this);
if (job->error())
mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
delete this;
}
void KMCachedImapJob::slotGetNextMessage(KIO::Job * job)
{
if (job) {
if (job->error()) {
mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
delete this;
return;
}
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if ( it == mAccount->mapJobData.end() ) {
delete this;
return;
}
if ((*it).data.size() > 0) {
QString uid = mMsg->headerField("X-UID");
(*it).data.resize((*it).data.size() + 1);
(*it).data[(*it).data.size() - 1] = '\0';
mMsg->fromString(QCString((*it).data));
//int idx = mFolder->find(mMsg);
//if( idx >= 0 ) mFolder->take(idx);
//else kdDebug() << "weird, message not in folder!?!" << endl;
mMsg->setHeaderField("X-UID",uid);
mMsg->setTransferInProgress( FALSE );
mMsg->setComplete( TRUE );
mFolder->addMsgInternal(mMsg);
emit messageRetrieved(mMsg);
/*mFolder->unGetMsg(idx);*/ // Is this OK? /steffen
} else {
emit messageRetrieved(NULL);
}
mMsg = NULL;
if (mAccount->slave()) mAccount->mapJobData.remove(it);
mAccount->displayProgress();
}
if( mUidList.isEmpty() ) {
//emit messageRetrieved(mMsg);
if (mAccount->slave()) mAccount->mJobList.remove(this);
delete this;
return;
}
mUid = mUidList.front(); mUidList.pop_front();
if( mFlags.isEmpty() ) mFlag = -1;
else {
mFlag = mFlags.front(); mFlags.pop_front();
}
mMsg = new KMMessage;
mMsg->setHeaderField("X-UID",QString::number(mUid));
if( mFlag > 0 ) mMsg->setStatus( KMFolderCachedImap::flagsToStatus(mFlag) );
KURL url = mAccount->getUrl();
url.setPath(mFolder->imapPath() + QString(";UID=%1").arg(mUid));
KMAcctCachedImap::jobData jd;
mMsg->setTransferInProgress(TRUE);
KIO::SimpleJob *simpleJob = KIO::get(url, FALSE, FALSE);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mJob = simpleJob;
mAccount->mapJobData.insert(mJob, jd);
connect(mJob, SIGNAL(result(KIO::Job *)),
this, SLOT(slotGetNextMessage(KIO::Job *)));
connect(mJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
mFolder, SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
mAccount->displayProgress();
}
void KMCachedImapJob::slotPutNextMessage()
{
if( mMsgList.isEmpty() ) {
mAccount->mJobList.remove(this);
delete this;
return;
}
mMsg = mMsgList.front(); mMsgList.pop_front();
assert( mMsg );
KURL url = mAccount->getUrl();
url.setPath(mFolder->imapPath() + ";SECTION="
+ QString::fromLatin1(KMFolderCachedImap::statusToFlags(mMsg->status())));
KMAcctCachedImap::jobData jd( url.url() );
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);
mData.resize(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;
mMsg->setTransferInProgress(TRUE);
KIO::SimpleJob *simpleJob = KIO::put(url, 0, FALSE, FALSE, FALSE);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mJob = simpleJob;
mAccount->mapJobData.insert(mJob, jd);
connect( mJob, SIGNAL( result(KIO::Job *) ), SLOT( slotPutMessageResult(KIO::Job *) ) );
connect( mJob, SIGNAL( dataReq(KIO::Job *, QByteArray &) ),
SLOT( slotPutMessageDataReq(KIO::Job *, QByteArray &) ) );
connect( mJob, SIGNAL( data(KIO::Job *, const QByteArray &) ),
mFolder, SLOT( slotSimpleData(KIO::Job *, const QByteArray &) ) );
mAccount->displayProgress();
}
//-----------------------------------------------------------------------------
void KMCachedImapJob::slotPutMessageDataReq(KIO::Job *job, QByteArray &data)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if (it == mAccount->mapJobData.end()) {
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 KMCachedImapJob::slotPutMessageResult(KIO::Job *job)
{
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if ( it == mAccount->mapJobData.end() ) {
delete this;
return;
}
if ( job->error() ) {
QStringList errors = job->detailedErrorStrings();
QString myError = "<qt><p><b>" + i18n("Error while uploading message")
+ "</b></p><p>" + i18n("Could not upload the message %1 on the server from folder %2 with URL %3.").arg((*it).items[0]).arg(mFolder->name()).arg((*it).url)
+ "</p><p>" + i18n("This could be because you don't have permission to do this. The error message from the server communication is here:") + "</p>";
KMessageBox::error( 0, myError + errors[1] + '\n' + errors[2], errors[0] );
if (mAccount->slave())
mAccount->mapJobData.remove(it);
delete this;
return;
} else {
// kdDebug() << "resulting data \"" << QCString((*it).data) << "\"" << endl;
emit messageStored(mMsg);
int i;
if( ( i = mFolder->find(mMsg) ) != -1 ) {
mFolder->quiet( TRUE );
mFolder->removeMsg(i);
mFolder->quiet( FALSE );
}
mMsg = NULL;
}
if (mAccount->slave()) mAccount->mapJobData.remove(it);
mAccount->displayProgress();
/*
if (mAccount->slave()) mAccount->mJobList.remove(this);
delete this;
*/
slotPutNextMessage();
}
void KMCachedImapJob::slotAddNextSubfolder(KIO::Job * job) {
if (job) {
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if ( job->error() && !(*it).parent->silentUpload() ) {
QStringList errors = job->detailedErrorStrings();
QString myError = "<qt><p><b>" + i18n("Error while uploading folder")
+ "</b></p><p>" + i18n("Could not make the folder %1 on the server.").arg((*it).items[0])
+ "</p><p>" + i18n("This could be because you don't have permission to do this or because the directory is already present on the server. The error message from the server communication is here:") + "</p>";
// kdDebug() << "Error messages:\n 0: " << errors[0].latin1() << "\n 1: " << errors[1].latin1() << "\n 2: " << errors[2].latin1() << endl;
KMessageBox::error( 0, myError + errors[1] + '\n' + errors[2], errors[0] );
}
(*it).parent->setSilentUpload( false );
mAccount->mapJobData.remove(it);
if( job->error() ) {
delete this;
return;
}
}
if (mFolderList.isEmpty()) {
// No more folders to add
delete this;
return;
}
KMFolderCachedImap *folder = mFolderList.front();
mFolderList.pop_front();
KURL url = mAccount->getUrl();
url.setPath(mFolder->imapPath() + folder->name());
KMAcctCachedImap::jobData jd( url.url(), folder );
KIO::SimpleJob *simpleJob = KIO::mkdir(url);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mJob = simpleJob;
mAccount->mapJobData.insert(mJob, jd);
connect( mJob, SIGNAL(result(KIO::Job *)), this, SLOT(slotAddNextSubfolder(KIO::Job *)) );
mAccount->displayProgress();
}
void KMCachedImapJob::slotDeleteNextFolder( KIO::Job *job )
{
if( job && job->error() ) {
job->showErrorDialog( 0L );
mAccount->mJobList.remove(this);
delete this;
return;
}
if( mFolderPathList.isEmpty() ) {
mAccount->mJobList.remove(this);
delete this;
return;
}
QString folderPath = mFolderPathList.front(); mFolderPathList.pop_front();
KURL url = mAccount->getUrl();
url.setPath(folderPath);
KMAcctCachedImap::jobData jd;
KIO::SimpleJob *simpleJob = KIO::file_delete(url, FALSE);
KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
mJob = simpleJob;
mAccount->mapJobData.insert(mJob, jd);
connect( mJob, SIGNAL( result(KIO::Job *) ), SLOT( slotDeleteNextFolder(KIO::Job *) ) );
mAccount->displayProgress();
}
void KMCachedImapJob::checkUidValidity() {
KURL url = mAccount->getUrl();
url.setPath( mFolder->imapPath() + ";UID=0:0" );
KMAcctCachedImap::jobData jd( url.url(), mFolder );
KIO::SimpleJob *job = KIO::get( url, FALSE, FALSE );
KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
mAccount->mapJobData.insert( 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 &)));
mAccount->displayProgress();
}
void KMCachedImapJob::slotCheckUidValidityResult(KIO::Job * job) {
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it = mAccount->mapJobData.find(job);
if( it == mAccount->mapJobData.end() ) {
delete this;
return;
}
if( job->error() ) {
job->showErrorDialog( 0 );
mAccount->mJobList.remove( this );
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() << "No uidvalidity available for folder " << mFolder->name() << endl;
return;
}
int b = cstr.find("\r\n", a);
if ( (b - a - 15) >= 0 ) {
QString uidv = cstr.mid(a + 15, b - a - 15);
// kdDebug() << "New uidv = " << uidv << ", old uidv = " << mFolder->uidValidity()
// << endl;
if( mFolder->uidValidity() != "" && mFolder->uidValidity() != uidv ) {
// kdDebug() << "Expunging the mailbox " << mFolder->name() << "!" << endl;
mFolder->expunge();
mFolder->setLastUid( 0 );
}
} else
kdDebug() << "No uidvalidity available for folder " << mFolder->name() << endl;
#if 0
// Set access control on the folder
a = cstr.find("X-Access: ");
if (a >= 0) {
b = cstr.find("\r\n", a);
QString access;
if ( (b - a - 10) >= 0 ) access = cstr.mid(a + 10, b - a - 10);
mReadOnly = access == "Read only";
}
#endif
mAccount->mapJobData.remove(it);
delete this;
}
void KMCachedImapJob::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->name().length() - 1);
imapPath += newName + '/';
urlDst.setPath( imapPath );
KMAcctCachedImap::jobData jd( newName, mFolder );
jd.path = imapPath;
KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, FALSE );
KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
mJob = simpleJob;
mAccount->mapJobData.insert( mJob, jd );
connect( mJob, SIGNAL(result(KIO::Job *)), SLOT(slotRenameFolderResult(KIO::Job *)) );
mAccount->displayProgress();
}
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*>(node);
if ( imapFolder->imapPath() != "" )
// 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->child(), oldPath, newPath );
}
}
node = dir->next();
}
}
}
void KMCachedImapJob::slotRenameFolderResult( KIO::Job *job ) {
QMap<KIO::Job *, KMAcctCachedImap::jobData>::Iterator it =
mAccount->mapJobData.find(job);
if( it == mAccount->mapJobData.end() ) {
// This shouldn't happen??
delete this;
return;
}
if( job->error() ) {
job->showErrorDialog( 0 );
} else {
// Okay, the folder seems to be renamed on the folder. Now rename it on disk
QString oldName = mFolder->name();
QString oldPath = mFolder->imapPath();
mFolder->setImapPath( (*it).path );
mFolder->KMFolder::rename( (*it).url );
if( oldPath.endsWith( "/" ) ) oldPath = oldPath.left( oldPath.length() -1 );
QString newPath = mFolder->imapPath();
if( newPath.endsWith( "/" ) ) newPath = newPath.left( newPath.length() -1 );
renameChildFolders( mFolder->child(), oldPath, newPath );
kernel->imapFolderMgr()->contentsChanged();
}
mAccount->mJobList.remove( this );
delete this;
return;
}
#include "kmfoldercachedimap.moc"