kmail

kmfoldersearch.cpp

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