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