kmail Library API Documentation

kmfoldersearch.cpp

00001 // Author: Don Sanders <sanders@kde.org>
00002 // License GPL
00003 
00004 //Factor byteswap stuff into one header file
00005 
00006 #include "kmfoldersearch.h"
00007 #include "kmfolderimap.h"
00008 #include "kmfoldermgr.h"
00009 #include "kmsearchpattern.h"
00010 #include <qfileinfo.h>
00011 
00012 #include <kdebug.h>
00013 #include <klocale.h>
00014 #include <kconfig.h>
00015 #include "kmmsgdict.h"
00016 #include "kmmsgindex.h"
00017 
00018 #include <assert.h>
00019 #include <stdio.h>
00020 #include <unistd.h>
00021 #include <errno.h>
00022 #include <stdlib.h>
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <sys/file.h>
00026 #include <utime.h>
00027 #include <config.h>
00028 
00029 #ifdef HAVE_BYTESWAP_H
00030 #include <byteswap.h>
00031 #endif
00032 
00033 // We define functions as kmail_swap_NN so that we don't get compile errors
00034 // on platforms where bswap_NN happens to be a function instead of a define.
00035 
00036 /* Swap bytes in 32 bit value.  */
00037 #ifdef bswap_32
00038 #define kmail_swap_32(x) bswap_32(x)
00039 #else
00040 #define kmail_swap_32(x) \
00041      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |           \
00042       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
00043 #endif
00044 
00045 // Current version of the .index.search files
00046 #define IDS_VERSION 1000
00047 // The asterisk at the end is important
00048 #define IDS_HEADER "# KMail-Search-IDs V%d\n*"
00049 #define IDS_HEADER_LEN 30
00050 
00051 
00052 KMSearch::KMSearch(QObject * parent, const char * name)
00053     :QObject(parent, name)
00054 {
00055   mRemainingFolders = -1;
00056   mRemainingMessages = -1;
00057   mRecursive = true;
00058   mRunByIndex = mRunning = false;
00059   mIdle = false;
00060   mRoot = 0;
00061   mSearchPattern = 0;
00062   mSearchedCount = 0;
00063   mFoundCount = 0;
00064   mProcessNextBatchTimer = new QTimer();
00065   connect(mProcessNextBatchTimer, SIGNAL(timeout()),
00066       this, SLOT(slotProcessNextBatch()));
00067 }
00068 
00069 KMSearch::~KMSearch()
00070 {
00071   delete mProcessNextBatchTimer;
00072   delete mSearchPattern;
00073 }
00074 
00075 bool KMSearch::write(QString location) const
00076 {
00077   KConfig config(location);
00078   config.setGroup("Search Folder");
00079   if (mSearchPattern)
00080       mSearchPattern->writeConfig(&config);
00081   if (mRoot.isNull())
00082       config.writeEntry("Base", "");
00083   else
00084       config.writeEntry("Base", mRoot->idString());
00085   config.writeEntry("Recursive", recursive());
00086   return true;
00087 }
00088 
00089 bool KMSearch::read(QString location)
00090 {
00091   KConfig config(location);
00092   config.setGroup("Search Folder");
00093   if (!mSearchPattern)
00094       mSearchPattern = new KMSearchPattern();
00095   mSearchPattern->readConfig(&config);
00096   QString rootString = config.readEntry("Base");
00097   mRoot = kmkernel->folderMgr()->findIdString(rootString);
00098   if (mRoot.isNull())
00099     mRoot = kmkernel->imapFolderMgr()->findIdString(rootString);
00100   if (mRoot.isNull())
00101     mRoot = kmkernel->dimapFolderMgr()->findIdString(rootString);
00102   mRecursive = config.readBoolEntry("Recursive");
00103   return true;
00104 }
00105 
00106 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern)
00107 {
00108     if (running())
00109     stop();
00110     if (mSearchPattern != searchPattern) {
00111     delete mSearchPattern;
00112     mSearchPattern = searchPattern;
00113     }
00114 }
00115 
00116 bool KMSearch::inScope(KMFolder* folder) const
00117 {
00118     if (mRoot.isNull() || QGuardedPtr<KMFolder>(folder) == mRoot)
00119     return true;
00120     if (!recursive())
00121     return false;
00122 
00123     KMFolderDir *rootDir = 0;
00124     if (mRoot.isNull())
00125     rootDir = &kmkernel->folderMgr()->dir();
00126     else
00127     rootDir = mRoot->child();
00128     KMFolderDir *ancestorDir = folder->parent();
00129     while (ancestorDir) {
00130     if (ancestorDir == rootDir)
00131         return true;
00132     ancestorDir = ancestorDir->parent();
00133     }
00134     return false;
00135 }
00136 
00137 void KMSearch::start()
00138 {
00139     if (running())
00140     return;
00141 
00142     if (!mSearchPattern) {
00143     emit finished(true);
00144     return;
00145     }
00146 
00147     mSearchedCount = 0;
00148     mFoundCount = 0;
00149     mRunning = true;
00150     mRunByIndex = false;
00151     if(kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery(this)) {
00152     mRunByIndex = true;
00153     return;
00154     }
00155 
00156     QValueList<QGuardedPtr<KMFolder> > folders;
00157     folders.append(mRoot);
00158     if (recursive()) { //Append all descendants to folders
00159     KMFolderNode* node;
00160     KMFolder* folder;
00161     QValueListConstIterator<QGuardedPtr<KMFolder> > it;
00162     for (it = folders.begin(); it != folders.end(); ++it) {
00163         folder = *it;
00164         KMFolderDir *dir = 0;
00165         if (folder)
00166         dir = folder->child();
00167         else
00168         dir = &kmkernel->folderMgr()->dir();
00169         if (!dir)
00170         continue;
00171         QPtrListIterator<KMFolderNode> it(*dir);
00172         while ((node = it.current())) {
00173         ++it;
00174         if (!node->isDir())
00175         {
00176             KMFolder* kmf = dynamic_cast<KMFolder*>(node);
00177             if (kmf)
00178             folders.append(kmf);
00179         }
00180         }
00181     }
00182     }
00183 
00184     mLastFolder = QString::null;
00185     mRemainingFolders = folders.count();
00186     mRemainingMessages = 0;
00187     QValueListConstIterator<QGuardedPtr<KMFolder> > it;
00188     for (it = folders.begin(); it != folders.end(); ++it) {
00189     KMFolder *folder = *it;
00190     if (!folder) {
00191         --mRemainingFolders;
00192         continue;
00193     }
00194     //TODO: Get rid of this protocol check, need a bool KMFolder::isComplete()
00195     if (folder->folderType() == KMFolderTypeImap) {
00196         KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder);
00197         if (imapFolder && imapFolder->getContentState() ==
00198         KMFolderImap::imapNoInformation) {
00199         mIncompleteFolders.append(imapFolder);
00200         connect(imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00201             SLOT(slotFolderComplete(KMFolderImap*, bool)));
00202         imapFolder->getFolder();
00203         } else {
00204         mFolders.append(folder);
00205         }
00206     } else {
00207         mFolders.append(folder);
00208     }
00209     }
00210 
00211     mProcessNextBatchTimer->start(0, true);
00212 }
00213 
00214 void KMSearch::stop()
00215 {
00216     if (!running())
00217     return;
00218     if(mRunByIndex) {
00219     if(kmkernel->msgIndex())
00220         kmkernel->msgIndex()->stopQuery(this);
00221     } else {
00222     //kill all pending jobs
00223     QValueListConstIterator<QGuardedPtr<KMFolderImap> > it;
00224     for (it = mIncompleteFolders.begin();
00225          it != mIncompleteFolders.end(); ++it) {
00226         KMFolder *folder = *it;
00227         if (folder)
00228         disconnect(folder,
00229                SIGNAL(folderComplete(KMFolderImap*, bool)),
00230                this,
00231                SLOT(slotFolderComplete(KMFolderImap*, bool)));
00232     }
00233     mIncompleteFolders.clear();
00234     QValueListConstIterator<QGuardedPtr<KMFolder> > jt;
00235     for (jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt) {
00236         KMFolder *folder = *jt;
00237         if (folder)
00238         folder->close();
00239     }
00240     }
00241     mOpenedFolders.clear();
00242     mRemainingMessages = -1;
00243     mRemainingFolders = -1;
00244     mFolders.clear();
00245     mLastFolder = "";
00246     mRunByIndex = mRunning = false;
00247     mIdle = false;
00248     emit finished(false);
00249 }
00250 
00251 void KMSearch::slotProcessNextBatch()
00252 {
00253     if (!running())
00254     return;
00255     mIdle = false;
00256 
00257     if (mSerNums.count() != 0) {
00258     int i = 100;
00259     QValueListIterator<Q_UINT32> it;
00260     for (it = mSerNums.begin(); it != mSerNums.end();) {
00261         if (--i == 0)
00262         break;
00263 
00264         Q_UINT32 serNum = *it;
00265         it = mSerNums.erase(it);
00266         --mRemainingMessages;
00267         ++mSearchedCount;
00268 
00269         //TODO: matches should try header rules first and check
00270             // rule->needsBody() and download imap message if needed
00271         if (mSearchPattern && !mSearchPattern->matches(serNum))
00272         continue;
00273         emit found(serNum);
00274         ++mFoundCount;
00275     }
00276     mProcessNextBatchTimer->start(0, true);
00277     return;
00278     }
00279 
00280     if (mFolders.count() != 0) {
00281     --mRemainingFolders;
00282     KMFolder *folder = *(mFolders.begin());
00283     if (folder) {
00284         if (folder->isSystemFolder())
00285         mLastFolder = i18n(folder->name().utf8());
00286         else
00287         mLastFolder = folder->name();
00288     }
00289     mFolders.erase(mFolders.begin());
00290     if (folder) {
00291         folder->open();
00292         mOpenedFolders.append(folder);
00293         for(int i = 0; i < folder->count(); ++i) {
00294         Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(folder, i);
00295         ++mRemainingMessages;
00296         mSerNums.append(serNum);
00297         }
00298     }
00299     mProcessNextBatchTimer->start(0, true);
00300     return;
00301     }
00302     if (mRemainingFolders == 0) {
00303     mRunning = false;
00304     QValueListConstIterator<QGuardedPtr<KMFolder> > it;
00305     for (it = mOpenedFolders.begin(); it != mOpenedFolders.end(); ++it) {
00306         KMFolder *folder = *it;
00307         if (folder)
00308         folder->close();
00309     }
00310     mOpenedFolders.clear();
00311     mRemainingMessages = -1;
00312     mRemainingFolders = -1;
00313     mFolders.clear();
00314     mLastFolder = "";
00315     emit finished(true);
00316     return;
00317     }
00318 
00319     //wait for imap folders to be retrieved
00320     mIdle = true;
00321 }
00322 
00323 void KMSearch::slotFolderComplete(KMFolderImap *folder, bool success)
00324 {
00325   disconnect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00326              this, SLOT(slotFolderComplete(KMFolderImap*, bool)));
00327 
00328   if (success) {
00329       //if !(mSearchPattern.hasBodyRules())
00330           mFolders.append(folder);
00331       //else
00332       //  get all the bodies of messages in the folder, need kroupware body caching
00333       if (mIdle)
00334       mProcessNextBatchTimer->start(0, true);
00335   } else {
00336       stop();
00337   }
00338 }
00339 
00340 
00341 //-----------------------------------------------------------------------------
00342 KMFolderSearch::KMFolderSearch(KMFolderDir* parent, const QString& name)
00343   : KMFolder(parent, name)
00344 {
00345     mIdsStream = 0;
00346     mSearch = 0;
00347     mInvalid = false;
00348     mUnlinked = true;
00349     mTempOpened = false;
00350 
00351     //Hook up some slots for live updating of search folders
00352     //TODO: Optimize folderInvalidated, folderAdded, folderRemoved
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(); //new search old index is obsolete
00415     emit cleared();
00416     mInvalid = false;
00417     setDirty( true ); //have to write the index
00418     if (!mUnlinked) {
00419     unlink(QFile::encodeName(indexLocation()));
00420     mUnlinked = true;
00421     }
00422     if (mSearch != search) {
00423     delete mSearch;
00424     mSearch = search; // take ownership
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(this);
00438     emit changed(); // really want a kmfolder cleared signal
00439     /* TODO There is KMFolder::cleared signal now. Adjust. */
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 ( parent() )
00451     parent()->manager()->invalidateFolder(kmkernel->msgDict(), this);
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) // A new search is scheduled don't bother doing anything
00469     return;
00470     int idx = -1;
00471     KMFolder *folder = 0;
00472     kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
00473     assert(folder && (idx != -1));
00474     if(mFolders.findIndex(folder) == -1) {
00475     folder->open();
00476     // Exceptional case, for when folder has invalid ids
00477     if (mInvalid)
00478         return;
00479     mFolders.append(folder);
00480     }
00481     setDirty( true ); //TODO append a single entry to .ids file and sync.
00482     if (!mUnlinked) {
00483     unlink(QFile::encodeName(indexLocation()));
00484     mUnlinked = true;
00485     }
00486     mSerNums.append(serNum);
00487     KMMsgBase *mb = folder->getMsgBase(idx);
00488     if (mb->isUnread() || mb->isNew()) {
00489        if (mUnreadMsgs == -1)
00490            mUnreadMsgs = 0;
00491        ++mUnreadMsgs;
00492        emit numUnreadMsgsChanged( this );
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 *folder = 0;
00505         kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
00506         assert(folder && (idx != -1));
00507         emit msgRemoved(this, 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     //Not supported search folder can't own messages
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     if (mOpenCount > 1)
00543     return 0;  // already open
00544 
00545     readConfig();
00546     if (!mSearch && !readSearch())
00547     return -1;
00548 
00549     emit cleared();
00550     if (!mSearch || !search()->running())
00551     if (!readIndex()) {
00552         executeSearch();
00553     }
00554 
00555     return 0;
00556 }
00557 
00558 int KMFolderSearch::canAccess()
00559 {
00560     assert(!name().isEmpty());
00561 
00562     if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0)
00563     return 1;
00564     return 0;
00565 }
00566 
00567 void KMFolderSearch::sync()
00568 {
00569     if (mDirty) {
00570     if (mSearch)
00571         mSearch->write(location());
00572     updateIndex();
00573     }
00574 }
00575 
00576 void KMFolderSearch::close(bool force)
00577 {
00578     if (mOpenCount <= 0) return;
00579     if (mOpenCount > 0) mOpenCount--;
00580     if (mOpenCount > 0 && !force) return;
00581 
00582     if (mAutoCreateIndex) {
00583     if (mSearch)
00584         mSearch->write(location());
00585     updateIndex();
00586     if (mSearch && search()->running())
00587         mSearch->stop();
00588     writeConfig();
00589     }
00590 
00591     //close all referenced folders
00592     QValueListIterator<QGuardedPtr<KMFolder> > fit;
00593     for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00594     if (!(*fit))
00595         continue;
00596     (*fit)->close();
00597     }
00598     mFolders.clear();
00599 
00600     clearIndex(TRUE);
00601 
00602     if (mIdsStream)
00603     fclose(mIdsStream);
00604 
00605     mOpenCount   = 0;
00606     mIdsStream = 0;
00607     mUnreadMsgs  = -1;
00608 }
00609 
00610 int KMFolderSearch::create(bool)
00611 {
00612     int old_umask;
00613     int rc = unlink(QFile::encodeName(location()));
00614     if (!rc)
00615     return rc;
00616     rc = 0;
00617 
00618     assert(!name().isEmpty());
00619     assert(mOpenCount == 0);
00620 
00621     kdDebug(5006) << "Creating folder " << location() << endl;
00622     if (access(QFile::encodeName(location()), F_OK) == 0) {
00623     kdDebug(5006) << "KMFolderSearch::create call to access function failed."
00624               << endl;
00625     return EEXIST;
00626     }
00627 
00628     old_umask = umask(077);
00629     FILE *mStream = fopen(QFile::encodeName(location()), "w+");
00630     umask(old_umask);
00631     if (!mStream) return errno;
00632     fclose(mStream);
00633 
00634     clearIndex();
00635     if (!mSearch) {
00636     mSearch = new KMSearch();
00637     QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00638     QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00639     }
00640     mSearch->write(location());
00641     mOpenCount++;
00642     mChanged = false;
00643     mUnreadMsgs = 0;
00644     mTotalMsgs = 0;
00645     return rc;
00646 }
00647 
00648 int KMFolderSearch::compact()
00649 {
00650     needsCompact = false;
00651     return 0;
00652 }
00653 
00654 bool KMFolderSearch::isReadOnly() const
00655 {
00656     return false; //TODO: Make it true and get that working ok
00657 }
00658 
00659 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00660                                      KMFolder*, QString, const AttachmentStrategy* ) const
00661 {
00662     // Should never be called
00663     assert(0);
00664     return 0;
00665 }
00666 
00667 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&,
00668                                        FolderJob::JobType, KMFolder*) const
00669 {
00670     // Should never be called
00671     assert(0);
00672     return 0;
00673 }
00674 
00675 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
00676 {
00677     int folderIdx = -1;
00678     KMFolder *folder = 0;
00679     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00680     return 0;
00681     kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx);
00682     assert(folder && (folderIdx != -1));
00683     return folder->getMsgBase(folderIdx);
00684 }
00685 
00686 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
00687 {
00688     int folderIdx = -1;
00689     KMFolder *folder = 0;
00690     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00691     return 0;
00692     kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx);
00693     if (!folder || folderIdx == -1)
00694     return 0; //exceptional case
00695     return folder->getMsgBase(folderIdx);
00696 }
00697 
00698 //-----------------------------------------------------------------------------
00699 KMMessage* KMFolderSearch::getMsg(int idx)
00700 {
00701     int folderIdx = -1;
00702     KMFolder *folder = 0;
00703     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00704     return 0;
00705     kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx);
00706     assert(folder && (folderIdx != -1));
00707     KMMessage* msg = folder->getMsg( folderIdx );
00708     return msg;
00709 }
00710 
00711 //-------------------------------------------------------------
00712 void
00713 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
00714 {
00715     if ( !msg || msg->transferInProgress() )
00716       return;
00717   /* While non-imap folders manage their jobs themselves, imap ones let
00718      their account manage them. Therefor first clear the jobs managed by
00719      this folder via the inherited method, then clear the imap ones. */
00720   KMFolder::ignoreJobsForMessage( msg );
00721 
00722   if (msg->parent()->folderType() == KMFolderTypeImap) {
00723     KMAcctImap *account;
00724     if ( !(account = static_cast<KMFolderImap*>(msg->parent())->account()) )
00725       return;
00726     account->ignoreJobsForMessage( msg );
00727   }
00728 }
00729 
00730 
00731 int KMFolderSearch::find(const KMMsgBase* msg) const
00732 {
00733     int pos = 0;
00734     Q_UINT32 serNum = msg->getMsgSerNum();
00735     QValueVector<Q_UINT32>::const_iterator it;
00736     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00737     if ((*it) == serNum)
00738         return pos;
00739     ++pos;
00740     }
00741     return -1;
00742 }
00743 
00744 QString KMFolderSearch::indexLocation() const
00745 {
00746     QString sLocation(path());
00747 
00748     if (!sLocation.isEmpty()) sLocation += '/';
00749     sLocation += '.';
00750     sLocation += dotEscape(fileName());
00751     sLocation += ".index";
00752     sLocation += ".search";
00753 
00754     return sLocation;
00755 }
00756 
00757 int KMFolderSearch::updateIndex()
00758 {
00759   if (mSearch && search()->running())
00760       unlink(QFile::encodeName(indexLocation()));
00761   else
00762       if (dirty())
00763       return writeIndex();
00764   return 0;
00765 }
00766 
00767 int KMFolderSearch::writeIndex( bool )
00768 {
00769     // TODO:If we fail to write the index we should panic the kernel
00770     // TODO:and the same for other folder types too, and the msgDict.
00771     QString filename = indexLocation();
00772     int old_umask = umask(077);
00773     QString tempName = filename + ".temp";
00774     unlink(QFile::encodeName(tempName));
00775 
00776     // We touch the folder, otherwise the index is regenerated, if KMail is
00777     // running, while the clock switches from daylight savings time to normal time
00778     utime(QFile::encodeName(location()), 0);
00779 
00780     FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00781     umask(old_umask);
00782 
00783     if (!tmpIndexStream) {
00784     kdDebug(5006) << "Cannot write '" << filename
00785               << strerror(errno) << " (" << errno << ")" << endl;
00786     truncate(QFile::encodeName(filename), 0);
00787     return -1;
00788     }
00789     fprintf(tmpIndexStream, IDS_HEADER, IDS_VERSION);
00790     Q_UINT32 byteOrder = 0x12345678;
00791     fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00792 
00793     Q_UINT32 count = mSerNums.count();
00794     if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
00795     fclose(tmpIndexStream);
00796     truncate(QFile::encodeName(filename), 0);
00797     return -1;
00798     }
00799 
00800     QValueVector<Q_UINT32>::iterator it;
00801     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00802     Q_UINT32 serNum = *it;
00803     if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
00804         return -1;
00805     }
00806     if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
00807     if (fflush(tmpIndexStream) != 0) return errno;
00808     if (fsync(fileno(tmpIndexStream)) != 0) return errno;
00809     if (fclose(tmpIndexStream) != 0) return errno;
00810 
00811     ::rename(QFile::encodeName(tempName), QFile::encodeName(indexLocation()));
00812     mDirty = FALSE;
00813     mUnlinked = FALSE;
00814 
00815     return 0;
00816 }
00817 
00818 DwString KMFolderSearch::getDwString(int idx)
00819 {
00820     return getMsgBase(idx)->parent()->getDwString( idx );
00821 }
00822 
00823 KMMessage* KMFolderSearch::readMsg(int idx)
00824 {
00825     int folderIdx = -1;
00826     KMFolder *folder = 0;
00827     kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx);
00828     assert(folder && (folderIdx != -1));
00829     return folder->getMsg( folderIdx );
00830 }
00831 
00832 bool KMFolderSearch::readIndex()
00833 {
00834     clearIndex();
00835     QString filename = indexLocation();
00836     mIdsStream = fopen(QFile::encodeName(filename), "r+");
00837     if (!mIdsStream)
00838     return false;
00839 
00840     int version = 0;
00841     fscanf(mIdsStream, IDS_HEADER, &version);
00842     if (version != IDS_VERSION) {
00843     fclose(mIdsStream);
00844     mIdsStream = 0;
00845     return false;
00846     }
00847     bool swapByteOrder;
00848     Q_UINT32 byte_order;
00849     if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
00850     fclose(mIdsStream);
00851     mIdsStream = 0;
00852     return false;
00853     }
00854     swapByteOrder = (byte_order == 0x78563412);
00855 
00856     Q_UINT32 count;
00857     if (!fread(&count, sizeof(count), 1, mIdsStream)) {
00858     fclose(mIdsStream);
00859     mIdsStream = 0;
00860     return false;
00861     }
00862     if (swapByteOrder)
00863     count = kmail_swap_32(count);
00864 
00865     mUnreadMsgs = 0;
00866     mSerNums.reserve(count);
00867     for (unsigned int index = 0; index < count; index++) {
00868     Q_UINT32 serNum;
00869     int folderIdx = -1;
00870     KMFolder *folder = 0;
00871     bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
00872     if (!readOk) {
00873         clearIndex();
00874         fclose(mIdsStream);
00875         mIdsStream = 0;
00876         return false;
00877     }
00878     if (swapByteOrder)
00879         serNum = kmail_swap_32(serNum);
00880 
00881     kmkernel->msgDict()->getLocation( serNum, &folder, &folderIdx );
00882     if (!folder || (folderIdx == -1)) {
00883         clearIndex();
00884         fclose(mIdsStream);
00885         mIdsStream = 0;
00886         return false;
00887     }
00888     mSerNums.push_back(serNum);
00889     if(mFolders.findIndex(folder) == -1) {
00890         folder->open();
00891         if (mInvalid) //exceptional case for when folder has invalid ids
00892         return false;
00893         mFolders.append(folder);
00894     }
00895     KMMsgBase *mb = folder->getMsgBase(folderIdx);
00896     if (!mb) //Exceptional case our .ids file is messed up
00897         return false;
00898     if (mb->isNew() || mb->isUnread()) {
00899         if (mUnreadMsgs == -1) ++mUnreadMsgs;
00900         ++mUnreadMsgs;
00901     }
00902     }
00903     mTotalMsgs = mSerNums.count();
00904     fclose(mIdsStream);
00905     mIdsStream = 0;
00906     mUnlinked = true;
00907     return true;
00908 }
00909 
00910 int KMFolderSearch::removeContents()
00911 {
00912     unlink(QFile::encodeName(location()));
00913     unlink(QFile::encodeName(indexLocation()));
00914     mUnlinked = true;
00915     return 0;
00916 }
00917 
00918 int KMFolderSearch::expungeContents()
00919 {
00920     setSearch(new KMSearch());
00921     return 0;
00922 }
00923 
00924 int KMFolderSearch::count(bool cache) const
00925 {
00926   int res = KMFolder::count(cache);
00927   if (res == -1) {
00928       // open()
00929       res = mSerNums.count();
00930       // close()
00931   }
00932   return res;
00933 }
00934 
00935 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
00936 {
00937     assert(idx >= 0 && idx < (int)mSerNums.count());
00938     KMMsgBase *msgBase = getMsgBase(idx);
00939     QValueVector<Q_UINT32>::iterator it = mSerNums.begin();
00940     mSerNums.erase(&it[idx]);
00941     return msgBase;
00942 }
00943 
00944 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
00945 {
00946     assert(idx >= 0 && idx < (int)mSerNums.count());
00947     Q_UNUSED( idx );
00948     return msg->parent()->setIndexEntry(msg->parent()->find(msg), msg);
00949 }
00950 
00951 void KMFolderSearch::clearIndex(bool, bool)
00952 {
00953     mSerNums.clear();
00954 }
00955 
00956 void KMFolderSearch::fillDictFromIndex(KMMsgDict *)
00957 {
00958     // No dict for search folders, as search folders don't own any messages
00959     return;
00960 }
00961 
00962 void KMFolderSearch::truncateIndex()
00963 {
00964     truncate(QFile::encodeName(indexLocation()), IDS_HEADER_LEN);
00965 }
00966 
00967 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum)
00968 {
00969     if (!search() && !readSearch())
00970     return;
00971     if (!search()->inScope(aFolder))
00972     return;
00973     if (!mTempOpened) {
00974     open();
00975     mTempOpened = true;
00976     }
00977 
00978     if (!search()->searchPattern())
00979     return;
00980 
00981     int idx = -1;
00982     KMFolder *folder = 0;
00983     kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
00984     assert(folder && (idx != -1));
00985     assert(folder == aFolder);
00986     if (!folder->isOpened())
00987       return;
00988 
00989     if (folder->folderType() == KMFolderTypeImap) {
00990         // Unless there is a search currently running, add the message to
00991     // the list of ones to check on folderCompleted and hook up the signal.
00992     if (!mSearch->running()) {
00993         mUnexaminedMessages.push(serNum);
00994             disconnect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00995                this, SLOT (examineCompletedFolder(KMFolderImap*, bool)));
00996         connect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00997                this, SLOT (examineCompletedFolder(KMFolderImap*, bool)));
00998     }
00999     } else {
01000         if (search()->searchPattern()->matches(serNum))
01001             if (mSearch->running()) {
01002                 mSearch->stop();
01003         mExecuteSearchTimer->start(0, true);
01004             } else {
01005                 addSerNum(serNum);
01006             }
01007     }
01008 }
01009 
01010 void KMFolderSearch::examineCompletedFolder(KMFolderImap *aFolder, bool success)
01011 {
01012     if (!success) return;
01013     disconnect (aFolder, SIGNAL(folderComplete(KMFolderImap*, bool)),
01014                 this, SLOT(examineCompletedFolder(KMFolderImap*, bool)));
01015     Q_UINT32 serNum;
01016     while (!mUnexaminedMessages.isEmpty()) {
01017         serNum = mUnexaminedMessages.pop();
01018         if (search()->searchPattern()->matches(serNum))
01019             addSerNum(serNum);
01020     }
01021 }
01022 
01023 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum)
01024 {
01025     if (!search() && !readSearch())
01026     return;
01027     if (!search()->inScope(folder))
01028     return;
01029     if (!mTempOpened) {
01030     open();
01031     mTempOpened = true;
01032     }
01033 
01034     if (mSearch->running()) {
01035     mSearch->stop();
01036     mExecuteSearchTimer->start(0, true);
01037     } else {
01038     removeSerNum(serNum);
01039     }
01040 }
01041 
01042 void KMFolderSearch::examineChangedMessage(KMFolder *folder, Q_UINT32 serNum, int delta)
01043 {
01044     if (!search() && !readSearch())
01045     return;
01046     if (!search()->inScope(folder))
01047     return;
01048     if (!mTempOpened) {
01049     open();
01050     mTempOpened = true;
01051     }
01052     QValueVector<Q_UINT32>::const_iterator it;
01053     it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01054     if (it != mSerNums.end()) {
01055     mUnreadMsgs += delta;
01056     emit numUnreadMsgsChanged( this );
01057     emit msgChanged( this, serNum, delta );
01058     }
01059 }
01060 
01061 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01062 {
01063     if (!search() && !readSearch())
01064     return;
01065     if (!search()->inScope(folder))
01066     return;
01067     if (mTempOpened) {
01068     close();
01069     mTempOpened = false;
01070     }
01071 
01072     mInvalid = true;
01073     if (mSearch)
01074     mSearch->stop();
01075 
01076     removeContents();
01077     if (!isOpened()) //give up, until the user manually opens the folder
01078     return;
01079 
01080     if (!mTempOpened) {
01081     open();
01082     mTempOpened = true;
01083     }
01084     mExecuteSearchTimer->start(0, true);
01085 }
01086 
01087 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01088 {
01089     examineInvalidatedFolder(folder);
01090     if (mSearch->root() == folder) {
01091     delete mSearch;
01092     mSearch = 0;
01093     }
01094 }
01095 
01096 void KMFolderSearch::propagateHeaderChanged(KMFolder *folder, int idx)
01097 {
01098     int pos = 0;
01099     if (!search() && !readSearch())
01100     return;
01101     if (!search()->inScope(folder))
01102     return;
01103     if (!mTempOpened) {
01104     open();
01105     mTempOpened = true;
01106     }
01107 
01108     Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(folder, idx);
01109     QValueVector<Q_UINT32>::const_iterator it;
01110     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01111     if ((*it) == serNum) {
01112         emit msgHeaderChanged(this, pos);
01113         return;
01114     }
01115     ++pos;
01116     }
01117 }
01118 
01119 #include "kmfoldersearch.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:28 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003