00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 <klocale.h>
00033 #include <kconfig.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
00052
00053
00054
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
00066 #define IDS_SEARCH_VERSION 1000
00067
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 KConfig 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 KConfig 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
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
00172 if ( kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery( this ) ) {
00173 mRunByIndex = true;
00174 return;
00175 }
00176
00177 mFolders.append( mRoot );
00178 if ( recursive() )
00179 {
00180
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::null;
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
00226
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::null;
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<Q_UINT32>, const KMSearchPattern*, bool ) ),
00266 this,
00267 TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<Q_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<Q_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<Q_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<Q_UINT32>,
00295 const KMSearchPattern*, bool ) ),
00296 this,
00297 TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<Q_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::null;
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
00327
00328 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00329 this, TQT_SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00330 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00331 this, TQT_SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00332 connect(kmkernel->folderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00333 this, TQT_SLOT(examineChangedMessage(KMFolder*, Q_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*, Q_UINT32)),
00344 this, TQT_SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00345 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00346 this, TQT_SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00347 connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00348 this, TQT_SLOT(examineChangedMessage(KMFolder*, Q_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*, Q_UINT32)),
00359 this, TQT_SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00360 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00361 this, TQT_SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00362 connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00363 this, TQT_SLOT(examineChangedMessage(KMFolder*, Q_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();
00390 emit cleared();
00391 mInvalid = false;
00392 setDirty( true );
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;
00401 if (mSearch) {
00402 TQObject::connect(search, TQT_SIGNAL(found(Q_UINT32)),
00403 TQT_SLOT(addSerNum(Q_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();
00415
00416 if (mSearch)
00417 mSearch->start();
00418 open("foldersearch");
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(Q_UINT32 serNum)
00442 {
00443 if (mInvalid)
00444 return;
00445 int idx = -1;
00446 KMFolder *aFolder = 0;
00447 KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00448
00449
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 );
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(Q_UINT32 serNum)
00476 {
00477 TQValueVector<Q_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
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(Q_UINT32)), TQT_SLOT(addSerNum(Q_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;
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
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(Q_UINT32)), TQT_SLOT(addSerNum(Q_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;
00626 }
00627
00628 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00629 KMFolder*, TQString, const AttachmentStrategy* ) const
00630 {
00631
00632 assert(0);
00633 return 0;
00634 }
00635
00636 FolderJob* KMFolderSearch::doCreateJob(TQPtrList<KMMessage>&, const TQString&,
00637 FolderJob::JobType, KMFolder*) const
00638 {
00639
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 || (Q_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 || (Q_UINT32)idx >= mSerNums.count())
00660 return 0;
00661 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00662 if (!folder || folderIdx == -1)
00663 return 0;
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 || (Q_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
00687
00688
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 Q_UINT32 serNum = msg->getMsgSerNum();
00705 TQValueVector<Q_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
00740
00741 TQString filename = indexLocation();
00742 int old_umask = umask(077);
00743 TQString tempName = filename + ".temp";
00744 unlink(TQFile::encodeName(tempName));
00745
00746
00747
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 Q_UINT32 byteOrder = 0x12345678;
00761 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00762
00763 Q_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<Q_UINT32>::iterator it;
00771 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00772 Q_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 Q_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 Q_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 Q_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)
00861 return false;
00862 folder->open("foldersearch");
00863 mFolders.append(folder);
00864 }
00865 KMMsgBase *mb = folder->getMsgBase(folderIdx);
00866 if (!mb)
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<Q_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
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, Q_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
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*, Q_UINT32, const KMSearchPattern*, bool ) ),
00963 this,
00964 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, Q_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 Q_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*, Q_UINT32,
00987 const KMSearchPattern*, bool ) ),
00988 this,
00989 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00990 const KMSearchPattern*, bool ) ) );
00991 mFoldersCurrentlyBeingSearched.remove( folder );
00992 } else {
00993 mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00994 }
00995
00996 if ( !matches ) {
00997 TQValueVector<Q_UINT32>::const_iterator it;
00998 it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
00999 if (it != mSerNums.end()) {
01000 removeSerNum( serNum );
01001 }
01002 return;
01003 }
01004
01005
01006
01007
01008
01009 TQValueVector<Q_UINT32>::const_iterator it;
01010 it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01011 if (it == mSerNums.end()) {
01012 addSerNum( serNum );
01013 }
01014
01015 }
01016
01017 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_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, Q_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<Q_UINT32>::const_iterator it;
01046 it = qFind( 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())
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 Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01106 TQValueVector<Q_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
01115 KMFolderOpener openAFolder(aFolder, "foldersearch");
01116
01117
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*, Q_UINT32, const KMSearchPattern*, bool ) ),
01124 this,
01125 TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, Q_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
01135
01136 if ( mTempOpened && mOpenCount == 1 )
01137 {
01138 examineInvalidatedFolder( folder );
01139 }
01140 }
01141
01142 #include "kmfoldersearch.moc"