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 <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 ) continue;
00224             // explicitely stop jobs for this folder as it will not be closed below
00225             // when the folder is currently selected
00226             if ( folder->folderType() == KMFolderTypeImap ) {
00227                 KMAcctImap *account =
00228                     static_cast<KMFolderImap*>( folder->storage() )->account();
00229                 account->ignoreJobsForFolder( folder );
00230             }
00231             folder->storage()->search( 0 );
00232             mSearchCount += folder->count();
00233             folder->close("kmsearch");
00234         }
00235     }
00236     mRemainingFolders = -1;
00237     mOpenedFolders.clear();
00238     mFolders.clear();
00239     mLastFolder = QString::null;
00240     mRunByIndex = mRunning = false;
00241     emit finished(false);
00242 }
00243 
00244 void KMSearch::indexFinished() {
00245     mRunning = false;
00246     mRunByIndex = false;
00247 }
00248 
00249 void KMSearch::slotProcessNextBatch()
00250 {
00251     if ( !running() )
00252         return;
00253 
00254     if ( mFolders.count() != 0 )
00255     {
00256         KMFolder *folder = *( mFolders.begin() );
00257         mFolders.erase( mFolders.begin() );
00258         if ( folder )
00259         {
00260             mLastFolder = folder->label();
00261             folder->open("kmsearch");
00262             mOpenedFolders.append( folder );
00263             connect( folder->storage(),
00264                 SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
00265                 this,
00266                 SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
00267             folder->storage()->search( mSearchPattern );
00268         } else
00269           --mRemainingFolders;
00270         mProcessNextBatchTimer->start( 0, true );
00271         return;
00272     }
00273 }
00274 
00275 void KMSearch::slotSearchFolderResult( KMFolder* folder,
00276                                        QValueList<Q_UINT32> serNums,
00277                                        const KMSearchPattern* pattern,
00278                                        bool complete )
00279 {
00280     if ( pattern != mSearchPattern ) return;
00281     kdDebug(5006) << k_funcinfo << folder->label() << " found " << serNums.count() << endl;
00282     mLastFolder = folder->label();
00283     QValueListIterator<Q_UINT32> it;
00284     for ( it = serNums.begin(); it != serNums.end(); ++it )
00285     {
00286       emit found( *it );
00287       ++mFoundCount;
00288     }
00289     if ( complete )
00290     {
00291       disconnect( folder->storage(),
00292           SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>,
00293                                 const KMSearchPattern*, bool ) ),
00294           this,
00295           SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>,
00296                                         const KMSearchPattern*, bool ) ) );
00297       --mRemainingFolders;
00298       mSearchCount += folder->count();
00299       folder->close("kmsearch");
00300       mOpenedFolders.remove( folder );
00301       if ( mRemainingFolders <= 0 )
00302       {
00303         mRemainingFolders = 0;
00304         mRunning = false;
00305         mLastFolder = QString::null;
00306         mRemainingFolders = -1;
00307         mFolders.clear();
00308         emit finished( true );
00309       }
00310     }
00311 }
00312 
00313 //-----------------------------------------------------------------------------
00314 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name)
00315   : FolderStorage(folder, name)
00316 {
00317     mIdsStream = 0;
00318     mSearch = 0;
00319     mInvalid = false;
00320     mUnlinked = true;
00321     mTempOpened = false;
00322     setNoChildren(true);
00323 
00324     //Hook up some slots for live updating of search folders
00325     //TODO: Optimize folderInvalidated, folderAdded, folderRemoved
00326     connect(kmkernel->folderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00327             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00328     connect(kmkernel->folderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00329             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00330     connect(kmkernel->folderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00331             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00332     connect(kmkernel->folderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00333             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00334     connect(kmkernel->folderMgr(), SIGNAL(folderAdded(KMFolder*)),
00335             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00336     connect(kmkernel->folderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00337             this, SLOT(examineRemovedFolder(KMFolder*)));
00338     connect(kmkernel->folderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00339             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00340 
00341     connect(kmkernel->imapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00342             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00343     connect(kmkernel->imapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00344             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00345     connect(kmkernel->imapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00346             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00347     connect(kmkernel->imapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00348             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00349     connect(kmkernel->imapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00350             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00351     connect(kmkernel->imapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00352             this, SLOT(examineRemovedFolder(KMFolder*)));
00353     connect(kmkernel->imapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00354             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00355 
00356     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00357             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00358     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00359             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00360     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00361             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00362     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00363             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00364     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00365             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00366     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00367             this, SLOT(examineRemovedFolder(KMFolder*)));
00368     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00369             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00370 
00371   mExecuteSearchTimer = new QTimer(0, "mExecuteSearchTimer");
00372   connect(mExecuteSearchTimer, SIGNAL(timeout()),
00373           this, SLOT(executeSearch()));
00374 }
00375 
00376 KMFolderSearch::~KMFolderSearch()
00377 {
00378     delete mExecuteSearchTimer;
00379     delete mSearch;
00380     mSearch = 0;
00381     if (mOpenCount > 0)
00382         close("~foldersearch", TRUE);
00383 }
00384 
00385 void KMFolderSearch::setSearch(KMSearch *search)
00386 {
00387     truncateIndex(); //new search old index is obsolete
00388     emit cleared();
00389     mInvalid = false;
00390     setDirty( true ); //have to write the index
00391     if (!mUnlinked) {
00392         unlink(QFile::encodeName(indexLocation()));
00393         mUnlinked = true;
00394     }
00395     if (mSearch != search) {
00396         mSearch->stop();
00397         delete mSearch;
00398         mSearch = search; // take ownership
00399         if (mSearch) {
00400             QObject::connect(search, SIGNAL(found(Q_UINT32)),
00401                     SLOT(addSerNum(Q_UINT32)));
00402             QObject::connect(search, SIGNAL(finished(bool)),
00403                     SLOT(searchFinished(bool)));
00404         }
00405     }
00406     if (mSearch)
00407         mSearch->write(location());
00408     clearIndex();
00409     mTotalMsgs = 0;
00410     mUnreadMsgs = 0;
00411     emit numUnreadMsgsChanged( folder() );
00412     emit changed(); // really want a kmfolder cleared signal
00413     /* TODO There is KMFolder::cleared signal now. Adjust. */
00414     if (mSearch)
00415         mSearch->start();
00416     open("foldersearch"); // will be closed in searchFinished
00417 }
00418 
00419 void KMFolderSearch::executeSearch()
00420 {
00421     if (mSearch)
00422         mSearch->stop();
00423     setSearch(mSearch);
00424     invalidateFolder();
00425 }
00426 
00427 const KMSearch* KMFolderSearch::search() const
00428 {
00429     return mSearch;
00430 }
00431 
00432 void KMFolderSearch::searchFinished(bool success)
00433 {
00434     if (!success)
00435         mSerNums.clear();
00436     close("foldersearch");
00437 }
00438 
00439 void KMFolderSearch::addSerNum(Q_UINT32 serNum)
00440 {
00441     if (mInvalid) // A new search is scheduled don't bother doing anything
00442         return;
00443     int idx = -1;
00444     KMFolder *aFolder = 0;
00445     KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00446     assert(aFolder && (idx != -1));
00447     if(mFolders.findIndex(aFolder) == -1) {
00448         aFolder->open("foldersearch");
00449         mFolders.append(aFolder);
00450     }
00451     setDirty( true ); //TODO append a single entry to .ids file and sync.
00452     if (!mUnlinked) {
00453         unlink(QFile::encodeName(indexLocation()));
00454         mUnlinked = true;
00455     }
00456     mSerNums.append(serNum);
00457     KMMsgBase *mb = aFolder->getMsgBase(idx);
00458     if (mb && (mb->isUnread() || mb->isNew())) {
00459        if (mUnreadMsgs == -1)
00460            mUnreadMsgs = 0;
00461        ++mUnreadMsgs;
00462        emit numUnreadMsgsChanged( folder() );
00463     }
00464     emitMsgAddedSignals(mSerNums.count()-1);
00465 }
00466 
00467 void KMFolderSearch::removeSerNum(Q_UINT32 serNum)
00468 {
00469     QValueVector<Q_UINT32>::const_iterator it;
00470     int i = 0;
00471     for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i)
00472         if ((*it) == serNum) {
00473             int idx = -1;
00474             KMFolder *aFolder = 0;
00475             KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00476             assert(aFolder && (idx != -1));
00477             emit msgRemoved(folder(), serNum);
00478             removeMsg(i);
00479             return;
00480         }
00481     if (!mUnlinked) {
00482         unlink(QFile::encodeName(indexLocation()));
00483         mUnlinked = true;
00484     }
00485 }
00486 
00487 int KMFolderSearch::addMsg(KMMessage*, int* index_return)
00488 {
00489     //Not supported search folder can't own messages
00490     *index_return = -1;
00491     return 0;
00492 }
00493 
00494 bool KMFolderSearch::readSearch()
00495 {
00496     mSearch = new KMSearch;
00497     QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00498     QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00499     return mSearch->read(location());
00500 }
00501 
00502 int KMFolderSearch::open(const char *)
00503 {
00504     mOpenCount++;
00505     kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00506     if (mOpenCount > 1)
00507         return 0;  // already open
00508 
00509     readConfig();
00510     if (!mSearch && !readSearch())
00511         return -1;
00512 
00513     emit cleared();
00514     if (!mSearch || !search()->running())
00515         if (!readIndex()) {
00516             executeSearch();
00517         }
00518 
00519     return 0;
00520 }
00521 
00522 int KMFolderSearch::canAccess()
00523 {
00524     assert(!folder()->name().isEmpty());
00525 
00526     if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0)
00527         return 1;
00528     return 0;
00529 }
00530 
00531 void KMFolderSearch::sync()
00532 {
00533     if (mDirty) {
00534         if (mSearch)
00535             mSearch->write(location());
00536         updateIndex();
00537     }
00538 }
00539 
00540 void KMFolderSearch::reallyDoClose(const char* owner)
00541 {
00542     if (mAutoCreateIndex) {
00543         if (mSearch)
00544             mSearch->write(location());
00545         updateIndex();
00546         if (mSearch && search()->running())
00547             mSearch->stop();
00548         writeConfig();
00549     }
00550 
00551     //close all referenced folders
00552     QValueListIterator<QGuardedPtr<KMFolder> > fit;
00553     for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00554         if (!(*fit))
00555             continue;
00556         (*fit)->close("foldersearch");
00557     }
00558     mFolders.clear();
00559 
00560     clearIndex(TRUE);
00561 
00562     if (mIdsStream)
00563         fclose(mIdsStream);
00564 
00565     mOpenCount   = 0;
00566     mIdsStream = 0;
00567     mUnreadMsgs  = -1;
00568 }
00569 
00570 int KMFolderSearch::create()
00571 {
00572     int old_umask;
00573     int rc = unlink(QFile::encodeName(location()));
00574     if (!rc)
00575         return rc;
00576     rc = 0;
00577 
00578     assert(!folder()->name().isEmpty());
00579     assert(mOpenCount == 0);
00580 
00581     kdDebug(5006) << "Creating folder " << location() << endl;
00582     if (access(QFile::encodeName(location()), F_OK) == 0) {
00583         kdDebug(5006) << "KMFolderSearch::create call to access function failed."
00584             << endl;
00585         return EEXIST;
00586     }
00587 
00588     old_umask = umask(077);
00589     FILE *mStream = fopen(QFile::encodeName(location()), "w+");
00590     umask(old_umask);
00591     if (!mStream) return errno;
00592     fclose(mStream);
00593 
00594     clearIndex();
00595     if (!mSearch) {
00596         mSearch = new KMSearch();
00597         QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00598         QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00599     }
00600     mSearch->write(location());
00601     mOpenCount++;
00602     mChanged = false;
00603     mUnreadMsgs = 0;
00604     mTotalMsgs = 0;
00605     return rc;
00606 }
00607 
00608 int KMFolderSearch::compact( bool )
00609 {
00610     needsCompact = false;
00611     return 0;
00612 }
00613 
00614 bool KMFolderSearch::isReadOnly() const
00615 {
00616     return false; //TODO: Make it true and get that working ok
00617 }
00618 
00619 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00620                                      KMFolder*, QString, const AttachmentStrategy* ) const
00621 {
00622     // Should never be called
00623     assert(0);
00624     return 0;
00625 }
00626 
00627 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&,
00628                                        FolderJob::JobType, KMFolder*) const
00629 {
00630     // Should never be called
00631     assert(0);
00632     return 0;
00633 }
00634 
00635 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
00636 {
00637     int folderIdx = -1;
00638     KMFolder *folder = 0;
00639     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00640         return 0;
00641     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00642     assert(folder && (folderIdx != -1));
00643     return folder->getMsgBase(folderIdx);
00644 }
00645 
00646 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
00647 {
00648     int folderIdx = -1;
00649     KMFolder *folder = 0;
00650     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00651         return 0;
00652     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00653     if (!folder || folderIdx == -1)
00654         return 0; //exceptional case
00655     return folder->getMsgBase(folderIdx);
00656 }
00657 
00658 //-----------------------------------------------------------------------------
00659 KMMessage* KMFolderSearch::getMsg(int idx)
00660 {
00661     int folderIdx = -1;
00662     KMFolder *folder = 0;
00663     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00664         return 0;
00665     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00666     assert(folder && (folderIdx != -1));
00667     KMMessage* msg = folder->getMsg( folderIdx );
00668     return msg;
00669 }
00670 
00671 //-------------------------------------------------------------
00672 void
00673 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
00674 {
00675     if ( !msg || msg->transferInProgress() )
00676         return;
00677     /* While non-imap folders manage their jobs themselves, imap ones let
00678        their account manage them. Therefor first clear the jobs managed by
00679        this folder via the inherited method, then clear the imap ones. */
00680     FolderStorage::ignoreJobsForMessage( msg );
00681 
00682     if (msg->parent()->folderType() == KMFolderTypeImap) {
00683         KMAcctImap *account =
00684             static_cast<KMFolderImap*>( msg->storage() )->account();
00685         if( !account )
00686             return;
00687         account->ignoreJobsForMessage( msg );
00688     }
00689 }
00690 
00691 
00692 int KMFolderSearch::find(const KMMsgBase* msg) const
00693 {
00694     int pos = 0;
00695     Q_UINT32 serNum = msg->getMsgSerNum();
00696     QValueVector<Q_UINT32>::const_iterator it;
00697     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00698         if ((*it) == serNum)
00699             return pos;
00700         ++pos;
00701     }
00702     return -1;
00703 }
00704 
00705 QString KMFolderSearch::indexLocation() const
00706 {
00707     QString sLocation(folder()->path());
00708 
00709     if (!sLocation.isEmpty()) sLocation += '/';
00710     sLocation += '.';
00711     sLocation += dotEscape(fileName());
00712     sLocation += ".index";
00713     sLocation += ".search";
00714 
00715     return sLocation;
00716 }
00717 
00718 int KMFolderSearch::updateIndex()
00719 {
00720     if (mSearch && search()->running())
00721         unlink(QFile::encodeName(indexLocation()));
00722     else
00723         if (dirty())
00724             return writeIndex();
00725     return 0;
00726 }
00727 
00728 int KMFolderSearch::writeIndex( bool )
00729 {
00730     // TODO:If we fail to write the index we should panic the kernel
00731     // TODO:and the same for other folder types too, and the msgDict.
00732     QString filename = indexLocation();
00733     int old_umask = umask(077);
00734     QString tempName = filename + ".temp";
00735     unlink(QFile::encodeName(tempName));
00736 
00737     // We touch the folder, otherwise the index is regenerated, if KMail is
00738     // running, while the clock switches from daylight savings time to normal time
00739     utime(QFile::encodeName(location()), 0);
00740 
00741     FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00742     umask(old_umask);
00743 
00744     if (!tmpIndexStream) {
00745         kdDebug(5006) << "Cannot write '" << filename
00746             << strerror(errno) << " (" << errno << ")" << endl;
00747         truncate(QFile::encodeName(filename), 0);
00748         return -1;
00749     }
00750     fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION);
00751     Q_UINT32 byteOrder = 0x12345678;
00752     fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00753 
00754     Q_UINT32 count = mSerNums.count();
00755     if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
00756         fclose(tmpIndexStream);
00757         truncate(QFile::encodeName(filename), 0);
00758         return -1;
00759     }
00760 
00761     QValueVector<Q_UINT32>::iterator it;
00762     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00763         Q_UINT32 serNum = *it;
00764         if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
00765             return -1;
00766     }
00767     if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
00768     if (fflush(tmpIndexStream) != 0) return errno;
00769     if (fsync(fileno(tmpIndexStream)) != 0) return errno;
00770     if (fclose(tmpIndexStream) != 0) return errno;
00771 
00772     ::rename(QFile::encodeName(tempName), QFile::encodeName(indexLocation()));
00773     mDirty = FALSE;
00774     mUnlinked = FALSE;
00775 
00776     return 0;
00777 }
00778 
00779 DwString KMFolderSearch::getDwString(int idx)
00780 {
00781     return getMsgBase(idx)->parent()->getDwString( idx );
00782 }
00783 
00784 KMMessage* KMFolderSearch::readMsg(int idx)
00785 {
00786     int folderIdx = -1;
00787     KMFolder *folder = 0;
00788     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00789     assert(folder && (folderIdx != -1));
00790     return folder->getMsg( folderIdx );
00791 }
00792 
00793 bool KMFolderSearch::readIndex()
00794 {
00795     clearIndex();
00796     QString filename = indexLocation();
00797     mIdsStream = fopen(QFile::encodeName(filename), "r+");
00798     if (!mIdsStream)
00799         return false;
00800 
00801     int version = 0;
00802     fscanf(mIdsStream, IDS_SEARCH_HEADER, &version);
00803     if (version != IDS_SEARCH_VERSION) {
00804         fclose(mIdsStream);
00805         mIdsStream = 0;
00806         return false;
00807     }
00808     bool swapByteOrder;
00809     Q_UINT32 byte_order;
00810     if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
00811         fclose(mIdsStream);
00812         mIdsStream = 0;
00813         return false;
00814     }
00815     swapByteOrder = (byte_order == 0x78563412);
00816 
00817     Q_UINT32 count;
00818     if (!fread(&count, sizeof(count), 1, mIdsStream)) {
00819         fclose(mIdsStream);
00820         mIdsStream = 0;
00821         return false;
00822     }
00823     if (swapByteOrder)
00824         count = kmail_swap_32(count);
00825 
00826     mUnreadMsgs = 0;
00827     mSerNums.reserve(count);
00828     for (unsigned int index = 0; index < count; index++) {
00829         Q_UINT32 serNum;
00830         int folderIdx = -1;
00831         KMFolder *folder = 0;
00832         bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
00833         if (!readOk) {
00834             clearIndex();
00835             fclose(mIdsStream);
00836             mIdsStream = 0;
00837             return false;
00838         }
00839         if (swapByteOrder)
00840             serNum = kmail_swap_32(serNum);
00841 
00842         KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx );
00843         if (!folder || (folderIdx == -1)) {
00844             clearIndex();
00845             fclose(mIdsStream);
00846             mIdsStream = 0;
00847             return false;
00848         }
00849         mSerNums.push_back(serNum);
00850         if(mFolders.findIndex(folder) == -1) {
00851             if (mInvalid) //exceptional case for when folder has invalid ids
00852                 return false;
00853             folder->open("foldersearch");
00854             mFolders.append(folder);
00855         }
00856         KMMsgBase *mb = folder->getMsgBase(folderIdx);
00857         if (!mb) //Exceptional case our .ids file is messed up
00858             return false;
00859         if (mb->isNew() || mb->isUnread()) {
00860             if (mUnreadMsgs == -1) ++mUnreadMsgs;
00861             ++mUnreadMsgs;
00862         }
00863     }
00864     mTotalMsgs = mSerNums.count();
00865     fclose(mIdsStream);
00866     mIdsStream = 0;
00867     mUnlinked = true;
00868     return true;
00869 }
00870 
00871 int KMFolderSearch::removeContents()
00872 {
00873     unlink(QFile::encodeName(location()));
00874     unlink(QFile::encodeName(indexLocation()));
00875     mUnlinked = true;
00876     return 0;
00877 }
00878 
00879 int KMFolderSearch::expungeContents()
00880 {
00881     setSearch(new KMSearch());
00882     return 0;
00883 }
00884 
00885 int KMFolderSearch::count(bool cache) const
00886 {
00887     Q_UNUSED(cache);
00888     return mSerNums.count();
00889 }
00890 
00891 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
00892 {
00893     assert(idx >= 0 && idx < (int)mSerNums.count());
00894     KMMsgBase *msgBase = getMsgBase(idx);
00895     QValueVector<Q_UINT32>::iterator it = mSerNums.begin();
00896     mSerNums.erase(&it[idx]);
00897     return msgBase;
00898 }
00899 
00900 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
00901 {
00902     assert(idx >= 0 && idx < (int)mSerNums.count());
00903     Q_UNUSED( idx );
00904     return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg);
00905 }
00906 
00907 void KMFolderSearch::clearIndex(bool, bool)
00908 {
00909   //close all referenced folders
00910   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00911   for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00912     if (!(*fit))
00913       continue;
00914     (*fit)->close("foldersearch");
00915   }
00916   mFolders.clear();
00917 
00918   mSerNums.clear();
00919 }
00920 
00921 void KMFolderSearch::truncateIndex()
00922 {
00923     truncate(QFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
00924 }
00925 
00926 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum)
00927 {
00928     if (!search() && !readSearch())
00929         return;
00930     if (!search()->inScope(aFolder))
00931         return;
00932     if (!mTempOpened) {
00933         open("foldersearch");
00934         mTempOpened = true;
00935     }
00936 
00937     if (!search()->searchPattern())
00938         return;
00939 
00940     int idx = -1;
00941     KMFolder *folder = 0;
00942     KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00943     assert(folder && (idx != -1));
00944     assert(folder == aFolder);
00945     KMFolderOpener openFolder(folder, "foldersearch");
00946 
00947     // if we are already checking this folder, refcount
00948     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00949       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00950       mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
00951     } else {
00952       connect( folder->storage(),
00953               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
00954               this,
00955               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00956                       const KMSearchPattern*, bool ) ) );
00957       mFoldersCurrentlyBeingSearched.insert( folder, 1 );
00958     }
00959     folder->storage()->search( search()->searchPattern(), serNum );
00960 }
00961 
00962 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder,
00963                                                Q_UINT32 serNum,
00964                                                const KMSearchPattern* pattern,
00965                                                bool matches )
00966 {
00967     if ( search()->searchPattern() != pattern ) return;
00968     kdDebug(5006) << folder->label() << ": serNum " << serNum
00969      << " matches?" << matches << endl;
00970     KMFolderOpener openFolder(folder, "foldersearch");
00971 
00972     Q_ASSERT( mFoldersCurrentlyBeingSearched.contains( folder ) );
00973 
00974     unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00975     if ( count == 1 ) {
00976       disconnect( folder->storage(),
00977                   SIGNAL( searchDone( KMFolder*, Q_UINT32,
00978                                       const KMSearchPattern*, bool ) ),
00979                   this,
00980                   SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00981                                                   const KMSearchPattern*, bool ) ) );
00982       mFoldersCurrentlyBeingSearched.remove( folder );
00983     } else {
00984       mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00985     }
00986 
00987     if ( !matches ) {
00988         QValueVector<Q_UINT32>::const_iterator it;
00989         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
00990         if (it != mSerNums.end()) {
00991             removeSerNum( serNum );
00992         }
00993         return;
00994     }
00995 
00996 //    if (mSearch->running()) {
00997 //        mSearch->stop();
00998 //        mExecuteSearchTimer->start( 0, true );
00999 //    } else {
01000         QValueVector<Q_UINT32>::const_iterator it;
01001         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01002         if (it == mSerNums.end()) {
01003             addSerNum( serNum );
01004         }
01005 //    }
01006 }
01007 
01008 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum)
01009 {
01010     if (!search() && !readSearch())
01011         return;
01012     if (!search()->inScope(folder))
01013         return;
01014     if (!mTempOpened) {
01015         open("foldersearch");
01016         mTempOpened = true;
01017     }
01018 
01019     if (mSearch->running()) {
01020         mExecuteSearchTimer->start(0, true);
01021     } else {
01022         removeSerNum(serNum);
01023     }
01024 }
01025 
01026 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, Q_UINT32 serNum, int delta)
01027 {
01028     if (!search() && !readSearch())
01029         return;
01030     if (!search()->inScope(aFolder))
01031         return;
01032     if (!mTempOpened) {
01033         open("foldersearch");
01034         mTempOpened = true;
01035     }
01036     QValueVector<Q_UINT32>::const_iterator it;
01037     it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01038     if (it != mSerNums.end()) {
01039         mUnreadMsgs += delta;
01040         emit numUnreadMsgsChanged( folder() );
01041         emit msgChanged( folder(), serNum, delta );
01042     }
01043 }
01044 
01045 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01046 {
01047     if (!search() && !readSearch())
01048         return;
01049     if (!search()->inScope(folder))
01050         return;
01051     if (mTempOpened) {
01052         close("foldersearch");
01053         mTempOpened = false;
01054     }
01055 
01056     mInvalid = true;
01057     if (mSearch)
01058         mSearch->stop();
01059 
01060     if (!mUnlinked) {
01061         unlink(QFile::encodeName(indexLocation()));
01062         mUnlinked = true;
01063     }
01064 
01065     if (!isOpened()) //give up, until the user manually opens the folder
01066         return;
01067 
01068     if (!mTempOpened) {
01069         open("foldersearch");
01070         mTempOpened = true;
01071     }
01072     mExecuteSearchTimer->start(0, true);
01073 }
01074 
01075 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01076 {
01077     examineInvalidatedFolder(folder);
01078     if (mSearch->root() == folder) {
01079         delete mSearch;
01080         mSearch = 0;
01081     }
01082 }
01083 
01084 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
01085 {
01086     int pos = 0;
01087     if (!search() && !readSearch())
01088         return;
01089     if (!search()->inScope(aFolder))
01090         return;
01091     if (!mTempOpened) {
01092         open("foldersearch");
01093         mTempOpened = true;
01094     }
01095 
01096     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01097     QValueVector<Q_UINT32>::const_iterator it;
01098     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01099         if ((*it) == serNum) {
01100             emit msgHeaderChanged(folder(), pos);
01101             break;
01102         }
01103         ++pos;
01104     }
01105     // let's try if the message matches our search
01106     KMFolderOpener openAFolder(aFolder, "foldersearch");
01107 
01108     // if we are already checking this folder, refcount
01109     if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
01110       unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
01111       mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
01112     } else {
01113       connect( aFolder->storage(),
01114               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
01115               this,
01116               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
01117                       const KMSearchPattern*, bool ) ) );
01118       mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
01119     }
01120     aFolder->storage()->search( search()->searchPattern(), serNum );
01121 }
01122 
01123 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
01124 {
01125   // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
01126   // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
01127   if ( mTempOpened && mOpenCount == 1 )
01128   {
01129     examineInvalidatedFolder( folder );
01130   }
01131 }
01132 
01133 #include "kmfoldersearch.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys