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