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 <qfile.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(QObject * parent, const char * name)
00073 :QObject(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 QTimer();
00084 connect(mProcessNextBatchTimer, SIGNAL(timeout()), this, SLOT(slotProcessNextBatch()));
00085 }
00086
00087 KMSearch::~KMSearch()
00088 {
00089 delete mProcessNextBatchTimer;
00090 delete mSearchPattern;
00091 }
00092
00093 bool KMSearch::write(QString 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(QString location)
00108 {
00109 KConfig config( location );
00110 config.setGroup( "Search Folder" );
00111 if ( !mSearchPattern )
00112 mSearchPattern = new KMSearchPattern();
00113 mSearchPattern->readConfig( &config );
00114 QString 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 QValueListIterator<QGuardedPtr<KMFolder> > fit;
00151 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00152 if (!(*fit))
00153 continue;
00154 (*fit)->close("kmsearch");
00155 }
00156 mFolders.clear();
00157
00158 if ( running() )
00159 return;
00160
00161 if ( !mSearchPattern ) {
00162 emit finished(true);
00163 return;
00164 }
00165
00166 mFoundCount = 0;
00167 mSearchCount = 0;
00168 mRunning = true;
00169 mRunByIndex = false;
00170
00171 if ( kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery( this ) ) {
00172 mRunByIndex = true;
00173 return;
00174 }
00175
00176 mFolders.append( mRoot );
00177 if ( recursive() )
00178 {
00179
00180 KMFolderNode* node;
00181 KMFolder* folder;
00182 QValueListConstIterator<QGuardedPtr<KMFolder> > it;
00183 for ( it = mFolders.begin(); it != mFolders.end(); ++it )
00184 {
00185 folder = *it;
00186 KMFolderDir *dir = 0;
00187 if ( folder )
00188 dir = folder->child();
00189 else
00190 dir = &kmkernel->folderMgr()->dir();
00191 if ( !dir )
00192 continue;
00193 QPtrListIterator<KMFolderNode> it(*dir);
00194 while ( (node = it.current()) ) {
00195 ++it;
00196 if ( !node->isDir() ) {
00197 KMFolder* kmf = dynamic_cast<KMFolder*>( node );
00198 if ( kmf )
00199 mFolders.append( kmf );
00200 }
00201 }
00202 }
00203 }
00204
00205 mRemainingFolders = mFolders.count();
00206 mLastFolder = QString::null;
00207 mProcessNextBatchTimer->start( 0, true );
00208 }
00209
00210 void KMSearch::stop()
00211 {
00212 if ( !running() )
00213 return;
00214 if ( mRunByIndex ) {
00215 if ( kmkernel->msgIndex() )
00216 kmkernel->msgIndex()->stopQuery( this );
00217 } else {
00218 mIncompleteFolders.clear();
00219 QValueListConstIterator<QGuardedPtr<KMFolder> > jt;
00220 for ( jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt ) {
00221 KMFolder *folder = *jt;
00222 if ( !folder ) continue;
00223
00224
00225 if ( folder->folderType() == KMFolderTypeImap ) {
00226 KMAcctImap *account =
00227 static_cast<KMFolderImap*>( folder->storage() )->account();
00228 account->ignoreJobsForFolder( folder );
00229 }
00230 folder->storage()->search( 0 );
00231 mSearchCount += folder->count();
00232 folder->close("kmsearch");
00233 }
00234 }
00235 mRemainingFolders = -1;
00236 mOpenedFolders.clear();
00237 mFolders.clear();
00238 mLastFolder = QString::null;
00239 mRunByIndex = mRunning = false;
00240 emit finished(false);
00241 }
00242
00243 void KMSearch::indexFinished() {
00244 mRunning = false;
00245 mRunByIndex = false;
00246 }
00247
00248 void KMSearch::slotProcessNextBatch()
00249 {
00250 if ( !running() )
00251 return;
00252
00253 if ( mFolders.count() != 0 )
00254 {
00255 KMFolder *folder = *( mFolders.begin() );
00256 mFolders.erase( mFolders.begin() );
00257 if ( folder )
00258 {
00259 mLastFolder = folder->label();
00260 folder->open("kmsearch");
00261 mOpenedFolders.append( folder );
00262 connect( folder->storage(),
00263 SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
00264 this,
00265 SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
00266 folder->storage()->search( mSearchPattern );
00267 } else
00268 --mRemainingFolders;
00269 mProcessNextBatchTimer->start( 0, true );
00270 return;
00271 }
00272 }
00273
00274 void KMSearch::slotSearchFolderResult( KMFolder* folder,
00275 QValueList<Q_UINT32> serNums,
00276 const KMSearchPattern* pattern,
00277 bool complete )
00278 {
00279 if ( pattern != mSearchPattern ) return;
00280 kdDebug(5006) << k_funcinfo << folder->label() << " found " << serNums.count() << endl;
00281 mLastFolder = folder->label();
00282 QValueListIterator<Q_UINT32> it;
00283 for ( it = serNums.begin(); it != serNums.end(); ++it )
00284 {
00285 emit found( *it );
00286 ++mFoundCount;
00287 }
00288 if ( complete )
00289 {
00290 disconnect( folder->storage(),
00291 SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>,
00292 const KMSearchPattern*, bool ) ),
00293 this,
00294 SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>,
00295 const KMSearchPattern*, bool ) ) );
00296 --mRemainingFolders;
00297 mSearchCount += folder->count();
00298 folder->close("kmsearch");
00299 mOpenedFolders.remove( folder );
00300 if ( mRemainingFolders <= 0 )
00301 {
00302 mRemainingFolders = 0;
00303 mRunning = false;
00304 mLastFolder = QString::null;
00305 mRemainingFolders = -1;
00306 mFolders.clear();
00307 emit finished( true );
00308 }
00309 }
00310 }
00311
00312
00313 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name)
00314 : FolderStorage(folder, name)
00315 {
00316 mIdsStream = 0;
00317 mSearch = 0;
00318 mInvalid = false;
00319 mUnlinked = true;
00320 mTempOpened = false;
00321 setNoChildren(true);
00322
00323
00324
00325 connect(kmkernel->folderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00326 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00327 connect(kmkernel->folderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00328 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00329 connect(kmkernel->folderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00330 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00331 connect(kmkernel->folderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00332 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00333 connect(kmkernel->folderMgr(), SIGNAL(folderAdded(KMFolder*)),
00334 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00335 connect(kmkernel->folderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00336 this, SLOT(examineRemovedFolder(KMFolder*)));
00337 connect(kmkernel->folderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00338 this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00339
00340 connect(kmkernel->imapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00341 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00342 connect(kmkernel->imapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00343 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00344 connect(kmkernel->imapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00345 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00346 connect(kmkernel->imapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00347 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00348 connect(kmkernel->imapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00349 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00350 connect(kmkernel->imapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00351 this, SLOT(examineRemovedFolder(KMFolder*)));
00352 connect(kmkernel->imapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00353 this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00354
00355 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00356 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00357 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00358 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00359 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00360 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00361 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00362 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00363 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00364 this, SLOT(examineInvalidatedFolder(KMFolder*)));
00365 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00366 this, SLOT(examineRemovedFolder(KMFolder*)));
00367 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00368 this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00369
00370 mExecuteSearchTimer = new QTimer();
00371 connect(mExecuteSearchTimer, SIGNAL(timeout()),
00372 this, SLOT(executeSearch()));
00373 }
00374
00375 KMFolderSearch::~KMFolderSearch()
00376 {
00377 delete mExecuteSearchTimer;
00378 delete mSearch;
00379 mSearch = 0;
00380 if (mOpenCount > 0)
00381 close("~foldersearch", TRUE);
00382 }
00383
00384 void KMFolderSearch::setSearch(KMSearch *search)
00385 {
00386 truncateIndex();
00387 emit cleared();
00388 mInvalid = false;
00389 setDirty( true );
00390 if (!mUnlinked) {
00391 unlink(QFile::encodeName(indexLocation()));
00392 mUnlinked = true;
00393 }
00394 if (mSearch != search) {
00395 mSearch->stop();
00396 delete mSearch;
00397 mSearch = search;
00398 if (mSearch) {
00399 QObject::connect(search, SIGNAL(found(Q_UINT32)),
00400 SLOT(addSerNum(Q_UINT32)));
00401 QObject::connect(search, SIGNAL(finished(bool)),
00402 SLOT(searchFinished(bool)));
00403 }
00404 }
00405 if (mSearch)
00406 mSearch->write(location());
00407 clearIndex();
00408 mTotalMsgs = 0;
00409 mUnreadMsgs = 0;
00410 emit numUnreadMsgsChanged( folder() );
00411 emit changed();
00412
00413 if (mSearch)
00414 mSearch->start();
00415 open("foldersearch");
00416 }
00417
00418 void KMFolderSearch::executeSearch()
00419 {
00420 if (mSearch)
00421 mSearch->stop();
00422 setSearch(mSearch);
00423 invalidateFolder();
00424 }
00425
00426 const KMSearch* KMFolderSearch::search() const
00427 {
00428 return mSearch;
00429 }
00430
00431 void KMFolderSearch::searchFinished(bool success)
00432 {
00433 if (!success)
00434 mSerNums.clear();
00435 close("foldersearch");
00436 }
00437
00438 void KMFolderSearch::addSerNum(Q_UINT32 serNum)
00439 {
00440 if (mInvalid)
00441 return;
00442 int idx = -1;
00443 KMFolder *aFolder = 0;
00444 KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00445 assert(aFolder && (idx != -1));
00446 if(mFolders.findIndex(aFolder) == -1) {
00447 aFolder->open("foldersearch");
00448
00449 if (mInvalid)
00450 return;
00451 mFolders.append(aFolder);
00452 }
00453 setDirty( true );
00454 if (!mUnlinked) {
00455 unlink(QFile::encodeName(indexLocation()));
00456 mUnlinked = true;
00457 }
00458 mSerNums.append(serNum);
00459 KMMsgBase *mb = aFolder->getMsgBase(idx);
00460 if (mb && (mb->isUnread() || mb->isNew())) {
00461 if (mUnreadMsgs == -1)
00462 mUnreadMsgs = 0;
00463 ++mUnreadMsgs;
00464 emit numUnreadMsgsChanged( folder() );
00465 }
00466 emitMsgAddedSignals(mSerNums.count()-1);
00467 }
00468
00469 void KMFolderSearch::removeSerNum(Q_UINT32 serNum)
00470 {
00471 QValueVector<Q_UINT32>::const_iterator it;
00472 int i = 0;
00473 for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i)
00474 if ((*it) == serNum) {
00475 int idx = -1;
00476 KMFolder *aFolder = 0;
00477 KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00478 assert(aFolder && (idx != -1));
00479 emit msgRemoved(folder(), serNum);
00480 removeMsg(i);
00481 return;
00482 }
00483 if (!mUnlinked) {
00484 unlink(QFile::encodeName(indexLocation()));
00485 mUnlinked = true;
00486 }
00487 }
00488
00489 int KMFolderSearch::addMsg(KMMessage*, int* index_return)
00490 {
00491
00492 *index_return = -1;
00493 return 0;
00494 }
00495
00496 bool KMFolderSearch::readSearch()
00497 {
00498 mSearch = new KMSearch;
00499 QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00500 QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00501 return mSearch->read(location());
00502 }
00503
00504 int KMFolderSearch::open(const char *)
00505 {
00506 mOpenCount++;
00507 kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00508 if (mOpenCount > 1)
00509 return 0;
00510
00511 readConfig();
00512 if (!mSearch && !readSearch())
00513 return -1;
00514
00515 emit cleared();
00516 if (!mSearch || !search()->running())
00517 if (!readIndex()) {
00518 executeSearch();
00519 }
00520
00521 return 0;
00522 }
00523
00524 int KMFolderSearch::canAccess()
00525 {
00526 assert(!folder()->name().isEmpty());
00527
00528 if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0)
00529 return 1;
00530 return 0;
00531 }
00532
00533 void KMFolderSearch::sync()
00534 {
00535 if (mDirty) {
00536 if (mSearch)
00537 mSearch->write(location());
00538 updateIndex();
00539 }
00540 }
00541
00542 void KMFolderSearch::close(const char *, bool force)
00543 {
00544 if (mOpenCount <= 0) return;
00545 if (mOpenCount > 0) mOpenCount--;
00546 if (mOpenCount > 0 && !force) return;
00547
00548 if (mAutoCreateIndex) {
00549 if (mSearch)
00550 mSearch->write(location());
00551 updateIndex();
00552 if (mSearch && search()->running())
00553 mSearch->stop();
00554 writeConfig();
00555 }
00556
00557
00558 QValueListIterator<QGuardedPtr<KMFolder> > fit;
00559 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00560 if (!(*fit))
00561 continue;
00562 (*fit)->close("foldersearch");
00563 }
00564 mFolders.clear();
00565
00566 clearIndex(TRUE);
00567
00568 if (mIdsStream)
00569 fclose(mIdsStream);
00570
00571 mOpenCount = 0;
00572 mIdsStream = 0;
00573 mUnreadMsgs = -1;
00574 }
00575
00576 int KMFolderSearch::create()
00577 {
00578 int old_umask;
00579 int rc = unlink(QFile::encodeName(location()));
00580 if (!rc)
00581 return rc;
00582 rc = 0;
00583
00584 assert(!folder()->name().isEmpty());
00585 assert(mOpenCount == 0);
00586
00587 kdDebug(5006) << "Creating folder " << location() << endl;
00588 if (access(QFile::encodeName(location()), F_OK) == 0) {
00589 kdDebug(5006) << "KMFolderSearch::create call to access function failed."
00590 << endl;
00591 return EEXIST;
00592 }
00593
00594 old_umask = umask(077);
00595 FILE *mStream = fopen(QFile::encodeName(location()), "w+");
00596 umask(old_umask);
00597 if (!mStream) return errno;
00598 fclose(mStream);
00599
00600 clearIndex();
00601 if (!mSearch) {
00602 mSearch = new KMSearch();
00603 QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00604 QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00605 }
00606 mSearch->write(location());
00607 mOpenCount++;
00608 mChanged = false;
00609 mUnreadMsgs = 0;
00610 mTotalMsgs = 0;
00611 return rc;
00612 }
00613
00614 int KMFolderSearch::compact( bool )
00615 {
00616 needsCompact = false;
00617 return 0;
00618 }
00619
00620 bool KMFolderSearch::isReadOnly() const
00621 {
00622 return false;
00623 }
00624
00625 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00626 KMFolder*, QString, const AttachmentStrategy* ) const
00627 {
00628
00629 assert(0);
00630 return 0;
00631 }
00632
00633 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&,
00634 FolderJob::JobType, KMFolder*) const
00635 {
00636
00637 assert(0);
00638 return 0;
00639 }
00640
00641 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
00642 {
00643 int folderIdx = -1;
00644 KMFolder *folder = 0;
00645 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00646 return 0;
00647 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00648 assert(folder && (folderIdx != -1));
00649 return folder->getMsgBase(folderIdx);
00650 }
00651
00652 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
00653 {
00654 int folderIdx = -1;
00655 KMFolder *folder = 0;
00656 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00657 return 0;
00658 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00659 if (!folder || folderIdx == -1)
00660 return 0;
00661 return folder->getMsgBase(folderIdx);
00662 }
00663
00664
00665 KMMessage* KMFolderSearch::getMsg(int idx)
00666 {
00667 int folderIdx = -1;
00668 KMFolder *folder = 0;
00669 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00670 return 0;
00671 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00672 assert(folder && (folderIdx != -1));
00673 KMMessage* msg = folder->getMsg( folderIdx );
00674 return msg;
00675 }
00676
00677
00678 void
00679 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
00680 {
00681 if ( !msg || msg->transferInProgress() )
00682 return;
00683
00684
00685
00686 FolderStorage::ignoreJobsForMessage( msg );
00687
00688 if (msg->parent()->folderType() == KMFolderTypeImap) {
00689 KMAcctImap *account =
00690 static_cast<KMFolderImap*>( msg->storage() )->account();
00691 if( !account )
00692 return;
00693 account->ignoreJobsForMessage( msg );
00694 }
00695 }
00696
00697
00698 int KMFolderSearch::find(const KMMsgBase* msg) const
00699 {
00700 int pos = 0;
00701 Q_UINT32 serNum = msg->getMsgSerNum();
00702 QValueVector<Q_UINT32>::const_iterator it;
00703 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00704 if ((*it) == serNum)
00705 return pos;
00706 ++pos;
00707 }
00708 return -1;
00709 }
00710
00711 QString KMFolderSearch::indexLocation() const
00712 {
00713 QString sLocation(folder()->path());
00714
00715 if (!sLocation.isEmpty()) sLocation += '/';
00716 sLocation += '.';
00717 sLocation += dotEscape(fileName());
00718 sLocation += ".index";
00719 sLocation += ".search";
00720
00721 return sLocation;
00722 }
00723
00724 int KMFolderSearch::updateIndex()
00725 {
00726 if (mSearch && search()->running())
00727 unlink(QFile::encodeName(indexLocation()));
00728 else
00729 if (dirty())
00730 return writeIndex();
00731 return 0;
00732 }
00733
00734 int KMFolderSearch::writeIndex( bool )
00735 {
00736
00737
00738 QString filename = indexLocation();
00739 int old_umask = umask(077);
00740 QString tempName = filename + ".temp";
00741 unlink(QFile::encodeName(tempName));
00742
00743
00744
00745 utime(QFile::encodeName(location()), 0);
00746
00747 FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00748 umask(old_umask);
00749
00750 if (!tmpIndexStream) {
00751 kdDebug(5006) << "Cannot write '" << filename
00752 << strerror(errno) << " (" << errno << ")" << endl;
00753 truncate(QFile::encodeName(filename), 0);
00754 return -1;
00755 }
00756 fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION);
00757 Q_UINT32 byteOrder = 0x12345678;
00758 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00759
00760 Q_UINT32 count = mSerNums.count();
00761 if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
00762 fclose(tmpIndexStream);
00763 truncate(QFile::encodeName(filename), 0);
00764 return -1;
00765 }
00766
00767 QValueVector<Q_UINT32>::iterator it;
00768 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00769 Q_UINT32 serNum = *it;
00770 if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
00771 return -1;
00772 }
00773 if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
00774 if (fflush(tmpIndexStream) != 0) return errno;
00775 if (fsync(fileno(tmpIndexStream)) != 0) return errno;
00776 if (fclose(tmpIndexStream) != 0) return errno;
00777
00778 ::rename(QFile::encodeName(tempName), QFile::encodeName(indexLocation()));
00779 mDirty = FALSE;
00780 mUnlinked = FALSE;
00781
00782 return 0;
00783 }
00784
00785 DwString KMFolderSearch::getDwString(int idx)
00786 {
00787 return getMsgBase(idx)->parent()->getDwString( idx );
00788 }
00789
00790 KMMessage* KMFolderSearch::readMsg(int idx)
00791 {
00792 int folderIdx = -1;
00793 KMFolder *folder = 0;
00794 KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00795 assert(folder && (folderIdx != -1));
00796 return folder->getMsg( folderIdx );
00797 }
00798
00799 bool KMFolderSearch::readIndex()
00800 {
00801 clearIndex();
00802 QString filename = indexLocation();
00803 mIdsStream = fopen(QFile::encodeName(filename), "r+");
00804 if (!mIdsStream)
00805 return false;
00806
00807 int version = 0;
00808 fscanf(mIdsStream, IDS_SEARCH_HEADER, &version);
00809 if (version != IDS_SEARCH_VERSION) {
00810 fclose(mIdsStream);
00811 mIdsStream = 0;
00812 return false;
00813 }
00814 bool swapByteOrder;
00815 Q_UINT32 byte_order;
00816 if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
00817 fclose(mIdsStream);
00818 mIdsStream = 0;
00819 return false;
00820 }
00821 swapByteOrder = (byte_order == 0x78563412);
00822
00823 Q_UINT32 count;
00824 if (!fread(&count, sizeof(count), 1, mIdsStream)) {
00825 fclose(mIdsStream);
00826 mIdsStream = 0;
00827 return false;
00828 }
00829 if (swapByteOrder)
00830 count = kmail_swap_32(count);
00831
00832 mUnreadMsgs = 0;
00833 mSerNums.reserve(count);
00834 for (unsigned int index = 0; index < count; index++) {
00835 Q_UINT32 serNum;
00836 int folderIdx = -1;
00837 KMFolder *folder = 0;
00838 bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
00839 if (!readOk) {
00840 clearIndex();
00841 fclose(mIdsStream);
00842 mIdsStream = 0;
00843 return false;
00844 }
00845 if (swapByteOrder)
00846 serNum = kmail_swap_32(serNum);
00847
00848 KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx );
00849 if (!folder || (folderIdx == -1)) {
00850 clearIndex();
00851 fclose(mIdsStream);
00852 mIdsStream = 0;
00853 return false;
00854 }
00855 mSerNums.push_back(serNum);
00856 if(mFolders.findIndex(folder) == -1) {
00857 folder->open("foldersearch");
00858 if (mInvalid)
00859 return false;
00860 mFolders.append(folder);
00861 }
00862 KMMsgBase *mb = folder->getMsgBase(folderIdx);
00863 if (!mb)
00864 return false;
00865 if (mb->isNew() || mb->isUnread()) {
00866 if (mUnreadMsgs == -1) ++mUnreadMsgs;
00867 ++mUnreadMsgs;
00868 }
00869 }
00870 mTotalMsgs = mSerNums.count();
00871 fclose(mIdsStream);
00872 mIdsStream = 0;
00873 mUnlinked = true;
00874 return true;
00875 }
00876
00877 int KMFolderSearch::removeContents()
00878 {
00879 unlink(QFile::encodeName(location()));
00880 unlink(QFile::encodeName(indexLocation()));
00881 mUnlinked = true;
00882 return 0;
00883 }
00884
00885 int KMFolderSearch::expungeContents()
00886 {
00887 setSearch(new KMSearch());
00888 return 0;
00889 }
00890
00891 int KMFolderSearch::count(bool cache) const
00892 {
00893 Q_UNUSED(cache);
00894 return mSerNums.count();
00895 }
00896
00897 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
00898 {
00899 assert(idx >= 0 && idx < (int)mSerNums.count());
00900 KMMsgBase *msgBase = getMsgBase(idx);
00901 QValueVector<Q_UINT32>::iterator it = mSerNums.begin();
00902 mSerNums.erase(&it[idx]);
00903 return msgBase;
00904 }
00905
00906 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
00907 {
00908 assert(idx >= 0 && idx < (int)mSerNums.count());
00909 Q_UNUSED( idx );
00910 return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg);
00911 }
00912
00913 void KMFolderSearch::clearIndex(bool, bool)
00914 {
00915
00916 QValueListIterator<QGuardedPtr<KMFolder> > fit;
00917 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00918 if (!(*fit))
00919 continue;
00920 (*fit)->close("foldersearch");
00921 }
00922 mFolders.clear();
00923
00924 mSerNums.clear();
00925 }
00926
00927 void KMFolderSearch::truncateIndex()
00928 {
00929 truncate(QFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
00930 }
00931
00932 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum)
00933 {
00934 if (!search() && !readSearch())
00935 return;
00936 if (!search()->inScope(aFolder))
00937 return;
00938 if (!mTempOpened) {
00939 open( "foldersearch" );
00940 mTempOpened = true;
00941 }
00942
00943 if (!search()->searchPattern())
00944 return;
00945
00946 int idx = -1;
00947 KMFolder *folder = 0;
00948 KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00949 assert(folder && (idx != -1));
00950 assert(folder == aFolder);
00951 folder->open("examineAddedMessage");
00952
00953
00954 if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00955 unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00956 mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
00957 } else {
00958 connect( folder->storage(),
00959 SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
00960 this,
00961 SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00962 const KMSearchPattern*, bool ) ) );
00963 mFoldersCurrentlyBeingSearched.insert( folder, 1 );
00964 }
00965 folder->storage()->search( search()->searchPattern(), serNum );
00966 folder->close("examineAddedMessage");
00967 }
00968
00969 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder,
00970 Q_UINT32 serNum,
00971 const KMSearchPattern* pattern,
00972 bool matches )
00973 {
00974 if ( search()->searchPattern() != pattern ) return;
00975 kdDebug(5006) << folder->label() << ": serNum " << serNum
00976 << " matches?" << matches << endl;
00977 folder->open("SearchExamineMsgDone");
00978
00979 if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00980 unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00981 if ( count == 1 ) {
00982 disconnect( folder->storage(),
00983 SIGNAL( searchDone( KMFolder*, Q_UINT32,
00984 const KMSearchPattern*, bool ) ),
00985 this,
00986 SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00987 const KMSearchPattern*, bool ) ) );
00988 mFoldersCurrentlyBeingSearched.remove( folder );
00989 } else {
00990 mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00991 }
00992 } else {
00993 Q_ASSERT( 0 );
00994 }
00995 folder->close("SearchExamineMsgDone");
00996
00997 if ( !matches ) {
00998 QValueVector<Q_UINT32>::const_iterator it;
00999 it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01000 if (it != mSerNums.end()) {
01001 removeSerNum( serNum );
01002 }
01003 return;
01004 }
01005
01006
01007
01008
01009
01010 QValueVector<Q_UINT32>::const_iterator it;
01011 it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01012 if (it == mSerNums.end()) {
01013 addSerNum( serNum );
01014 }
01015
01016 }
01017
01018 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum)
01019 {
01020 if (!search() && !readSearch())
01021 return;
01022 if (!search()->inScope(folder))
01023 return;
01024 if (!mTempOpened) {
01025 open("foldersearch");
01026 mTempOpened = true;
01027 }
01028
01029 if (mSearch->running()) {
01030 mExecuteSearchTimer->start(0, true);
01031 } else {
01032 removeSerNum(serNum);
01033 }
01034 }
01035
01036 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, Q_UINT32 serNum, int delta)
01037 {
01038 if (!search() && !readSearch())
01039 return;
01040 if (!search()->inScope(aFolder))
01041 return;
01042 if (!mTempOpened) {
01043 open("foldersearch");
01044 mTempOpened = true;
01045 }
01046 QValueVector<Q_UINT32>::const_iterator it;
01047 it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01048 if (it != mSerNums.end()) {
01049 mUnreadMsgs += delta;
01050 emit numUnreadMsgsChanged( folder() );
01051 emit msgChanged( folder(), serNum, delta );
01052 }
01053 }
01054
01055 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01056 {
01057 if (!search() && !readSearch())
01058 return;
01059 if (!search()->inScope(folder))
01060 return;
01061 if (mTempOpened) {
01062 close("foldersearch");
01063 mTempOpened = false;
01064 }
01065
01066 mInvalid = true;
01067 if (mSearch)
01068 mSearch->stop();
01069
01070 if (!mUnlinked) {
01071 unlink(QFile::encodeName(indexLocation()));
01072 mUnlinked = true;
01073 }
01074
01075 if (!isOpened())
01076 return;
01077
01078 if (!mTempOpened) {
01079 open("foldersearch");
01080 mTempOpened = true;
01081 }
01082 mExecuteSearchTimer->start(0, true);
01083 }
01084
01085 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01086 {
01087 examineInvalidatedFolder(folder);
01088 if (mSearch->root() == folder) {
01089 delete mSearch;
01090 mSearch = 0;
01091 }
01092 }
01093
01094 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
01095 {
01096 int pos = 0;
01097 if (!search() && !readSearch())
01098 return;
01099 if (!search()->inScope(aFolder))
01100 return;
01101 if (!mTempOpened) {
01102 open("foldersearch");
01103 mTempOpened = true;
01104 }
01105
01106 Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01107 QValueVector<Q_UINT32>::const_iterator it;
01108 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01109 if ((*it) == serNum) {
01110 emit msgHeaderChanged(folder(), pos);
01111 break;
01112 }
01113 ++pos;
01114 }
01115
01116 aFolder->open("foldersearch");
01117
01118
01119 if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
01120 unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
01121 mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
01122 } else {
01123 connect( aFolder->storage(),
01124 SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
01125 this,
01126 SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
01127 const KMSearchPattern*, bool ) ) );
01128 mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
01129 }
01130 aFolder->storage()->search( search()->searchPattern(), serNum );
01131 }
01132
01133 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
01134 {
01135
01136
01137 if ( mTempOpened && mOpenCount == 1 )
01138 {
01139 examineInvalidatedFolder( folder );
01140 }
01141 }
01142
01143 #include "kmfoldersearch.moc"