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