kmacctcachedimap.cpp
00001 00032 #ifdef HAVE_CONFIG_H 00033 #include <config.h> 00034 #endif 00035 00036 #include "kmacctcachedimap.h" 00037 using KMail::SieveConfig; 00038 00039 #include "kmfoldertree.h" 00040 #include "kmfoldermgr.h" 00041 #include "kmfiltermgr.h" 00042 #include "kmfoldercachedimap.h" 00043 #include "kmmainwin.h" 00044 #include "accountmanager.h" 00045 using KMail::AccountManager; 00046 #include "progressmanager.h" 00047 00048 #include <tdeio/passdlg.h> 00049 #include <tdeio/scheduler.h> 00050 #include <tdeio/slave.h> 00051 #include <kdebug.h> 00052 #include <kstandarddirs.h> 00053 #include <tdeapplication.h> 00054 #include <tdeconfig.h> 00055 00056 #include <tqstylesheet.h> 00057 00058 KMAcctCachedImap::KMAcctCachedImap( AccountManager* aOwner, 00059 const TQString& aAccountName, uint id ) 00060 : KMail::ImapAccountBase( aOwner, aAccountName, id ), mFolder( 0 ), 00061 mAnnotationCheckPassed(false), 00062 mGroupwareType( GroupwareKolab ), 00063 mSentCustomLoginCommand(false) 00064 { 00065 // Never EVER set this for the cached IMAP account 00066 mAutoExpunge = false; 00067 } 00068 00069 00070 //----------------------------------------------------------------------------- 00071 KMAcctCachedImap::~KMAcctCachedImap() 00072 { 00073 killAllJobsInternal( true ); 00074 } 00075 00076 00077 //----------------------------------------------------------------------------- 00078 TQString KMAcctCachedImap::type() const 00079 { 00080 return "cachedimap"; 00081 } 00082 00083 void KMAcctCachedImap::init() { 00084 ImapAccountBase::init(); 00085 } 00086 00087 //----------------------------------------------------------------------------- 00088 void KMAcctCachedImap::pseudoAssign( const KMAccount * a ) { 00089 killAllJobs( true ); 00090 if (mFolder) 00091 { 00092 mFolder->setContentState(KMFolderCachedImap::imapNoInformation); 00093 mFolder->setSubfolderState(KMFolderCachedImap::imapNoInformation); 00094 } 00095 ImapAccountBase::pseudoAssign( a ); 00096 } 00097 00098 //----------------------------------------------------------------------------- 00099 void KMAcctCachedImap::setImapFolder(KMFolderCachedImap *aFolder) 00100 { 00101 mFolder = aFolder; 00102 mFolder->setImapPath( "/" ); 00103 mFolder->setAccount( this ); 00104 } 00105 00106 00107 //----------------------------------------------------------------------------- 00108 void KMAcctCachedImap::setAutoExpunge( bool /*aAutoExpunge*/ ) 00109 { 00110 // Never EVER set this for the cached IMAP account 00111 mAutoExpunge = false; 00112 } 00113 00114 //----------------------------------------------------------------------------- 00115 void KMAcctCachedImap::killAllJobs( bool disconnectSlave ) 00116 { 00117 //kdDebug(5006) << "killAllJobs: disconnectSlave=" << disconnectSlave << " " << mapJobData.count() << " jobs in map." << endl; 00118 TQValueList<KMFolderCachedImap*> folderList = killAllJobsInternal( disconnectSlave ); 00119 for( TQValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) { 00120 KMFolderCachedImap *fld = *it; 00121 fld->resetSyncState(); 00122 fld->setContentState(KMFolderCachedImap::imapNoInformation); 00123 fld->setSubfolderState(KMFolderCachedImap::imapNoInformation); 00124 fld->sendFolderComplete(false); 00125 } 00126 } 00127 00128 //----------------------------------------------------------------------------- 00129 // Common between killAllJobs and the destructor - which shouldn't call sendFolderComplete 00130 TQValueList<KMFolderCachedImap*> KMAcctCachedImap::killAllJobsInternal( bool disconnectSlave ) 00131 { 00132 // Make list of folders to reset. This must be done last, since folderComplete 00133 // can trigger the next queued mail check already. 00134 TQValueList<KMFolderCachedImap*> folderList; 00135 TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin(); 00136 for (; it != mapJobData.end(); ++it) { 00137 if ((*it).parent) 00138 folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage()); 00139 // Kill the job - except if it's the one that already died and is calling us 00140 if ( !it.key()->error() && mSlave ) { 00141 it.key()->kill(); 00142 mSlave = 0; // killing a job, kills the slave 00143 } 00144 } 00145 mapJobData.clear(); 00146 00147 // Clear the joblist. Make SURE to stop the job emitting "finished" 00148 for( TQPtrListIterator<CachedImapJob> it( mJobList ); it.current(); ++it ) 00149 it.current()->setPassiveDestructor( true ); 00150 KMAccount::deleteFolderJobs(); 00151 00152 if ( disconnectSlave && mSlave ) { 00153 TDEIO::Scheduler::disconnectSlave( mSlave ); 00154 mSlave = 0; 00155 } 00156 return folderList; 00157 } 00158 00159 //----------------------------------------------------------------------------- 00160 void KMAcctCachedImap::cancelMailCheck() 00161 { 00162 // Make list of folders to reset, like in killAllJobs 00163 TQValueList<KMFolderCachedImap*> folderList; 00164 TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin(); 00165 for (; it != mapJobData.end(); ++it) { 00166 if ( (*it).cancellable && (*it).parent ) 00167 folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage()); 00168 } 00169 // Kill jobs 00170 ImapAccountBase::cancelMailCheck(); 00171 // Reset sync states and emit folderComplete, this is important for 00172 // KMAccount::checkingMail() to be reset, in case we restart checking mail later. 00173 for( TQValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) { 00174 KMFolderCachedImap *fld = *it; 00175 fld->resetSyncState(); 00176 fld->setContentState(KMFolderCachedImap::imapNoInformation); 00177 fld->setSubfolderState(KMFolderCachedImap::imapNoInformation); 00178 fld->sendFolderComplete(false); 00179 } 00180 } 00181 00182 // Reimplemented from ImapAccountBase because we only check one folder at a time 00183 void KMAcctCachedImap::slotCheckQueuedFolders() 00184 { 00185 mMailCheckFolders.clear(); 00186 mMailCheckFolders.append( mFoldersQueuedForChecking.front() ); 00187 mFoldersQueuedForChecking.pop_front(); 00188 if ( mFoldersQueuedForChecking.isEmpty() ) 00189 disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), 00190 this, TQT_SLOT( slotCheckQueuedFolders() ) ); 00191 00192 kmkernel->acctMgr()->singleCheckMail(this, true); 00193 mMailCheckFolders.clear(); 00194 } 00195 00196 void KMAcctCachedImap::processNewMail( bool /*interactive*/ ) 00197 { 00198 assert( mFolder ); // George says "better to crash then lose mail" 00199 00200 if ( mMailCheckFolders.isEmpty() ) 00201 processNewMail( mFolder, true ); 00202 else { 00203 KMFolder* f = mMailCheckFolders.front(); 00204 mMailCheckFolders.pop_front(); 00205 00206 // Only check mail if the folder really exists, it might have been removed by the sync in 00207 // the meantime. 00208 if ( f ) { 00209 processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), !checkingSingleFolder() ); 00210 } 00211 } 00212 } 00213 00214 void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder, 00215 bool recurse ) 00216 { 00217 assert( folder ); // George says "better to crash then lose mail" 00218 00219 // This should never be set for a cached IMAP account 00220 mAutoExpunge = false; 00221 mCountLastUnread = 0; 00222 mUnreadBeforeCheck.clear(); 00223 // stop sending noops during sync, that will keep the connection open 00224 mNoopTimer.stop(); 00225 00226 // reset namespace todo 00227 if ( folder == mFolder && !namespaces().isEmpty() ) { 00228 TQStringList nsToList = namespaces()[PersonalNS]; 00229 TQStringList otherNSToCheck = namespaces()[OtherUsersNS]; 00230 otherNSToCheck += namespaces()[SharedNS]; 00231 for ( TQStringList::Iterator it = otherNSToCheck.begin(); 00232 it != otherNSToCheck.end(); ++it ) { 00233 if ( (*it).isEmpty() ) { 00234 // empty namespaces are included in the "normal" listing 00235 // as the folders are created under the root folder 00236 nsToList += *it; 00237 } 00238 } 00239 folder->setNamespacesToList( nsToList ); 00240 } 00241 00242 Q_ASSERT( !mMailCheckProgressItem ); 00243 mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem( 00244 "MailCheck" + TQString::number( id() ), 00245 TQStyleSheet::escape( folder->label() ), // will be changed immediately in serverSync anyway 00246 TQString(), 00247 true, // can be cancelled 00248 useSSL() || useTLS() ); 00249 connect( mMailCheckProgressItem, TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ), 00250 this, TQT_SLOT( slotProgressItemCanceled( KPIM::ProgressItem* ) ) ); 00251 00252 folder->setAccount(this); 00253 connect(folder, TQT_SIGNAL(folderComplete(KMFolderCachedImap*, bool)), 00254 this, TQT_SLOT(postProcessNewMail(KMFolderCachedImap*, bool))); 00255 folder->serverSync( recurse ); 00256 } 00257 00258 void KMAcctCachedImap::postProcessNewMail( KMFolderCachedImap* folder, bool ) 00259 { 00260 mNoopTimer.start( 60000 ); // send a noop every minute to avoid "connection broken" errors 00261 disconnect(folder, TQT_SIGNAL(folderComplete(KMFolderCachedImap*, bool)), 00262 this, TQT_SLOT(postProcessNewMail(KMFolderCachedImap*, bool))); 00263 mMailCheckProgressItem->setComplete(); 00264 mMailCheckProgressItem = 0; 00265 00266 if ( folder == mFolder ) { 00267 // We remove everything from the deleted folders list after a full sync. 00268 // Even if it fails (no permission), because on the next sync we want the folder to reappear, 00269 // instead of the user being stuck with "can't delete" every time. 00270 // And we do it for _all_ deleted folders, even those that were deleted on the server in the first place (slotListResult). 00271 // Otherwise this might have side effects much later (e.g. when regaining permissions to a folder we could see before) 00272 00273 #if 0 // this opens a race: delete a folder during a sync (after the sync checked that folder), and it'll be forgotten... 00274 mDeletedFolders.clear(); 00275 #endif 00276 mPreviouslyDeletedFolders.clear(); 00277 } 00278 00279 KMail::ImapAccountBase::postProcessNewMail(); 00280 } 00281 00282 void KMAcctCachedImap::addUnreadMsgCount( const KMFolderCachedImap *folder, 00283 int countUnread ) 00284 { 00285 if ( folder->imapPath() != "/INBOX/" ) { 00286 // new mail in INBOX is processed with KMAccount::processNewMsg() and 00287 // therefore doesn't need to be counted here 00288 const TQString folderId = folder->folder()->idString(); 00289 int newInFolder = countUnread; 00290 if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() ) 00291 newInFolder -= mUnreadBeforeCheck[folderId]; 00292 if ( newInFolder > 0 ) 00293 addToNewInFolder( folderId, newInFolder ); 00294 } 00295 mCountUnread += countUnread; 00296 } 00297 00298 void KMAcctCachedImap::addLastUnreadMsgCount( const KMFolderCachedImap *folder, 00299 int countLastUnread ) 00300 { 00301 mUnreadBeforeCheck[folder->folder()->idString()] = countLastUnread; 00302 mCountLastUnread += countLastUnread; 00303 } 00304 00305 // 00306 // 00307 // read/write config 00308 // 00309 // 00310 00311 void KMAcctCachedImap::readConfig( /*const*/ TDEConfig/*Base*/ & config ) { 00312 ImapAccountBase::readConfig( config ); 00313 // Apparently this method is only ever called once (from KMKernel::init) so this is ok 00314 mPreviouslyDeletedFolders = config.readListEntry( "deleted-folders" ); 00315 mDeletedFolders.clear(); // but just in case... 00316 const TQStringList oldPaths = config.readListEntry( "renamed-folders-paths" ); 00317 const TQStringList newNames = config.readListEntry( "renamed-folders-names" ); 00318 TQStringList::const_iterator it = oldPaths.begin(); 00319 TQStringList::const_iterator nameit = newNames.begin(); 00320 for( ; it != oldPaths.end() && nameit != newNames.end(); ++it, ++nameit ) { 00321 addRenamedFolder( *it, TQString(), *nameit ); 00322 } 00323 mGroupwareType = (GroupwareType)config.readNumEntry( "groupwareType", GroupwareKolab ); 00324 } 00325 00326 void KMAcctCachedImap::writeConfig( TDEConfig/*Base*/ & config ) /*const*/ { 00327 ImapAccountBase::writeConfig( config ); 00328 config.writeEntry( "deleted-folders", mDeletedFolders + mPreviouslyDeletedFolders ); 00329 config.writeEntry( "renamed-folders-paths", mRenamedFolders.keys() ); 00330 const TQValueList<RenamedFolder> values = mRenamedFolders.values(); 00331 TQStringList lstNames; 00332 TQValueList<RenamedFolder>::const_iterator it = values.begin(); 00333 for ( ; it != values.end() ; ++it ) 00334 lstNames.append( (*it).mNewName ); 00335 config.writeEntry( "renamed-folders-names", lstNames ); 00336 config.writeEntry( "groupwareType", mGroupwareType ); 00337 } 00338 00339 void KMAcctCachedImap::invalidateIMAPFolders() 00340 { 00341 invalidateIMAPFolders( mFolder ); 00342 } 00343 00344 void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder ) 00345 { 00346 if( !folder || !folder->folder() ) 00347 return; 00348 00349 folder->setAccount(this); 00350 00351 TQStringList strList; 00352 TQValueList<TQGuardedPtr<KMFolder> > folderList; 00353 kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList, 00354 folder->folder()->child(), TQString(), 00355 false ); 00356 TQValueList<TQGuardedPtr<KMFolder> >::Iterator it; 00357 mCountLastUnread = 0; 00358 mUnreadBeforeCheck.clear(); 00359 00360 for( it = folderList.begin(); it != folderList.end(); ++it ) { 00361 KMFolder *f = *it; 00362 if( f && f->folderType() == KMFolderTypeCachedImap ) { 00363 KMFolderCachedImap *cfolder = static_cast<KMFolderCachedImap*>(f->storage()); 00364 // This invalidates the folder completely 00365 cfolder->setUidValidity("INVALID"); 00366 cfolder->writeUidCache(); 00367 } 00368 } 00369 folder->setUidValidity("INVALID"); 00370 folder->writeUidCache(); 00371 00372 processNewMailInFolder( folder->folder(), Recursive ); 00373 } 00374 00375 //----------------------------------------------------------------------------- 00376 void KMAcctCachedImap::addDeletedFolder( KMFolder* folder ) 00377 { 00378 if ( !folder || folder->folderType() != KMFolderTypeCachedImap ) 00379 return; 00380 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(folder->storage()); 00381 addDeletedFolder( storage->imapPath() ); 00382 kdDebug(5006) << k_funcinfo << storage->imapPath() << endl; 00383 00384 // Add all child folders too 00385 if( folder->child() ) { 00386 KMFolderNode *node = folder->child()->first(); 00387 while( node ) { 00388 if( !node->isDir() ) { 00389 addDeletedFolder( static_cast<KMFolder*>( node ) ); // recurse 00390 } 00391 node = folder->child()->next(); 00392 } 00393 } 00394 } 00395 00396 void KMAcctCachedImap::addDeletedFolder( const TQString& imapPath ) 00397 { 00398 mDeletedFolders << imapPath; 00399 } 00400 00401 TQStringList KMAcctCachedImap::deletedFolderPaths( const TQString& subFolderPath ) const 00402 { 00403 TQStringList lst; 00404 for ( TQStringList::const_iterator it = mDeletedFolders.begin(); it != mDeletedFolders.end(); ++it ) { 00405 if ( (*it).startsWith( subFolderPath ) ) 00406 // We must reverse the order, so that sub sub sub folders are deleted first 00407 lst.prepend( *it ); 00408 } 00409 for ( TQStringList::const_iterator it = mPreviouslyDeletedFolders.begin(); it != mPreviouslyDeletedFolders.end(); ++it ) { 00410 if ( (*it).startsWith( subFolderPath ) ) 00411 lst.prepend( *it ); 00412 } 00413 kdDebug(5006) << "KMAcctCachedImap::deletedFolderPaths for " << subFolderPath << " returning: " << lst << endl; 00414 Q_ASSERT( !lst.isEmpty() ); 00415 return lst; 00416 } 00417 00418 bool KMAcctCachedImap::isDeletedFolder( const TQString& subFolderPath ) const 00419 { 00420 return mDeletedFolders.find( subFolderPath ) != mDeletedFolders.end(); 00421 } 00422 00423 bool KMAcctCachedImap::isPreviouslyDeletedFolder( const TQString& subFolderPath ) const 00424 { 00425 return mPreviouslyDeletedFolders.find( subFolderPath ) != mPreviouslyDeletedFolders.end(); 00426 } 00427 00428 void KMAcctCachedImap::removeDeletedFolder( const TQString& subFolderPath ) 00429 { 00430 mDeletedFolders.remove( subFolderPath ); 00431 mPreviouslyDeletedFolders.remove( subFolderPath ); 00432 } 00433 00434 void KMAcctCachedImap::addRenamedFolder( const TQString& subFolderPath, const TQString& oldLabel, const TQString& newName ) 00435 { 00436 mRenamedFolders.insert( subFolderPath, RenamedFolder( oldLabel, newName ) ); 00437 } 00438 00439 void KMAcctCachedImap::removeRenamedFolder( const TQString& subFolderPath ) 00440 { 00441 mRenamedFolders.remove( subFolderPath ); 00442 } 00443 00444 void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* ) 00445 { 00446 bool abortConnection = !mSlaveConnected; 00447 killAllJobs( abortConnection ); 00448 if ( abortConnection ) { 00449 // If we were trying to connect, tell kmfoldercachedimap so that it moves on 00450 emit connectionResult( TDEIO::ERR_USER_CANCELED, TQString() ); 00451 } 00452 } 00453 00454 FolderStorage* KMAcctCachedImap::rootFolder() const 00455 { 00456 return mFolder; 00457 } 00458 00459 00460 TQString KMAcctCachedImap::renamedFolder( const TQString& imapPath ) const 00461 { 00462 TQMap<TQString, RenamedFolder>::ConstIterator renit = mRenamedFolders.find( imapPath ); 00463 if ( renit != mRenamedFolders.end() ) 00464 return (*renit).mNewName; 00465 return TQString(); 00466 } 00467 00468 #include "kmacctcachedimap.moc"