kmail Library API Documentation

kmfoldercachedimap.cpp

00001 00032 #ifdef HAVE_CONFIG_H 00033 #include <config.h> 00034 #endif 00035 00036 #include <errno.h> 00037 00038 #include "kmkernel.h" 00039 #include "kmfoldercachedimap.h" 00040 #include "undostack.h" 00041 #include "kmfoldermgr.h" 00042 #include "kmmessage.h" 00043 #include "kmacctcachedimap.h" 00044 #include "kmacctmgr.h" 00045 #include "kmailicalifaceimpl.h" 00046 #include "kmfolder.h" 00047 #include "kmdict.h" 00048 #include "acljobs.h" 00049 #include "broadcaststatus.h" 00050 using KPIM::BroadcastStatus; 00051 #include "progressmanager.h" 00052 00053 using KMail::CachedImapJob; 00054 using KMail::ImapAccountBase; 00055 #include "listjob.h" 00056 using KMail::ListJob; 00057 00058 #include <kapplication.h> 00059 #include <kmessagebox.h> 00060 #include <klocale.h> 00061 #include <kdebug.h> 00062 #include <kconfig.h> 00063 #include <kio/global.h> 00064 #include <kio/scheduler.h> 00065 #include <qbuffer.h> 00066 #include <qfile.h> 00067 #include <qlabel.h> 00068 #include <qlayout.h> 00069 #include <qvaluelist.h> 00070 00071 #define UIDCACHE_VERSION 1 00072 00073 00074 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget* parent, 00075 const char* name ) 00076 : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ), 00077 Cancel | User1 | User2, Cancel, parent, name, true ), 00078 rc( Cancel ) 00079 { 00080 QFrame* page = plainPage(); 00081 QVBoxLayout *topLayout = new QVBoxLayout( page, 0 ); 00082 QString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>" 00083 "<p>If you have problems with synchronizing an IMAP " 00084 "folder, you should first try rebuilding the index " 00085 "file. This will take some time to rebuild, but will " 00086 "not cause any problems.</p><p>If that is not enough, " 00087 "you can try refreshing the IMAP cache. If you do this, " 00088 "you will loose all your local changes for this folder " 00089 "and all it's subfolders.</p>" ); 00090 topLayout->addWidget( new QLabel( txt, page ) ); 00091 enableButtonSeparator( true ); 00092 00093 setButtonText( User1, i18n( "Refresh &Cache" ) ); 00094 setButtonText( User2, i18n( "Rebuild &Index" ) ); 00095 00096 connect( this, SIGNAL( user1Clicked () ), this, SLOT( slotRebuildCache() ) ); 00097 connect( this, SIGNAL( user2Clicked () ), this, SLOT( slotRebuildIndex() ) ); 00098 } 00099 00100 int DImapTroubleShootDialog::run() 00101 { 00102 DImapTroubleShootDialog d; 00103 d.exec(); 00104 return d.rc; 00105 } 00106 00107 void DImapTroubleShootDialog::slotRebuildCache() 00108 { 00109 rc = User1; 00110 done( User1 ); 00111 } 00112 00113 void DImapTroubleShootDialog::slotRebuildIndex() 00114 { 00115 rc = User2; 00116 done( User2 ); 00117 } 00118 00119 00120 KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName ) 00121 : KMFolderMaildir( folder, aName ), 00122 mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ), 00123 mSubfolderState( imapNoInformation ), mIsSelected( false ), 00124 mCheckFlags( true ), mAccount( NULL ), uidMapDirty( true ), 00125 uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ), 00126 mUserRights( 0 ), mFolderRemoved( false ), mResync( false ), 00127 /*mHoldSyncs( false ),*/ mRecurse( true ), 00128 mContentsTypeChanged( false ), mStatusChangedLocally( false ) 00129 { 00130 setUidValidity(""); 00131 readUidCache(); 00132 00133 mProgress = 0; 00134 } 00135 00136 KMFolderCachedImap::~KMFolderCachedImap() 00137 { 00138 if( !mFolderRemoved ) { 00139 // Only write configuration when the folder haven't been deleted 00140 KConfig* config = KMKernel::config(); 00141 KConfigGroupSaver saver( config, "Folder-" + folder()->idString() ); 00142 config->writeEntry( "ImapPath", mImapPath ); 00143 config->writeEntry( "NoContent", mNoContent ); 00144 config->writeEntry( "ReadOnly", mReadOnly ); 00145 config->writeEntry( "StatusChangedLocally", mStatusChangedLocally ); 00146 00147 writeUidCache(); 00148 } 00149 00150 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() ); 00151 } 00152 00153 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent ) 00154 { 00155 setAccount( parent->account() ); 00156 // Now that we have an account, tell it that this folder was created: 00157 // if this folder was just removed, then we don't really want to remove it from the server. 00158 mAccount->removeDeletedFolder( imapPath() ); 00159 setUserRights( parent->userRights() ); 00160 } 00161 00162 void KMFolderCachedImap::readConfig() 00163 { 00164 KConfig* config = KMKernel::config(); 00165 KConfigGroupSaver saver( config, "Folder-" + folder()->idString() ); 00166 if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" ); 00167 if( QString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" ) 00168 { 00169 folder()->setLabel( i18n( "inbox" ) ); 00170 // for the icon 00171 folder()->setSystemFolder( true ); 00172 } 00173 mNoContent = config->readBoolEntry( "NoContent", false ); 00174 mReadOnly = config->readBoolEntry( "ReadOnly", false ); 00175 00176 KMFolderMaildir::readConfig(); 00177 mContentsTypeChanged = false; 00178 mStatusChangedLocally = 00179 config->readBoolEntry( "StatusChangedLocally", false ); 00180 } 00181 00182 void KMFolderCachedImap::remove() 00183 { 00184 mFolderRemoved = true; 00185 00186 QString part1 = folder()->path() + "/." + dotEscape(name()); 00187 QString uidCacheFile = part1 + ".uidcache"; 00188 // This is the account folder of an account that was just removed 00189 // When this happens, be sure to delete all traces of the cache 00190 if( QFile::exists(uidCacheFile) ) 00191 unlink( QFile::encodeName( uidCacheFile ) ); 00192 KIO::del( KURL::fromPathOrURL( part1 + ".directory" ) ); 00193 00194 // Tell the account (see listDirectory2) 00195 if (mAccount) 00196 mAccount->addDeletedFolder( imapPath() ); 00197 00198 FolderStorage::remove(); 00199 } 00200 00201 QString KMFolderCachedImap::uidCacheLocation() const 00202 { 00203 QString sLocation(folder()->path()); 00204 if (!sLocation.isEmpty()) sLocation += '/'; 00205 return sLocation + '.' + dotEscape(fileName()) + ".uidcache"; 00206 } 00207 00208 int KMFolderCachedImap::readUidCache() 00209 { 00210 QFile uidcache( uidCacheLocation() ); 00211 if( uidcache.open( IO_ReadOnly ) ) { 00212 char buf[1024]; 00213 int len = uidcache.readLine( buf, sizeof(buf) ); 00214 if( len > 0 ) { 00215 int cacheVersion; 00216 sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion ); 00217 if( cacheVersion == UIDCACHE_VERSION ) { 00218 len = uidcache.readLine( buf, sizeof(buf) ); 00219 if( len > 0 ) { 00220 setUidValidity( QString::fromLocal8Bit( buf).stripWhiteSpace() ); 00221 len = uidcache.readLine( buf, sizeof(buf) ); 00222 if( len > 0 ) { 00223 // load the last known highest uid from the on disk cache 00224 setLastUid( QString::fromLocal8Bit( buf).stripWhiteSpace().toULong() ); 00225 return 0; 00226 } 00227 } 00228 } 00229 } 00230 } 00231 return -1; 00232 } 00233 00234 int KMFolderCachedImap::writeUidCache() 00235 { 00236 if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) { 00237 // No info from the server yet, remove the file. 00238 if( QFile::exists( uidCacheLocation() ) ) 00239 unlink( QFile::encodeName( uidCacheLocation() ) ); 00240 return 0; 00241 } 00242 00243 QFile uidcache( uidCacheLocation() ); 00244 if( uidcache.open( IO_WriteOnly ) ) { 00245 QTextStream str( &uidcache ); 00246 str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl; 00247 str << uidValidity() << endl; 00248 str << lastUid() << endl; 00249 uidcache.flush(); 00250 fsync( uidcache.handle() ); /* this is probably overkill */ 00251 uidcache.close(); 00252 return 0; 00253 } else { 00254 return errno; /* does QFile set errno? */ 00255 } 00256 } 00257 00258 void KMFolderCachedImap::reloadUidMap() 00259 { 00260 uidMap.clear(); 00261 open(); 00262 for( int i = 0; i < count(); ++i ) { 00263 KMMsgBase *msg = getMsgBase( i ); 00264 if( !msg ) continue; 00265 ulong uid = msg->UID(); 00266 uidMap.insert( uid, i ); 00267 } 00268 close(); 00269 uidMapDirty = false; 00270 } 00271 00272 /* Reimplemented from KMFolderMaildir */ 00273 KMMessage* KMFolderCachedImap::take(int idx) 00274 { 00275 uidMapDirty = true; 00276 return KMFolderMaildir::take(idx); 00277 } 00278 00279 // Add a message without clearing it's X-UID field. 00280 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail, 00281 int* index_return ) 00282 { 00283 // Possible optimization: Only dirty if not filtered below 00284 ulong uid = msg->UID(); 00285 if( uid != 0 ) { 00286 uidMapDirty = true; 00287 } 00288 00289 // Add the message 00290 int rc = KMFolderMaildir::addMsg(msg, index_return); 00291 00292 if( newMail && imapPath() == "/INBOX/" ) 00293 // This is a new message. Filter it 00294 mAccount->processNewMsg( msg ); 00295 00296 return rc; 00297 } 00298 00299 /* Reimplemented from KMFolderMaildir */ 00300 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return) 00301 { 00302 // Strip the IMAP UID 00303 msg->removeHeaderField( "X-UID" ); 00304 msg->setUID( 0 ); 00305 00306 // Add it to storage 00307 return addMsgInternal( msg, false, index_return ); 00308 } 00309 00310 00311 /* Reimplemented from KMFolderMaildir */ 00312 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet) 00313 { 00314 uidMapDirty = true; 00315 // Remove it from disk 00316 KMFolderMaildir::removeMsg(idx,imapQuiet); 00317 } 00318 00319 bool KMFolderCachedImap::canRemoveFolder() const { 00320 // If this has subfolders it can't be removed 00321 if( folder() && folder()->child() && folder()->child()->count() > 0 ) 00322 return false; 00323 00324 #if 0 00325 // No special condition here, so let base class decide 00326 return KMFolderMaildir::canRemoveFolder(); 00327 #endif 00328 return true; 00329 } 00330 00331 /* Reimplemented from KMFolderDir */ 00332 int KMFolderCachedImap::rename( const QString& aName, 00333 KMFolderDir* /*aParent*/ ) 00334 { 00335 if ( aName == name() ) 00336 // Stupid user trying to rename it to it's old name :) 00337 return 0; 00338 00339 if( account() == 0 || imapPath().isEmpty() ) { // I don't think any of this can happen anymore 00340 QString err = i18n("You must synchronize with the server before renaming IMAP folders."); 00341 KMessageBox::error( 0, err ); 00342 return -1; 00343 } 00344 00345 // Make the change appear to the user with setLabel, but we'll do the change 00346 // on the server during the next sync. 00347 mAccount->addRenamedFolder( imapPath(), folder()->label(), aName ); 00348 folder()->setLabel( aName ); 00349 00350 return 0; 00351 } 00352 00353 KMFolder* KMFolderCachedImap::trashFolder() const 00354 { 00355 QString trashStr = account()->trash(); 00356 return kmkernel->dimapFolderMgr()->findIdString( trashStr ); 00357 } 00358 00359 void KMFolderCachedImap::setLastUid( ulong uid ) 00360 { 00361 mLastUid = uid; 00362 if( uidWriteTimer == -1 ) 00363 // Write in one minute 00364 uidWriteTimer = startTimer( 60000 ); 00365 } 00366 00367 void KMFolderCachedImap::timerEvent( QTimerEvent* ) 00368 { 00369 killTimer( uidWriteTimer ); 00370 uidWriteTimer = -1; 00371 writeUidCache(); 00372 } 00373 00374 ulong KMFolderCachedImap::lastUid() 00375 { 00376 return mLastUid; 00377 } 00378 00379 KMMsgBase* KMFolderCachedImap::findByUID( ulong uid ) 00380 { 00381 bool mapReloaded = false; 00382 if( uidMapDirty ) { 00383 reloadUidMap(); 00384 mapReloaded = true; 00385 } 00386 00387 QMap<ulong,int>::Iterator it = uidMap.find( uid ); 00388 if( it != uidMap.end() ) { 00389 KMMsgBase *msg = getMsgBase( *it ); 00390 if( msg && msg->UID() == uid ) 00391 return msg; 00392 } 00393 // Not found by now 00394 if( mapReloaded ) 00395 // Not here then 00396 return 0; 00397 // There could be a problem in the maps. Rebuild them and try again 00398 reloadUidMap(); 00399 it = uidMap.find( uid ); 00400 if( it != uidMap.end() ) 00401 // Since the uid map is just rebuilt, no need for the sanity check 00402 return getMsg( *it ); 00403 // Then it's not here 00404 return 0; 00405 } 00406 00407 // This finds and sets the proper account for this folder if it has 00408 // not been done 00409 KMAcctCachedImap *KMFolderCachedImap::account() const 00410 { 00411 if( (KMAcctCachedImap *)mAccount == 0 ) { 00412 // Find the account 00413 mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) ); 00414 } 00415 00416 return mAccount; 00417 } 00418 00419 void KMFolderCachedImap::slotTroubleshoot() 00420 { 00421 const int rc = DImapTroubleShootDialog::run(); 00422 00423 if( rc == KDialogBase::User1 ) { 00424 // Refresh cache 00425 if( !account() ) { 00426 KMessageBox::sorry( 0, i18n("No account setup for this folder.\n" 00427 "Please try running a sync before this.") ); 00428 return; 00429 } 00430 QString str = i18n("Are you sure you want to refresh the IMAP cache of " 00431 "the folder %1 and all it's subfolders?\nThis will " 00432 "remove all changes you have done locally to your " 00433 "folders").arg( label() ); 00434 QString s1 = i18n("Refresh IMAP Cache"); 00435 QString s2 = i18n("&Refresh"); 00436 if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) == 00437 KMessageBox::Continue ) 00438 account()->invalidateIMAPFolders( this ); 00439 } else if( rc == KDialogBase::User2 ) { 00440 // Rebuild index file 00441 createIndexFromContents(); 00442 KMessageBox::information( 0, i18n( "The index of this folder has been " 00443 "recreated." ) ); 00444 } 00445 } 00446 00447 void KMFolderCachedImap::serverSync( bool recurse ) 00448 { 00449 if( mSyncState != SYNC_STATE_INITIAL ) { 00450 if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ) ) == KMessageBox::Yes ) { 00451 mSyncState = SYNC_STATE_INITIAL; 00452 } else return; 00453 } 00454 00455 mRecurse = recurse; 00456 assert( account() ); 00457 00458 mAccount->mailCheckProgressItem()->reset(); 00459 mAccount->mailCheckProgressItem()->setTotalItems( 100 ); 00460 mProgress = 0; 00461 00462 #if 0 00463 if( mHoldSyncs ) { 00464 // All done for this folder. 00465 account()->mailCheckProgressItem()->setProgress( 100 ); 00466 mProgress = 100; // all done 00467 newState( mProgress, i18n("Synchronization skipped")); 00468 mSyncState = SYNC_STATE_INITIAL; 00469 emit folderComplete( this, true ); 00470 return; 00471 } 00472 #endif 00473 mTentativeHighestUid = 0; // reset, last sync could have been canceled 00474 00475 mResync = false; 00476 serverSyncInternal(); 00477 } 00478 00479 QString KMFolderCachedImap::state2String( int state ) const 00480 { 00481 switch( state ) { 00482 case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL"; 00483 case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES"; 00484 case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS"; 00485 case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS"; 00486 case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS"; 00487 case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2"; 00488 case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS"; 00489 case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES"; 00490 case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES"; 00491 case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES"; 00492 case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES"; 00493 case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX"; 00494 case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS"; 00495 case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS"; 00496 case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS"; 00497 case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS"; 00498 case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS"; 00499 case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY"; 00500 case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER"; 00501 default: return "Unknown state"; 00502 } 00503 } 00504 00505 /* 00506 Progress calculation: each step is assigned a span. Initially the total is 100. 00507 But if we skip a step, don't increase the progress. 00508 This leaves more room for the step a with variable size (get_messages) 00509 connecting 5 00510 getuserrights 5 00511 rename 5 00512 check_uidvalidity 5 00513 create_subfolders 5 00514 put_messages 10 (but it can take a very long time, with many messages....) 00515 upload_flags 5 00516 list_subfolders 5 00517 list_subfolders2 0 (all local) 00518 delete_subfolders 5 00519 list_messages 10 00520 delete_messages 10 00521 expunge_messages 5 00522 get_messages variable (remaining-5) i.e. minimum 15. 00523 set_acls 0 (rare) 00524 get_acls 5 00525 00526 noContent folders have only a few of the above steps 00527 (permissions, and all subfolder stuff), so its steps should be given more span 00528 00529 */ 00530 00531 // While the server synchronization is running, mSyncState will hold 00532 // the state that should be executed next 00533 void KMFolderCachedImap::serverSyncInternal() 00534 { 00535 // This is used to stop processing when we're about to exit 00536 // and the current job wasn't cancellable. 00537 // For user-requested abort, we'll use signalAbortRequested instead. 00538 if( kmkernel->mailCheckAborted() ) { 00539 resetSyncState(); 00540 emit folderComplete( this, false ); 00541 return; 00542 } 00543 00544 //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl; 00545 switch( mSyncState ) { 00546 case SYNC_STATE_INITIAL: 00547 { 00548 mProgress = 0; 00549 newState( mProgress, i18n("Synchronizing")); 00550 00551 open(); 00552 if ( !noContent() ) 00553 mAccount->addLastUnreadMsgCount( this, countUnread() ); 00554 00555 // Connect to the server (i.e. prepare the slave) 00556 ImapAccountBase::ConnectionState cs = mAccount->makeConnection(); 00557 if ( cs == ImapAccountBase::Error ) { 00558 // Cancelled by user, or slave can't start 00559 // kdDebug(5006) << "makeConnection said Error, aborting." << endl; 00560 // We stop here. We're already in SYNC_STATE_INITIAL for the next time. 00561 newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) ); 00562 close(); 00563 emit folderComplete(this, FALSE); 00564 break; 00565 } else if ( cs == ImapAccountBase::Connecting ) { 00566 // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl; 00567 newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) ); 00568 // We'll wait for the connectionResult signal from the account. 00569 connect( mAccount, SIGNAL( connectionResult(int, const QString&) ), 00570 this, SLOT( slotConnectionResult(int, const QString&) ) ); 00571 break; 00572 } else { 00573 // Connected 00574 // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl; 00575 mSyncState = SYNC_STATE_GET_USERRIGHTS; 00576 // Fall through to next state 00577 } 00578 } 00579 00580 case SYNC_STATE_GET_USERRIGHTS: 00581 mSyncState = SYNC_STATE_RENAME_FOLDER; 00582 00583 if( !noContent() && mAccount->hasACLSupport() ) { 00584 // Check the user's own rights. We do this every time in case they changed. 00585 newState( mProgress, i18n("Checking permissions")); 00586 connect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ), 00587 this, SLOT( slotReceivedUserRights( KMFolder* ) ) ); 00588 mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case 00589 break; 00590 } 00591 00592 case SYNC_STATE_RENAME_FOLDER: 00593 { 00594 mSyncState = SYNC_STATE_CHECK_UIDVALIDITY; 00595 // Returns the new name if the folder was renamed, empty otherwise. 00596 QString newName = mAccount->renamedFolder( imapPath() ); 00597 if ( !newName.isEmpty() ) { 00598 newState( mProgress, i18n("Renaming folder") ); 00599 CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this ); 00600 connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) ); 00601 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00602 job->start(); 00603 break; 00604 } 00605 } 00606 00607 case SYNC_STATE_CHECK_UIDVALIDITY: 00608 mSyncState = SYNC_STATE_CREATE_SUBFOLDERS; 00609 if( !noContent() ) { 00610 checkUidValidity(); 00611 break; 00612 } 00613 // Else carry on 00614 00615 case SYNC_STATE_CREATE_SUBFOLDERS: 00616 mSyncState = SYNC_STATE_PUT_MESSAGES; 00617 createNewFolders(); 00618 break; 00619 00620 case SYNC_STATE_PUT_MESSAGES: 00621 mSyncState = SYNC_STATE_UPLOAD_FLAGS; 00622 if( !noContent() ) { 00623 uploadNewMessages(); 00624 break; 00625 } 00626 // Else carry on 00627 case SYNC_STATE_UPLOAD_FLAGS: 00628 mSyncState = SYNC_STATE_LIST_SUBFOLDERS; 00629 if( !noContent() ) { 00630 // We haven't downloaded messages yet, so we need to build the map. 00631 if( uidMapDirty ) 00632 reloadUidMap(); 00633 // Upload flags, unless we know from the ACL that we're not allowed 00634 // to do that or they did not change locally 00635 if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::WriteFlags ) ) { 00636 if ( mStatusChangedLocally ) { 00637 uploadFlags(); 00638 break; 00639 } else { 00640 kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl; 00641 } 00642 } 00643 } 00644 // Else carry on 00645 case SYNC_STATE_LIST_SUBFOLDERS: 00646 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2; 00647 newState( mProgress, i18n("Retrieving folderlist")); 00648 if( !listDirectory() ) { 00649 mSyncState = SYNC_STATE_INITIAL; 00650 KMessageBox::error(0, i18n("Error while retrieving the folderlist")); 00651 } 00652 break; 00653 00654 case SYNC_STATE_LIST_SUBFOLDERS2: 00655 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS; 00656 mProgress += 10; 00657 newState( mProgress, i18n("Retrieving subfolders")); 00658 listDirectory2(); 00659 break; 00660 00661 case SYNC_STATE_DELETE_SUBFOLDERS: 00662 mSyncState = SYNC_STATE_LIST_MESSAGES; 00663 if( !foldersForDeletionOnServer.isEmpty() ) { 00664 newState( mProgress, i18n("Deleting folders from server")); 00665 CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer, 00666 CachedImapJob::tDeleteFolders, this ); 00667 connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) ); 00668 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00669 job->start(); 00670 break; 00671 } 00672 // Not needed, the next step emits newState very quick 00673 //newState( mProgress, i18n("No folders to delete from server")); 00674 // Carry on 00675 00676 case SYNC_STATE_LIST_MESSAGES: 00677 mSyncState = SYNC_STATE_DELETE_MESSAGES; 00678 if( !noContent() ) { 00679 newState( mProgress, i18n("Retrieving message list")); 00680 listMessages(); 00681 break; 00682 } 00683 // Else carry on 00684 00685 case SYNC_STATE_DELETE_MESSAGES: 00686 mSyncState = SYNC_STATE_EXPUNGE_MESSAGES; 00687 if( !noContent() ) { 00688 if( deleteMessages() ) { 00689 // Fine, we will continue with the next state 00690 } else { 00691 // No messages to delete, skip to GET_MESSAGES 00692 newState( mProgress, i18n("No messages to delete...")); 00693 mSyncState = SYNC_STATE_GET_MESSAGES; 00694 serverSyncInternal(); 00695 } 00696 break; 00697 } 00698 // Else carry on 00699 00700 case SYNC_STATE_EXPUNGE_MESSAGES: 00701 mSyncState = SYNC_STATE_GET_MESSAGES; 00702 if( !noContent() ) { 00703 newState( mProgress, i18n("Expunging deleted messages")); 00704 CachedImapJob *job = new CachedImapJob( QString::null, 00705 CachedImapJob::tExpungeFolder, this ); 00706 connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) ); 00707 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00708 job->start(); 00709 break; 00710 } 00711 // Else carry on 00712 00713 case SYNC_STATE_GET_MESSAGES: 00714 mSyncState = SYNC_STATE_HANDLE_INBOX; 00715 if( !noContent() ) { 00716 if( !mMsgsForDownload.isEmpty() ) { 00717 newState( mProgress, i18n("Retrieving new messages")); 00718 CachedImapJob *job = new CachedImapJob( mMsgsForDownload, 00719 CachedImapJob::tGetMessage, 00720 this ); 00721 connect( job, SIGNAL( progress(unsigned long, unsigned long) ), 00722 this, SLOT( slotProgress(unsigned long, unsigned long) ) ); 00723 connect( job, SIGNAL( finished() ), this, SLOT( slotUpdateLastUid() ) ); 00724 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00725 job->start(); 00726 mMsgsForDownload.clear(); 00727 break; 00728 } else { 00729 newState( mProgress, i18n("No new messages from server")); 00730 /* There were no messages to download, but it could be that we uploaded some 00731 which we didn't need to download again because we already knew the uid. 00732 Now that we are sure there is nothing to download, and everything that had 00733 to be deleted on the server has been deleted, adjust our local notion of the 00734 highes uid seen thus far. */ 00735 slotUpdateLastUid(); 00736 if( mLastUid == 0 && uidWriteTimer == -1 ) 00737 // This is probably a new and empty folder. Write the UID cache 00738 writeUidCache(); 00739 } 00740 } 00741 00742 // Else carry on 00743 00744 case SYNC_STATE_HANDLE_INBOX: 00745 // Wrap up the 'download emails' stage. We always end up at 95 here. 00746 mProgress = 95; 00747 00748 if( mResync ) { 00749 // Some conflict have been resolved, so restart the sync 00750 mResync = false; 00751 mSyncState = SYNC_STATE_INITIAL; 00752 serverSyncInternal(); 00753 break; 00754 } else 00755 // Continue with the ACLs 00756 mSyncState = SYNC_STATE_SET_ACLS; 00757 00758 case SYNC_STATE_SET_ACLS: 00759 mSyncState = SYNC_STATE_GET_ACLS; 00760 00761 if( !noContent() && mAccount->hasACLSupport() ) { 00762 bool hasChangedACLs = false; 00763 ACLList::ConstIterator it = mACLList.begin(); 00764 for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) { 00765 hasChangedACLs = (*it).changed; 00766 } 00767 if ( hasChangedACLs ) { 00768 newState( mProgress, i18n("Setting permissions")); 00769 KURL url = mAccount->getUrl(); 00770 url.setPath( imapPath() ); 00771 KIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList ); 00772 ImapAccountBase::jobData jd( url.url(), folder() ); 00773 mAccount->insertJob(job, jd); 00774 00775 connect(job, SIGNAL(result(KIO::Job *)), 00776 SLOT(slotMultiSetACLResult(KIO::Job *))); 00777 connect(job, SIGNAL(aclChanged( const QString&, int )), 00778 SLOT(slotACLChanged( const QString&, int )) ); 00779 break; 00780 } 00781 } 00782 00783 case SYNC_STATE_GET_ACLS: 00784 // Continue with the subfolders 00785 mSyncState = SYNC_STATE_FIND_SUBFOLDERS; 00786 00787 if( !noContent() && mAccount->hasACLSupport() ) { 00788 newState( mProgress, i18n( "Retrieving permissions" ) ); 00789 mAccount->getACL( folder(), mImapPath ); 00790 connect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), 00791 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); 00792 break; 00793 } 00794 00795 case SYNC_STATE_FIND_SUBFOLDERS: 00796 { 00797 mProgress = 98; 00798 newState( mProgress, i18n("Updating cache file")); 00799 00800 mSyncState = SYNC_STATE_SYNC_SUBFOLDERS; 00801 mSubfoldersForSync.clear(); 00802 mCurrentSubfolder = 0; 00803 if( folder() && folder()->child() ) { 00804 KMFolderNode *node = folder()->child()->first(); 00805 while( node ) { 00806 if( !node->isDir() ) { 00807 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 00808 // Only sync folders that have been accepted by the server 00809 if ( !storage->imapPath().isEmpty() 00810 // and that were not just deleted from it 00811 && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) 00812 mSubfoldersForSync << storage; 00813 } 00814 node = folder()->child()->next(); 00815 } 00816 } 00817 } 00818 00819 // All done for this folder. 00820 mProgress = 100; // all done 00821 newState( mProgress, i18n("Synchronization done")); 00822 00823 if ( !mRecurse ) // "check mail for this folder" only 00824 mSubfoldersForSync.clear(); 00825 00826 // Carry on 00827 case SYNC_STATE_SYNC_SUBFOLDERS: 00828 { 00829 if( mCurrentSubfolder ) { 00830 disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), 00831 this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); 00832 mCurrentSubfolder = 0; 00833 } 00834 00835 if( mSubfoldersForSync.isEmpty() ) { 00836 mSyncState = SYNC_STATE_INITIAL; 00837 mAccount->addUnreadMsgCount( this, countUnread() ); // before closing 00838 close(); 00839 emit folderComplete( this, TRUE ); 00840 } else { 00841 mCurrentSubfolder = mSubfoldersForSync.front(); 00842 mSubfoldersForSync.pop_front(); 00843 connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), 00844 this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); 00845 00846 // kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl; 00847 assert( !mCurrentSubfolder->imapPath().isEmpty() ); 00848 mCurrentSubfolder->setAccount( account() ); 00849 mCurrentSubfolder->serverSync( mRecurse /*which is true*/ ); 00850 } 00851 } 00852 break; 00853 00854 default: 00855 kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state " 00856 << mSyncState << endl; 00857 } 00858 } 00859 00860 /* Connected to the imap account's connectionResult signal. 00861 Emitted when the slave connected or failed to connect. 00862 */ 00863 void KMFolderCachedImap::slotConnectionResult( int errorCode, const QString& errorMsg ) 00864 { 00865 disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ), 00866 this, SLOT( slotConnectionResult(int, const QString&) ) ); 00867 if ( !errorCode ) { 00868 // Success 00869 mSyncState = SYNC_STATE_GET_USERRIGHTS; 00870 mProgress += 5; 00871 serverSyncInternal(); 00872 } else { 00873 // Error (error message already shown by the account) 00874 newState( mProgress, KIO::buildErrorString( errorCode, errorMsg )); 00875 emit folderComplete(this, FALSE); 00876 } 00877 } 00878 00879 /* find new messages (messages without a UID) */ 00880 QValueList<unsigned long> KMFolderCachedImap::findNewMessages() 00881 { 00882 QValueList<unsigned long> result; 00883 for( int i = 0; i < count(); ++i ) { 00884 KMMsgBase *msg = getMsgBase( i ); 00885 if( !msg ) continue; /* what goes on if getMsg() returns 0? */ 00886 if ( msg->UID() == 0 ) 00887 result.append( msg->getMsgSerNum() ); 00888 } 00889 return result; 00890 } 00891 00892 /* Upload new messages to server */ 00893 void KMFolderCachedImap::uploadNewMessages() 00894 { 00895 QValueList<unsigned long> newMsgs = findNewMessages(); 00896 if( !newMsgs.isEmpty() ) { 00897 00898 newState( mProgress, i18n("Uploading messages to server")); 00899 CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this ); 00900 connect( job, SIGNAL( progress( unsigned long, unsigned long) ), 00901 this, SLOT( slotPutProgress(unsigned long, unsigned long) ) ); 00902 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00903 job->start(); 00904 } else { 00905 newState( mProgress, i18n("No messages to upload to server")); 00906 00907 serverSyncInternal(); 00908 } 00909 } 00910 00911 /* Progress info during uploadNewMessages */ 00912 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total ) 00913 { 00914 // (going from mProgress to mProgress+10) 00915 int progressSpan = 10; 00916 newState( mProgress + (progressSpan * done) / total, QString::null ); 00917 if ( done == total ) // we're done 00918 mProgress += progressSpan; 00919 } 00920 00921 /* Upload message flags to server */ 00922 void KMFolderCachedImap::uploadFlags() 00923 { 00924 if ( !uidMap.isEmpty() ) { 00925 mStatusFlagsJobs = 0; 00926 newState( mProgress, i18n("Uploading status of messages to server")); 00927 00928 // FIXME DUPLICATED FROM KMFOLDERIMAP 00929 QMap< QString, QStringList > groups; 00930 //open(); //already done 00931 for( int i = 0; i < count(); ++i ) { 00932 KMMsgBase* msg = getMsgBase( i ); 00933 if( !msg || msg->UID() == 0 ) 00934 // Either not a valid message or not one that is on the server yet 00935 continue; 00936 00937 QString flags = KMFolderImap::statusToFlags(msg->status()); 00938 // Collect uids for each typem of flags. 00939 QString uid; 00940 uid.setNum( msg->UID() ); 00941 groups[flags].append(uid); 00942 } 00943 QMapIterator< QString, QStringList > dit; 00944 for( dit = groups.begin(); dit != groups.end(); ++dit ) { 00945 QCString flags = dit.key().latin1(); 00946 QStringList sets = KMFolderImap::makeSets( (*dit), true ); 00947 mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap.... 00948 // Send off a status setting job for each set. 00949 for( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) { 00950 QString imappath = imapPath() + ";UID=" + ( *slit ); 00951 mAccount->setImapStatus(folder(), imappath, flags); 00952 } 00953 } 00954 // FIXME END DUPLICATED FROM KMFOLDERIMAP 00955 00956 if ( mStatusFlagsJobs ) { 00957 connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ), 00958 this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) ); 00959 return; 00960 } 00961 } 00962 newState( mProgress, i18n("No messages to upload to server")); 00963 serverSyncInternal(); 00964 } 00965 00966 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const QString&, bool cont) 00967 { 00968 if ( folder->storage() == this ) { 00969 --mStatusFlagsJobs; 00970 if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting 00971 disconnect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ), 00972 this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) ); 00973 if ( mStatusFlagsJobs == 0 && cont ) { 00974 mProgress += 5; 00975 serverSyncInternal(); 00976 } 00977 } 00978 } 00979 00980 00981 void KMFolderCachedImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle) 00982 { 00983 KMFolderMaildir::setStatus(ids, status, toggle); 00984 // This is not perfect, what if the status didn't really change? Oh well ... 00985 mStatusChangedLocally = true; 00986 } 00987 00988 /* Upload new folders to server */ 00989 void KMFolderCachedImap::createNewFolders() 00990 { 00991 QValueList<KMFolderCachedImap*> newFolders = findNewFolders(); 00992 //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl; 00993 if( !newFolders.isEmpty() ) { 00994 newState( mProgress, i18n("Creating subfolders on server")); 00995 CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this ); 00996 connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) ); 00997 connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) ); 00998 job->start(); 00999 } else { 01000 serverSyncInternal(); 01001 } 01002 } 01003 01004 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders() 01005 { 01006 QValueList<KMFolderCachedImap*> newFolders; 01007 if( folder() && folder()->child() ) { 01008 KMFolderNode *node = folder()->child()->first(); 01009 while( node ) { 01010 if( !node->isDir() ) { 01011 if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) { 01012 kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! " 01013 << node->name() << " is not an IMAP folder\n"; 01014 node = folder()->child()->next(); 01015 assert(0); 01016 } 01017 KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01018 if( folder->imapPath().isEmpty() ) newFolders << folder; 01019 } 01020 node = folder()->child()->next(); 01021 } 01022 } 01023 return newFolders; 01024 } 01025 01026 bool KMFolderCachedImap::deleteMessages() 01027 { 01028 /* Delete messages from cache that are gone from the server */ 01029 QPtrList<KMMessage> msgsForDeletion; 01030 01031 // It is not possible to just go over all indices and remove 01032 // them one by one because the index list can get resized under 01033 // us. So use msg pointers instead 01034 01035 QMap<ulong,int>::const_iterator it = uidMap.constBegin(); 01036 for( ; it != uidMap.end(); it++ ) { 01037 ulong uid ( it.key() ); 01038 if( uid!=0 && !uidsOnServer.find( uid ) ) 01039 msgsForDeletion.append( getMsg( *it ) ); 01040 } 01041 01042 if( !msgsForDeletion.isEmpty() ) { 01043 removeMsg( msgsForDeletion ); 01044 } 01045 01046 /* Delete messages from the server that we dont have anymore */ 01047 if( !uidsForDeletionOnServer.isEmpty() ) { 01048 newState( mProgress, i18n("Deleting removed messages from server")); 01049 QStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true ); 01050 uidsForDeletionOnServer.clear(); 01051 if( sets.count() > 1 ) { 01052 // Rerun the sync until the messages are all deleted 01053 mResync = true; 01054 } 01055 //kdDebug(5006) << "Deleting " << sets.front() << " from server folder " << imapPath() << endl; 01056 CachedImapJob *job = new CachedImapJob( sets.front(), CachedImapJob::tDeleteMessage, 01057 this ); 01058 connect( job, SIGNAL( result(KMail::FolderJob *) ), 01059 this, SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) ); 01060 job->start(); 01061 return true; 01062 } else { 01063 return false; 01064 } 01065 } 01066 01067 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job ) 01068 { 01069 if ( job->error() ) { 01070 // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages 01071 mSyncState = SYNC_STATE_GET_MESSAGES; 01072 } 01073 mProgress += 10; 01074 serverSyncInternal(); 01075 } 01076 01077 void KMFolderCachedImap::checkUidValidity() { 01078 // IMAP root folders don't seem to have a UID validity setting. 01079 // Also, don't try the uid validity on new folders 01080 if( imapPath().isEmpty() || imapPath() == "/" ) 01081 // Just proceed 01082 serverSyncInternal(); 01083 else { 01084 newState( mProgress, i18n("Checking folder validity")); 01085 CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this ); 01086 connect( job, SIGNAL( result( KMail::FolderJob* ) ), 01087 this, SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) ); 01088 job->start(); 01089 } 01090 } 01091 01092 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job ) 01093 { 01094 if ( job->error() ) { // there was an error and the user chose "continue" 01095 // We can't continue doing anything in the same folder though, it would delete all mails. 01096 // But we can continue to subfolders if any. Well we can also try annotation/acl stuff... 01097 mSyncState = SYNC_STATE_HANDLE_INBOX; 01098 } 01099 mProgress += 5; 01100 serverSyncInternal(); 01101 } 01102 01103 /* This will only list the messages in a folder. 01104 No directory listing done*/ 01105 void KMFolderCachedImap::listMessages() { 01106 if( imapPath() == "/" ) { 01107 // Don't list messages on the root folder 01108 serverSyncInternal(); 01109 return; 01110 } 01111 01112 if( !mAccount->slave() ) { // sync aborted 01113 resetSyncState(); 01114 emit folderComplete( this, false ); 01115 return; 01116 } 01117 uidsOnServer.clear(); 01118 uidsOnServer.resize( count() * 2 ); 01119 uidsForDeletionOnServer.clear(); 01120 mMsgsForDownload.clear(); 01121 mUidsForDownload.clear(); 01122 01123 CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this ); 01124 connect( job, SIGNAL( result(KMail::FolderJob *) ), 01125 this, SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) ); 01126 job->start(); 01127 } 01128 01129 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job) 01130 { 01131 getMessagesResult(job, true); 01132 } 01133 01134 // Connected to the listMessages job in CachedImapJob 01135 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data) 01136 { 01137 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 01138 if ( it == mAccount->jobsEnd() ) { // Shouldn't happen 01139 kdDebug(5006) << "could not find job!?!?!" << endl; 01140 serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */ 01141 return; 01142 } 01143 (*it).cdata += QCString(data, data.size() + 1); 01144 int pos = (*it).cdata.find("\r\n--IMAPDIGEST"); 01145 if (pos > 0) { 01146 int a = (*it).cdata.find("\r\nX-uidValidity:"); 01147 if (a != -1) { 01148 int b = (*it).cdata.find("\r\n", a + 17); 01149 setUidValidity((*it).cdata.mid(a + 17, b - a - 17)); 01150 } 01151 a = (*it).cdata.find("\r\nX-Access:"); 01152 if (a != -1) { 01153 int b = (*it).cdata.find("\r\n", a + 12); 01154 QString access = (*it).cdata.mid(a + 12, b - a - 12); 01155 mReadOnly = access == "Read only"; 01156 } 01157 (*it).cdata.remove(0, pos); 01158 } 01159 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 01160 // Start with something largish when rebuilding the cache 01161 if ( uidsOnServer.size() == 0 ) 01162 uidsOnServer.resize( KMail::nextPrime( 2000 ) ); 01163 int flags; 01164 const int v = 42; 01165 while (pos >= 0) { 01166 KMMessage msg; 01167 msg.fromString((*it).cdata.mid(16, pos - 16)); 01168 flags = msg.headerField("X-Flags").toInt(); 01169 bool deleted = ( flags & 8 ); 01170 ulong uid = msg.UID(); 01171 if ( !deleted ) { 01172 if( uid != 0 ) { 01173 if ( uidsOnServer.count() == uidsOnServer.size() ) { 01174 uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) ); 01175 kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl; 01176 } 01177 uidsOnServer.insert( uid, &v ); 01178 } 01179 if ( uid <= lastUid() ) { 01180 /* 01181 * If this message UID is not present locally, then it must 01182 * have been deleted by the user, so we delete it on the 01183 * server also. 01184 * 01185 * This relies heavily on lastUid() being correct at all times. 01186 */ 01187 // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl; 01188 KMMsgBase *existingMessage = findByUID(uid); 01189 // if this is a read only folder, ignore status updates from the server 01190 // since we can't write our status back our local version is what has to 01191 // be considered correct. 01192 if( !existingMessage ) { 01193 // kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl; 01194 uidsForDeletionOnServer << uid; 01195 } else { 01196 if (!mReadOnly) { 01197 /* The message is OK, update flags */ 01198 KMFolderImap::flagsToStatus( existingMessage, flags ); 01199 } 01200 } 01201 // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl; 01202 } else { 01203 // The message is new since the last sync, but we might have just uploaded it, in which case 01204 // the uid map already contains it. 01205 if ( !uidMap.contains( uid ) ) { 01206 ulong size = msg.headerField("X-Length").toULong(); 01207 mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size); 01208 if( imapPath() == "/INBOX/" ) 01209 mUidsForDownload << uid; 01210 } 01211 // Remember the highest uid and once the download is completed, update mLastUid 01212 if ( uid > mTentativeHighestUid ) 01213 mTentativeHighestUid = uid; 01214 } 01215 } 01216 (*it).cdata.remove(0, pos); 01217 (*it).done++; 01218 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 01219 } 01220 } 01221 01222 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet ) 01223 { 01224 mProgress += 10; 01225 if( job->error() ) { // error listing messages but the user chose to continue 01226 mContentState = imapNoInformation; 01227 } else { 01228 if( lastSet ) { // always true here (this comes from online-imap...) 01229 mContentState = imapFinished; 01230 mStatusChangedLocally = false; // we are up to date again 01231 } 01232 } 01233 serverSyncInternal(); 01234 } 01235 01236 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total) 01237 { 01238 int progressSpan = 100 - 5 - mProgress; 01239 //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl; 01240 // Progress info while retrieving new emails 01241 // (going from mProgress to mProgress+progressSpan) 01242 newState( mProgress + (progressSpan * done) / total, QString::null ); 01243 } 01244 01245 01246 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount) 01247 { 01248 assert( aAccount->isA("KMAcctCachedImap") ); 01249 mAccount = aAccount; 01250 if( imapPath()=="/" ) aAccount->setFolder( folder() ); 01251 01252 // Folder was renamed in a previous session, and the user didn't sync yet 01253 QString newName = mAccount->renamedFolder( imapPath() ); 01254 if ( !newName.isEmpty() ) 01255 folder()->setLabel( newName ); 01256 01257 if( !folder() || !folder()->child() || !folder()->child()->count() ) return; 01258 for( KMFolderNode* node = folder()->child()->first(); node; 01259 node = folder()->child()->next() ) 01260 if (!node->isDir()) 01261 static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount); 01262 } 01263 01264 01265 // This lists the subfolders on the server 01266 // and (in slotListResult) takes care of folders that have been removed on the server 01267 bool KMFolderCachedImap::listDirectory(bool secondStep) 01268 { 01269 mSubfolderState = imapInProgress; 01270 if( !mAccount->slave() ) { // sync aborted 01271 resetSyncState(); 01272 emit folderComplete( this, false ); 01273 return false; 01274 } 01275 // reset 01276 if ( this == mAccount->rootFolder() ) 01277 mAccount->setHasInbox( false ); 01278 01279 // get the folders 01280 ImapAccountBase::ListType type = ImapAccountBase::List; 01281 if ( mAccount->onlySubscribedFolders() ) 01282 type = ImapAccountBase::ListSubscribed; 01283 ListJob* job = new ListJob( this, mAccount, type, secondStep, 01284 false, mAccount->hasInbox() ); 01285 connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&, 01286 const QStringList&, const QStringList&, const ImapAccountBase::jobData&)), 01287 this, SLOT(slotListResult(const QStringList&, const QStringList&, 01288 const QStringList&, const QStringList&, const ImapAccountBase::jobData&))); 01289 job->start(); 01290 01291 return true; 01292 } 01293 01294 void KMFolderCachedImap::slotListResult( const QStringList& folderNames, 01295 const QStringList& folderPaths, 01296 const QStringList& folderMimeTypes, 01297 const QStringList& folderAttributes, 01298 const ImapAccountBase::jobData& jobData ) 01299 { 01300 //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths=" << folderPaths << " mimeTypes=" << folderMimeTypes << endl; 01301 mSubfolderNames = folderNames; 01302 mSubfolderPaths = folderPaths; 01303 mSubfolderMimeTypes = folderMimeTypes; 01304 mSubfolderAttributes = folderAttributes; 01305 01306 mSubfolderState = imapFinished; 01307 bool it_inboxOnly = jobData.inboxOnly; 01308 // pass it to listDirectory2 01309 mCreateInbox = jobData.createInbox; 01310 01311 if (it_inboxOnly) { 01312 // list again only for the INBOX 01313 listDirectory(TRUE); 01314 return; 01315 } 01316 01317 if ( folder()->isSystemFolder() && mImapPath == "/INBOX/" 01318 && mAccount->prefix() == "/INBOX/" ) 01319 { 01320 // do not create folders under INBOX 01321 mCreateInbox = false; 01322 mSubfolderNames.clear(); 01323 } 01324 folder()->createChildFolder(); 01325 // Find all subfolders present on disk but not on the server 01326 KMFolderNode *node = folder()->child()->first(); 01327 QPtrList<KMFolder> toRemove; 01328 while (node) { 01329 if (!node->isDir() ) { 01330 KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01331 if ( mSubfolderNames.findIndex(node->name()) == -1 && 01332 (node->name().upper() != "INBOX" || !mCreateInbox) ) 01333 { 01334 // This subfolder isn't present on the server 01335 if( !f->imapPath().isEmpty() ) { 01336 // The folder has an imap path set, so it has been 01337 // on the server before. Delete it locally. 01338 toRemove.append( f->folder() ); 01339 kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl; 01340 } else { // shouldn't happen 01341 kdDebug(5006) << node->name() << " isn't on the server, but has no imapPath. ERROR - why didn't createNewFolders create it?" << endl; 01342 } 01343 } else { // folder both local and on server 01344 //kdDebug(5006) << node->name() << " is on the server." << endl; 01345 } 01346 } else { 01347 //kdDebug(5006) << "skipping dir node:" << node->name() << endl; 01348 } 01349 node = folder()->child()->next(); 01350 } 01351 01352 for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) 01353 kmkernel->dimapFolderMgr()->remove( doomed ); 01354 01355 mProgress += 5; 01356 serverSyncInternal(); 01357 } 01358 01359 // This synchronizes the local folders as needed (creation/deletion). No network communication here. 01360 void KMFolderCachedImap::listDirectory2() { 01361 foldersForDeletionOnServer.clear(); 01362 QString path = folder()->path(); 01363 KMFolderCachedImap *f = 0; 01364 kmkernel->dimapFolderMgr()->quiet(true); 01365 01366 if (mCreateInbox) 01367 { 01368 KMFolderNode *node; 01369 // create the INBOX 01370 for (node = folder()->child()->first(); node; node = folder()->child()->next()) 01371 if (!node->isDir() && node->name() == "INBOX") break; 01372 if (node) 01373 f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01374 else { 01375 KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap); 01376 if (newFolder) 01377 f = static_cast<KMFolderCachedImap*>(newFolder->storage()); 01378 } 01379 f->setAccount(mAccount); 01380 f->setImapPath("/INBOX/"); 01381 f->folder()->setLabel(i18n("inbox")); 01382 if (!node) { 01383 f->close(); 01384 kmkernel->dimapFolderMgr()->contentsChanged(); 01385 } 01386 // so we have an INBOX 01387 mAccount->setHasInbox( true ); 01388 } 01389 01390 // Find all subfolders present on server but not on disk 01391 for (uint i = 0; i < mSubfolderNames.count(); i++) { 01392 01393 if (mSubfolderNames[i].upper() == "INBOX" && 01394 mSubfolderPaths[i] == "/INBOX/" && 01395 mAccount->hasInbox()) // do not create an additional inbox 01396 continue; 01397 01398 // Find the subdir, if already present 01399 KMFolderNode *node; 01400 for (node = folder()->child()->first(); node; 01401 node = folder()->child()->next()) 01402 if (!node->isDir() && node->name() == mSubfolderNames[i]) break; 01403 01404 if (!node) { 01405 // This folder is not present here 01406 // Either it's new on the server, or we just deleted it. 01407 QString subfolderPath = mSubfolderPaths[i]; 01408 // The code used to look at the uidcache to know if it was "just deleted". 01409 // But this breaks with noContent folders and with shared folders. 01410 // So instead we keep a list in the account. 01411 bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath ); 01412 // That list is saved/restored across sessions, but to avoid any mistake, 01413 // ask for confirmation if the folder was deleted in a previous session 01414 // (could be that the folder was deleted & recreated meanwhile from another client...) 01415 if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) { 01416 locallyDeleted = KMessageBox::warningYesNo( 01417 0, i18n( "<qt><p>It seems that the folder <b>%1</b> was deleted. Do you want to delete it from the server?</p></qt>" ).arg( mSubfolderNames[i] ) ) == KMessageBox::Yes; 01418 } 01419 01420 if ( locallyDeleted ) { 01421 kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl; 01422 foldersForDeletionOnServer << subfolderPath; 01423 } else { 01424 kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl; 01425 KMFolder* newFolder = folder()->child()->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap); 01426 if (newFolder) 01427 f = static_cast<KMFolderCachedImap*>(newFolder->storage()); 01428 if (f) { 01429 f->close(); 01430 f->setAccount(mAccount); 01431 kmkernel->dimapFolderMgr()->contentsChanged(); 01432 } else { 01433 kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl; 01434 } 01435 } 01436 } else { // Folder found locally 01437 if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap ) 01438 f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage()); 01439 } 01440 01441 if( f ) { 01442 // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath() 01443 // << "\nSetting imapPath " << mSubfolderPaths[i] << endl; 01444 // Write folder settings 01445 f->setAccount(mAccount); 01446 f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory"); 01447 f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest"); 01448 f->setImapPath(mSubfolderPaths[i]); 01449 } 01450 } 01451 kmkernel->dimapFolderMgr()->quiet(false); 01452 emit listComplete(this); 01453 serverSyncInternal(); 01454 } 01455 01456 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success) 01457 { 01458 Q_UNUSED(sub); 01459 //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl; 01460 if ( success ) { 01461 serverSyncInternal(); 01462 } 01463 else 01464 { 01465 // success == false means the sync was aborted. 01466 if ( mCurrentSubfolder ) { 01467 Q_ASSERT( sub == mCurrentSubfolder ); 01468 disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), 01469 this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); 01470 mCurrentSubfolder = 0; 01471 } 01472 01473 mSubfoldersForSync.clear(); 01474 mSyncState = SYNC_STATE_INITIAL; 01475 close(); 01476 emit folderComplete( this, false ); 01477 } 01478 } 01479 01480 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data) 01481 { 01482 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 01483 if (it == mAccount->jobsEnd()) return; 01484 QBuffer buff((*it).data); 01485 buff.open(IO_WriteOnly | IO_Append); 01486 buff.writeBlock(data.data(), data.size()); 01487 buff.close(); 01488 } 01489 01490 01491 FolderJob* 01492 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder, 01493 QString, const AttachmentStrategy* ) const 01494 { 01495 QPtrList<KMMessage> msgList; 01496 msgList.append( msg ); 01497 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 ); 01498 job->setParentFolder( this ); 01499 return job; 01500 } 01501 01502 FolderJob* 01503 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets, 01504 FolderJob::JobType jt, KMFolder *folder ) const 01505 { 01506 //FIXME: how to handle sets here? 01507 Q_UNUSED( sets ); 01508 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 ); 01509 job->setParentFolder( this ); 01510 return job; 01511 } 01512 01513 void 01514 KMFolderCachedImap::setUserRights( unsigned int userRights ) 01515 { 01516 mUserRights = userRights; 01517 } 01518 01519 void 01520 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder ) 01521 { 01522 if ( folder->storage() == this ) { 01523 disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ), 01524 this, SLOT( slotReceivedUserRights( KMFolder* ) ) ); 01525 if ( mUserRights == 0 ) // didn't work 01526 mUserRights = -1; // error code (used in folderdia) 01527 else 01528 mReadOnly = ( mUserRights & KMail::ACLJobs::Insert ) == 0; 01529 mProgress += 5; 01530 serverSyncInternal(); 01531 } 01532 } 01533 01534 void 01535 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList ) 01536 { 01537 if ( folder->storage() == this ) { 01538 disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), 01539 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); 01540 mACLList = aclList; 01541 serverSyncInternal(); 01542 } 01543 } 01544 01545 void 01546 KMFolderCachedImap::setACLList( const ACLList& arr ) 01547 { 01548 mACLList = arr; 01549 } 01550 01551 void 01552 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job) 01553 { 01554 KMAcctCachedImap::JobIterator it = mAccount->findJob(job); 01555 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen 01556 if ( (*it).parent != folder() ) return; // Shouldn't happen 01557 01558 if ( job->error() ) 01559 // Display error but don't abort the sync just for this 01560 // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel 01561 job->showErrorDialog(); 01562 01563 if (mAccount->slave()) mAccount->removeJob(job); 01564 serverSyncInternal(); 01565 } 01566 01567 void 01568 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions ) 01569 { 01570 // The job indicates success in changing the permissions for this user 01571 // -> we note that it's been done. 01572 for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) { 01573 if ( (*it).userId == userId && (*it).permissions == permissions ) { 01574 if ( permissions == -1 ) // deleted 01575 mACLList.erase( it ); 01576 else // added/modified 01577 (*it).changed = false; 01578 return; 01579 } 01580 } 01581 } 01582 01583 // called by KMAcctCachedImap::killAllJobs 01584 void KMFolderCachedImap::resetSyncState() 01585 { 01586 mSubfoldersForSync.clear(); 01587 mSyncState = SYNC_STATE_INITIAL; 01588 close(); 01589 // Don't use newState here, it would revert to mProgress (which is < current value when listing messages) 01590 ProgressItem *progressItem = mAccount->mailCheckProgressItem(); 01591 QString str = i18n("Aborted"); 01592 if (progressItem) 01593 progressItem->setStatus( str ); 01594 emit statusMsg( str ); 01595 } 01596 01597 void KMFolderCachedImap::slotIncreaseProgress() 01598 { 01599 mProgress += 5; 01600 } 01601 01602 void KMFolderCachedImap::newState( int progress, const QString& syncStatus ) 01603 { 01604 //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl; 01605 ProgressItem *progressItem = mAccount->mailCheckProgressItem(); 01606 if( progressItem ) 01607 progressItem->setCompletedItems( progress ); 01608 if ( !syncStatus.isEmpty() ) { 01609 QString str; 01610 // For a subfolder, show the label. But for the main folder, it's already shown. 01611 if ( mAccount->imapFolder() == this ) 01612 str = syncStatus; 01613 else 01614 str = QString( "%1: %2" ).arg( label() ).arg( syncStatus ); 01615 if( progressItem ) 01616 progressItem->setStatus( str ); 01617 emit statusMsg( str ); 01618 } 01619 if( progressItem ) 01620 progressItem->updateProgress(); 01621 } 01622 01623 void KMFolderCachedImap::setSubfolderState( imapState state ) 01624 { 01625 mSubfolderState = state; 01626 if ( state == imapNoInformation && folder()->child() ) 01627 { 01628 // pass through to childs 01629 KMFolderNode* node; 01630 QPtrListIterator<KMFolderNode> it( *folder()->child() ); 01631 for ( ; (node = it.current()); ) 01632 { 01633 ++it; 01634 if (node->isDir()) continue; 01635 KMFolder *folder = static_cast<KMFolder*>(node); 01636 static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state ); 01637 } 01638 } 01639 } 01640 01641 void KMFolderCachedImap::setImapPath(const QString &path) 01642 { 01643 mImapPath = path; 01644 } 01645 01646 void KMFolderCachedImap::setContentsType( KMail::FolderContentsType type ) 01647 { 01648 if ( type != mContentsType ) { 01649 FolderStorage::setContentsType( type ); 01650 mContentsTypeChanged = true; 01651 } 01652 } 01653 01654 void KMFolderCachedImap::slotUpdateLastUid() 01655 { 01656 if( mTentativeHighestUid != 0 ) 01657 setLastUid( mTentativeHighestUid ); 01658 mTentativeHighestUid = 0; 01659 } 01660 01661 #include "kmfoldercachedimap.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 27 12:52:32 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003