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 <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 // We define functions as kmail_swap_NN so that we don't get compile errors
00037 // on platforms where bswap_NN happens to be a function instead of a define.
00038 
00039 /* Swap bytes in 32 bit value.  */
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 // Current version of the .index.search files
00051 #define IDS_SEARCH_VERSION 1000
00052 // The asterisk at the end is important
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()) { //Append all descendants to folders
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         //TODO: Get rid of this protocol check, need a bool KMFolder::isComplete()
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         //kill all pending jobs
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             //TODO: matches should try header rules first and check
00268             // rule->needsBody() and download imap message if needed
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                 // Prepend message to the list to search newest mail first.
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     //wait for imap folders to be retrieved
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         //if !(mSearchPattern.hasBodyRules())
00329         mFolders.append(folder->folder());
00330         //else
00331         //  get all the bodies of messages in the folder, need kroupware body caching
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     //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( folder() );
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(); // will be closed in searchFinished
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) // A new search is scheduled don't bother doing anything
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         // Exceptional case, for when folder has invalid ids
00477         if (mInvalid)
00478             return;
00479         mFolders.append(aFolder);
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 = 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     //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     kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00543     if (mOpenCount > 1)
00544         return 0;  // already open
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     //close all referenced folders
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; //TODO: Make it true and get that working ok
00658 }
00659 
00660 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00661                                      KMFolder*, QString, const AttachmentStrategy* ) const
00662 {
00663     // Should never be called
00664     assert(0);
00665     return 0;
00666 }
00667 
00668 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&,
00669                                        FolderJob::JobType, KMFolder*) const
00670 {
00671     // Should never be called
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; //exceptional case
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     /* While non-imap folders manage their jobs themselves, imap ones let
00719        their account manage them. Therefor first clear the jobs managed by
00720        this folder via the inherited method, then clear the imap ones. */
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     // TODO:If we fail to write the index we should panic the kernel
00772     // TODO:and the same for other folder types too, and the msgDict.
00773     QString filename = indexLocation();
00774     int old_umask = umask(077);
00775     QString tempName = filename + ".temp";
00776     unlink(QFile::encodeName(tempName));
00777 
00778     // We touch the folder, otherwise the index is regenerated, if KMail is
00779     // running, while the clock switches from daylight savings time to normal time
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) //exceptional case for when folder has invalid ids
00894                 return false;
00895             mFolders.append(folder);
00896         }
00897         KMMsgBase *mb = folder->getMsgBase(folderIdx);
00898         if (!mb) //Exceptional case our .ids file is messed up
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     // No dict for search folders, as search folders don't own any messages
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         // Unless there is a search currently running, add the message to
00988         // the list of ones to check on folderCompleted and hook up the signal.
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()) //give up, until the user manually opens the folder
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   // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
01125   // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
01126   if ( mTempOpened && mOpenCount == 1 )
01127   {
01128     examineInvalidatedFolder( folder );
01129   }
01130 }
01131 
01132 #include "kmfoldersearch.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Dec 21 14:24:54 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003