#ifdef HAVE_CONFIG_H #include #endif #include #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 #include #include #include #include #include #include #include #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::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::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("<::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( 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(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(node)->imapPath() != "" ) // Only sync folders that have been accepted by the server mSubfoldersForSync << static_cast(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"< KMFolderCachedImap::findNewMessages() { //kdDebug() << "KMFolderCachedImap::findNewMessages(), message count is " << count() << endl; QValueList 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 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 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::findNewFolders() { QValueList 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(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 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::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="< 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::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 "<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 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::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(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::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 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(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(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 (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] <name() << " is a " << node->className() << endl; if( node->isA("KMFolderCachedImap") ) folder = static_cast(node); } if (folder && folder->imapPath() == "") { // kdDebug() << "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("<::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::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 tmp; for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it ) tmp.append( (*it).toInt() ); return makeSets(tmp, sort); } QStringList KMFolderCachedImap::makeSets(QValueList& 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::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& uids, JobType type, KMFolderCachedImap* folder, const QValueList& flags ) : QObject( 0, "cached_imap_job" ), mFolder( folder ), mUidList( uids ), mFlags( flags ), mMsg(0) { init( type ); } KMCachedImapJob::KMCachedImapJob( const QValueList& msgs, JobType type, KMFolderCachedImap* folder ) : QObject( 0,"cached_imap_job" ), mFolder( folder ), mMsgList( msgs ), mMsg(0) { init( type ); } KMCachedImapJob::KMCachedImapJob( const QValueList& 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(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::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::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::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::Iterator it = mAccount->mapJobData.find(job); if ( it == mAccount->mapJobData.end() ) { delete this; return; } if ( job->error() ) { QStringList errors = job->detailedErrorStrings(); QString myError = "

" + i18n("Error while uploading message") + "

" + 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) + "

" + i18n("This could be because you don't have permission to do this. The error message from the server communication is here:") + "

"; 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::Iterator it = mAccount->mapJobData.find(job); if ( job->error() && !(*it).parent->silentUpload() ) { QStringList errors = job->detailedErrorStrings(); QString myError = "

" + i18n("Error while uploading folder") + "

" + i18n("Could not make the folder %1 on the server.").arg((*it).items[0]) + "

" + 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:") + "

"; // 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::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(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::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"