kmail

kmfoldersearch.cpp

00001 /*
00002     This file is part of KMail, the KDE mail client.
00003     Copyright (c) 2000 Don Sanders <sanders@kde.org>
00004 
00005     KMail is free software; you can redistribute it and/or modify it
00006     under the terms of the GNU General Public License, version 2, as
00007     published by the Free Software Foundation.
00008 
00009     KMail is distributed in the hope that it will be useful, but
00010     WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00017 */
00018 
00019 //Factor byteswap stuff into one header file
00020 
00021 #include <config.h>
00022 
00023 #include "kmfoldersearch.h"
00024 #include "kmfolderimap.h"
00025 #include "kmfoldermgr.h"
00026 #include "kmsearchpattern.h"
00027 #include "kmmsgdict.h"
00028 #include "index.h"
00029 #include "jobscheduler.h"
00030 
00031 #include <kdebug.h>
00032 #include <klocale.h>
00033 #include <kconfig.h>
00034 
00035 #include <assert.h>
00036 #include <stdio.h>
00037 #include <unistd.h>
00038 #include <errno.h>
00039 #include <stdlib.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <sys/file.h>
00043 #include <utime.h>
00044 
00045 #include <qfile.h>
00046 
00047 #ifdef HAVE_BYTESWAP_H
00048 #include <byteswap.h>
00049 #endif
00050 
00051 // We define functions as kmail_swap_NN so that we don't get compile errors
00052 // on platforms where bswap_NN happens to be a function instead of a define.
00053 
00054 /* Swap bytes in 32 bit value.  */
00055 #ifndef kmail_swap_32
00056 #ifdef bswap_32
00057 #define kmail_swap_32(x) bswap_32(x)
00058 #else
00059 #define kmail_swap_32(x) \
00060      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |               \
00061       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
00062 #endif
00063 #endif // kmail_swap_32
00064 
00065 // Current version of the .index.search files
00066 #define IDS_SEARCH_VERSION 1000
00067 // The asterisk at the end is important
00068 #define IDS_SEARCH_HEADER "# KMail-Search-IDs V%d\n*"
00069 #define IDS_SEARCH_HEADER_LEN 30
00070 
00071 
00072 KMSearch::KMSearch(QObject * parent, const char * name)
00073     :QObject(parent, name)
00074 {
00075     mRemainingFolders = -1;
00076     mRecursive = true;
00077     mRunByIndex = mRunning = false;
00078     mRoot = 0;
00079     mSearchPattern = 0;
00080     mFoundCount = 0;
00081     mSearchCount = 0;
00082 
00083     mProcessNextBatchTimer = new QTimer(0, "mProcessNextBatchTimer");
00084     connect(mProcessNextBatchTimer, SIGNAL(timeout()), this, SLOT(slotProcessNextBatch()));
00085 }
00086 
00087 KMSearch::~KMSearch()
00088 {
00089     delete mProcessNextBatchTimer;
00090     delete mSearchPattern;
00091 }
00092 
00093 bool KMSearch::write(QString location) const
00094 {
00095     KConfig config(location);
00096     config.setGroup("Search Folder");
00097     if (mSearchPattern)
00098         mSearchPattern->writeConfig(&config);
00099     if (mRoot.isNull())
00100         config.writeEntry("Base", "");
00101     else
00102         config.writeEntry("Base", mRoot->idString());
00103     config.writeEntry("Recursive", recursive());
00104     return true;
00105 }
00106 
00107 bool KMSearch::read(QString location)
00108 {
00109     KConfig config( location );
00110     config.setGroup( "Search Folder" );
00111     if ( !mSearchPattern )
00112         mSearchPattern = new KMSearchPattern();
00113     mSearchPattern->readConfig( &config );
00114     QString rootString = config.readEntry( "Base" );
00115     mRoot = kmkernel->findFolderById( rootString );
00116     mRecursive = config.readBoolEntry( "Recursive" );
00117     return true;
00118 }
00119 
00120 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern)
00121 {
00122     if ( running() )
00123         stop();
00124     if ( mSearchPattern != searchPattern ) {
00125         delete mSearchPattern;
00126         mSearchPattern = searchPattern;
00127     }
00128 }
00129 
00130 bool KMSearch::inScope(KMFolder* folder) const
00131 {
00132     if ( mRoot.isNull() || folder == mRoot )
00133         return true;
00134     if ( !recursive() )
00135         return false;
00136 
00137     KMFolderDir *rootDir = mRoot->child();
00138     KMFolderDir *ancestorDir = folder->parent();
00139     while ( ancestorDir ) {
00140         if ( ancestorDir == rootDir )
00141             return true;
00142         ancestorDir = ancestorDir->parent();
00143     }
00144     return false;
00145 }
00146 
00147 void KMSearch::start()
00148 {
00149     //close all referenced folders
00150     QValueListIterator<QGuardedPtr<KMFolder> > fit;
00151     for (fit = 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(0, "mExecuteSearchTimer");
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::reallyDoClose(const char *)
00543 {
00544     if (mAutoCreateIndex) {
00545         if (mSearch)
00546             mSearch->write(location());
00547         updateIndex();
00548         if (mSearch && search()->running())
00549             mSearch->stop();
00550         writeConfig();
00551     }
00552 
00553     //close all referenced folders
00554     QValueListIterator<QGuardedPtr<KMFolder> > fit;
00555     for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00556         if (!(*fit))
00557             continue;
00558         (*fit)->close("foldersearch");
00559     }
00560     mFolders.clear();
00561 
00562     clearIndex(TRUE);
00563 
00564     if (mIdsStream)
00565         fclose(mIdsStream);
00566 
00567     mOpenCount   = 0;
00568     mIdsStream = 0;
00569     mUnreadMsgs  = -1;
00570 }
00571 
00572 int KMFolderSearch::create()
00573 {
00574     int old_umask;
00575     int rc = unlink(QFile::encodeName(location()));
00576     if (!rc)
00577         return rc;
00578     rc = 0;
00579 
00580     assert(!folder()->name().isEmpty());
00581     assert(mOpenCount == 0);
00582 
00583     kdDebug(5006) << "Creating folder " << location() << endl;
00584     if (access(QFile::encodeName(location()), F_OK) == 0) {
00585         kdDebug(5006) << "KMFolderSearch::create call to access function failed."
00586             << endl;
00587         return EEXIST;
00588     }
00589 
00590     old_umask = umask(077);
00591     FILE *mStream = fopen(QFile::encodeName(location()), "w+");
00592     umask(old_umask);
00593     if (!mStream) return errno;
00594     fclose(mStream);
00595 
00596     clearIndex();
00597     if (!mSearch) {
00598         mSearch = new KMSearch();
00599         QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32)));
00600         QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool)));
00601     }
00602     mSearch->write(location());
00603     mOpenCount++;
00604     mChanged = false;
00605     mUnreadMsgs = 0;
00606     mTotalMsgs = 0;
00607     return rc;
00608 }
00609 
00610 int KMFolderSearch::compact( bool )
00611 {
00612     needsCompact = false;
00613     return 0;
00614 }
00615 
00616 bool KMFolderSearch::isReadOnly() const
00617 {
00618     return false; //TODO: Make it true and get that working ok
00619 }
00620 
00621 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
00622                                      KMFolder*, QString, const AttachmentStrategy* ) const
00623 {
00624     // Should never be called
00625     assert(0);
00626     return 0;
00627 }
00628 
00629 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&,
00630                                        FolderJob::JobType, KMFolder*) const
00631 {
00632     // Should never be called
00633     assert(0);
00634     return 0;
00635 }
00636 
00637 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
00638 {
00639     int folderIdx = -1;
00640     KMFolder *folder = 0;
00641     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00642         return 0;
00643     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00644     assert(folder && (folderIdx != -1));
00645     return folder->getMsgBase(folderIdx);
00646 }
00647 
00648 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
00649 {
00650     int folderIdx = -1;
00651     KMFolder *folder = 0;
00652     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00653         return 0;
00654     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00655     if (!folder || folderIdx == -1)
00656         return 0; //exceptional case
00657     return folder->getMsgBase(folderIdx);
00658 }
00659 
00660 //-----------------------------------------------------------------------------
00661 KMMessage* KMFolderSearch::getMsg(int idx)
00662 {
00663     int folderIdx = -1;
00664     KMFolder *folder = 0;
00665     if (idx < 0 || (Q_UINT32)idx >= mSerNums.count())
00666         return 0;
00667     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00668     assert(folder && (folderIdx != -1));
00669     KMMessage* msg = folder->getMsg( folderIdx );
00670     return msg;
00671 }
00672 
00673 //-------------------------------------------------------------
00674 void
00675 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
00676 {
00677     if ( !msg || msg->transferInProgress() )
00678         return;
00679     /* While non-imap folders manage their jobs themselves, imap ones let
00680        their account manage them. Therefor first clear the jobs managed by
00681        this folder via the inherited method, then clear the imap ones. */
00682     FolderStorage::ignoreJobsForMessage( msg );
00683 
00684     if (msg->parent()->folderType() == KMFolderTypeImap) {
00685         KMAcctImap *account =
00686             static_cast<KMFolderImap*>( msg->storage() )->account();
00687         if( !account )
00688             return;
00689         account->ignoreJobsForMessage( msg );
00690     }
00691 }
00692 
00693 
00694 int KMFolderSearch::find(const KMMsgBase* msg) const
00695 {
00696     int pos = 0;
00697     Q_UINT32 serNum = msg->getMsgSerNum();
00698     QValueVector<Q_UINT32>::const_iterator it;
00699     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00700         if ((*it) == serNum)
00701             return pos;
00702         ++pos;
00703     }
00704     return -1;
00705 }
00706 
00707 QString KMFolderSearch::indexLocation() const
00708 {
00709     QString sLocation(folder()->path());
00710 
00711     if (!sLocation.isEmpty()) sLocation += '/';
00712     sLocation += '.';
00713     sLocation += dotEscape(fileName());
00714     sLocation += ".index";
00715     sLocation += ".search";
00716 
00717     return sLocation;
00718 }
00719 
00720 int KMFolderSearch::updateIndex()
00721 {
00722     if (mSearch && search()->running())
00723         unlink(QFile::encodeName(indexLocation()));
00724     else
00725         if (dirty())
00726             return writeIndex();
00727     return 0;
00728 }
00729 
00730 int KMFolderSearch::writeIndex( bool )
00731 {
00732     // TODO:If we fail to write the index we should panic the kernel
00733     // TODO:and the same for other folder types too, and the msgDict.
00734     QString filename = indexLocation();
00735     int old_umask = umask(077);
00736     QString tempName = filename + ".temp";
00737     unlink(QFile::encodeName(tempName));
00738 
00739     // We touch the folder, otherwise the index is regenerated, if KMail is
00740     // running, while the clock switches from daylight savings time to normal time
00741     utime(QFile::encodeName(location()), 0);
00742 
00743     FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00744     umask(old_umask);
00745 
00746     if (!tmpIndexStream) {
00747         kdDebug(5006) << "Cannot write '" << filename
00748             << strerror(errno) << " (" << errno << ")" << endl;
00749         truncate(QFile::encodeName(filename), 0);
00750         return -1;
00751     }
00752     fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION);
00753     Q_UINT32 byteOrder = 0x12345678;
00754     fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00755 
00756     Q_UINT32 count = mSerNums.count();
00757     if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
00758         fclose(tmpIndexStream);
00759         truncate(QFile::encodeName(filename), 0);
00760         return -1;
00761     }
00762 
00763     QValueVector<Q_UINT32>::iterator it;
00764     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
00765         Q_UINT32 serNum = *it;
00766         if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
00767             return -1;
00768     }
00769     if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
00770     if (fflush(tmpIndexStream) != 0) return errno;
00771     if (fsync(fileno(tmpIndexStream)) != 0) return errno;
00772     if (fclose(tmpIndexStream) != 0) return errno;
00773 
00774     ::rename(QFile::encodeName(tempName), QFile::encodeName(indexLocation()));
00775     mDirty = FALSE;
00776     mUnlinked = FALSE;
00777 
00778     return 0;
00779 }
00780 
00781 DwString KMFolderSearch::getDwString(int idx)
00782 {
00783     return getMsgBase(idx)->parent()->getDwString( idx );
00784 }
00785 
00786 KMMessage* KMFolderSearch::readMsg(int idx)
00787 {
00788     int folderIdx = -1;
00789     KMFolder *folder = 0;
00790     KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
00791     assert(folder && (folderIdx != -1));
00792     return folder->getMsg( folderIdx );
00793 }
00794 
00795 bool KMFolderSearch::readIndex()
00796 {
00797     clearIndex();
00798     QString filename = indexLocation();
00799     mIdsStream = fopen(QFile::encodeName(filename), "r+");
00800     if (!mIdsStream)
00801         return false;
00802 
00803     int version = 0;
00804     fscanf(mIdsStream, IDS_SEARCH_HEADER, &version);
00805     if (version != IDS_SEARCH_VERSION) {
00806         fclose(mIdsStream);
00807         mIdsStream = 0;
00808         return false;
00809     }
00810     bool swapByteOrder;
00811     Q_UINT32 byte_order;
00812     if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
00813         fclose(mIdsStream);
00814         mIdsStream = 0;
00815         return false;
00816     }
00817     swapByteOrder = (byte_order == 0x78563412);
00818 
00819     Q_UINT32 count;
00820     if (!fread(&count, sizeof(count), 1, mIdsStream)) {
00821         fclose(mIdsStream);
00822         mIdsStream = 0;
00823         return false;
00824     }
00825     if (swapByteOrder)
00826         count = kmail_swap_32(count);
00827 
00828     mUnreadMsgs = 0;
00829     mSerNums.reserve(count);
00830     for (unsigned int index = 0; index < count; index++) {
00831         Q_UINT32 serNum;
00832         int folderIdx = -1;
00833         KMFolder *folder = 0;
00834         bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
00835         if (!readOk) {
00836             clearIndex();
00837             fclose(mIdsStream);
00838             mIdsStream = 0;
00839             return false;
00840         }
00841         if (swapByteOrder)
00842             serNum = kmail_swap_32(serNum);
00843 
00844         KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx );
00845         if (!folder || (folderIdx == -1)) {
00846             clearIndex();
00847             fclose(mIdsStream);
00848             mIdsStream = 0;
00849             return false;
00850         }
00851         mSerNums.push_back(serNum);
00852         if(mFolders.findIndex(folder) == -1) {
00853             folder->open("foldersearch");
00854             if (mInvalid) //exceptional case for when folder has invalid ids
00855                 return false;
00856             mFolders.append(folder);
00857         }
00858         KMMsgBase *mb = folder->getMsgBase(folderIdx);
00859         if (!mb) //Exceptional case our .ids file is messed up
00860             return false;
00861         if (mb->isNew() || mb->isUnread()) {
00862             if (mUnreadMsgs == -1) ++mUnreadMsgs;
00863             ++mUnreadMsgs;
00864         }
00865     }
00866     mTotalMsgs = mSerNums.count();
00867     fclose(mIdsStream);
00868     mIdsStream = 0;
00869     mUnlinked = true;
00870     return true;
00871 }
00872 
00873 int KMFolderSearch::removeContents()
00874 {
00875     unlink(QFile::encodeName(location()));
00876     unlink(QFile::encodeName(indexLocation()));
00877     mUnlinked = true;
00878     return 0;
00879 }
00880 
00881 int KMFolderSearch::expungeContents()
00882 {
00883     setSearch(new KMSearch());
00884     return 0;
00885 }
00886 
00887 int KMFolderSearch::count(bool cache) const
00888 {
00889     Q_UNUSED(cache);
00890     return mSerNums.count();
00891 }
00892 
00893 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
00894 {
00895     assert(idx >= 0 && idx < (int)mSerNums.count());
00896     KMMsgBase *msgBase = getMsgBase(idx);
00897     QValueVector<Q_UINT32>::iterator it = mSerNums.begin();
00898     mSerNums.erase(&it[idx]);
00899     return msgBase;
00900 }
00901 
00902 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
00903 {
00904     assert(idx >= 0 && idx < (int)mSerNums.count());
00905     Q_UNUSED( idx );
00906     return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg);
00907 }
00908 
00909 void KMFolderSearch::clearIndex(bool, bool)
00910 {
00911   //close all referenced folders
00912   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00913   for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
00914     if (!(*fit))
00915       continue;
00916     (*fit)->close("foldersearch");
00917   }
00918   mFolders.clear();
00919 
00920   mSerNums.clear();
00921 }
00922 
00923 void KMFolderSearch::truncateIndex()
00924 {
00925     truncate(QFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
00926 }
00927 
00928 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum)
00929 {
00930     if (!search() && !readSearch())
00931         return;
00932     if (!search()->inScope(aFolder))
00933         return;
00934     if (!mTempOpened) {
00935         open( "foldersearch" );
00936         mTempOpened = true;
00937     }
00938 
00939     if (!search()->searchPattern())
00940         return;
00941 
00942     int idx = -1;
00943     KMFolder *folder = 0;
00944     KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
00945     assert(folder && (idx != -1));
00946     assert(folder == aFolder);
00947     folder->open("examineAddedMessage");
00948 
00949     // if we are already checking this folder, refcount
00950     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00951       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00952       mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
00953     } else {
00954       connect( folder->storage(),
00955               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
00956               this,
00957               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00958                       const KMSearchPattern*, bool ) ) );
00959       mFoldersCurrentlyBeingSearched.insert( folder, 1 );
00960     }
00961     folder->storage()->search( search()->searchPattern(), serNum );
00962     folder->close("examineAddedMessage");
00963 }
00964 
00965 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder,
00966                                                Q_UINT32 serNum,
00967                                                const KMSearchPattern* pattern,
00968                                                bool matches )
00969 {
00970     if ( search()->searchPattern() != pattern ) return;
00971     kdDebug(5006) << folder->label() << ": serNum " << serNum
00972      << " matches?" << matches << endl;
00973     folder->open("SearchExamineMsgDone");
00974 
00975     if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
00976       unsigned int count = mFoldersCurrentlyBeingSearched[folder];
00977       if ( count == 1 ) {
00978         disconnect( folder->storage(),
00979                     SIGNAL( searchDone( KMFolder*, Q_UINT32,
00980                                         const KMSearchPattern*, bool ) ),
00981                     this,
00982                     SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
00983                                                     const KMSearchPattern*, bool ) ) );
00984         mFoldersCurrentlyBeingSearched.remove( folder );
00985       } else {
00986         mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
00987       }
00988     } else {
00989       Q_ASSERT( 0 ); // Can't happen (TM)
00990     }
00991     folder->close("SearchExamineMsgDone");
00992 
00993     if ( !matches ) {
00994         QValueVector<Q_UINT32>::const_iterator it;
00995         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
00996         if (it != mSerNums.end()) {
00997             removeSerNum( serNum );
00998         }
00999         return;
01000     }
01001 
01002 //    if (mSearch->running()) {
01003 //        mSearch->stop();
01004 //        mExecuteSearchTimer->start( 0, true );
01005 //    } else {
01006         QValueVector<Q_UINT32>::const_iterator it;
01007         it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01008         if (it == mSerNums.end()) {
01009             addSerNum( serNum );
01010         }
01011 //    }
01012 }
01013 
01014 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum)
01015 {
01016     if (!search() && !readSearch())
01017         return;
01018     if (!search()->inScope(folder))
01019         return;
01020     if (!mTempOpened) {
01021         open("foldersearch");
01022         mTempOpened = true;
01023     }
01024 
01025     if (mSearch->running()) {
01026         mExecuteSearchTimer->start(0, true);
01027     } else {
01028         removeSerNum(serNum);
01029     }
01030 }
01031 
01032 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, Q_UINT32 serNum, int delta)
01033 {
01034     if (!search() && !readSearch())
01035         return;
01036     if (!search()->inScope(aFolder))
01037         return;
01038     if (!mTempOpened) {
01039         open("foldersearch");
01040         mTempOpened = true;
01041     }
01042     QValueVector<Q_UINT32>::const_iterator it;
01043     it = qFind( mSerNums.begin(), mSerNums.end(), serNum );
01044     if (it != mSerNums.end()) {
01045         mUnreadMsgs += delta;
01046         emit numUnreadMsgsChanged( folder() );
01047         emit msgChanged( folder(), serNum, delta );
01048     }
01049 }
01050 
01051 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
01052 {
01053     if (!search() && !readSearch())
01054         return;
01055     if (!search()->inScope(folder))
01056         return;
01057     if (mTempOpened) {
01058         close("foldersearch");
01059         mTempOpened = false;
01060     }
01061 
01062     mInvalid = true;
01063     if (mSearch)
01064         mSearch->stop();
01065 
01066     if (!mUnlinked) {
01067         unlink(QFile::encodeName(indexLocation()));
01068         mUnlinked = true;
01069     }
01070 
01071     if (!isOpened()) //give up, until the user manually opens the folder
01072         return;
01073 
01074     if (!mTempOpened) {
01075         open("foldersearch");
01076         mTempOpened = true;
01077     }
01078     mExecuteSearchTimer->start(0, true);
01079 }
01080 
01081 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
01082 {
01083     examineInvalidatedFolder(folder);
01084     if (mSearch->root() == folder) {
01085         delete mSearch;
01086         mSearch = 0;
01087     }
01088 }
01089 
01090 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
01091 {
01092     int pos = 0;
01093     if (!search() && !readSearch())
01094         return;
01095     if (!search()->inScope(aFolder))
01096         return;
01097     if (!mTempOpened) {
01098         open("foldersearch");
01099         mTempOpened = true;
01100     }
01101 
01102     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
01103     QValueVector<Q_UINT32>::const_iterator it;
01104     for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
01105         if ((*it) == serNum) {
01106             emit msgHeaderChanged(folder(), pos);
01107             break;
01108         }
01109         ++pos;
01110     }
01111     // let's try if the message matches our search
01112     aFolder->open("foldersearch");
01113 
01114     // if we are already checking this folder, refcount
01115     if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
01116       unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
01117       mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
01118     } else {
01119       connect( aFolder->storage(),
01120               SIGNAL( searchDone( KMFolder*, Q_UINT32, const KMSearchPattern*, bool ) ),
01121               this,
01122               SLOT( slotSearchExamineMsgDone( KMFolder*, Q_UINT32,
01123                       const KMSearchPattern*, bool ) ) );
01124       mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
01125     }
01126     aFolder->storage()->search( search()->searchPattern(), serNum );
01127 }
01128 
01129 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
01130 {
01131   // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
01132   // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
01133   if ( mTempOpened && mOpenCount == 1 )
01134   {
01135     examineInvalidatedFolder( folder );
01136   }
01137 }
01138 
01139 #include "kmfoldersearch.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys