kmail

kmfoldersearch.cpp
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 <tqfile.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(TQObject * parent, const char * name)
00073     :TQObject(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 TQTimer(0, "mProcessNextBatchTimer");
00084     connect(mProcessNextBatchTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotProcessNextBatch()));
00085 }
00086 
00087 KMSearch::~KMSearch()
00088 {
00089     delete mProcessNextBatchTimer;
00090     delete mSearchPattern;
00091 }
00092 
00093 bool KMSearch::write(TQString 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(TQString location)
00108 {
00109     KConfig config( location );
00110     config.setGroup( "Search Folder" );
00111     if ( !mSearchPattern )
00112         mSearchPattern = new KMSearchPattern();
00113     mSearchPattern->readConfig( &config );
00114     TQString 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     TQValueListIterator<TQGuardedPtr<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         TQValueListConstIterator<TQGuardedPtr<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             TQPtrListIterator<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 = TQString();
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         TQValueListConstIterator<TQGuardedPtr<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 = TQString();
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                 TQT_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ),
00266                 this,
00267                 TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_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                                        TQValueList<TQ_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     TQValueListIterator<TQ_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           TQT_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>,
00295                                 const KMSearchPattern*, bool ) ),
00296           this,
00297           TQT_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_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 = TQString();
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(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
00329             this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
00330     connect(kmkernel->folderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
00331             this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
00332     connect(kmkernel->folderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
00333             this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
00334     connect(kmkernel->folderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)),
00335             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00336     connect(kmkernel->folderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)),
00337             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00338     connect(kmkernel->folderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)),
00339             this, TQT_SLOT(examineRemovedFolder(KMFolder*)));
00340     connect(kmkernel->folderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)),
00341             this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int)));
00342 
00343     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
00344             this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
00345     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
00346             this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
00347     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
00348             this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
00349     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)),
00350             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00351     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)),
00352             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00353     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)),
00354             this, TQT_SLOT(examineRemovedFolder(KMFolder*)));
00355     connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)),
00356             this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int)));
00357 
00358     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
00359             this, TQT_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
00360     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
00361             this, TQT_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
00362     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
00363             this, TQT_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
00364     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)),
00365             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00366     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderAdded(KMFolder*)),
00367             this, TQT_SLOT(examineInvalidatedFolder(KMFolder*)));
00368     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)),
00369             this, TQT_SLOT(examineRemovedFolder(KMFolder*)));
00370     connect(kmkernel->dimapFolderMgr(), TQT_SIGNAL(msgHeaderChanged(KMFolder*,int)),
00371             this, TQT_SLOT(propagateHeaderChanged(KMFolder*,int)));
00372 
00373   mExecuteSearchTimer = new TQTimer(0, "mExecuteSearchTimer");
00374   connect(mExecuteSearchTimer, TQT_SIGNAL(timeout()),
00375           this, TQT_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(TQFile::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             TQObject::connect(search, TQT_SIGNAL(found(TQ_UINT32)),
00403                     TQT_SLOT(addSerNum(TQ_UINT32)));
00404             TQObject::connect(search, TQT_SIGNAL(finished(bool)),
00405                     TQT_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(TQ_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(TQFile::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(TQ_UINT32 serNum)
00476 {
00477     TQValueVector<TQ_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(TQFile::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     TQObject::connect(mSearch, TQT_SIGNAL(found(TQ_UINT32)), TQT_SLOT(addSerNum(TQ_UINT32)));
00506     TQObject::connect(mSearch, TQT_SIGNAL(finished(bool)), TQT_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(TQFile::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     Q_UNUSED( owner );
00551     if (mAutoCreateIndex) {
00552         if (mSearch)
00553             mSearch->write(location());
00554         updateIndex();
00555         if (mSearch && search()->running())
00556             mSearch->stop();
00557         writeConfig();
00558     }
00559 
00560     //close all referenced folders
00561     TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
00562     for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00563         if (!(*fit))
00564             continue;
00565         (*fit)->close("foldersearch");
00566     }
00567     mFolders.clear();
00568 
00569     clearIndex(TRUE);
00570 
00571     if (mIdsStream)
00572         fclose(mIdsStream);
00573 
00574     mOpenCount   = 0;
00575     mIdsStream = 0;
00576     mUnreadMsgs  = -1;
00577 }
00578 
00579 int KMFolderSearch::create()
00580 {
00581     int old_umask;
00582     int rc = unlink(TQFile::encodeName(location()));
00583     if (!rc)
00584         return rc;
00585     rc = 0;
00586 
00587     assert(!folder()->name().isEmpty());
00588     assert(mOpenCount == 0);
00589 
00590     kdDebug(5006) << "Creating folder " << location() << endl;
00591     if (access(TQFile::encodeName(location()), F_OK) == 0) {
00592         kdDebug(5006) << "KMFolderSearch::create call to access function failed."
00593             << endl;
00594         return EEXIST;
00595     }
00596 
00597     old_umask = umask(077);
00598     FILE *mStream = fopen(TQFile::encodeName(location()), "w+");
00599     umask(old_umask);
00600     if (!mStream) return errno;
00601     fclose(mStream);
00602 
00603     clearIndex();
00604     if (!mSearch) {
00605         mSearch = new KMSearch();
00606         TQObject::connect(mSearch, TQT_SIGNAL(found(TQ_UINT32)), TQT_SLOT(addSerNum(TQ_UINT32)));
00607         TQObject::connect(mSearch, TQT_SIGNAL(finished(bool)), TQT_SLOT(searchFinished(bool)));
00608     }
00609     mSearch->write(location());
00610     mOpenCount++;
00611     mChanged = false;
00612     mUnreadMsgs = 0;
00613     mTotalMsgs = 0;
00614     return rc;
00615 }
00616 
00617 int KMFolderSearch::compact( bool )
00618 {
00619     needsCompact = false;
00620     return 0;
00621 }
00622 
00623 bool KMFolderSearch::isReadOnly() const
00624 {
00625     return false; //TODO: Make it true and get that working ok
00626 }
00627 
00628 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00629                                      KMFolder*, TQString, const AttachmentStrategy* ) const
00630 {
00631     // Should never be called
00632     assert(0);
00633     return 0;
00634 }
00635 
00636 FolderJob* KMFolderSearch::doCreateJob(TQPtrList<KMMessage>&, const TQString&,
00637                                        FolderJob::JobType, KMFolder*) const
00638 {
00639     // Should never be called
00640     assert(0);
00641     return 0;
00642 }
00643 
00644 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
00645 {
00646     int folderIdx = -1;
00647     KMFolder *folder = 0;
00648     if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
00649         return 0;
00650     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00651     assert(folder && (folderIdx != -1));
00652     return folder->getMsgBase(folderIdx);
00653 }
00654 
00655 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
00656 {
00657     int folderIdx = -1;
00658     KMFolder *folder = 0;
00659     if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
00660         return 0;
00661     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00662     if (!folder || folderIdx == -1)
00663         return 0; //exceptional case
00664     return folder->getMsgBase(folderIdx);
00665 }
00666 
00667 //-----------------------------------------------------------------------------
00668 KMMessage* KMFolderSearch::getMsg(int idx)
00669 {
00670     int folderIdx = -1;
00671     KMFolder *folder = 0;
00672     if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
00673         return 0;
00674     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00675     assert(folder && (folderIdx != -1));
00676     KMMessage* msg = folder->getMsg( folderIdx );
00677     return msg;
00678 }
00679 
00680 //-------------------------------------------------------------
00681 void
00682 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
00683 {
00684     if ( !msg || msg->transferInProgress() )
00685         return;
00686     /* While non-imap folders manage their jobs themselves, imap ones let
00687        their account manage them. Therefor first clear the jobs managed by
00688        this folder via the inherited method, then clear the imap ones. */
00689     FolderStorage::ignoreJobsForMessage( msg );
00690 
00691     if (msg->parent()->folderType() == KMFolderTypeImap) {
00692         KMAcctImap *account =
00693             static_cast<KMFolderImap*>( msg->storage() )->account();
00694         if( !account )
00695             return;
00696         account->ignoreJobsForMessage( msg );
00697     }
00698 }
00699 
00700 
00701 int KMFolderSearch::find(const KMMsgBase* msg) const
00702 {
00703     int pos = 0;
00704     TQ_UINT32 serNum = msg->getMsgSerNum();
00705     TQValueVector<TQ_UINT32>::const_iterator it;
00706     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00707         if ((*it) == serNum)
00708             return pos;
00709         ++pos;
00710     }
00711     return -1;
00712 }
00713 
00714 TQString KMFolderSearch::indexLocation() const
00715 {
00716     TQString sLocation(folder()->path());
00717 
00718     if (!sLocation.isEmpty()) sLocation += '/';
00719     sLocation += '.';
00720     sLocation += dotEscape(fileName());
00721     sLocation += ".index";
00722     sLocation += ".search";
00723 
00724     return sLocation;
00725 }
00726 
00727 int KMFolderSearch::updateIndex()
00728 {
00729     if (mSearch && search()->running())
00730         unlink(TQFile::encodeName(indexLocation()));
00731     else
00732         if (dirty())
00733             return writeIndex();
00734     return 0;
00735 }
00736 
00737 int KMFolderSearch::writeIndex( bool )
00738 {
00739     // TODO:If we fail to write the index we should panic the kernel
00740     // TODO:and the same for other folder types too, and the msgDict.
00741     TQString filename = indexLocation();
00742     int old_umask = umask(077);
00743     TQString tempName = filename + ".temp";
00744     unlink(TQFile::encodeName(tempName));
00745 
00746     // We touch the folder, otherwise the index is regenerated, if KMail is
00747     // running, while the clock switches from daylight savings time to normal time
00748     utime(TQFile::encodeName(location()), 0);
00749 
00750     FILE *tmpIndexStream = fopen(TQFile::encodeName(tempName), "w");
00751     umask(old_umask);
00752 
00753     if (!tmpIndexStream) {
00754         kdDebug(5006) << "Cannot write '" << filename
00755             << strerror(errno) << " (" << errno << ")" << endl;
00756         truncate(TQFile::encodeName(filename), 0);
00757         return -1;
00758     }
00759     fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION);
00760     TQ_UINT32 byteOrder = 0x12345678;
00761     fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00762 
00763     TQ_UINT32 count = mSerNums.count();
00764     if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
00765         fclose(tmpIndexStream);
00766         truncate(TQFile::encodeName(filename), 0);
00767         return -1;
00768     }
00769 
00770     TQValueVector<TQ_UINT32>::iterator it;
00771     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00772         TQ_UINT32 serNum = *it;
00773         if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
00774             return -1;
00775     }
00776     if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
00777     if (fflush(tmpIndexStream) != 0) return errno;
00778     if (fsync(fileno(tmpIndexStream)) != 0) return errno;
00779     if (fclose(tmpIndexStream) != 0) return errno;
00780 
00781     ::rename(TQFile::encodeName(tempName), TQFile::encodeName(indexLocation()));
00782     mDirty = FALSE;
00783     mUnlinked = FALSE;
00784 
00785     return 0;
00786 }
00787 
00788 DwString KMFolderSearch::getDwString(int idx)
00789 {
00790     return getMsgBase(idx)->parent()->getDwString( idx );
00791 }
00792 
00793 KMMessage* KMFolderSearch::readMsg(int idx)
00794 {
00795     int folderIdx = -1;
00796     KMFolder *folder = 0;
00797     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00798     assert(folder && (folderIdx != -1));
00799     return folder->getMsg( folderIdx );
00800 }
00801 
00802 bool KMFolderSearch::readIndex()
00803 {
00804     clearIndex();
00805     TQString filename = indexLocation();
00806     mIdsStream = fopen(TQFile::encodeName(filename), "r+");
00807     if (!mIdsStream)
00808         return false;
00809 
00810     int version = 0;
00811     fscanf(mIdsStream, IDS_SEARCH_HEADER, &version);
00812     if (version != IDS_SEARCH_VERSION) {
00813         fclose(mIdsStream);
00814         mIdsStream = 0;
00815         return false;
00816     }
00817     bool swapByteOrder;
00818     TQ_UINT32 byte_order;
00819     if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
00820         fclose(mIdsStream);
00821         mIdsStream = 0;
00822         return false;
00823     }
00824     swapByteOrder = (byte_order == 0x78563412);
00825 
00826     TQ_UINT32 count;
00827     if (!fread(&count, sizeof(count), 1, mIdsStream)) {
00828         fclose(mIdsStream);
00829         mIdsStream = 0;
00830         return false;
00831     }
00832     if (swapByteOrder)
00833         count = kmail_swap_32(count);
00834 
00835     mUnreadMsgs = 0;
00836     mSerNums.reserve(count);
00837     for (unsigned int index = 0; index < count; index++) {
00838         TQ_UINT32 serNum;
00839         int folderIdx = -1;
00840         KMFolder *folder = 0;
00841         bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
00842         if (!readOk) {
00843             clearIndex();
00844             fclose(mIdsStream);
00845             mIdsStream = 0;
00846             return false;
00847         }
00848         if (swapByteOrder)
00849             serNum = kmail_swap_32(serNum);
00850 
00851         KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx );
00852         if (!folder || (folderIdx == -1)) {
00853             clearIndex();
00854             fclose(mIdsStream);
00855             mIdsStream = 0;
00856             return false;
00857         }
00858         mSerNums.push_back(serNum);
00859         if(mFolders.findIndex(folder) == -1) {
00860             if (mInvalid) //exceptional case for when folder has invalid ids
00861                 return false;
00862             folder->open("foldersearch");
00863             mFolders.append(folder);
00864         }
00865         KMMsgBase *mb = folder->getMsgBase(folderIdx);
00866         if (!mb) //Exceptional case our .ids file is messed up
00867             return false;
00868         if (mb->isNew() || mb->isUnread()) {
00869             if (mUnreadMsgs == -1) ++mUnreadMsgs;
00870             ++mUnreadMsgs;
00871         }
00872     }
00873     mTotalMsgs = mSerNums.count();
00874     fclose(mIdsStream);
00875     mIdsStream = 0;
00876     mUnlinked = true;
00877     return true;
00878 }
00879 
00880 int KMFolderSearch::removeContents()
00881 {
00882     unlink(TQFile::encodeName(location()));
00883     unlink(TQFile::encodeName(indexLocation()));
00884     mUnlinked = true;
00885     return 0;
00886 }
00887 
00888 int KMFolderSearch::expungeContents()
00889 {
00890     setSearch(new KMSearch());
00891     return 0;
00892 }
00893 
00894 int KMFolderSearch::count(bool cache) const
00895 {
00896     Q_UNUSED(cache);
00897     return mSerNums.count();
00898 }
00899 
00900 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
00901 {
00902     assert(idx >= 0 && idx < (int)mSerNums.count());
00903     KMMsgBase *msgBase = getMsgBase(idx);
00904     TQValueVector<TQ_UINT32>::iterator it = mSerNums.begin();
00905     mSerNums.erase(&it[idx]);
00906     return msgBase;
00907 }
00908 
00909 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
00910 {
00911     assert(idx >= 0 && idx < (int)mSerNums.count());
00912     Q_UNUSED( idx );
00913     return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg);
00914 }
00915 
00916 void KMFolderSearch::clearIndex(bool, bool)
00917 {
00918   //close all referenced folders
00919   TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
00920   for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00921     if (!(*fit))
00922       continue;
00923     (*fit)->close("foldersearch");
00924   }
00925   mFolders.clear();
00926 
00927   mSerNums.clear();
00928 }
00929 
00930 void KMFolderSearch::truncateIndex()
00931 {
00932     truncate(TQFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
00933 }
00934 
00935 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, TQ_UINT32 serNum)
00936 {
00937     if (!search() && !readSearch())
00938         return;
00939     if (!search()->inScope(aFolder))
00940         return;
00941     if (!mTempOpened) {
00942         open("foldersearch");
00943         mTempOpened = true;
00944     }
00945 
00946     if (!search()->searchPattern())
00947         return;
00948 
00949     int idx = -1;
00950     KMFolder *folder = 0;
00951     KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00952     assert(folder && (idx != -1));
00953     assert(folder == aFolder);
00954     KMFolderOpener openFolder(folder, "foldersearch");
00955 
00956     // if we are already checking this folder, refcount
00957     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00958       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00959       mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
00960     } else {
00961       connect( folder->storage(),
00962               TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ),
00963               this,
00964               TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
00965                       const KMSearchPattern*, bool ) ) );
00966       mFoldersCurrentlyBeingSearched.insert( folder, 1 );
00967     }
00968     folder->storage()->search( search()->searchPattern(), serNum );
00969 }
00970 
00971 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder,
00972                                                TQ_UINT32 serNum,
00973                                                const KMSearchPattern* pattern,
00974                                                bool matches )
00975 {
00976     if ( search()->searchPattern() != pattern ) return;
00977     kdDebug(5006) << folder->label() << ": serNum " << serNum
00978      << " matches?" << matches << endl;
00979     KMFolderOpener openFolder(folder, "foldersearch");
00980 
00981     Q_ASSERT( mFoldersCurrentlyBeingSearched.contains( folder ) );
00982 
00983     unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00984     if ( count == 1 ) {
00985       disconnect( folder->storage(),
00986                   TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32,
00987                                       const KMSearchPattern*, bool ) ),
00988                   this,
00989                   TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
00990                                                   const KMSearchPattern*, bool ) ) );
00991       mFoldersCurrentlyBeingSearched.remove( folder );
00992     } else {
00993       mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00994     }
00995 
00996     if ( !matches ) {
00997         TQValueVector<TQ_UINT32>::const_iterator it;
00998         it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
00999         if (it != mSerNums.end()) {
01000             removeSerNum( serNum );
01001         }
01002         return;
01003     }
01004 
01005 //    if (mSearch->running()) {
01006 //        mSearch->stop();
01007 //        mExecuteSearchTimer->start( 0, true );
01008 //    } else {
01009         TQValueVector<TQ_UINT32>::const_iterator it;
01010         it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
01011         if (it == mSerNums.end()) {
01012             addSerNum( serNum );
01013         }
01014 //    }
01015 }
01016 
01017 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, TQ_UINT32 serNum)
01018 {
01019     if (!search() && !readSearch())
01020         return;
01021     if (!search()->inScope(folder))
01022         return;
01023     if (!mTempOpened) {
01024         open("foldersearch");
01025         mTempOpened = true;
01026     }
01027 
01028     if (mSearch->running()) {
01029         mExecuteSearchTimer->start(0, true);
01030     } else {
01031         removeSerNum(serNum);
01032     }
01033 }
01034 
01035 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, TQ_UINT32 serNum, int delta)
01036 {
01037     if (!search() && !readSearch())
01038         return;
01039     if (!search()->inScope(aFolder))
01040         return;
01041     if (!mTempOpened) {
01042         open("foldersearch");
01043         mTempOpened = true;
01044     }
01045     TQValueVector<TQ_UINT32>::const_iterator it;
01046     it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
01047     if (it != mSerNums.end()) {
01048         mUnreadMsgs += delta;
01049         emit numUnreadMsgsChanged( folder() );
01050         emit msgChanged( folder(), serNum, delta );
01051     }
01052 }
01053 
01054 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01055 {
01056     if (!search() && !readSearch())
01057         return;
01058     if (!search()->inScope(folder))
01059         return;
01060     if (mTempOpened) {
01061         close("foldersearch");
01062         mTempOpened = false;
01063     }
01064 
01065     mInvalid = true;
01066     if (mSearch)
01067         mSearch->stop();
01068 
01069     if (!mUnlinked) {
01070         unlink(TQFile::encodeName(indexLocation()));
01071         mUnlinked = true;
01072     }
01073 
01074     if (!isOpened()) //give up, until the user manually opens the folder
01075         return;
01076 
01077     if (!mTempOpened) {
01078         open("foldersearch");
01079         mTempOpened = true;
01080     }
01081     mExecuteSearchTimer->start(0, true);
01082 }
01083 
01084 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01085 {
01086     examineInvalidatedFolder(folder);
01087     if (mSearch->root() == folder) {
01088         delete mSearch;
01089         mSearch = 0;
01090     }
01091 }
01092 
01093 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
01094 {
01095     int pos = 0;
01096     if (!search() && !readSearch())
01097         return;
01098     if (!search()->inScope(aFolder))
01099         return;
01100     if (!mTempOpened) {
01101         open("foldersearch");
01102         mTempOpened = true;
01103     }
01104 
01105     TQ_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01106     TQValueVector<TQ_UINT32>::const_iterator it;
01107     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01108         if ((*it) == serNum) {
01109             emit msgHeaderChanged(folder(), pos);
01110             break;
01111         }
01112         ++pos;
01113     }
01114     // let's try if the message matches our search
01115     KMFolderOpener openAFolder(aFolder, "foldersearch");
01116 
01117     // if we are already checking this folder, refcount
01118     if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
01119       unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
01120       mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
01121     } else {
01122       connect( aFolder->storage(),
01123               TQT_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ),
01124               this,
01125               TQT_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
01126                       const KMSearchPattern*, bool ) ) );
01127       mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
01128     }
01129     aFolder->storage()->search( search()->searchPattern(), serNum );
01130 }
01131 
01132 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
01133 {
01134   // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
01135   // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
01136   if ( mTempOpened && mOpenCount == 1 )
01137   {
01138     examineInvalidatedFolder( folder );
01139   }
01140 }
01141 
01142 #include "kmfoldersearch.moc"