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