00001
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025 #include <errno.h>
00026
00027 #include "kmkernel.h"
00028 #include "kmfoldercachedimap.h"
00029 #include "undostack.h"
00030 #include "kmfoldermgr.h"
00031 #include "kmmessage.h"
00032 #include "kmacctcachedimap.h"
00033 #include "kmacctmgr.h"
00034 #include "imapprogressdialog.h"
00035 #include "kmgroupware.h"
00036 #include "kmailicalifaceimpl.h"
00037
00038 using KMail::CachedImapJob;
00039 using KMail::ImapAccountBase;
00040
00041 #include <kapplication.h>
00042 #include <kmessagebox.h>
00043 #include <klocale.h>
00044 #include <kdebug.h>
00045 #include <kconfig.h>
00046 #include <kio/global.h>
00047 #include <kio/scheduler.h>
00048 #include <qbuffer.h>
00049 #include <qfile.h>
00050 #include <qlabel.h>
00051 #include <qlayout.h>
00052 #include <qvaluelist.h>
00053
00054 #define UIDCACHE_VERSION 1
00055
00056
00057 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget* parent,
00058 const char* name )
00059 : KDialogBase( Plain, i18n( "Troubleshooting the IMAP cache" ),
00060 Cancel | User1 | User2, Cancel, parent, name, true ),
00061 rc( Cancel )
00062 {
00063 QFrame* page = plainPage();
00064 QVBoxLayout *topLayout = new QVBoxLayout( page, 0 );
00065 QString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
00066 "<p>If you have problems with synchronizing an IMAP "
00067 "folder, you should first try rebuilding the index "
00068 "file. This will take some time to rebuild, but will "
00069 "not cause any problems.</p><p>If that is not enough, "
00070 "you can try refreshing the IMAP cache. If you do this, "
00071 "you will loose all your local changes for this folder "
00072 "and all it's subfolders.</p>" );
00073 topLayout->addWidget( new QLabel( txt, page ) );
00074 enableButtonSeparator( true );
00075
00076 setButtonText( User1, i18n( "Refresh &Cache" ) );
00077 setButtonText( User2, i18n( "Rebuild &Index" ) );
00078
00079 connect( this, SIGNAL( user1Clicked () ), this, SLOT( slotRebuildCache() ) );
00080 connect( this, SIGNAL( user2Clicked () ), this, SLOT( slotRebuildIndex() ) );
00081 }
00082
00083 int DImapTroubleShootDialog::run()
00084 {
00085 DImapTroubleShootDialog d;
00086 d.exec();
00087 return d.rc;
00088 }
00089
00090 void DImapTroubleShootDialog::slotRebuildCache()
00091 {
00092 rc = User1;
00093 done( User1 );
00094 }
00095
00096 void DImapTroubleShootDialog::slotRebuildIndex()
00097 {
00098 rc = User2;
00099 done( User2 );
00100 }
00101
00102
00103 KMFolderCachedImap::KMFolderCachedImap( KMFolderDir* aParent,
00104 const QString& aName )
00105 : KMFolderMaildir( aParent, aName ),
00106 mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
00107 mSubfolderState( imapNoInformation ), mIsSelected( false ),
00108 mCheckFlags( true ), mAccount( NULL ), uidMapDirty( true ),
00109 mLastUid( 0 ), uidWriteTimer( -1 ),
00110 mIsConnected( false ), mFolderRemoved( false ), mResync( false ),
00111 mSuppressDialog( false ), mHoldSyncs( false ), mRemoveRightAway( false )
00112 {
00113 KConfig* config = KMKernel::config();
00114 KConfigGroupSaver saver(config, "Folder-" + idString());
00115 if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath");
00116 if (aName == "INBOX" && mImapPath == "/INBOX/")
00117 {
00118
00119 }
00120 mIsSystemFolder = false;
00121 mNoContent = config->readBoolEntry("NoContent", FALSE);
00122 mReadOnly = config->readBoolEntry("ReadOnly", FALSE);
00123
00124 connect( this, SIGNAL( listMessagesComplete() ),
00125 this, SLOT( serverSyncInternal() ) );
00126
00127 setUidValidity("");
00128 mLastUid=0;
00129 readUidCache();
00130
00131 mProgress = 0;
00132 }
00133
00134 KMFolderCachedImap::~KMFolderCachedImap()
00135 {
00136 if( !mFolderRemoved ) {
00137
00138 KConfig* config = KMKernel::config();
00139 KConfigGroupSaver saver(config, "Folder-" + idString());
00140 config->writeEntry("ImapPath", mImapPath);
00141 config->writeEntry("NoContent", mNoContent);
00142 config->writeEntry("ReadOnly", mReadOnly);
00143
00144 writeUidCache();
00145 }
00146
00147 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this);
00148 }
00149
00150 int KMFolderCachedImap::remove()
00151 {
00152 mFolderRemoved = true;
00153 int rc = KMFolderMaildir::remove();
00154
00155 if( mRemoveRightAway ) {
00156
00157
00158 QString part1 = path() + "/." + dotEscape(name());
00159 QString uidCacheFile = part1 + ".uidcache";
00160 if( QFile::exists(uidCacheFile) )
00161 unlink( QFile::encodeName( uidCacheFile ) );
00162 KIO::del( KURL( part1 + ".directory" ) );
00163 } else {
00164
00165
00166
00167
00168 }
00169
00170 return rc;
00171 }
00172
00173 QString KMFolderCachedImap::uidCacheLocation() const
00174 {
00175 QString sLocation(path());
00176 if (!sLocation.isEmpty()) sLocation += '/';
00177 return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
00178 }
00179
00180 int KMFolderCachedImap::readUidCache()
00181 {
00182 QFile uidcache( uidCacheLocation() );
00183 if( uidcache.open( IO_ReadOnly ) ) {
00184 char buf[1024];
00185 int len = uidcache.readLine( buf, sizeof(buf) );
00186 if( len > 0 ) {
00187 int cacheVersion;
00188 sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion );
00189 if( cacheVersion == UIDCACHE_VERSION ) {
00190 len = uidcache.readLine( buf, sizeof(buf) );
00191 if( len > 0 ) {
00192 setUidValidity( QString::fromLocal8Bit( buf).stripWhiteSpace() );
00193 len = uidcache.readLine( buf, sizeof(buf) );
00194 if( len > 0 ) {
00195 mLastUid =
00196 QString::fromLocal8Bit( buf).stripWhiteSpace().toULong();
00197 return 0;
00198 }
00199 }
00200 }
00201 }
00202 }
00203 return -1;
00204 }
00205
00206 int KMFolderCachedImap::writeUidCache()
00207 {
00208 if( lastUid() == 0 || uidValidity().isEmpty() )
00209
00210 return 0;
00211
00212 QFile uidcache( uidCacheLocation() );
00213 if( uidcache.open( IO_WriteOnly ) ) {
00214 QTextStream str( &uidcache );
00215 str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
00216 str << uidValidity() << endl;
00217 str << lastUid() << endl;
00218 uidcache.flush();
00219 fsync( uidcache.handle() );
00220 uidcache.close();
00221 return 0;
00222 } else {
00223 return errno;
00224 }
00225 }
00226
00227 void KMFolderCachedImap::reloadUidMap()
00228 {
00229 uidMap.clear();
00230 open();
00231 for( int i = 0; i < count(); ++i ) {
00232 bool unget = !isMessage(i);
00233 bool ok;
00234 KMMessage *msg = getMsg(i);
00235 if( !msg ) continue;
00236 ulong uid = msg->headerField("X-UID").toULong(&ok);
00237 if (unget) unGetMsg(i);
00238 if( ok ) {
00239 uidMap.insert( uid, i );
00240 if( uid > mLastUid ) setLastUid( uid );
00241 }
00242 }
00243 close();
00244 uidMapDirty = false;
00245 }
00246
00247
00248 KMMessage* KMFolderCachedImap::take(int idx)
00249 {
00250 uidMapDirty = true;
00251 return KMFolderMaildir::take(idx);
00252 }
00253
00254
00255 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
00256 int* index_return )
00257 {
00258
00259 bool ok;
00260 ulong uid = msg->headerField("X-UID").toULong( &ok );
00261 if( ok ) {
00262 uidMapDirty = true;
00263 if( uid > mLastUid )
00264 setLastUid( uid );
00265 }
00266
00267
00268 int rc = KMFolderMaildir::addMsg(msg, index_return);
00269
00270 if( newMail && imapPath() == "/INBOX/" )
00271
00272 mAccount->processNewMsg( msg );
00273
00274 return rc;
00275 }
00276
00277
00278 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
00279 {
00280
00281 msg->removeHeaderField( "X-UID" );
00282
00283
00284 return addMsgInternal( msg, false, index_return );
00285 }
00286
00287
00288
00289 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
00290 {
00291 uidMapDirty = true;
00292
00293
00294 KMFolderMaildir::removeMsg(idx,imapQuiet);
00295
00296 kmkernel->dimapFolderMgr()->contentsChanged();
00297 }
00298
00299 bool KMFolderCachedImap::canRemoveFolder() const {
00300
00301 if( child() != 0 && child()->count() > 0 )
00302 return false;
00303
00304 #if 0
00305
00306 return KMFolderMaildir::canRemoveFolder();
00307 #endif
00308 return true;
00309 }
00310
00311
00312 int KMFolderCachedImap::rename( const QString& aName,
00313 KMFolderDir* )
00314 {
00315 if ( aName == name() )
00316
00317 return 0;
00318
00319 if( mSyncState != SYNC_STATE_INITIAL ) {
00320 KMessageBox::error( 0, i18n("You can't rename a folder when a sync is in progress") );
00321 return -1;
00322 }
00323
00324 if( account() == 0 ) {
00325 QString err = i18n("You must synchronize with the server before renaming IMAP folders.");
00326 KMessageBox::error( 0, err );
00327 return -1;
00328 }
00329
00330 CachedImapJob *job = new CachedImapJob( aName, CachedImapJob::tRenameFolder, this );
00331 job->start();
00332 return 0;
00333 }
00334
00335 void KMFolderCachedImap::setLastUid( ulong uid )
00336 {
00337 mLastUid = uid;
00338 if( uidWriteTimer == -1 )
00339
00340 uidWriteTimer = startTimer( 60000 );
00341 }
00342
00343 void KMFolderCachedImap::timerEvent( QTimerEvent* )
00344 {
00345 killTimer( uidWriteTimer );
00346 uidWriteTimer = -1;
00347 writeUidCache();
00348 }
00349
00350 ulong KMFolderCachedImap::lastUid()
00351 {
00352 return mLastUid;
00353 }
00354
00355 KMMessage* KMFolderCachedImap::findByUID( ulong uid )
00356 {
00357 bool mapReloaded = false;
00358 if( uidMapDirty ) {
00359 reloadUidMap();
00360 mapReloaded = true;
00361 }
00362
00363 QMap<ulong,int>::Iterator it = uidMap.find( uid );
00364 if( it != uidMap.end() ) {
00365 bool unget = !isMessage(count() - 1);
00366 KMMessage* msg = getMsg( *it );
00367 if( msg && msg->headerField("X-UID").toULong() == uid )
00368 return msg;
00369 else if( unget )
00370 unGetMsg( *it );
00371 }
00372
00373
00374 if( mapReloaded )
00375
00376 return 0;
00377
00378
00379 reloadUidMap();
00380 it = uidMap.find( uid );
00381 if( it != uidMap.end() )
00382
00383 return getMsg( *it );
00384
00385
00386 return 0;
00387 }
00388
00389
00390
00391 KMAcctCachedImap *KMFolderCachedImap::account()
00392 {
00393 if( (KMAcctCachedImap *)mAccount == 0 ) {
00394
00395 mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->find( name() ) );
00396 }
00397
00398 return mAccount;
00399 }
00400
00401 void KMFolderCachedImap::slotTroubleshoot()
00402 {
00403 const int rc = DImapTroubleShootDialog::run();
00404
00405 if( rc == KDialogBase::User1 ) {
00406
00407 if( !account() ) {
00408 KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
00409 "Please try running a sync before this.") );
00410 return;
00411 }
00412 QString str = i18n("Are you sure you want to refresh the IMAP cache of "
00413 "the folder %1 and all it's subfolders?\nThis will "
00414 "remove all changes you have done locally to your "
00415 "folders").arg( name() );
00416 QString s1 = i18n("Refresh IMAP Cache");
00417 QString s2 = i18n("&Refresh");
00418 if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
00419 KMessageBox::Continue )
00420 account()->invalidateIMAPFolders( this );
00421 } else if( rc == KDialogBase::User2 ) {
00422
00423 createIndexFromContents();
00424 KMessageBox::information( 0, i18n( "The index of this folder has been "
00425 "recreated." ) );
00426 }
00427 }
00428
00429 void KMFolderCachedImap::processNewMail()
00430 {
00431 if( account() )
00432 account()->processNewMail( this, true );
00433 }
00434
00435 void KMFolderCachedImap::serverSync( bool suppressDialog )
00436 {
00437 if( mSyncState != SYNC_STATE_INITIAL ) {
00438 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 ) {
00439 mSyncState = SYNC_STATE_INITIAL;
00440 } else return;
00441 }
00442
00443 assert( account() );
00444
00445
00446 mSuppressDialog = suppressDialog;
00447 if( mIsConnected != mAccount->isProgressDialogEnabled() &&
00448 suppressDialog )
00449 {
00450 if( !mIsConnected )
00451 connect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
00452 account()->imapProgressDialog(),
00453 SLOT( syncState( const QString&, int, const QString& ) ) );
00454 else
00455 disconnect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
00456 account()->imapProgressDialog(),
00457 SLOT( syncState( const QString&, int, const QString& ) ) );
00458 mIsConnected = mAccount->isProgressDialogEnabled();
00459 }
00460
00461 if( mHoldSyncs ) {
00462
00463 mProgress = 100;
00464 emit newState( name(), mProgress, i18n("Synchronization skipped"));
00465 mAccount->displayProgress();
00466 mSyncState = SYNC_STATE_INITIAL;
00467 emit statusMsg( i18n("%1: Synchronization done").arg(name()) );
00468 emit folderComplete( this, true );
00469 return;
00470 }
00471
00472 mResync = false;
00473 serverSyncInternal();
00474 }
00475
00476 QString KMFolderCachedImap::state2String( int state ) const
00477 {
00478 switch( state ) {
00479 case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
00480 case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
00481 case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
00482 case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
00483 case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
00484 case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
00485 case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
00486 case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
00487 case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
00488 case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
00489 case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
00490 case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
00491 case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
00492 case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
00493 default: return "Unknown state";
00494 }
00495 }
00496
00497
00498
00499
00500 void KMFolderCachedImap::serverSyncInternal()
00501 {
00502 switch( mSyncState ) {
00503 case SYNC_STATE_INITIAL:
00504 {
00505 mProgress = 0;
00506 emit statusMsg( i18n("%1: Synchronizing").arg(name()) );
00507 emit newState( name(), mProgress, i18n("Synchronizing"));
00508
00509 open();
00510
00511 kdDebug(5006) << k_funcinfo << " making connection" << endl;
00512
00513 ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
00514 if ( cs == ImapAccountBase::Error ) {
00515
00516 kdDebug(5006) << "makeConnection said Error, aborting." << endl;
00517
00518 emit folderComplete(this, FALSE);
00519 break;
00520 } else if ( cs == ImapAccountBase::Connecting )
00521 {
00522 kdDebug(5006) << "makeConnection said Connecting, waiting for signal."
00523 << endl;
00524
00525 connect( mAccount, SIGNAL( connectionResult(int) ),
00526 this, SLOT( slotConnectionResult(int) ) );
00527 break;
00528 } else
00529 {
00530 kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
00531 mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00532
00533 }
00534 }
00535 case SYNC_STATE_CHECK_UIDVALIDITY:
00536 emit syncRunning( this, true );
00537 mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
00538 if( !noContent() ) {
00539
00540
00541 checkUidValidity();
00542 break;
00543 }
00544
00545
00546 case SYNC_STATE_CREATE_SUBFOLDERS:
00547 mSyncState = SYNC_STATE_PUT_MESSAGES;
00548 createNewFolders();
00549 break;
00550
00551 case SYNC_STATE_PUT_MESSAGES:
00552 mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
00553 if( !noContent() ) {
00554 uploadNewMessages();
00555 break;
00556 }
00557
00558
00559 case SYNC_STATE_LIST_SUBFOLDERS:
00560 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
00561 mProgress += 10;
00562 emit statusMsg( i18n("%1: Retrieving folderlist").arg(name()) );
00563 emit newState( name(), mProgress, i18n("Retrieving folderlist"));
00564 if( !listDirectory() ) {
00565 mSyncState = SYNC_STATE_INITIAL;
00566 KMessageBox::error(0, i18n("Error during listDirectory()"));
00567 }
00568 break;
00569
00570 case SYNC_STATE_LIST_SUBFOLDERS2:
00571 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
00572 mProgress += 10;
00573 emit newState( name(), mProgress, i18n("Retrieving subfolders"));
00574 listDirectory2();
00575 break;
00576
00577 case SYNC_STATE_DELETE_SUBFOLDERS:
00578 mSyncState = SYNC_STATE_LIST_MESSAGES;
00579 emit syncState( SYNC_STATE_DELETE_SUBFOLDERS, foldersForDeletionOnServer.count() );
00580 if( !foldersForDeletionOnServer.isEmpty() ) {
00581 emit statusMsg( i18n("%1: Deleting folders %2 from server").arg(name())
00582 .arg( foldersForDeletionOnServer.join(", ") ) );
00583 emit newState( name(), mProgress, i18n("Deleting folders from server"));
00584 CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
00585 CachedImapJob::tDeleteFolders, this );
00586 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00587 job->start();
00588 break;
00589 } else
00590 emit newState( name(), mProgress, i18n("No folders to delete from server"));
00591
00592
00593 case SYNC_STATE_LIST_MESSAGES:
00594 mSyncState = SYNC_STATE_DELETE_MESSAGES;
00595 mProgress += 10;
00596 if( !noContent() ) {
00597 emit statusMsg( i18n("%1: Retrieving messagelist").arg(name()) );
00598 emit newState( name(), mProgress, i18n("Retrieving messagelist"));
00599
00600 listMessages();
00601 break;
00602 }
00603
00604
00605 case SYNC_STATE_DELETE_MESSAGES:
00606 mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
00607 if( !noContent() ) {
00608 if( deleteMessages() ) {
00609
00610 } else {
00611
00612 emit newState( name(), mProgress, i18n("No messages to delete..."));
00613 mSyncState = SYNC_STATE_GET_MESSAGES;
00614 serverSyncInternal();
00615 }
00616 break;
00617 }
00618
00619
00620 case SYNC_STATE_EXPUNGE_MESSAGES:
00621 mSyncState = SYNC_STATE_GET_MESSAGES;
00622 if( !noContent() ) {
00623 mProgress += 10;
00624 emit statusMsg( i18n("%1: Expunging deleted messages").arg(name()) );
00625 emit newState( name(), mProgress, i18n("Expunging deleted messages"));
00626 CachedImapJob *job = new CachedImapJob( QString::null,
00627 CachedImapJob::tExpungeFolder, this );
00628 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00629 job->start();
00630 break;
00631 }
00632
00633
00634 case SYNC_STATE_GET_MESSAGES:
00635 mSyncState = SYNC_STATE_HANDLE_INBOX;
00636 if( !noContent() ) {
00637
00638 if( !mMsgsForDownload.isEmpty() ) {
00639 emit statusMsg( i18n("%1: Retrieving new messages").arg(name()) );
00640 emit newState( name(), mProgress, i18n("Retrieving new messages"));
00641 CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
00642 CachedImapJob::tGetMessage,
00643 this );
00644 connect( job, SIGNAL( progress(unsigned long, unsigned long) ),
00645 this, SLOT( slotProgress(unsigned long, unsigned long) ) );
00646 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00647 job->start();
00648 mMsgsForDownload.clear();
00649 break;
00650 } else {
00651 emit newState( name(), mProgress, i18n("No new messages from server"));
00652 }
00653 }
00654
00655
00656 case SYNC_STATE_HANDLE_INBOX:
00657
00658 mProgress += 20;
00659
00660
00661 if( mResync ) {
00662
00663 mResync = false;
00664 mSyncState = SYNC_STATE_INITIAL;
00665 serverSyncInternal();
00666 break;
00667 } else
00668
00669 mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
00670
00671 case SYNC_STATE_FIND_SUBFOLDERS:
00672 {
00673 emit newState( name(), mProgress, i18n("Updating cache file"));
00674
00675 mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
00676 mSubfoldersForSync.clear();
00677 mCurrentSubfolder = 0;
00678 if( child() ) {
00679 KMFolderNode *node = child()->first();
00680 while( node ) {
00681 if( !node->isDir() ) {
00682 if ( !static_cast<KMFolderCachedImap*>(node)->imapPath().isEmpty() )
00683
00684 mSubfoldersForSync << static_cast<KMFolderCachedImap*>(node);
00685 }
00686 node = child()->next();
00687 }
00688 }
00689 }
00690
00691
00692 mProgress = 100;
00693 emit newState( name(), mProgress, i18n("Synchronization done"));
00694 emit syncRunning( this, false );
00695 mAccount->displayProgress();
00696
00697
00698 case SYNC_STATE_SYNC_SUBFOLDERS:
00699 {
00700 if( mCurrentSubfolder ) {
00701 disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
00702 this, SLOT( serverSyncInternal() ) );
00703 mCurrentSubfolder = 0;
00704 }
00705
00706 if( mSubfoldersForSync.isEmpty() ) {
00707 mSyncState = SYNC_STATE_INITIAL;
00708 emit statusMsg( i18n("%1: Synchronization done").arg(name()) );
00709 emit folderComplete( this, TRUE );
00710 close();
00711 } else {
00712 mCurrentSubfolder = mSubfoldersForSync.front();
00713 mSubfoldersForSync.pop_front();
00714 connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
00715 this, SLOT( serverSyncInternal() ) );
00716
00717
00718 assert( !mCurrentSubfolder->imapPath().isEmpty() );
00719 mCurrentSubfolder->setAccount( account() );
00720 mCurrentSubfolder->serverSync( mSuppressDialog );
00721 }
00722 }
00723 break;
00724
00725 default:
00726 kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
00727 << mSyncState << endl;
00728 }
00729 }
00730
00731
00732
00733
00734 void KMFolderCachedImap::slotConnectionResult( int errorCode )
00735 {
00736 kdDebug(5006) << k_funcinfo << errorCode << endl;
00737 disconnect( mAccount, SIGNAL( connectionResult(int) ),
00738 this, SLOT( slotConnectionResult(int) ) );
00739 if ( !errorCode ) {
00740
00741 mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00742 serverSyncInternal();
00743 } else {
00744
00745 emit folderComplete(this, FALSE);
00746 }
00747 }
00748
00749
00750 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
00751 {
00752 QValueList<unsigned long> result;
00753 for( int i = 0; i < count(); ++i ) {
00754 bool unget = !isMessage(i);
00755 KMMessage *msg = getMsg(i);
00756 if( !msg ) continue;
00757 if( msg->headerField("X-UID").isEmpty() ) {
00758 result.append( msg->getMsgSerNum() );
00759 } else {
00760 if (unget) unGetMsg(i);
00761 }
00762 }
00763 return result;
00764 }
00765
00766
00767 void KMFolderCachedImap::uploadNewMessages()
00768 {
00769 QValueList<unsigned long> newMsgs = findNewMessages();
00770 emit syncState( SYNC_STATE_PUT_MESSAGES, newMsgs.count() );
00771 mProgress += 10;
00772
00773 if( !newMsgs.isEmpty() ) {
00774 emit statusMsg( i18n("%1: Uploading messages to server").arg(name()) );
00775
00776 emit newState( name(), mProgress, i18n("Uploading messages to server"));
00777 CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
00778 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00779 job->start();
00780 } else {
00781 emit newState( name(), mProgress, i18n("No messages to upload to server"));
00782
00783 serverSyncInternal();
00784 }
00785 }
00786
00787
00788 void KMFolderCachedImap::createNewFolders()
00789 {
00790 QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
00791
00792 mProgress += 10;
00793
00794 if( !newFolders.isEmpty() ) {
00795 emit statusMsg( i18n("%1: Creating subfolders on server").arg(name()) );
00796 emit newState( name(), mProgress, i18n("Creating subfolders on server"));
00797 CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
00798 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00799 job->start();
00800 } else {
00801 serverSyncInternal();
00802 }
00803 }
00804
00805 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
00806 {
00807 QValueList<KMFolderCachedImap*> newFolders;
00808 if( child() ) {
00809 KMFolderNode *node = child()->first();
00810 while( node ) {
00811 if( !node->isDir() ) {
00812 if( !node->isA("KMFolderCachedImap") ) {
00813 kdDebug(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
00814 << node->name() << " is not an IMAP folder. It is a "
00815 << node->className() << endl;
00816 node = child()->next();
00817 assert(0);
00818 }
00819 KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(node);
00820 if( folder->imapPath().isEmpty() ) newFolders << folder;
00821 }
00822 node = child()->next();
00823 }
00824 }
00825 return newFolders;
00826 }
00827
00828 bool KMFolderCachedImap::deleteMessages()
00829 {
00830
00831 QPtrList<KMMessage> msgsForDeletion;
00832
00833
00834
00835
00836 for( int i = 0; i < count(); ++i ) {
00837 bool unget = !isMessage(i);
00838 KMMessage *msg = getMsg(i);
00839 if( !msg ) continue;
00840 bool ok;
00841 ulong uid = msg->headerField( "X-UID" ).toULong( &ok );
00842 if( ok && !uidsOnServer.contains( uid ) )
00843 msgsForDeletion.append( msg );
00844 else
00845 if (unget) unGetMsg(i);
00846 }
00847
00848 if( !msgsForDeletion.isEmpty() ) {
00849 emit statusMsg( i18n("%1: Deleting removed messages from cache").arg(name()) );
00850 removeMsg( msgsForDeletion );
00851 }
00852
00853 mProgress += 10;
00854
00855 emit newState( name(), mProgress, i18n("Deleting removed messages from server"));
00856
00857
00858 if( !uidsForDeletionOnServer.isEmpty() ) {
00859 emit statusMsg( i18n("%1: Deleting removed messages from server").arg(name()) );
00860 QStringList sets = makeSets( uidsForDeletionOnServer, true );
00861 uidsForDeletionOnServer.clear();
00862 if( sets.count() > 1 ) {
00863
00864 mResync = true;
00865 }
00866
00867 CachedImapJob *job = new CachedImapJob( sets.front(), CachedImapJob::tDeleteMessage,
00868 this );
00869 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00870 job->start();
00871 return true;
00872 } else {
00873 return false;
00874 }
00875 }
00876
00877 void KMFolderCachedImap::checkUidValidity() {
00878
00879
00880 if( imapPath().isEmpty() || imapPath() == "/" )
00881
00882 serverSyncInternal();
00883 else {
00884 mProgress += 10;
00885
00886 emit newState( name(), mProgress, i18n("Checking folder validity"));
00887 emit statusMsg( i18n("%1: Checking folder validity").arg(name()) );
00888 CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
00889 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00890 job->start();
00891 }
00892 }
00893
00894
00895
00896 void KMFolderCachedImap::listMessages() {
00897 if( imapPath() == "/" ) {
00898
00899 serverSyncInternal();
00900 return;
00901 }
00902
00903 if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
00904 emit listMessagesComplete();
00905 emit folderComplete( this, false );
00906 return;
00907 }
00908 uidsOnServer.clear();
00909 uidsForDeletionOnServer.clear();
00910 mMsgsForDownload.clear();
00911 mUidsForDownload.clear();
00912 KURL url = mAccount->getUrl();
00913 url.setPath(imapPath() + ";UID=1:*;SECTION=ENVELOPE");
00914 KMAcctCachedImap::jobData jd( url.url(), this );
00915
00916 KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00917 KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00918 mAccount->insertJob(newJob, jd);
00919
00920 connect( newJob, SIGNAL( result( KIO::Job* ) ),
00921 this, SLOT( slotGetLastMessagesResult( KIO::Job* ) ) );
00922 connect( newJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00923 this, SLOT( slotGetMessagesData( KIO::Job* , const QByteArray& ) ) );
00924 }
00925
00926 void KMFolderCachedImap::slotGetLastMessagesResult(KIO::Job * job)
00927 {
00928 getMessagesResult(job, true);
00929 }
00930
00931
00932
00933 void KMFolderCachedImap::slotGetMessagesResult(KIO::Job * job)
00934 {
00935 getMessagesResult(job, false);
00936 }
00937
00938
00939 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
00940 {
00941 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00942 if ( it == mAccount->jobsEnd() ) {
00943 kdDebug(5006) << "could not find job!?!?!" << endl;
00944 serverSyncInternal();
00945 return;
00946 }
00947 (*it).cdata += QCString(data, data.size() + 1);
00948 int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
00949 if (pos > 0) {
00950 int p = (*it).cdata.find("\r\nX-uidValidity:");
00951 if (p != -1)
00952 setUidValidity((*it).cdata.mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
00953 (*it).cdata.remove(0, pos);
00954 }
00955 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00956
00957 int flags;
00958 while (pos >= 0) {
00959 KMMessage *msg = new KMMessage;
00960 msg->fromString((*it).cdata.mid(16, pos - 16));
00961 flags = msg->headerField("X-Flags").toInt();
00962 bool ok;
00963 ulong uid = msg->headerField("X-UID").toULong(&ok);
00964 if( ok ) uidsOnServer.append( uid );
00965 if ( uid <= lastUid()) {
00966
00967
00968
00969
00970
00971 KMMsgBase *existingMessage = findByUID(uid);
00972 if( !existingMessage ) {
00973
00974 uidsForDeletionOnServer << uid;
00975 } else {
00976
00977 flagsToStatus( existingMessage, flags );
00978 }
00979 delete msg;
00980 } else {
00981 ulong size = msg->headerField("X-Length").toULong();
00982 mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
00983 if( imapPath() == "/INBOX/" )
00984 mUidsForDownload << uid;
00985
00986 delete msg;
00987 }
00988 (*it).cdata.remove(0, pos);
00989 (*it).done++;
00990 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00991 mAccount->displayProgress();
00992 }
00993 }
00994
00995 void KMFolderCachedImap::getMessagesResult( KIO::Job * job, bool lastSet )
00996 {
00997
00998 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00999 if ( it == mAccount->jobsEnd() ) {
01000 kdDebug(5006) << "could not find job!?!?!" << endl;
01001 serverSyncInternal();
01002 return;
01003 }
01004
01005 if( job->error() ) {
01006 mAccount->slotSlaveError( mAccount->slave(), job->error(),
01007 job->errorText() );
01008 mContentState = imapNoInformation;
01009 emit folderComplete(this, FALSE);
01010 } else if (lastSet) mContentState = imapFinished;
01011 mAccount->removeJob(it);
01012 if( lastSet )
01013 emit listMessagesComplete();
01014 }
01015
01016 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01017 {
01018
01019
01020
01021 emit newState( name(), mProgress + (20 * done) / total, QString::null);
01022 }
01023
01024
01025 void KMFolderCachedImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
01026 {
01027 if (flags & 4)
01028 msg->setStatus( KMMsgStatusFlag );
01029 if (flags & 2)
01030 msg->setStatus( KMMsgStatusReplied );
01031 if (flags & 1)
01032 msg->setStatus( KMMsgStatusOld );
01033
01034 if (msg->isOfUnknownStatus()) {
01035 if (newMsg)
01036 msg->setStatus( KMMsgStatusNew );
01037 else
01038 msg->setStatus( KMMsgStatusUnread );
01039 }
01040 }
01041
01042
01043 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01044 {
01045 assert( aAccount->isA("KMAcctCachedImap") );
01046 mAccount = aAccount;
01047 if( imapPath()=="/" ) aAccount->setFolder(this);
01048
01049 if( !mChild || mChild->count() == 0) return;
01050 for( KMFolderNode* node = mChild->first(); node; node = mChild->next() )
01051 if (!node->isDir())
01052 static_cast<KMFolderCachedImap*>(node)->setAccount(aAccount);
01053 }
01054
01055
01056
01057 bool KMFolderCachedImap::listDirectory()
01058 {
01059 mSubfolderState = imapInProgress;
01060 KURL url = mAccount->getUrl();
01061 url.setPath(imapPath() + ";TYPE="
01062 + (mAccount->onlySubscribedFolders() ? "LSUB" : "LIST"));
01063 KMAcctCachedImap::jobData jd( url.url(), this );
01064 mSubfolderNames.clear();
01065 mSubfolderPaths.clear();
01066 mSubfolderMimeTypes.clear();
01067
01068 if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01069 emit folderComplete( this, false );
01070 return false;
01071 }
01072
01073 KIO::SimpleJob *job = KIO::listDir(url, FALSE);
01074 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01075 mAccount->insertJob(job, jd);
01076 connect(job, SIGNAL(result(KIO::Job *)),
01077 this, SLOT(slotListResult(KIO::Job *)));
01078 connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
01079 this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
01080
01081 return TRUE;
01082 }
01083
01084 void KMFolderCachedImap::slotListResult(KIO::Job * job)
01085 {
01086 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01087 if( it == mAccount->jobsEnd() ) {
01088 kdDebug(5006) << "could not find job!?!?!" << endl;
01089 serverSyncInternal();
01090 return;
01091 }
01092
01093 if( job->error() ) {
01094 kdDebug(5006) << "listDirectory() - slotListResult: Job error\n";
01095 mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
01096 }
01097
01098 mSubfolderState = imapFinished;
01099 mAccount->removeJob(it);
01100
01101 if (!job->error()) {
01102 kmkernel->dimapFolderMgr()->quiet(TRUE);
01103 createChildFolder();
01104
01105
01106 KMFolderCachedImap *folder;
01107 KMFolderNode *node = mChild->first();
01108 QPtrList<KMFolder> toRemove;
01109 while (node) {
01110 if (!node->isDir() ) {
01111 if( mSubfolderNames.findIndex(node->name()) == -1) {
01112
01113 kdDebug(5006) << node->name() << " isn't on the server." << endl;
01114 folder = static_cast<KMFolderCachedImap*>(node);
01115 if( !folder->uidValidity().isEmpty() ) {
01116
01117
01118 toRemove.append( folder );
01119 }
01120 }
01121 }
01122 node = mChild->next();
01123 }
01124
01125 for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
01126 kmkernel->dimapFolderMgr()->remove( doomed );
01127
01128 mAccount->displayProgress();
01129 serverSyncInternal();
01130 }
01131 }
01132
01133
01134 void KMFolderCachedImap::listDirectory2() {
01135 foldersForDeletionOnServer.clear();
01136
01137
01138 for (uint i = 0; i < mSubfolderNames.count(); i++) {
01139 KMFolderCachedImap *folder = 0;
01140
01141
01142 KMFolderNode *node;
01143 for (node = mChild->first(); node; node = mChild->next())
01144 if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
01145
01146 if (!node) {
01147
01148 QString part1 = path() + "/." + dotEscape(name()) + ".directory/."
01149 + dotEscape(mSubfolderNames[i]);
01150 QString uidCacheFile = part1 + ".uidcache";
01151 if( QFile::exists(uidCacheFile) ) {
01152
01153 unlink( QFile::encodeName( uidCacheFile ) );
01154 foldersForDeletionOnServer << mSubfolderPaths[i];
01155
01156 KIO::del( KURL( part1 + ".directory" ) );
01157 } else {
01158
01159 folder = static_cast<KMFolderCachedImap*>
01160 (mChild->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap));
01161 if (folder) {
01162 folder->close();
01163 folder->setAccount(mAccount);
01164 kmkernel->dimapFolderMgr()->contentsChanged();
01165 } else {
01166 kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl;
01167 }
01168 }
01169 } else {
01170
01171 if( node->isA("KMFolderCachedImap") )
01172 folder = static_cast<KMFolderCachedImap*>(node);
01173 }
01174
01175 if( folder && folder->imapPath().isEmpty() ) {
01176
01177
01178
01179 folder->setAccount(mAccount);
01180 folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
01181 folder->setImapPath(mSubfolderPaths[i]);
01182 }
01183 }
01184
01185 kmkernel->dimapFolderMgr()->quiet(FALSE);
01186 emit listComplete(this);
01187 serverSyncInternal();
01188 }
01189
01190
01191
01192 void KMFolderCachedImap::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds)
01193 {
01194
01195 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01196 if (it == mAccount->jobsEnd()) return;
01197
01198 QString name;
01199 KURL url;
01200 QString mimeType;
01201 for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01202 udsIt != uds.end(); udsIt++)
01203 {
01204
01205 mimeType = QString::null;
01206
01207
01208 for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01209 eIt != (*udsIt).end(); eIt++)
01210 {
01211
01212 if ((*eIt).m_uds == KIO::UDS_NAME)
01213 name = (*eIt).m_str;
01214 else if ((*eIt).m_uds == KIO::UDS_URL)
01215 url = KURL((*eIt).m_str, 106);
01216 else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01217 mimeType = (*eIt).m_str;
01218 }
01219
01220
01221
01222
01223
01224 if ((mimeType == "inode/directory" || mimeType == "message/digest"
01225 || mimeType == "message/directory")
01226 && name != ".." && (mAccount->hiddenFolders() || name.at(0) != '.'))
01227 {
01228
01229 if (mSubfolderNames.findIndex(name) == -1) {
01230 mSubfolderNames.append(name);
01231 mSubfolderPaths.append(url.path());
01232 mSubfolderMimeTypes.append(mimeType);
01233 }
01234 }
01235 }
01236 }
01237
01238 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01239 {
01240 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01241 if (it == mAccount->jobsEnd()) return;
01242 QBuffer buff((*it).data);
01243 buff.open(IO_WriteOnly | IO_Append);
01244 buff.writeBlock(data.data(), data.size());
01245 buff.close();
01246 }
01247
01248
01249 QStringList KMFolderCachedImap::makeSets(QStringList& uids, bool sort)
01250 {
01251 QValueList<ulong> tmp;
01252 for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
01253 tmp.append( (*it).toInt() );
01254 return makeSets(tmp, sort);
01255 }
01256
01257 QStringList KMFolderCachedImap::makeSets(QValueList<ulong>& uids, bool sort)
01258 {
01259 QStringList sets;
01260 QString set;
01261
01262 if (uids.size() == 1)
01263 {
01264 sets.append(QString::number(uids.first()));
01265 return sets;
01266 }
01267
01268 if (sort) qHeapSort(uids);
01269
01270 ulong last = 0;
01271
01272 bool inserted = false;
01273
01274 for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01275 {
01276 if (it == uids.begin() || set.isEmpty()) {
01277 set = QString::number(*it);
01278 inserted = true;
01279 } else
01280 {
01281 if (last+1 != *it)
01282 {
01283
01284 if (inserted)
01285 set += ',' + QString::number(*it);
01286 else
01287 set += ':' + QString::number(last) + ',' + QString::number(*it);
01288 inserted = true;
01289 if (set.length() > 100)
01290 {
01291
01292 sets.append(set);
01293 set = "";
01294 }
01295 } else {
01296 inserted = false;
01297 }
01298 }
01299 last = *it;
01300 }
01301
01302 if (!inserted)
01303 set += ':' + QString::number(uids.last());
01304
01305 if (!set.isEmpty()) sets.append(set);
01306
01307 return sets;
01308 }
01309
01310 FolderJob*
01311 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
01312 QString, const AttachmentStrategy* ) const
01313 {
01314 QPtrList<KMMessage> msgList;
01315 msgList.append( msg );
01316 CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01317 return job;
01318 }
01319
01320 FolderJob*
01321 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01322 FolderJob::JobType jt, KMFolder *folder ) const
01323 {
01324
01325 Q_UNUSED( sets );
01326 CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01327 return job;
01328 }
01329
01330 #include "kmfoldercachedimap.moc"