kmfoldersearch.cpp
00001 /* 00002 This file is part of KMail, the KDE mail client. 00003 Copyright (c) 2000 Don Sanders <sanders@kde.org> 00004 00005 KMail is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU General Public License, version 2, as 00007 published by the Free Software Foundation. 00008 00009 KMail is distributed in the hope that it will be useful, but 00010 WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00017 */ 00018 00019 //Factor byteswap stuff into one header file 00020 00021 #include <config.h> 00022 00023 #include "kmfoldersearch.h" 00024 #include "kmfolderimap.h" 00025 #include "kmfoldermgr.h" 00026 #include "kmsearchpattern.h" 00027 #include "kmmsgdict.h" 00028 #include "index.h" 00029 #include "jobscheduler.h" 00030 00031 #include <kdebug.h> 00032 #include <tdelocale.h> 00033 #include <tdeconfig.h> 00034 00035 #include <assert.h> 00036 #include <stdio.h> 00037 #include <unistd.h> 00038 #include <errno.h> 00039 #include <stdlib.h> 00040 #include <sys/types.h> 00041 #include <sys/stat.h> 00042 #include <sys/file.h> 00043 #include <utime.h> 00044 00045 #include <tqfile.h> 00046 00047 #ifdef HAVE_BYTESWAP_H 00048 #include <byteswap.h> 00049 #endif 00050 00051 // We define functions as kmail_swap_NN so that we don't get compile errors 00052 // on platforms where bswap_NN happens to be a function instead of a define. 00053 00054 /* Swap bytes in 32 bit value. */ 00055 #ifndef kmail_swap_32 00056 #ifdef bswap_32 00057 #define kmail_swap_32(x) bswap_32(x) 00058 #else 00059 #define kmail_swap_32(x) \ 00060 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ 00061 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 00062 #endif 00063 #endif // kmail_swap_32 00064 00065 // Current version of the .index.search files 00066 #define IDS_SEARCH_VERSION 1000 00067 // The asterisk at the end is important 00068 #define IDS_SEARCH_HEADER "# KMail-Search-IDs V%d\n*" 00069 #define IDS_SEARCH_HEADER_LEN 30 00070 00071 00072 KMSearch::KMSearch(TQObject * parent, const char * name) 00073 :TQObject(parent, name) 00074 { 00075 mRemainingFolders = -1; 00076 mRecursive = true; 00077 mRunByIndex = mRunning = false; 00078 mRoot = 0; 00079 mSearchPattern = 0; 00080 mFoundCount = 0; 00081 mSearchCount = 0; 00082 00083 mProcessNextBatchTimer = new TQTimer(0, "mProcessNextBatchTimer"); 00084 connect(mProcessNextBatchTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotProcessNextBatch())); 00085 } 00086 00087 KMSearch::~KMSearch() 00088 { 00089 delete mProcessNextBatchTimer; 00090 delete mSearchPattern; 00091 } 00092 00093 bool KMSearch::write(TQString location) const 00094 { 00095 TDEConfig config(location); 00096 config.setGroup("Search Folder"); 00097 if (mSearchPattern) 00098 mSearchPattern->writeConfig(&config); 00099 if (mRoot.isNull()) 00100 config.writeEntry("Base", ""); 00101 else 00102 config.writeEntry("Base", mRoot->idString()); 00103 config.writeEntry("Recursive", recursive()); 00104 return true; 00105 } 00106 00107 bool KMSearch::read(TQString location) 00108 { 00109 TDEConfig config( location ); 00110 config.setGroup( "Search Folder" ); 00111 if ( !mSearchPattern ) 00112 mSearchPattern = new KMSearchPattern(); 00113 mSearchPattern->readConfig( &config ); 00114 TQString rootString = config.readEntry( "Base" ); 00115 mRoot = kmkernel->findFolderById( rootString ); 00116 mRecursive = config.readBoolEntry( "Recursive" ); 00117 return true; 00118 } 00119 00120 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern) 00121 { 00122 if ( running() ) 00123 stop(); 00124 if ( mSearchPattern != searchPattern ) { 00125 delete mSearchPattern; 00126 mSearchPattern = searchPattern; 00127 } 00128 } 00129 00130 bool KMSearch::inScope(KMFolder* folder) const 00131 { 00132 if ( mRoot.isNull() || folder == mRoot ) 00133 return true; 00134 if ( !recursive() ) 00135 return false; 00136 00137 KMFolderDir *rootDir = mRoot->child(); 00138 KMFolderDir *ancestorDir = folder->parent(); 00139 while ( ancestorDir ) { 00140 if ( ancestorDir == rootDir ) 00141 return true; 00142 ancestorDir = ancestorDir->parent(); 00143 } 00144 return false; 00145 } 00146 00147 void KMSearch::start() 00148 { 00149 //close all referenced folders 00150 TQValueListIterator<TQGuardedPtr<KMFolder> > fit; 00151 for (fit = mOpenedFolders.begin(); fit != mOpenedFolders.end(); ++fit) { 00152 if (!(*fit)) 00153 continue; 00154 (*fit)->close( "kmsearch" ); 00155 } 00156 mOpenedFolders.clear(); 00157 mFolders.clear(); 00158 00159 if ( running() ) 00160 return; 00161 00162 if ( !mSearchPattern ) { 00163 emit finished(true); 00164 return; 00165 } 00166 00167 mFoundCount = 0; 00168 mSearchCount = 0; 00169 mRunning = true; 00170 mRunByIndex = false; 00171 // check if this query can be done with the index 00172 if ( kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery( this ) ) { 00173 mRunByIndex = true; 00174 return; 00175 } 00176 00177 mFolders.append( mRoot ); 00178 if ( recursive() ) 00179 { 00180 //Append all descendants to folders 00181 KMFolderNode* node; 00182 KMFolder* folder; 00183 TQValueListConstIterator<TQGuardedPtr<KMFolder> > it; 00184 for ( it = mFolders.begin(); it != mFolders.end(); ++it ) 00185 { 00186 folder = *it; 00187 KMFolderDir *dir = 0; 00188 if ( folder ) 00189 dir = folder->child(); 00190 else 00191 dir = &kmkernel->folderMgr()->dir(); 00192 if ( !dir ) 00193 continue; 00194 TQPtrListIterator<KMFolderNode> it(*dir); 00195 while ( (node = it.current()) ) { 00196 ++it; 00197 if ( !node->isDir() ) { 00198 KMFolder* kmf = dynamic_cast<KMFolder*>( node ); 00199 if ( kmf ) 00200 mFolders.append( kmf ); 00201 } 00202 } 00203 } 00204 } 00205 00206 mRemainingFolders = mFolders.count(); 00207 mLastFolder = TQString(); 00208 mProcessNextBatchTimer->start( 0, true ); 00209 } 00210 00211 void KMSearch::stop() 00212 { 00213 if ( !running() ) 00214 return; 00215 if ( mRunByIndex ) { 00216 if ( kmkernel->msgIndex() ) 00217 kmkernel->msgIndex()->stopQuery( this ); 00218 } else { 00219 mIncompleteFolders.clear(); 00220 TQValueListConstIterator<TQGuardedPtr<KMFolder> > jt; 00221 for ( jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt ) { 00222 KMFolder *folder = *jt; 00223 if ( !folder ) 00224 continue; 00225 // explicitely stop jobs for this folder as it will not be closed below 00226 // when the folder is currently selected 00227 if ( folder->folderType() == KMFolderTypeImap ) { 00228 KMAcctImap *account = 00229 static_cast<KMFolderImap*>( folder->storage() )->account(); 00230 account->ignoreJobsForFolder( folder ); 00231 } 00232 folder->storage()->search( 0 ); 00233 mSearchCount += folder->count(); 00234 folder->close("kmsearch"); 00235 } 00236 } 00237 mRemainingFolders = -1; 00238 mOpenedFolders.clear(); 00239 mFolders.clear(); 00240 mLastFolder = TQString(); 00241 mRunByIndex = mRunning = false; 00242 emit finished(false); 00243 } 00244 00245 void KMSearch::indexFinished() { 00246 mRunning = false; 00247 mRunByIndex = false; 00248 } 00249 00250 void KMSearch::slotProcessNextBatch() 00251 { 00252 if ( !running() ) 00253 return; 00254 00255 if ( mFolders.count() != 0 ) 00256 { 00257 KMFolder *folder = *( mFolders.begin() ); 00258 mFolders.erase( mFolders.begin() ); 00259 if ( folder ) 00260 { 00261 mLastFolder = folder->label(); 00262 folder->open("kmsearch"); 00263 mOpenedFolders.append( folder ); 00264 connect( folder->storage(), 00265 TQT_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ), 00266 this, 00267 TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ) ); 00268 folder->storage()->search( mSearchPattern ); 00269 } else 00270 --mRemainingFolders; 00271 mProcessNextBatchTimer->start( 0, true ); 00272 return; 00273 } 00274 } 00275 00276 void KMSearch::slotSearchFolderResult( KMFolder* folder, 00277 TQValueList<TQ_UINT32> serNums, 00278 const KMSearchPattern* pattern, 00279 bool complete ) 00280 { 00281 if ( pattern != mSearchPattern ) 00282 return; 00283 kdDebug(5006) << k_funcinfo << folder->label() << " found " << serNums.count() << endl; 00284 mLastFolder = folder->label(); 00285 TQValueListIterator<TQ_UINT32> it; 00286 for ( it = serNums.begin(); it != serNums.end(); ++it ) 00287 { 00288 emit found( *it ); 00289 ++mFoundCount; 00290 } 00291 if ( complete ) 00292 { 00293 disconnect( folder->storage(), 00294 TQT_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>, 00295 const KMSearchPattern*, bool ) ), 00296 this, 00297 TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_UINT32>, 00298 const KMSearchPattern*, bool ) ) ); 00299 --mRemainingFolders; 00300 mSearchCount += folder->count(); 00301 folder->close("kmsearch"); 00302 mOpenedFolders.remove( folder ); 00303 if ( mRemainingFolders <= 0 ) 00304 { 00305 mRemainingFolders = 0; 00306 mRunning = false; 00307 mLastFolder = TQString(); 00308 mRemainingFolders = -1; 00309 mFolders.clear(); 00310 emit finished( true ); 00311 } 00312 } 00313 } 00314 00315 //----------------------------------------------------------------------------- 00316 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name) 00317 : FolderStorage(folder, name) 00318 { 00319 mIdsStream = 0; 00320 mSearch = 0; 00321 mInvalid = false; 00322 mUnlinked = true; 00323 mTempOpened = false; 00324 setNoChildren(true); 00325 00326 //Hook up some slots for live updating of search folders 00327 //TODO: Optimize folderInvalidated, folderAdded, folderRemoved 00328 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 00329 this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32))); 00330 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)), 00331 this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32))); 00332 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)), 00333 this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int))); 00334 connect(kmkernel->folderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)), 00335 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00336 connect(kmkernel->folderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)), 00337 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00338 connect(kmkernel->folderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), 00339 this, TQT_SLOT(examineRemovedFolder(KMFolder*))); 00340 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)), 00341 this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int))); 00342 00343 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 00344 this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32))); 00345 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)), 00346 this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32))); 00347 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)), 00348 this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int))); 00349 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)), 00350 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00351 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)), 00352 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00353 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), 00354 this, TQT_SLOT(examineRemovedFolder(KMFolder*))); 00355 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)), 00356 this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int))); 00357 00358 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 00359 this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32))); 00360 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)), 00361 this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32))); 00362 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)), 00363 this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int))); 00364 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)), 00365 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00366 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)), 00367 this, TQT_SLOT(examineInvalidatedFolder(KMFolder*))); 00368 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), 00369 this, TQT_SLOT(examineRemovedFolder(KMFolder*))); 00370 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)), 00371 this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int))); 00372 00373 mExecuteSearchTimer = new TQTimer(0, "mExecuteSearchTimer"); 00374 connect(mExecuteSearchTimer, TQT_SIGNAL(timeout()), 00375 this, TQT_SLOT(executeSearch())); 00376 } 00377 00378 KMFolderSearch::~KMFolderSearch() 00379 { 00380 delete mExecuteSearchTimer; 00381 delete mSearch; 00382 mSearch = 0; 00383 if (mOpenCount > 0) 00384 close("~foldersearch", TRUE); 00385 } 00386 00387 void KMFolderSearch::setSearch(KMSearch *search) 00388 { 00389 truncateIndex(); //new search old index is obsolete 00390 emit cleared(); 00391 mInvalid = false; 00392 setDirty( true ); //have to write the index 00393 if (!mUnlinked) { 00394 unlink(TQFile::encodeName(indexLocation())); 00395 mUnlinked = true; 00396 } 00397 if (mSearch != search) { 00398 mSearch->stop(); 00399 delete mSearch; 00400 mSearch = search; // take ownership 00401 if (mSearch) { 00402 TQObject::connect(search, TQT_SIGNAL(found(TQ_UINT32)), 00403 TQT_SLOT(addSerNum(TQ_UINT32))); 00404 TQObject::connect(search, TQT_SIGNAL(finished(bool)), 00405 TQT_SLOT(searchFinished(bool))); 00406 } 00407 } 00408 if (mSearch) 00409 mSearch->write(location()); 00410 clearIndex(); 00411 mTotalMsgs = 0; 00412 mUnreadMsgs = 0; 00413 emit numUnreadMsgsChanged( folder() ); 00414 emit changed(); // really want a kmfolder cleared signal 00415 /* TODO There is KMFolder::cleared signal now. Adjust. */ 00416 if (mSearch) 00417 mSearch->start(); 00418 open("foldersearch"); // will be closed in searchFinished 00419 } 00420 00421 void KMFolderSearch::executeSearch() 00422 { 00423 if (mSearch) 00424 mSearch->stop(); 00425 setSearch(mSearch); 00426 invalidateFolder(); 00427 } 00428 00429 const KMSearch* KMFolderSearch::search() const 00430 { 00431 return mSearch; 00432 } 00433 00434 void KMFolderSearch::searchFinished(bool success) 00435 { 00436 if (!success) 00437 mSerNums.clear(); 00438 close("foldersearch"); 00439 } 00440 00441 void KMFolderSearch::addSerNum(TQ_UINT32 serNum) 00442 { 00443 if (mInvalid) // A new search is scheduled don't bother doing anything 00444 return; 00445 int idx = -1; 00446 KMFolder *aFolder = 0; 00447 KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx); 00448 // warn instead of assert() because of 00449 // https://intevation.de/roundup/kolab/issue2216 00450 if (!aFolder || (idx == -1)) { 00451 kdDebug(5006) << "Not adding message with serNum " << serNum 00452 << ": folder is " << aFolder << ", index is " << idx << endl; 00453 return; 00454 } 00455 if(mFolders.findIndex(aFolder) == -1) { 00456 aFolder->open("foldersearch"); 00457 mFolders.append(aFolder); 00458 } 00459 setDirty( true ); //TODO append a single entry to .ids file and sync. 00460 if (!mUnlinked) { 00461 unlink(TQFile::encodeName(indexLocation())); 00462 mUnlinked = true; 00463 } 00464 mSerNums.append(serNum); 00465 KMMsgBase *mb = aFolder->getMsgBase(idx); 00466 if (mb && (mb->isUnread() || mb->isNew())) { 00467 if (mUnreadMsgs == -1) 00468 mUnreadMsgs = 0; 00469 ++mUnreadMsgs; 00470 emit numUnreadMsgsChanged( folder() ); 00471 } 00472 emitMsgAddedSignals(mSerNums.count()-1); 00473 } 00474 00475 void KMFolderSearch::removeSerNum(TQ_UINT32 serNum) 00476 { 00477 TQValueVector<TQ_UINT32>::const_iterator it; 00478 int i = 0; 00479 for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i) 00480 if ((*it) == serNum) { 00481 int idx = -1; 00482 KMFolder *aFolder = 0; 00483 KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx); 00484 assert(aFolder && (idx != -1)); 00485 emit msgRemoved(folder(), serNum); 00486 removeMsg(i); 00487 return; 00488 } 00489 if (!mUnlinked) { 00490 unlink(TQFile::encodeName(indexLocation())); 00491 mUnlinked = true; 00492 } 00493 } 00494 00495 int KMFolderSearch::addMsg(KMMessage*, int* index_return) 00496 { 00497 //Not supported search folder can't own messages 00498 *index_return = -1; 00499 return 0; 00500 } 00501 00502 bool KMFolderSearch::readSearch() 00503 { 00504 mSearch = new KMSearch; 00505 TQObject::connect(mSearch, TQT_SIGNAL(found(TQ_UINT32)), TQT_SLOT(addSerNum(TQ_UINT32))); 00506 TQObject::connect(mSearch, TQT_SIGNAL(finished(bool)), TQT_SLOT(searchFinished(bool))); 00507 return mSearch->read(location()); 00508 } 00509 00510 int KMFolderSearch::open(const char *) 00511 { 00512 mOpenCount++; 00513 kmkernel->jobScheduler()->notifyOpeningFolder( folder() ); 00514 if (mOpenCount > 1) 00515 return 0; // already open 00516 00517 readConfig(); 00518 if (!mSearch && !readSearch()) 00519 return -1; 00520 00521 emit cleared(); 00522 if (!mSearch || !search()->running()) 00523 if (!readIndex()) { 00524 executeSearch(); 00525 } 00526 00527 return 0; 00528 } 00529 00530 int KMFolderSearch::canAccess() 00531 { 00532 assert(!folder()->name().isEmpty()); 00533 00534 if (access(TQFile::encodeName(location()), R_OK | W_OK | X_OK) != 0) 00535 return 1; 00536 return 0; 00537 } 00538 00539 void KMFolderSearch::sync() 00540 { 00541 if (mDirty) { 00542 if (mSearch) 00543 mSearch->write(location()); 00544 updateIndex(); 00545 } 00546 } 00547 00548 void KMFolderSearch::reallyDoClose(const char* owner) 00549 { 00550 Q_UNUSED( owner ); 00551 if (mAutoCreateIndex) { 00552 if (mSearch) 00553 mSearch->write(location()); 00554 updateIndex(); 00555 if (mSearch && search()->running()) 00556 mSearch->stop(); 00557 writeConfig(); 00558 } 00559 00560 //close all referenced folders 00561 TQValueListIterator<TQGuardedPtr<KMFolder> > fit; 00562 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) { 00563 if (!(*fit)) 00564 continue; 00565 (*fit)->close("foldersearch"); 00566 } 00567 mFolders.clear(); 00568 00569 clearIndex(TRUE); 00570 00571 if (mIdsStream) 00572 fclose(mIdsStream); 00573 00574 mOpenCount = 0; 00575 mIdsStream = 0; 00576 mUnreadMsgs = -1; 00577 } 00578 00579 int KMFolderSearch::create() 00580 { 00581 int old_umask; 00582 int rc = unlink(TQFile::encodeName(location())); 00583 if (!rc) 00584 return rc; 00585 rc = 0; 00586 00587 assert(!folder()->name().isEmpty()); 00588 assert(mOpenCount == 0); 00589 00590 kdDebug(5006) << "Creating folder " << location() << endl; 00591 if (access(TQFile::encodeName(location()), F_OK) == 0) { 00592 kdDebug(5006) << "KMFolderSearch::create call to access function failed." 00593 << endl; 00594 return EEXIST; 00595 } 00596 00597 old_umask = umask(077); 00598 FILE *mStream = fopen(TQFile::encodeName(location()), "w+"); 00599 umask(old_umask); 00600 if (!mStream) return errno; 00601 fclose(mStream); 00602 00603 clearIndex(); 00604 if (!mSearch) { 00605 mSearch = new KMSearch(); 00606 TQObject::connect(mSearch, TQT_SIGNAL(found(TQ_UINT32)), TQT_SLOT(addSerNum(TQ_UINT32))); 00607 TQObject::connect(mSearch, TQT_SIGNAL(finished(bool)), TQT_SLOT(searchFinished(bool))); 00608 } 00609 mSearch->write(location()); 00610 mOpenCount++; 00611 mChanged = false; 00612 mUnreadMsgs = 0; 00613 mTotalMsgs = 0; 00614 return rc; 00615 } 00616 00617 int KMFolderSearch::compact( bool ) 00618 { 00619 needsCompact = false; 00620 return 0; 00621 } 00622 00623 bool KMFolderSearch::isReadOnly() const 00624 { 00625 return false; //TODO: Make it true and get that working ok 00626 } 00627 00628 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType, 00629 KMFolder*, TQString, const AttachmentStrategy* ) const 00630 { 00631 // Should never be called 00632 assert(0); 00633 return 0; 00634 } 00635 00636 FolderJob* KMFolderSearch::doCreateJob(TQPtrList<KMMessage>&, const TQString&, 00637 FolderJob::JobType, KMFolder*) const 00638 { 00639 // Should never be called 00640 assert(0); 00641 return 0; 00642 } 00643 00644 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const 00645 { 00646 int folderIdx = -1; 00647 KMFolder *folder = 0; 00648 if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count()) 00649 return 0; 00650 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx); 00651 assert(folder && (folderIdx != -1)); 00652 return folder->getMsgBase(folderIdx); 00653 } 00654 00655 KMMsgBase* KMFolderSearch::getMsgBase(int idx) 00656 { 00657 int folderIdx = -1; 00658 KMFolder *folder = 0; 00659 if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count()) 00660 return 0; 00661 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx); 00662 if (!folder || folderIdx == -1) 00663 return 0; //exceptional case 00664 return folder->getMsgBase(folderIdx); 00665 } 00666 00667 //----------------------------------------------------------------------------- 00668 KMMessage* KMFolderSearch::getMsg(int idx) 00669 { 00670 int folderIdx = -1; 00671 KMFolder *folder = 0; 00672 if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count()) 00673 return 0; 00674 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx); 00675 assert(folder && (folderIdx != -1)); 00676 KMMessage* msg = folder->getMsg( folderIdx ); 00677 return msg; 00678 } 00679 00680 //------------------------------------------------------------- 00681 void 00682 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg ) 00683 { 00684 if ( !msg || msg->transferInProgress() ) 00685 return; 00686 /* While non-imap folders manage their jobs themselves, imap ones let 00687 their account manage them. Therefor first clear the jobs managed by 00688 this folder via the inherited method, then clear the imap ones. */ 00689 FolderStorage::ignoreJobsForMessage( msg ); 00690 00691 if (msg->parent()->folderType() == KMFolderTypeImap) { 00692 KMAcctImap *account = 00693 static_cast<KMFolderImap*>( msg->storage() )->account(); 00694 if( !account ) 00695 return; 00696 account->ignoreJobsForMessage( msg ); 00697 } 00698 } 00699 00700 00701 int KMFolderSearch::find(const KMMsgBase* msg) const 00702 { 00703 int pos = 0; 00704 TQ_UINT32 serNum = msg->getMsgSerNum(); 00705 TQValueVector<TQ_UINT32>::const_iterator it; 00706 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 00707 if ((*it) == serNum) 00708 return pos; 00709 ++pos; 00710 } 00711 return -1; 00712 } 00713 00714 TQString KMFolderSearch::indexLocation() const 00715 { 00716 TQString sLocation(folder()->path()); 00717 00718 if (!sLocation.isEmpty()) sLocation += '/'; 00719 sLocation += '.'; 00720 sLocation += dotEscape(fileName()); 00721 sLocation += ".index"; 00722 sLocation += ".search"; 00723 00724 return sLocation; 00725 } 00726 00727 int KMFolderSearch::updateIndex() 00728 { 00729 if (mSearch && search()->running()) 00730 unlink(TQFile::encodeName(indexLocation())); 00731 else 00732 if (dirty()) 00733 return writeIndex(); 00734 return 0; 00735 } 00736 00737 int KMFolderSearch::writeIndex( bool ) 00738 { 00739 // TODO:If we fail to write the index we should panic the kernel 00740 // TODO:and the same for other folder types too, and the msgDict. 00741 TQString filename = indexLocation(); 00742 int old_umask = umask(077); 00743 TQString tempName = filename + ".temp"; 00744 unlink(TQFile::encodeName(tempName)); 00745 00746 // We touch the folder, otherwise the index is regenerated, if KMail is 00747 // running, while the clock switches from daylight savings time to normal time 00748 utime(TQFile::encodeName(location()), 0); 00749 00750 FILE *tmpIndexStream = fopen(TQFile::encodeName(tempName), "w"); 00751 umask(old_umask); 00752 00753 if (!tmpIndexStream) { 00754 kdDebug(5006) << "Cannot write '" << filename 00755 << strerror(errno) << " (" << errno << ")" << endl; 00756 truncate(TQFile::encodeName(filename), 0); 00757 return -1; 00758 } 00759 fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION); 00760 TQ_UINT32 byteOrder = 0x12345678; 00761 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream); 00762 00763 TQ_UINT32 count = mSerNums.count(); 00764 if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) { 00765 fclose(tmpIndexStream); 00766 truncate(TQFile::encodeName(filename), 0); 00767 return -1; 00768 } 00769 00770 TQValueVector<TQ_UINT32>::iterator it; 00771 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 00772 TQ_UINT32 serNum = *it; 00773 if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream)) 00774 return -1; 00775 } 00776 if (ferror(tmpIndexStream)) return ferror(tmpIndexStream); 00777 if (fflush(tmpIndexStream) != 0) return errno; 00778 if (fsync(fileno(tmpIndexStream)) != 0) return errno; 00779 if (fclose(tmpIndexStream) != 0) return errno; 00780 00781 ::rename(TQFile::encodeName(tempName), TQFile::encodeName(indexLocation())); 00782 mDirty = FALSE; 00783 mUnlinked = FALSE; 00784 00785 return 0; 00786 } 00787 00788 DwString KMFolderSearch::getDwString(int idx) 00789 { 00790 return getMsgBase(idx)->parent()->getDwString( idx ); 00791 } 00792 00793 KMMessage* KMFolderSearch::readMsg(int idx) 00794 { 00795 int folderIdx = -1; 00796 KMFolder *folder = 0; 00797 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx); 00798 assert(folder && (folderIdx != -1)); 00799 return folder->getMsg( folderIdx ); 00800 } 00801 00802 bool KMFolderSearch::readIndex() 00803 { 00804 clearIndex(); 00805 TQString filename = indexLocation(); 00806 mIdsStream = fopen(TQFile::encodeName(filename), "r+"); 00807 if (!mIdsStream) 00808 return false; 00809 00810 int version = 0; 00811 fscanf(mIdsStream, IDS_SEARCH_HEADER, &version); 00812 if (version != IDS_SEARCH_VERSION) { 00813 fclose(mIdsStream); 00814 mIdsStream = 0; 00815 return false; 00816 } 00817 bool swapByteOrder; 00818 TQ_UINT32 byte_order; 00819 if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) { 00820 fclose(mIdsStream); 00821 mIdsStream = 0; 00822 return false; 00823 } 00824 swapByteOrder = (byte_order == 0x78563412); 00825 00826 TQ_UINT32 count; 00827 if (!fread(&count, sizeof(count), 1, mIdsStream)) { 00828 fclose(mIdsStream); 00829 mIdsStream = 0; 00830 return false; 00831 } 00832 if (swapByteOrder) 00833 count = kmail_swap_32(count); 00834 00835 mUnreadMsgs = 0; 00836 mSerNums.reserve(count); 00837 for (unsigned int index = 0; index < count; index++) { 00838 TQ_UINT32 serNum; 00839 int folderIdx = -1; 00840 KMFolder *folder = 0; 00841 bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream); 00842 if (!readOk) { 00843 clearIndex(); 00844 fclose(mIdsStream); 00845 mIdsStream = 0; 00846 return false; 00847 } 00848 if (swapByteOrder) 00849 serNum = kmail_swap_32(serNum); 00850 00851 KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx ); 00852 if (!folder || (folderIdx == -1)) { 00853 clearIndex(); 00854 fclose(mIdsStream); 00855 mIdsStream = 0; 00856 return false; 00857 } 00858 mSerNums.push_back(serNum); 00859 if(mFolders.findIndex(folder) == -1) { 00860 if (mInvalid) //exceptional case for when folder has invalid ids 00861 return false; 00862 folder->open("foldersearch"); 00863 mFolders.append(folder); 00864 } 00865 KMMsgBase *mb = folder->getMsgBase(folderIdx); 00866 if (!mb) //Exceptional case our .ids file is messed up 00867 return false; 00868 if (mb->isNew() || mb->isUnread()) { 00869 if (mUnreadMsgs == -1) ++mUnreadMsgs; 00870 ++mUnreadMsgs; 00871 } 00872 } 00873 mTotalMsgs = mSerNums.count(); 00874 fclose(mIdsStream); 00875 mIdsStream = 0; 00876 mUnlinked = true; 00877 return true; 00878 } 00879 00880 int KMFolderSearch::removeContents() 00881 { 00882 unlink(TQFile::encodeName(location())); 00883 unlink(TQFile::encodeName(indexLocation())); 00884 mUnlinked = true; 00885 return 0; 00886 } 00887 00888 int KMFolderSearch::expungeContents() 00889 { 00890 setSearch(new KMSearch()); 00891 return 0; 00892 } 00893 00894 int KMFolderSearch::count(bool cache) const 00895 { 00896 Q_UNUSED(cache); 00897 return mSerNums.count(); 00898 } 00899 00900 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx) 00901 { 00902 assert(idx >= 0 && idx < (int)mSerNums.count()); 00903 KMMsgBase *msgBase = getMsgBase(idx); 00904 TQValueVector<TQ_UINT32>::iterator it = mSerNums.begin(); 00905 mSerNums.erase(&it[idx]); 00906 return msgBase; 00907 } 00908 00909 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg) 00910 { 00911 assert(idx >= 0 && idx < (int)mSerNums.count()); 00912 Q_UNUSED( idx ); 00913 return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg); 00914 } 00915 00916 void KMFolderSearch::clearIndex(bool, bool) 00917 { 00918 //close all referenced folders 00919 TQValueListIterator<TQGuardedPtr<KMFolder> > fit; 00920 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) { 00921 if (!(*fit)) 00922 continue; 00923 (*fit)->close("foldersearch"); 00924 } 00925 mFolders.clear(); 00926 00927 mSerNums.clear(); 00928 } 00929 00930 void KMFolderSearch::truncateIndex() 00931 { 00932 truncate(TQFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN); 00933 } 00934 00935 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, TQ_UINT32 serNum) 00936 { 00937 if (!search() && !readSearch()) 00938 return; 00939 if (!search()->inScope(aFolder)) 00940 return; 00941 if (!mTempOpened) { 00942 open("foldersearch"); 00943 mTempOpened = true; 00944 } 00945 00946 if (!search()->searchPattern()) 00947 return; 00948 00949 int idx = -1; 00950 KMFolder *folder = 0; 00951 KMMsgDict::instance()->getLocation(serNum, &folder, &idx); 00952 assert(folder && (idx != -1)); 00953 assert(folder == aFolder); 00954 KMFolderOpener openFolder(folder, "foldersearch"); 00955 00956 // if we are already checking this folder, refcount 00957 if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) { 00958 unsigned int count = mFoldersCurrentlyBeingSearched[folder]; 00959 mFoldersCurrentlyBeingSearched.replace( folder, count+1 ); 00960 } else { 00961 connect( folder->storage(), 00962 TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ), 00963 this, 00964 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32, 00965 const KMSearchPattern*, bool ) ) ); 00966 mFoldersCurrentlyBeingSearched.insert( folder, 1 ); 00967 } 00968 folder->storage()->search( search()->searchPattern(), serNum ); 00969 } 00970 00971 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder, 00972 TQ_UINT32 serNum, 00973 const KMSearchPattern* pattern, 00974 bool matches ) 00975 { 00976 if ( search()->searchPattern() != pattern ) return; 00977 kdDebug(5006) << folder->label() << ": serNum " << serNum 00978 << " matches?" << matches << endl; 00979 KMFolderOpener openFolder(folder, "foldersearch"); 00980 00981 Q_ASSERT( mFoldersCurrentlyBeingSearched.contains( folder ) ); 00982 00983 unsigned int count = mFoldersCurrentlyBeingSearched[folder]; 00984 if ( count == 1 ) { 00985 disconnect( folder->storage(), 00986 TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32, 00987 const KMSearchPattern*, bool ) ), 00988 this, 00989 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32, 00990 const KMSearchPattern*, bool ) ) ); 00991 mFoldersCurrentlyBeingSearched.remove( folder ); 00992 } else { 00993 mFoldersCurrentlyBeingSearched.replace( folder, count-1 ); 00994 } 00995 00996 if ( !matches ) { 00997 TQValueVector<TQ_UINT32>::const_iterator it; 00998 it = tqFind( mSerNums.begin(), mSerNums.end(), serNum ); 00999 if (it != mSerNums.end()) { 01000 removeSerNum( serNum ); 01001 } 01002 return; 01003 } 01004 01005 // if (mSearch->running()) { 01006 // mSearch->stop(); 01007 // mExecuteSearchTimer->start( 0, true ); 01008 // } else { 01009 TQValueVector<TQ_UINT32>::const_iterator it; 01010 it = tqFind( mSerNums.begin(), mSerNums.end(), serNum ); 01011 if (it == mSerNums.end()) { 01012 addSerNum( serNum ); 01013 } 01014 // } 01015 } 01016 01017 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, TQ_UINT32 serNum) 01018 { 01019 if (!search() && !readSearch()) 01020 return; 01021 if (!search()->inScope(folder)) 01022 return; 01023 if (!mTempOpened) { 01024 open("foldersearch"); 01025 mTempOpened = true; 01026 } 01027 01028 if (mSearch->running()) { 01029 mExecuteSearchTimer->start(0, true); 01030 } else { 01031 removeSerNum(serNum); 01032 } 01033 } 01034 01035 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, TQ_UINT32 serNum, int delta) 01036 { 01037 if (!search() && !readSearch()) 01038 return; 01039 if (!search()->inScope(aFolder)) 01040 return; 01041 if (!mTempOpened) { 01042 open("foldersearch"); 01043 mTempOpened = true; 01044 } 01045 TQValueVector<TQ_UINT32>::const_iterator it; 01046 it = tqFind( mSerNums.begin(), mSerNums.end(), serNum ); 01047 if (it != mSerNums.end()) { 01048 mUnreadMsgs += delta; 01049 emit numUnreadMsgsChanged( folder() ); 01050 emit msgChanged( folder(), serNum, delta ); 01051 } 01052 } 01053 01054 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder) 01055 { 01056 if (!search() && !readSearch()) 01057 return; 01058 if (!search()->inScope(folder)) 01059 return; 01060 if (mTempOpened) { 01061 close("foldersearch"); 01062 mTempOpened = false; 01063 } 01064 01065 mInvalid = true; 01066 if (mSearch) 01067 mSearch->stop(); 01068 01069 if (!mUnlinked) { 01070 unlink(TQFile::encodeName(indexLocation())); 01071 mUnlinked = true; 01072 } 01073 01074 if (!isOpened()) //give up, until the user manually opens the folder 01075 return; 01076 01077 if (!mTempOpened) { 01078 open("foldersearch"); 01079 mTempOpened = true; 01080 } 01081 mExecuteSearchTimer->start(0, true); 01082 } 01083 01084 void KMFolderSearch::examineRemovedFolder(KMFolder *folder) 01085 { 01086 examineInvalidatedFolder(folder); 01087 if (mSearch->root() == folder) { 01088 delete mSearch; 01089 mSearch = 0; 01090 } 01091 } 01092 01093 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx) 01094 { 01095 int pos = 0; 01096 if (!search() && !readSearch()) 01097 return; 01098 if (!search()->inScope(aFolder)) 01099 return; 01100 if (!mTempOpened) { 01101 open("foldersearch"); 01102 mTempOpened = true; 01103 } 01104 01105 TQ_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx); 01106 TQValueVector<TQ_UINT32>::const_iterator it; 01107 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 01108 if ((*it) == serNum) { 01109 emit msgHeaderChanged(folder(), pos); 01110 break; 01111 } 01112 ++pos; 01113 } 01114 // let's try if the message matches our search 01115 KMFolderOpener openAFolder(aFolder, "foldersearch"); 01116 01117 // if we are already checking this folder, refcount 01118 if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) { 01119 unsigned int count = mFoldersCurrentlyBeingSearched[aFolder]; 01120 mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 ); 01121 } else { 01122 connect( aFolder->storage(), 01123 TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ), 01124 this, 01125 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32, 01126 const KMSearchPattern*, bool ) ) ); 01127 mFoldersCurrentlyBeingSearched.insert( aFolder, 1 ); 01128 } 01129 aFolder->storage()->search( search()->searchPattern(), serNum ); 01130 } 01131 01132 void KMFolderSearch::tryReleasingFolder(KMFolder* folder) 01133 { 01134 // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1. 01135 // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing 01136 if ( mTempOpened && mOpenCount == 1 ) 01137 { 01138 examineInvalidatedFolder( folder ); 01139 } 01140 } 01141 01142 #include "kmfoldersearch.moc"