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( 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 (*it).cdata.remove(0, pos);
00987 (*it).done++;
00988 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00989 mAccount->displayProgress();
00990 }
00991 }
00992
00993 void KMFolderCachedImap::getMessagesResult( KIO::Job * job, bool lastSet )
00994 {
00995
00996 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00997 if ( it == mAccount->jobsEnd() ) {
00998 kdDebug(5006) << "could not find job!?!?!" << endl;
00999 serverSyncInternal();
01000 return;
01001 }
01002
01003 if( job->error() ) {
01004 mAccount->slotSlaveError( mAccount->slave(), job->error(),
01005 job->errorText() );
01006 mContentState = imapNoInformation;
01007 emit folderComplete(this, FALSE);
01008 } else if (lastSet) mContentState = imapFinished;
01009 mAccount->removeJob(it);
01010 if( lastSet )
01011 emit listMessagesComplete();
01012 }
01013
01014 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01015 {
01016
01017
01018
01019 emit newState( name(), mProgress + (20 * done) / total, QString::null);
01020 }
01021
01022
01023 void KMFolderCachedImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
01024 {
01025 if (flags & 4)
01026 msg->setStatus( KMMsgStatusFlag );
01027 if (flags & 2)
01028 msg->setStatus( KMMsgStatusReplied );
01029 if (flags & 1)
01030 msg->setStatus( KMMsgStatusOld );
01031
01032 if (msg->isOfUnknownStatus()) {
01033 if (newMsg)
01034 msg->setStatus( KMMsgStatusNew );
01035 else
01036 msg->setStatus( KMMsgStatusUnread );
01037 }
01038 }
01039
01040
01041 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01042 {
01043 assert( aAccount->isA("KMAcctCachedImap") );
01044 mAccount = aAccount;
01045 if( imapPath()=="/" ) aAccount->setFolder(this);
01046
01047 if( !mChild || mChild->count() == 0) return;
01048 for( KMFolderNode* node = mChild->first(); node; node = mChild->next() )
01049 if (!node->isDir())
01050 static_cast<KMFolderCachedImap*>(node)->setAccount(aAccount);
01051 }
01052
01053
01054
01055 bool KMFolderCachedImap::listDirectory()
01056 {
01057 mSubfolderState = imapInProgress;
01058 KURL url = mAccount->getUrl();
01059 url.setPath(imapPath() + ";TYPE="
01060 + (mAccount->onlySubscribedFolders() ? "LSUB" : "LIST"));
01061 KMAcctCachedImap::jobData jd( url.url(), this );
01062 mSubfolderNames.clear();
01063 mSubfolderPaths.clear();
01064 mSubfolderMimeTypes.clear();
01065
01066 if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01067 emit folderComplete( this, false );
01068 return false;
01069 }
01070
01071 KIO::SimpleJob *job = KIO::listDir(url, FALSE);
01072 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01073 mAccount->insertJob(job, jd);
01074 connect(job, SIGNAL(result(KIO::Job *)),
01075 this, SLOT(slotListResult(KIO::Job *)));
01076 connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
01077 this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
01078
01079 return TRUE;
01080 }
01081
01082 void KMFolderCachedImap::slotListResult(KIO::Job * job)
01083 {
01084 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01085 if( it == mAccount->jobsEnd() ) {
01086 kdDebug(5006) << "could not find job!?!?!" << endl;
01087 serverSyncInternal();
01088 return;
01089 }
01090
01091 if( job->error() ) {
01092 kdDebug(5006) << "listDirectory() - slotListResult: Job error\n";
01093 mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
01094 }
01095
01096 mSubfolderState = imapFinished;
01097 mAccount->removeJob(it);
01098
01099 if (!job->error()) {
01100 kmkernel->dimapFolderMgr()->quiet(TRUE);
01101 createChildFolder();
01102
01103
01104 KMFolderCachedImap *folder;
01105 KMFolderNode *node = mChild->first();
01106 QPtrList<KMFolder> toRemove;
01107 while (node) {
01108 if (!node->isDir() ) {
01109 if( mSubfolderNames.findIndex(node->name()) == -1) {
01110
01111 kdDebug(5006) << node->name() << " isn't on the server." << endl;
01112 folder = static_cast<KMFolderCachedImap*>(node);
01113 if( !folder->uidValidity().isEmpty() ) {
01114
01115
01116 toRemove.append( folder );
01117 }
01118 }
01119 }
01120 node = mChild->next();
01121 }
01122
01123 for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
01124 kmkernel->dimapFolderMgr()->remove( doomed );
01125
01126 mAccount->displayProgress();
01127 serverSyncInternal();
01128 }
01129 }
01130
01131
01132 void KMFolderCachedImap::listDirectory2() {
01133 foldersForDeletionOnServer.clear();
01134
01135
01136 for (uint i = 0; i < mSubfolderNames.count(); i++) {
01137 KMFolderCachedImap *folder = 0;
01138
01139
01140 KMFolderNode *node;
01141 for (node = mChild->first(); node; node = mChild->next())
01142 if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
01143
01144 if (!node) {
01145
01146 QString part1 = path() + "/." + dotEscape(name()) + ".directory/."
01147 + dotEscape(mSubfolderNames[i]);
01148 QString uidCacheFile = part1 + ".uidcache";
01149 if( QFile::exists(uidCacheFile) ) {
01150
01151 unlink( QFile::encodeName( uidCacheFile ) );
01152 foldersForDeletionOnServer << mSubfolderPaths[i];
01153
01154 KIO::del( part1 + ".directory" );
01155 } else {
01156
01157 folder = static_cast<KMFolderCachedImap*>
01158 (mChild->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap));
01159 if (folder) {
01160 folder->close();
01161 folder->setAccount(mAccount);
01162 kmkernel->dimapFolderMgr()->contentsChanged();
01163 } else {
01164 kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl;
01165 }
01166 }
01167 } else {
01168
01169 if( node->isA("KMFolderCachedImap") )
01170 folder = static_cast<KMFolderCachedImap*>(node);
01171 }
01172
01173 if( folder && folder->imapPath().isEmpty() ) {
01174
01175
01176
01177 folder->setAccount(mAccount);
01178 folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
01179 folder->setImapPath(mSubfolderPaths[i]);
01180 }
01181 }
01182
01183 kmkernel->dimapFolderMgr()->quiet(FALSE);
01184 emit listComplete(this);
01185 serverSyncInternal();
01186 }
01187
01188
01189
01190 void KMFolderCachedImap::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds)
01191 {
01192
01193 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01194 if (it == mAccount->jobsEnd()) return;
01195
01196 QString name;
01197 KURL url;
01198 QString mimeType;
01199 for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01200 udsIt != uds.end(); udsIt++)
01201 {
01202
01203 mimeType = QString::null;
01204
01205
01206 for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01207 eIt != (*udsIt).end(); eIt++)
01208 {
01209
01210 if ((*eIt).m_uds == KIO::UDS_NAME)
01211 name = (*eIt).m_str;
01212 else if ((*eIt).m_uds == KIO::UDS_URL)
01213 url = KURL((*eIt).m_str, 106);
01214 else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01215 mimeType = (*eIt).m_str;
01216 }
01217
01218
01219
01220
01221
01222 if ((mimeType == "inode/directory" || mimeType == "message/digest"
01223 || mimeType == "message/directory")
01224 && name != ".." && (mAccount->hiddenFolders() || name.at(0) != '.'))
01225 {
01226
01227 if (mSubfolderNames.findIndex(name) == -1) {
01228 mSubfolderNames.append(name);
01229 mSubfolderPaths.append(url.path());
01230 mSubfolderMimeTypes.append(mimeType);
01231 }
01232 }
01233 }
01234 }
01235
01236 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01237 {
01238 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01239 if (it == mAccount->jobsEnd()) return;
01240 QBuffer buff((*it).data);
01241 buff.open(IO_WriteOnly | IO_Append);
01242 buff.writeBlock(data.data(), data.size());
01243 buff.close();
01244 }
01245
01246
01247 QStringList KMFolderCachedImap::makeSets(QStringList& uids, bool sort)
01248 {
01249 QValueList<ulong> tmp;
01250 for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
01251 tmp.append( (*it).toInt() );
01252 return makeSets(tmp, sort);
01253 }
01254
01255 QStringList KMFolderCachedImap::makeSets(QValueList<ulong>& uids, bool sort)
01256 {
01257 QStringList sets;
01258 QString set;
01259
01260 if (uids.size() == 1)
01261 {
01262 sets.append(QString::number(uids.first()));
01263 return sets;
01264 }
01265
01266 if (sort) qHeapSort(uids);
01267
01268 ulong last = 0;
01269
01270 bool inserted = false;
01271
01272 for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01273 {
01274 if (it == uids.begin() || set.isEmpty()) {
01275 set = QString::number(*it);
01276 inserted = true;
01277 } else
01278 {
01279 if (last+1 != *it)
01280 {
01281
01282 if (inserted)
01283 set += ',' + QString::number(*it);
01284 else
01285 set += ':' + QString::number(last) + ',' + QString::number(*it);
01286 inserted = true;
01287 if (set.length() > 100)
01288 {
01289
01290 sets.append(set);
01291 set = "";
01292 }
01293 } else {
01294 inserted = false;
01295 }
01296 }
01297 last = *it;
01298 }
01299
01300 if (!inserted)
01301 set += ':' + QString::number(uids.last());
01302
01303 if (!set.isEmpty()) sets.append(set);
01304
01305 return sets;
01306 }
01307
01308 FolderJob*
01309 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
01310 QString, const AttachmentStrategy* ) const
01311 {
01312 QPtrList<KMMessage> msgList;
01313 msgList.append( msg );
01314 CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01315 return job;
01316 }
01317
01318 FolderJob*
01319 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01320 FolderJob::JobType jt, KMFolder *folder ) const
01321 {
01322
01323 Q_UNUSED( sets );
01324 CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01325 return job;
01326 }
01327
01328 #include "kmfoldercachedimap.moc"