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