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 
00082     mProcessNextBatchTimer = new QTimer();
00083     connect(mProcessNextBatchTimer, SIGNAL(timeout()), this, SLOT(slotProcessNextBatch()));
00084 }
00085 
00086 KMSearch::~KMSearch()
00087 {
00088     delete mProcessNextBatchTimer;
00089     delete mSearchPattern;
00090 }
00091 
00092 bool KMSearch::write(QString location) const
00093 {
00094     KConfig config(location);
00095     config.setGroup("Search Folder");
00096     if (mSearchPattern)
00097         mSearchPattern->writeConfig(&config);
00098     if (mRoot.isNull())
00099         config.writeEntry("Base", "");
00100     else
00101         config.writeEntry("Base", mRoot->idString());
00102     config.writeEntry("Recursive", recursive());
00103     return true;
00104 }
00105 
00106 bool KMSearch::read(QString location)
00107 {
00108     KConfig config( location );
00109     config.setGroup( "Search Folder" );
00110     if ( !mSearchPattern )
00111         mSearchPattern = new KMSearchPattern();
00112     mSearchPattern->readConfig( &config );
00113     QString rootString = config.readEntry( "Base" );
00114     mRoot = kmkernel->findFolderById( rootString );
00115     mRecursive = config.readBoolEntry( "Recursive" );
00116     return true;
00117 }
00118 
00119 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern)
00120 {
00121     if ( running() )
00122         stop();
00123     if ( mSearchPattern != searchPattern ) {
00124         delete mSearchPattern;
00125         mSearchPattern = searchPattern;
00126     }
00127 }
00128 
00129 bool KMSearch::inScope(KMFolder* folder) const
00130 {
00131     if ( mRoot.isNull() || folder == mRoot )
00132         return true;
00133     if ( !recursive() )
00134         return false;
00135 
00136     KMFolderDir *rootDir = mRoot->child();
00137     KMFolderDir *ancestorDir = folder->parent();
00138     while ( ancestorDir ) {
00139         if ( ancestorDir == rootDir )
00140             return true;
00141         ancestorDir = ancestorDir->parent();
00142     }
00143     return false;
00144 }
00145 
00146 void KMSearch::start()
00147 {
00148     if ( running() )
00149         return;
00150 
00151     if ( !mSearchPattern ) {
00152         emit finished(true);
00153         return;
00154     }
00155 
00156     mFoundCount = 0;
00157     mRunning = true;
00158     mRunByIndex = false;
00159     // check if this query can be done with the index
00160     if ( kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery( this ) ) {
00161         mRunByIndex = true;
00162         return;
00163     }
00164 
00165     mFolders.append( mRoot );
00166     if ( recursive() )
00167     {
00168         //Append all descendants to folders
00169         KMFolderNode* node;
00170         KMFolder* folder;
00171         QValueListConstIterator<QGuardedPtr<KMFolder> > it;
00172         for ( it = mFolders.begin(); it != mFolders.end(); ++it )
00173         {
00174             folder = *it;
00175             KMFolderDir *dir = 0;
00176             if ( folder )
00177                 dir = folder->child();
00178             else
00179                 dir = &kmkernel->folderMgr()->dir();
00180             if ( !dir )
00181                 continue;
00182             QPtrListIterator<KMFolderNode> it(*dir);
00183             while ( (node = it.current()) ) {
00184                 ++it;
00185                 if ( !node->isDir() ) {
00186                     KMFolder* kmf = dynamic_cast<KMFolder*>( node );
00187                     if ( kmf )
00188                         mFolders.append( kmf );
00189                 }
00190             }
00191         }
00192     }
00193 
00194     mRemainingFolders = mFolders.count();
00195     mLastFolder = QString::null;
00196     mProcessNextBatchTimer->start( 0, true );
00197 }
00198 
00199 void KMSearch::stop()
00200 {
00201     if ( !running() )
00202         return;
00203     if ( mRunByIndex ) {
00204         if ( kmkernel->msgIndex() )
00205             kmkernel->msgIndex()->stopQuery( this );
00206     } else {
00207         mIncompleteFolders.clear();
00208         QValueListConstIterator<QGuardedPtr<KMFolder> > jt;
00209         for ( jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt ) {
00210             KMFolder *folder = *jt;
00211             if ( !folder ) continue;
00212             // explicitely stop jobs for this folder as it will not be closed below
00213             // when the folder is currently selected
00214             if ( folder->folderType() == KMFolderTypeImap ) {
00215                 KMAcctImap *account =
00216                     static_cast<KMFolderImap*>( folder->storage() )->account();
00217                 account->ignoreJobsForFolder( folder );
00218             }
00219             folder->storage()->search( 0 );
00220             folder->close();
00221         }
00222     }
00223     mRemainingFolders = -1;
00224     mOpenedFolders.clear();
00225     mFolders.clear();
00226     mLastFolder = QString::null;
00227     mRunByIndex = mRunning = false;
00228     emit finished(false);
00229 }
00230 
00231 void KMSearch::indexFinished() {
00232     mRunning = false;
00233     mRunByIndex = false;
00234 }
00235 
00236 void KMSearch::slotProcessNextBatch()
00237 {
00238     if ( !running() )
00239         return;
00240 
00241     if ( mFolders.count() != 0 )
00242     {
00243         KMFolder *folder = *( mFolders.begin() );
00244         mFolders.erase( mFolders.begin() );
00245         if ( folder )
00246         {
00247             mLastFolder = folder->label();
00248             folder->open();
00249             mOpenedFolders.append( folder );
00250             connect( folder->storage(),
00251                 SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
00252                 this,
00253                 SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
00254             folder->storage()->search( mSearchPattern );
00255         } else
00256           --mRemainingFolders;
00257         mProcessNextBatchTimer->start( 0, true );
00258         return;
00259     }
00260 }
00261 
00262 void KMSearch::slotSearchFolderResult( KMFolder* folder,
00263                                        QValueList<Q_UINT32> serNums,
00264                                        const KMSearchPattern* pattern,
00265                                        bool complete )
00266 {
00267     if ( pattern != mSearchPattern ) return;
00268     kdDebug(5006) << k_funcinfo << folder->label() << " found " << serNums.count() << endl;
00269     mLastFolder = folder->label();
00270     QValueListIterator<Q_UINT32> it;
00271     for ( it = serNums.begin(); it != serNums.end(); ++it )
00272     {
00273       emit found( *it );
00274       ++mFoundCount;
00275     }
00276     if ( complete )
00277     {
00278       disconnect( folder->storage(),
00279           SIGNAL( searchResult( KMFolder*, QValueList<Q_UINT32>,
00280                                 const KMSearchPattern*, bool ) ),
00281           this,
00282           SLOT( slotSearchFolderResult( KMFolder*, QValueList<Q_UINT32>,
00283                                         const KMSearchPattern*, bool ) ) );
00284       --mRemainingFolders;
00285       folder->close();
00286       mOpenedFolders.remove( folder );
00287       if ( mRemainingFolders <= 0 )
00288       {
00289         mRemainingFolders = 0;
00290         mRunning = false;
00291         mLastFolder = QString::null;
00292         mRemainingFolders = -1;
00293         mFolders.clear();
00294         emit finished( true );
00295       }
00296     }
00297 }
00298 
00299 //-----------------------------------------------------------------------------
00300 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name)
00301   : FolderStorage(folder, name)
00302 {
00303     mIdsStream = 0;
00304     mSearch = 0;
00305     mInvalid = false;
00306     mUnlinked = true;
00307     mTempOpened = false;
00308     setNoChildren(true);
00309 
00310     //Hook up some slots for live updating of search folders
00311     //TODO: Optimize folderInvalidated, folderAdded, folderRemoved
00312     connect(kmkernel->folderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00313             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00314     connect(kmkernel->folderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00315             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00316     connect(kmkernel->folderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00317             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00318     connect(kmkernel->folderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00319             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00320     connect(kmkernel->folderMgr(), SIGNAL(folderAdded(KMFolder*)),
00321             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00322     connect(kmkernel->folderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00323             this, SLOT(examineRemovedFolder(KMFolder*)));
00324     connect(kmkernel->folderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00325             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00326 
00327     connect(kmkernel->imapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00328             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00329     connect(kmkernel->imapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00330             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00331     connect(kmkernel->imapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00332             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00333     connect(kmkernel->imapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00334             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00335     connect(kmkernel->imapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00336             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00337     connect(kmkernel->imapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00338             this, SLOT(examineRemovedFolder(KMFolder*)));
00339     connect(kmkernel->imapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00340             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00341 
00342     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00343             this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32)));
00344     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00345             this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32)));
00346     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00347             this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int)));
00348     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00349             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00350     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderAdded(KMFolder*)),
00351             this, SLOT(examineInvalidatedFolder(KMFolder*)));
00352     connect(kmkernel->dimapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)),
00353             this, SLOT(examineRemovedFolder(KMFolder*)));
00354     connect(kmkernel->dimapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)),
00355             this, SLOT(propagateHeaderChanged(KMFolder*,int)));
00356 
00357   mExecuteSearchTimer = new QTimer();
00358   connect(mExecuteSearchTimer, SIGNAL(timeout()),
00359           this, SLOT(executeSearch()));
00360 }
00361 
00362 KMFolderSearch::~KMFolderSearch()
00363 {
00364     delete mExecuteSearchTimer;
00365     delete mSearch;
00366     mSearch = 0;
00367     if (mOpenCount > 0)
00368         close(TRUE);
00369 }
00370 
00371 void KMFolderSearch::setSearch(KMSearch *search)
00372 {
00373     truncateIndex(); //new search old index is obsolete
00374     emit cleared();
00375     mInvalid = false;
00376     setDirty( true ); //have to write the index
00377     if (!mUnlinked) {
00378         unlink(QFile::encodeName(indexLocation()));
00379         mUnlinked = true;
00380     }
00381     if (mSearch != search) {
00382         mSearch->stop();
00383         delete mSearch;
00384         mSearch = search; // take ownership
00385         if (mSearch) {
00386             QObject::connect(search, SIGNAL(found(Q_UINT32)),
00387                     SLOT(addSerNum(Q_UINT32)));
00388             QObject::connect(search, SIGNAL(finished(bool)),
00389                     SLOT(searchFinished(bool)));
00390         }
00391     }
00392     if (mSearch)
00393         mSearch->write(location());
00394     clearIndex();
00395     mTotalMsgs = 0;
00396     mUnreadMsgs = 0;
00397     emit numUnreadMsgsChanged( folder() );
00398     emit changed(); // really want a kmfolder cleared signal
00399     /* TODO There is KMFolder::cleared signal now. Adjust. */
00400     if (mSearch)
00401         mSearch->start();
00402     open(); // will be closed in searchFinished
00403 }
00404 
00405 void KMFolderSearch::executeSearch()
00406 {
00407     if (mSearch)
00408         mSearch->stop();
00409     setSearch(mSearch);
00410     invalidateFolder();
00411 }
00412 
00413 const KMSearch* KMFolderSearch::search() const
00414 {
00415     return mSearch;
00416 }
00417 
00418 void KMFolderSearch::searchFinished(bool success)
00419 {
00420     if (!success)
00421         mSerNums.clear();
00422     close();
00423 }
00424 
00425 void KMFolderSearch::addSerNum(Q_UINT32 serNum)
00426 {
00427     if (mInvalid) // A new search is scheduled don't bother doing anything
00428         return;
00429     int idx = -1;
00430     KMFolder *aFolder = 0;
00431     KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00432     assert(aFolder && (idx != -1));
00433     if(mFolders.findIndex(aFolder) == -1) {
00434         aFolder->open();
00435         // Exceptional case, for when folder has invalid ids
00436         if (mInvalid)
00437             return;
00438         mFolders.append(aFolder);
00439     }
00440     setDirty( true ); //TODO append a single entry to .ids file and sync.
00441     if (!mUnlinked) {
00442         unlink(QFile::encodeName(indexLocation()));
00443         mUnlinked = true;
00444     }
00445     mSerNums.append(serNum);
00446     KMMsgBase *mb = aFolder->getMsgBase(idx);
00447     if (mb && (mb->isUnread() || mb->isNew())) {
00448        if (mUnreadMsgs == -1)
00449            mUnreadMsgs = 0;
00450        ++mUnreadMsgs;
00451        emit numUnreadMsgsChanged( folder() );
00452     }
00453     emitMsgAddedSignals(mSerNums.count()-1);
00454 }
00455 
00456 void KMFolderSearch::removeSerNum(Q_UINT32 serNum)
00457 {
00458     QValueVector<Q_UINT32>::const_iterator it;
00459     int i = 0;
00460     for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i)
00461         if ((*it) == serNum) {
00462             int idx = -1;
00463             KMFolder *aFolder = 0;
00464             KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
00465             assert(aFolder && (idx != -1));
00466             emit msgRemoved(folder(), serNum);
00467             removeMsg(i);
00468             return;
00469         }
00470     if (!mUnlinked) {
00471         unlink(QFile::encodeName(indexLocation()));
00472         mUnlinked = true;
00473     }
00474 }
00475 
00476 QCString& KMFolderSearch::getMsgString(int idx, QCString& mDest)
00477 {
00478     KMFolder *folder = getMsgBase(idx)->parent();
00479     assert(folder);
00480     return folder->getMsgString(folder->find(getMsgBase(idx)), mDest);
00481 }
00482 
00483 int KMFolderSearch::addMsg(KMMessage*, int* index_return)
00484 {
00485     //Not supported search folder can't own messages
00486     *index_return = -1;
00487     return 0;
00488 }
00489 
00490 bool KMFolderSearch::readSearch()
00491 {
00492     mSearch = new KMSearch;
00493     QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00494     QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00495     return mSearch->read(location());
00496 }
00497 
00498 int KMFolderSearch::open()
00499 {
00500     mOpenCount++;
00501     kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00502     if (mOpenCount > 1)
00503         return 0;  // already open
00504 
00505     readConfig();
00506     if (!mSearch && !readSearch())
00507         return -1;
00508 
00509     emit cleared();
00510     if (!mSearch || !search()->running())
00511         if (!readIndex()) {
00512             executeSearch();
00513         }
00514 
00515     return 0;
00516 }
00517 
00518 int KMFolderSearch::canAccess()
00519 {
00520     assert(!folder()->name().isEmpty());
00521 
00522     if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0)
00523         return 1;
00524     return 0;
00525 }
00526 
00527 void KMFolderSearch::sync()
00528 {
00529     if (mDirty) {
00530         if (mSearch)
00531             mSearch->write(location());
00532         updateIndex();
00533     }
00534 }
00535 
00536 void KMFolderSearch::close(bool force)
00537 {
00538     if (mOpenCount <= 0) return;
00539     if (mOpenCount > 0) mOpenCount--;
00540     if (mOpenCount > 0 && !force) return;
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();
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             folder->open();
00852             if (mInvalid) //exceptional case for when folder has invalid ids
00853                 return false;
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     mSerNums.clear();
00910 }
00911 
00912 void KMFolderSearch::truncateIndex()
00913 {
00914     truncate(QFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
00915 }
00916 
00917 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum)
00918 {
00919     if (!search() && !readSearch())
00920         return;
00921     if (!search()->inScope(aFolder))
00922         return;
00923     if (!mTempOpened) {
00924         open();
00925         mTempOpened = true;
00926     }
00927 
00928     if (!search()->searchPattern())
00929         return;
00930 
00931     int idx = -1;
00932     KMFolder *folder = 0;
00933     KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00934     assert(folder && (idx != -1));
00935     assert(folder == aFolder);
00936     folder->open();
00937 
00938     // if we are already checking this folder, refcount
00939     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00940       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00941       mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
00942     } else {
00943       connect( folder->storage(), 
00944               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
00945               this,
00946               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32, 
00947                       const KMSearchPattern*, bool ) ) );
00948       mFoldersCurrentlyBeingSearched.insert( folder, 1 );
00949     }
00950     folder->storage()->search( search()->searchPattern(), serNum );
00951 }
00952 
00953 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder, 
00954                                                Q_UINT32 serNum, 
00955                                                const KMSearchPattern* pattern,
00956                                                bool matches )
00957 {
00958     if ( search()->searchPattern() != pattern ) return;
00959     kdDebug(5006) << folder->label() << ": serNum " << serNum
00960      << " matches?" << matches << endl;
00961 
00962     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00963       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00964       if ( count == 1 ) {
00965         disconnect( folder->storage(),
00966                     SIGNAL( searchDone( KMFolder*, Q_UINT32,
00967                                         const KMSearchPattern*, bool ) ),
00968                     this,
00969                     SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00970                                                     const KMSearchPattern*, bool ) ) );
00971         mFoldersCurrentlyBeingSearched.remove( folder );
00972       } else {
00973         mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00974       }
00975     } else {
00976       Q_ASSERT( 0 ); // Can't happen (TM)
00977     }
00978     folder->close();
00979 
00980     if ( !matches ) {
00981         QValueVector<Q_UINT32>::const_iterator it;
00982         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
00983         if (it != mSerNums.end()) {
00984             removeSerNum( serNum );
00985         }
00986         return;
00987     }
00988 
00989 //    if (mSearch->running()) {
00990 //        mSearch->stop();
00991 //        mExecuteSearchTimer->start( 0, true );
00992 //    } else {
00993         QValueVector<Q_UINT32>::const_iterator it;
00994         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
00995         if (it == mSerNums.end()) {
00996             addSerNum( serNum );
00997         }
00998 //    }
00999 }
01000 
01001 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum)
01002 {
01003     if (!search() && !readSearch())
01004         return;
01005     if (!search()->inScope(folder))
01006         return;
01007     if (!mTempOpened) {
01008         open();
01009         mTempOpened = true;
01010     }
01011 
01012     if (mSearch->running()) {
01013         mExecuteSearchTimer->start(0, true);
01014     } else {
01015         removeSerNum(serNum);
01016     }
01017 }
01018 
01019 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, Q_UINT32 serNum, int delta)
01020 {
01021     if (!search() && !readSearch())
01022         return;
01023     if (!search()->inScope(aFolder))
01024         return;
01025     if (!mTempOpened) {
01026         open();
01027         mTempOpened = true;
01028     }
01029     QValueVector<Q_UINT32>::const_iterator it;
01030     it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01031     if (it != mSerNums.end()) {
01032         mUnreadMsgs += delta;
01033         emit numUnreadMsgsChanged( folder() );
01034         emit msgChanged( folder(), serNum, delta );
01035     }
01036 }
01037 
01038 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01039 {
01040     if (!search() && !readSearch())
01041         return;
01042     if (!search()->inScope(folder))
01043         return;
01044     if (mTempOpened) {
01045         close();
01046         mTempOpened = false;
01047     }
01048 
01049     mInvalid = true;
01050     if (mSearch)
01051         mSearch->stop();
01052 
01053     if (!mUnlinked) {
01054         unlink(QFile::encodeName(indexLocation()));
01055         mUnlinked = true;
01056     }
01057 
01058     if (!isOpened()) //give up, until the user manually opens the folder
01059         return;
01060 
01061     if (!mTempOpened) {
01062         open();
01063         mTempOpened = true;
01064     }
01065     mExecuteSearchTimer->start(0, true);
01066 }
01067 
01068 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01069 {
01070     examineInvalidatedFolder(folder);
01071     if (mSearch->root() == folder) {
01072         delete mSearch;
01073         mSearch = 0;
01074     }
01075 }
01076 
01077 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
01078 {
01079     int pos = 0;
01080     if (!search() && !readSearch())
01081         return;
01082     if (!search()->inScope(aFolder))
01083         return;
01084     if (!mTempOpened) {
01085         open();
01086         mTempOpened = true;
01087     }
01088 
01089     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01090     QValueVector<Q_UINT32>::const_iterator it;
01091     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01092         if ((*it) == serNum) {
01093             emit msgHeaderChanged(folder(), pos);
01094             break;
01095         }
01096         ++pos;
01097     }
01098     // let's try if the message matches our search
01099     aFolder->open();
01100 
01101     // if we are already checking this folder, refcount
01102     if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
01103       unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
01104       mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
01105     } else {
01106       connect( aFolder->storage(), 
01107               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
01108               this,
01109               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32, 
01110                       const KMSearchPattern*, bool ) ) );
01111       mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
01112     }
01113     aFolder->storage()->search( search()->searchPattern(), serNum );
01114 }
01115 
01116 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
01117 {
01118   // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
01119   // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
01120   if ( mTempOpened && mOpenCount == 1 )
01121   {
01122     examineInvalidatedFolder( folder );
01123   }
01124 }
01125 
01126 #include "kmfoldersearch.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys