kmail

folderstorage.cpp

Go to the documentation of this file.
00001 /*
00002     Virtual base class for mail storage.
00003 
00004     This file is part of KMail.
00005 
00006     Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 
00023     In addition, as a special exception, the copyright holders give
00024     permission to link the code of this program with any edition of
00025     the Qt library by Trolltech AS, Norway (or with modified versions
00026     of Qt that use the same license as Qt), and distribute linked
00027     combinations including the two.  You must obey the GNU General
00028     Public License in all respects for all of the code used other than
00029     Qt.  If you modify this file, you may extend this exception to
00030     your version of the file, but you are not obligated to do so.  If
00031     you do not wish to do so, delete this exception statement from
00032     your version.
00033 */
00034 
00035 #include <config.h>
00036 
00037 #include "folderstorage.h"
00038 #include "kmfolder.h"
00039 #include "kmkernel.h"
00040 
00041 #include "kmfolderimap.h" //for the nasty imap hacks, FIXME
00042 #include "undostack.h"
00043 #include "kmmsgdict.h"
00044 #include "kmfoldermgr.h"
00045 #include "kmcommands.h"
00046 #include "listjob.h"
00047 using KMail::ListJob;
00048 #include "kmsearchpattern.h"
00049 #include "globalsettings.h"
00050 
00051 #include <klocale.h>
00052 #include <kconfig.h>
00053 #include <kdebug.h>
00054 
00055 #include <qfile.h>
00056 #include <qregexp.h>
00057 
00058 #include <mimelib/mimepp.h>
00059 #include <errno.h>
00060 
00061 //-----------------------------------------------------------------------------
00062 
00063 FolderStorage::FolderStorage( KMFolder* folder, const char* aName )
00064   : QObject( folder, aName ), mFolder( folder ), mEmitChangedTimer( 0L )
00065 {
00066   mOpenCount = 0;
00067   mQuiet = 0;
00068   mChanged = false;
00069   mAutoCreateIndex = true;
00070   mExportsSernums = false;
00071   mDirty = false;
00072   mUnreadMsgs = -1;
00073   mGuessedUnreadMsgs = -1;
00074   mTotalMsgs = -1;
00075   mSize = -1;
00076   needsCompact    = false;
00077   mConvertToUtf8  = false;
00078   mCompactable     = true;
00079   mNoContent      = false;
00080   mNoChildren     = false;
00081   mRDict = 0;
00082   mDirtyTimer = new QTimer(this, "mDirtyTimer");
00083   connect(mDirtyTimer, SIGNAL(timeout()),
00084       this, SLOT(updateIndex()));
00085 
00086   mHasChildren = HasNoChildren;
00087   mContentsType = KMail::ContentsTypeMail;
00088  
00089   connect(this, SIGNAL(closed(KMFolder*)), mFolder, SIGNAL(closed()));  
00090 }
00091 
00092 //-----------------------------------------------------------------------------
00093 FolderStorage::~FolderStorage()
00094 {
00095   mJobList.setAutoDelete( true );
00096   QObject::disconnect( SIGNAL(destroyed(QObject*)), this, 0 );
00097   mJobList.clear();
00098   KMMsgDict::deleteRentry(mRDict);
00099 }
00100 
00101 
00102 void FolderStorage::close( const char* owner, bool aForced )
00103 {
00104   if (mOpenCount <= 0) return;
00105   if (mOpenCount > 0) mOpenCount--;
00106   if (mOpenCount > 0 && !aForced) return;
00107 
00108   // kdWarning() << "Really closing: " << folder()->prettyURL()  << kdBacktrace() << endl;
00109   reallyDoClose(owner);
00110 }
00111 
00112 //-----------------------------------------------------------------------------
00113 QString FolderStorage::dotEscape(const QString& aStr)
00114 {
00115   if (aStr[0] != '.') return aStr;
00116   return aStr.left(aStr.find(QRegExp("[^\\.]"))) + aStr;
00117 }
00118 
00119 void FolderStorage::addJob( FolderJob* job ) const
00120 {
00121   QObject::connect( job, SIGNAL(destroyed(QObject*)),
00122                     SLOT(removeJob(QObject*)) );
00123   mJobList.append( job );
00124 }
00125 
00126 void FolderStorage::removeJob( QObject* job )
00127 {
00128   mJobList.remove( static_cast<FolderJob*>( job ) );
00129 }
00130 
00131 
00132 //-----------------------------------------------------------------------------
00133 QString FolderStorage::location() const
00134 {
00135   QString sLocation(const_cast<FolderStorage*>(this)->folder()->path());
00136 
00137   if (!sLocation.isEmpty()) sLocation += '/';
00138   sLocation += dotEscape(fileName());
00139 
00140   return sLocation;
00141 }
00142 
00143 QString FolderStorage::fileName() const
00144 {
00145   return mFolder->name();
00146 }
00147 
00148 
00149 
00150 //-----------------------------------------------------------------------------
00151 void FolderStorage::setAutoCreateIndex(bool autoIndex)
00152 {
00153   mAutoCreateIndex = autoIndex;
00154 }
00155 
00156 //-----------------------------------------------------------------------------
00157 void FolderStorage::setDirty(bool f)
00158 {
00159   mDirty = f;
00160   if (mDirty  && mAutoCreateIndex)
00161     mDirtyTimer->changeInterval( mDirtyTimerInterval );
00162   else
00163     mDirtyTimer->stop();
00164 }
00165 
00166 //-----------------------------------------------------------------------------
00167 void FolderStorage::markNewAsUnread()
00168 {
00169   KMMsgBase* msgBase;
00170   int i;
00171 
00172   for (i=0; i< count(); ++i)
00173   {
00174     if (!(msgBase = getMsgBase(i))) continue;
00175     if (msgBase->isNew())
00176     {
00177       msgBase->setStatus(KMMsgStatusUnread);
00178       msgBase->setDirty(true);
00179     }
00180   }
00181 }
00182 
00183 void FolderStorage::markUnreadAsRead()
00184 {
00185   KMMsgBase* msgBase;
00186   SerNumList serNums;
00187 
00188   for (int i=count()-1; i>=0; --i)
00189   {
00190     msgBase = getMsgBase(i);
00191     assert(msgBase);
00192     if (msgBase->isNew() || msgBase->isUnread())
00193     {
00194       serNums.append( msgBase->getMsgSerNum() );
00195     }
00196   }
00197   if (serNums.empty())
00198     return;
00199 
00200   KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums );
00201   command->start();
00202 }
00203 
00204 //-----------------------------------------------------------------------------
00205 void FolderStorage::quiet(bool beQuiet)
00206 {
00207 
00208   if (beQuiet)
00209   {
00210     /* Allocate the timer here to don't have one timer for each folder. BTW,
00211      * a timer is created when a folder is checked
00212      */
00213     if ( !mEmitChangedTimer) {
00214       mEmitChangedTimer= new QTimer( this, "mEmitChangedTimer" );
00215       connect( mEmitChangedTimer, SIGNAL( timeout() ),
00216       this, SLOT( slotEmitChangedTimer() ) );
00217     }
00218     mQuiet++;
00219   } else {
00220     mQuiet--;
00221     if (mQuiet <= 0)
00222     {
00223       delete mEmitChangedTimer;
00224       mEmitChangedTimer=0L;
00225 
00226       mQuiet = 0;
00227       if (mChanged) {
00228        emit changed();
00229        // Don't hurt emit this if the mUnreadMsg really don't change
00230        // We emit it here, because this signal is delayed if mQuiet >0
00231        emit numUnreadMsgsChanged( folder() );
00232       }
00233       mChanged = false;
00234     }
00235   }
00236 }
00237 
00238 //-----------------------------------------------------------------------------
00239 
00241 int operator<( KMMsgBase & m1, KMMsgBase & m2 )
00242 {
00243   return (m1.date() < m2.date());
00244 }
00245 
00247 int operator==( KMMsgBase & m1, KMMsgBase & m2 )
00248 {
00249   return (m1.date() == m2.date());
00250 }
00251 
00252 
00253 //-----------------------------------------------------------------------------
00254 int FolderStorage::expungeOldMsg(int days)
00255 {
00256   int i, msgnb=0;
00257   time_t msgTime, maxTime;
00258   const KMMsgBase* mb;
00259   QValueList<int> rmvMsgList;
00260 
00261   maxTime = time(0) - days * 3600 * 24;
00262 
00263   for (i=count()-1; i>=0; i--) {
00264     mb = getMsgBase(i);
00265     assert(mb);
00266     msgTime = mb->date();
00267 
00268     if (msgTime < maxTime) {
00269       //kdDebug(5006) << "deleting msg " << i << " : " << mb->subject() << " - " << mb->dateStr(); // << endl;
00270       removeMsg( i );
00271       msgnb++;
00272     }
00273   }
00274   return msgnb;
00275 }
00276 
00277 //------------------------------------------
00278 void FolderStorage::slotEmitChangedTimer()
00279 {
00280   emit changed();
00281   mChanged=false;
00282 }
00283 //-----------------------------------------------------------------------------
00284 void FolderStorage::emitMsgAddedSignals(int idx)
00285 {
00286   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder() , idx );
00287   if (!mQuiet) {
00288     emit msgAdded(idx);
00289   } else {
00292     if ( !mEmitChangedTimer->isActive() ) {
00293       mEmitChangedTimer->start( 3000 );
00294     }
00295     mChanged=true;
00296   }
00297   emit msgAdded( folder(), serNum );
00298 }
00299 
00300 //-----------------------------------------------------------------------------
00301 bool FolderStorage::canAddMsgNow(KMMessage* aMsg, int* aIndex_ret)
00302 {
00303   if (aIndex_ret) *aIndex_ret = -1;
00304   KMFolder *msgParent = aMsg->parent();
00305   // If the message has a parent and is in transfer, bail out. If it does not
00306   // have a parent we want to be able to add it even if it is in transfer.
00307   if (aMsg->transferInProgress() && msgParent)
00308       return false;
00309   if (!aMsg->isComplete() && msgParent && msgParent->folderType() == KMFolderTypeImap)
00310   {
00311     FolderJob *job = msgParent->createJob(aMsg);
00312     connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00313             SLOT(reallyAddMsg(KMMessage*)));
00314     job->start();
00315     aMsg->setTransferInProgress( true );
00316     return false;
00317   }
00318   return true;
00319 }
00320 
00321 
00322 //-----------------------------------------------------------------------------
00323 void FolderStorage::reallyAddMsg(KMMessage* aMsg)
00324 {
00325   if (!aMsg) // the signal that is connected can call with aMsg=0
00326     return;
00327   aMsg->setTransferInProgress( false );
00328   aMsg->setComplete( true );
00329   KMFolder *aFolder = aMsg->parent();
00330   int index;
00331   ulong serNum = aMsg->getMsgSerNum();
00332   bool undo = aMsg->enableUndo();
00333   addMsg(aMsg, &index);
00334   if (index < 0) return;
00335   unGetMsg(index);
00336   if (undo)
00337   {
00338     kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() );
00339   }
00340 }
00341 
00342 
00343 //-----------------------------------------------------------------------------
00344 void FolderStorage::reallyAddCopyOfMsg(KMMessage* aMsg)
00345 {
00346   if ( !aMsg ) return; // messageRetrieved(0) is always possible
00347   aMsg->setParent( 0 );
00348   aMsg->setTransferInProgress( false );
00349   addMsg( aMsg );
00350   unGetMsg( count() - 1 );
00351 }
00352 
00353 int FolderStorage::find( const KMMessage * msg ) const {
00354   return find( &msg->toMsgBase() );
00355 }
00356 
00357 //-----------------------------------------------------------------------------
00358 void FolderStorage::removeMsg(const QPtrList<KMMsgBase>& msgList, bool imapQuiet)
00359 {
00360   for( QPtrListIterator<KMMsgBase> it( msgList ); *it; ++it )
00361   {
00362     int idx = find(it.current());
00363     assert( idx != -1);
00364     removeMsg(idx, imapQuiet);
00365   }
00366 }
00367 
00368 //-----------------------------------------------------------------------------
00369 void FolderStorage::removeMsg(const QPtrList<KMMessage>& msgList, bool imapQuiet)
00370 {
00371   for( QPtrListIterator<KMMessage> it( msgList ); *it; ++it )
00372   {
00373     int idx = find(it.current());
00374     assert( idx != -1);
00375     removeMsg(idx, imapQuiet);
00376   }
00377 }
00378 
00379 //-----------------------------------------------------------------------------
00380 void FolderStorage::removeMsg(int idx, bool)
00381 {
00382   //assert(idx>=0);
00383   if(idx < 0)
00384   {
00385     kdDebug(5006) << "FolderStorage::removeMsg() : idx < 0\n" << endl;
00386     return;
00387   }
00388 
00389   KMMsgBase* mb = getMsgBase(idx);
00390 
00391   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx );
00392   if (serNum != 0)
00393     emit msgRemoved( folder(), serNum );
00394   mb = takeIndexEntry( idx );
00395 
00396   setDirty( true );
00397   needsCompact=true; // message is taken from here - needs to be compacted
00398 
00399   if (mb->isUnread() || mb->isNew() ||
00400       (folder() == kmkernel->outboxFolder())) {
00401     --mUnreadMsgs;
00402     if ( !mQuiet ) {
00403 //      kdDebug( 5006 ) << "FolderStorage::msgStatusChanged" << endl;
00404       emit numUnreadMsgsChanged( folder() );
00405     }else{
00406       if ( !mEmitChangedTimer->isActive() ) {
00407 //        kdDebug( 5006 )<< "EmitChangedTimer started" << endl;
00408         mEmitChangedTimer->start( 3000 );
00409       }
00410       mChanged = true;
00411     }
00412   }
00413   --mTotalMsgs;
00414 
00415   mSize = -1;
00416   QString msgIdMD5 = mb->msgIdMD5();
00417   emit msgRemoved( idx, msgIdMD5 );
00418   emit msgRemoved( folder() );
00419 }
00420 
00421 
00422 //-----------------------------------------------------------------------------
00423 KMMessage* FolderStorage::take(int idx)
00424 {
00425   KMMsgBase* mb;
00426   KMMessage* msg;
00427 
00428   assert(idx>=0 && idx<=count());
00429 
00430   mb = getMsgBase(idx);
00431   if (!mb) return 0;
00432   if (!mb->isMessage()) readMsg(idx);
00433   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx );
00434   emit msgRemoved( folder(), serNum );
00435 
00436   msg = (KMMessage*)takeIndexEntry(idx);
00437 
00438   if (msg->isUnread() || msg->isNew() ||
00439       ( folder() == kmkernel->outboxFolder() )) {
00440     --mUnreadMsgs;
00441     if ( !mQuiet ) {
00442       emit numUnreadMsgsChanged( folder() );
00443     }else{
00444       if ( !mEmitChangedTimer->isActive() ) {
00445         mEmitChangedTimer->start( 3000 );
00446       }
00447       mChanged = true;
00448     }
00449   }
00450   --mTotalMsgs;
00451   msg->setParent(0);
00452   setDirty( true );
00453   mSize = -1;
00454   needsCompact=true; // message is taken from here - needs to be compacted
00455   QString msgIdMD5 = msg->msgIdMD5();
00456   emit msgRemoved( idx, msgIdMD5 );
00457   emit msgRemoved( folder() );
00458 
00459   return msg;
00460 }
00461 
00462 void FolderStorage::take(QPtrList<KMMessage> msgList)
00463 {
00464   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00465   {
00466     if (msg->parent())
00467     {
00468       int idx = msg->parent()->find(msg);
00469       if ( idx >= 0 )
00470         take(idx);
00471     }
00472   }
00473 }
00474 
00475 
00476 //-----------------------------------------------------------------------------
00477 KMMessage* FolderStorage::getMsg(int idx)
00478 {
00479   if ( mOpenCount <= 0 ) {
00480     kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl; 
00481     return 0;
00482   }
00483   if ( idx < 0 || idx >= count() ) { 
00484     kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl; 
00485     return 0;
00486   }
00487 
00488   KMMsgBase* mb = getMsgBase(idx);
00489   if (!mb) {
00490     kdWarning(5006) << "FolderStorage::getMsg, getMsgBase failed for index: " << idx << endl;
00491     return 0;
00492   }
00493 
00494   KMMessage *msg = 0;
00495   bool undo = mb->enableUndo();
00496   if (mb->isMessage()) {
00497       msg = ((KMMessage*)mb);
00498   } else {
00499       QString mbSubject = mb->subject();
00500       msg = readMsg(idx);
00501       // sanity check
00502       if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) {
00503         kdDebug(5006) << "Error: " << location() <<
00504           " Index file is inconsistent with folder file. This should never happen." << endl;
00505         mCompactable = false; // Don't compact
00506         writeConfig();
00507       }
00508 
00509   }
00510   // Either isMessage and we had a sernum, or readMsg gives us one
00511   // (via insertion into mMsgList). sernum == 0 may still occur due to
00512   // an outdated or corrupt IMAP cache.
00513   if ( msg->getMsgSerNum() == 0 ) {
00514     kdWarning(5006) << "FolderStorage::getMsg, message has no sernum, index: " << idx << endl;
00515     return 0;
00516   }
00517   msg->setEnableUndo(undo);
00518   msg->setComplete( true );
00519   return msg;
00520 }
00521 
00522 //-----------------------------------------------------------------------------
00523 KMMessage* FolderStorage::readTemporaryMsg(int idx)
00524 {
00525   if(!(idx >= 0 && idx <= count()))
00526     return 0;
00527 
00528   KMMsgBase* mb = getMsgBase(idx);
00529   if (!mb) return 0;
00530 
00531   unsigned long sernum = mb->getMsgSerNum();
00532 
00533   KMMessage *msg = 0;
00534   bool undo = mb->enableUndo();
00535   if (mb->isMessage()) {
00536     // the caller will delete it, so we must make a copy it
00537     msg = new KMMessage(*(KMMessage*)mb);
00538     msg->setMsgSerNum(sernum);
00539     msg->setComplete( true );
00540   } else {
00541     // ## Those two lines need to be moved to a virtual method for KMFolderSearch, like readMsg
00542     msg = new KMMessage(*(KMMsgInfo*)mb);
00543     msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum
00544     msg->setComplete( true );
00545     msg->fromDwString(getDwString(idx));
00546   }
00547   msg->setEnableUndo(undo);
00548   return msg;
00549 }
00550 
00551 
00552 //-----------------------------------------------------------------------------
00553 KMMsgInfo* FolderStorage::unGetMsg(int idx)
00554 {
00555   KMMsgBase* mb;
00556 
00557   if(!(idx >= 0 && idx <= count()))
00558     return 0;
00559 
00560   mb = getMsgBase(idx);
00561   if (!mb) return 0;
00562 
00563 
00564   if (mb->isMessage()) {
00565     // Remove this message from all jobs' list it might still be on.
00566     // setIndexEntry deletes the message.
00567     KMMessage *msg = static_cast<KMMessage*>(mb);
00568     if ( msg->transferInProgress() ) return 0;
00569     ignoreJobsForMessage( msg );
00570     return setIndexEntry( idx, msg );
00571   }
00572 
00573   return 0;
00574 }
00575 
00576 
00577 //-----------------------------------------------------------------------------
00578 bool FolderStorage::isMessage(int idx)
00579 {
00580   KMMsgBase* mb;
00581   if (!(idx >= 0 && idx <= count())) return false;
00582   mb = getMsgBase(idx);
00583   return (mb && mb->isMessage());
00584 }
00585 
00586 //-----------------------------------------------------------------------------
00587 FolderJob* FolderStorage::createJob( KMMessage *msg, FolderJob::JobType jt,
00588                                 KMFolder *folder, QString partSpecifier,
00589                                 const AttachmentStrategy *as ) const
00590 {
00591   FolderJob * job = doCreateJob( msg, jt, folder, partSpecifier, as );
00592   if ( job )
00593     addJob( job );
00594   return job;
00595 }
00596 
00597 //-----------------------------------------------------------------------------
00598 FolderJob* FolderStorage::createJob( QPtrList<KMMessage>& msgList, const QString& sets,
00599                                 FolderJob::JobType jt, KMFolder *folder ) const
00600 {
00601   FolderJob * job = doCreateJob( msgList, sets, jt, folder );
00602   if ( job )
00603     addJob( job );
00604   return job;
00605 }
00606 
00607 //-----------------------------------------------------------------------------
00608 int FolderStorage::moveMsg(KMMessage* aMsg, int* aIndex_ret)
00609 {
00610   assert(aMsg != 0);
00611   KMFolder* msgParent = aMsg->parent();
00612 
00613   if (msgParent)
00614     msgParent->open("moveMsgSrc");
00615 
00616   open("moveMsgDest");
00617   int rc = addMsg(aMsg, aIndex_ret);
00618   close("moveMsgDest");
00619 
00620   if (msgParent)
00621     msgParent->close("moveMsgSrc");
00622 
00623   return rc;
00624 }
00625 
00626 //-----------------------------------------------------------------------------
00627 int FolderStorage::moveMsg(QPtrList<KMMessage> msglist, int* aIndex_ret)
00628 {
00629   KMMessage* aMsg = msglist.first();
00630   assert(aMsg != 0);
00631   KMFolder* msgParent = aMsg->parent();
00632 
00633   if (msgParent)
00634     msgParent->open("foldermovemsg");
00635 
00636   QValueList<int> index;
00637   open("moveMsg");
00638   int rc = addMsg(msglist, index);
00639   close("moveMsg");
00640   // FIXME: we want to have a QValueList to pass it back, so change this method
00641   if ( !index.isEmpty() )
00642     aIndex_ret = &index.first();
00643 
00644   if (msgParent)
00645     msgParent->close("foldermovemsg");
00646 
00647   return rc;
00648 }
00649 
00650 
00651 //-----------------------------------------------------------------------------
00652 int FolderStorage::rename(const QString& newName, KMFolderDir *newParent)
00653 {
00654   QString oldLoc, oldIndexLoc, oldIdsLoc, newLoc, newIndexLoc, newIdsLoc;
00655   QString oldSubDirLoc, newSubDirLoc;
00656   QString oldName;
00657   int rc=0;
00658   KMFolderDir *oldParent;
00659 
00660   assert(!newName.isEmpty());
00661 
00662   oldLoc = location();
00663   oldIndexLoc = indexLocation();
00664   oldSubDirLoc = folder()->subdirLocation();
00665   oldIdsLoc =  KMMsgDict::instance()->getFolderIdsLocation( *this );
00666   QString oldConfigString = "Folder-" + folder()->idString();
00667 
00668   close("rename", true);
00669 
00670   oldName = folder()->fileName();
00671   oldParent = folder()->parent();
00672   if (newParent)
00673     folder()->setParent( newParent );
00674 
00675   folder()->setName(newName);
00676   newLoc = location();
00677   newIndexLoc = indexLocation();
00678   newSubDirLoc = folder()->subdirLocation();
00679   newIdsLoc = KMMsgDict::instance()->getFolderIdsLocation( *this );
00680 
00681   if (::rename(QFile::encodeName(oldLoc), QFile::encodeName(newLoc))) {
00682     folder()->setName(oldName);
00683     folder()->setParent(oldParent);
00684     rc = errno;
00685   }
00686   else {
00687     // rename/move index file and index.sorted file
00688     if (!oldIndexLoc.isEmpty()) {
00689       ::rename(QFile::encodeName(oldIndexLoc), QFile::encodeName(newIndexLoc));
00690       ::rename(QFile::encodeName(oldIndexLoc) + ".sorted",
00691                QFile::encodeName(newIndexLoc) + ".sorted");
00692     }
00693 
00694     // rename/move serial number file
00695     if (!oldIdsLoc.isEmpty())
00696       ::rename(QFile::encodeName(oldIdsLoc), QFile::encodeName(newIdsLoc));
00697 
00698     // rename/move the subfolder directory
00699     KMFolderDir* child = 0;
00700     if( folder() )
00701       child = folder()->child();
00702 
00703     if (!::rename(QFile::encodeName(oldSubDirLoc), QFile::encodeName(newSubDirLoc) )) {
00704       // now that the subfolder directory has been renamed and/or moved also
00705       // change the name that is stored in the corresponding KMFolderNode
00706       // (provide that the name actually changed)
00707       if( child && ( oldName != newName ) ) {
00708         child->setName( "." + QFile::encodeName(newName) + ".directory" );
00709       }
00710     }
00711 
00712     // if the folder is being moved then move its node and, if necessary, also
00713     // the associated subfolder directory node to the new parent
00714     if (newParent) {
00715       if (oldParent->findRef( folder() ) != -1)
00716         oldParent->take();
00717       newParent->inSort( folder() );
00718       if ( child ) {
00719         if ( child->parent()->findRef( child ) != -1 )
00720           child->parent()->take();
00721         newParent->inSort( child );
00722         child->setParent( newParent );
00723       }
00724     }
00725   }
00726 
00727   writeConfig();
00728 
00729   // delete the old entry as we get two entries with the same ID otherwise
00730   if ( oldConfigString != "Folder-" + folder()->idString() )
00731     KMKernel::config()->deleteGroup( oldConfigString );
00732 
00733   emit locationChanged( oldLoc, newLoc );
00734   emit nameChanged();
00735   kmkernel->folderMgr()->contentsChanged();
00736   emit closed(folder()); // let the ticket owners regain
00737   return rc;
00738 }
00739 
00740 
00741 //-----------------------------------------------------------------------------
00742 void FolderStorage::remove()
00743 {
00744   assert(!folder()->name().isEmpty());
00745 
00746   clearIndex( true, mExportsSernums ); // delete and remove from dict if necessary
00747   close("remove", true);
00748 
00749   if ( mExportsSernums ) {
00750     KMMsgDict::mutableInstance()->removeFolderIds( *this );
00751     mExportsSernums = false;    // do not writeFolderIds after removal
00752   }
00753   unlink(QFile::encodeName(indexLocation()) + ".sorted");
00754   unlink(QFile::encodeName(indexLocation()));
00755 
00756   int rc = removeContents();
00757 
00758   needsCompact = false; //we are dead - no need to compact us
00759 
00760   // Erase settings, otherwise they might interfer when recreating the folder
00761   KConfig* config = KMKernel::config();
00762   config->deleteGroup( "Folder-" + folder()->idString() );
00763 
00764   emit closed(folder());
00765   emit removed(folder(), (rc ? false : true));
00766 }
00767 
00768 
00769 //-----------------------------------------------------------------------------
00770 int FolderStorage::expunge()
00771 {
00772   assert(!folder()->name().isEmpty());
00773 
00774   clearIndex( true, mExportsSernums );   // delete and remove from dict, if needed
00775   close( "expunge", true );
00776 
00777   if ( mExportsSernums )
00778     KMMsgDict::mutableInstance()->removeFolderIds( *this );
00779   if ( mAutoCreateIndex )
00780     truncateIndex();
00781   else unlink(QFile::encodeName(indexLocation()));
00782 
00783   int rc = expungeContents();
00784   if (rc) return rc;
00785 
00786   mDirty = false;
00787   needsCompact = false; //we're cleared and truncated no need to compact
00788 
00789   mUnreadMsgs = 0;
00790   mTotalMsgs = 0;
00791   mSize = 0;
00792   emit numUnreadMsgsChanged( folder() );
00793   if ( mAutoCreateIndex ) // FIXME Heh? - Till
00794     writeConfig();
00795   emit changed();
00796   emit expunged( folder() );
00797 
00798   return 0;
00799 }
00800 
00801 //-----------------------------------------------------------------------------
00802 QString FolderStorage::label() const
00803 {
00804   return folder()->label();
00805 }
00806 
00807 int FolderStorage::count(bool cache) const
00808 {
00809   if (cache && mTotalMsgs != -1)
00810     return mTotalMsgs;
00811   else
00812     return -1;
00813 }
00814 
00815 //-----------------------------------------------------------------------------
00816 int FolderStorage::countUnread()
00817 {
00818   if (mGuessedUnreadMsgs > -1)
00819     return mGuessedUnreadMsgs;
00820   if (mUnreadMsgs > -1)
00821     return mUnreadMsgs;
00822 
00823   readConfig();
00824 
00825   if (mUnreadMsgs > -1)
00826     return mUnreadMsgs;
00827 
00828   open("countunread"); // will update unreadMsgs
00829   int unread = mUnreadMsgs;
00830   close("countunread");
00831   return (unread > 0) ? unread : 0;
00832 }
00833 
00834 Q_INT64 FolderStorage::folderSize() const
00835 {
00836     if ( mSize != -1 ) {
00837         return mSize;
00838     } else {
00839         return doFolderSize();
00840     }
00841 }
00842 
00843 
00844 /*virtual*/
00845 bool FolderStorage::isCloseToQuota() const
00846 {
00847   return false;
00848 }
00849 
00850 //-----------------------------------------------------------------------------
00851 void FolderStorage::msgStatusChanged(const KMMsgStatus oldStatus,
00852   const KMMsgStatus newStatus, int idx)
00853 {
00854   int oldUnread = 0;
00855   int newUnread = 0;
00856 
00857   if (((oldStatus & KMMsgStatusUnread || oldStatus & KMMsgStatusNew) &&
00858       !(oldStatus & KMMsgStatusIgnored)) ||
00859       (folder() == kmkernel->outboxFolder()))
00860     oldUnread = 1;
00861   if (((newStatus & KMMsgStatusUnread || newStatus & KMMsgStatusNew) &&
00862       !(newStatus & KMMsgStatusIgnored)) ||
00863       (folder() == kmkernel->outboxFolder()))
00864     newUnread = 1;
00865   int deltaUnread = newUnread - oldUnread;
00866 
00867   mDirtyTimer->changeInterval(mDirtyTimerInterval);
00868   if (deltaUnread != 0) {
00869     if (mUnreadMsgs < 0) mUnreadMsgs = 0;
00870     mUnreadMsgs += deltaUnread;
00871     if ( !mQuiet ) {
00872       emit numUnreadMsgsChanged( folder() );
00873     }else{
00874       if ( !mEmitChangedTimer->isActive() ) {
00875         mEmitChangedTimer->start( 3000 );
00876       }
00877       mChanged = true;
00878     }
00879     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(folder(), idx);
00880     emit msgChanged( folder(), serNum, deltaUnread );
00881   }
00882 }
00883 
00884 //-----------------------------------------------------------------------------
00885 void FolderStorage::headerOfMsgChanged(const KMMsgBase* aMsg, int idx)
00886 {
00887   if (idx < 0)
00888     idx = aMsg->parent()->find( aMsg );
00889 
00890   if (idx >= 0 )
00891   {
00892     if ( !mQuiet )
00893       emit msgHeaderChanged(folder(), idx);
00894     else{
00895       if ( !mEmitChangedTimer->isActive() ) {
00896         mEmitChangedTimer->start( 3000 );
00897       }
00898       mChanged = true;
00899     }
00900   } else
00901     mChanged = true;
00902 }
00903 
00904 //-----------------------------------------------------------------------------
00905 void FolderStorage::readConfig()
00906 {
00907   //kdDebug(5006)<<"#### READING CONFIG  = "<< name() <<endl;
00908   KConfig* config = KMKernel::config();
00909   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00910   if (mUnreadMsgs == -1)
00911     mUnreadMsgs = config->readNumEntry("UnreadMsgs", -1);
00912   if (mTotalMsgs == -1)
00913     mTotalMsgs = config->readNumEntry("TotalMsgs", -1);
00914   mCompactable = config->readBoolEntry("Compactable", true);
00915   if ( mSize == -1 )
00916       mSize = config->readNum64Entry("FolderSize", -1);
00917   
00918   int type = config->readNumEntry( "ContentsType", 0 );
00919   if ( type < 0 || type > KMail::ContentsTypeLast ) type = 0;
00920   setContentsType( static_cast<KMail::FolderContentsType>( type ) );
00921 
00922   if( folder() ) folder()->readConfig( config );
00923 }
00924 
00925 //-----------------------------------------------------------------------------
00926 void FolderStorage::writeConfig()
00927 {
00928   KConfig* config = KMKernel::config();
00929   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00930   config->writeEntry("UnreadMsgs",
00931       mGuessedUnreadMsgs == -1 ? mUnreadMsgs : mGuessedUnreadMsgs);
00932   config->writeEntry("TotalMsgs", mTotalMsgs);
00933   config->writeEntry("Compactable", mCompactable);
00934   config->writeEntry("ContentsType", mContentsType);
00935   config->writeEntry("FolderSize", mSize);
00936 
00937   // Write the KMFolder parts
00938   if( folder() ) folder()->writeConfig( config );
00939 
00940   GlobalSettings::self()->requestSync();
00941 }
00942 
00943 //-----------------------------------------------------------------------------
00944 void FolderStorage::correctUnreadMsgsCount()
00945 {
00946   open("countunreadmsg");
00947   close("countunreadmsg");
00948   emit numUnreadMsgsChanged( folder() );
00949 }
00950 
00951 void FolderStorage::registerWithMessageDict()
00952 {
00953   mExportsSernums = true;
00954   readFolderIdsFile();
00955 }
00956 
00957 void FolderStorage::deregisterFromMessageDict()
00958 {
00959   writeFolderIdsFile();
00960   mExportsSernums = false;
00961 }
00962 
00963 void FolderStorage::readFolderIdsFile()
00964 {
00965   if ( !mExportsSernums ) return;
00966   if ( KMMsgDict::mutableInstance()->readFolderIds( *this ) == -1 ) {
00967     invalidateFolder();
00968   }
00969   if ( !KMMsgDict::mutableInstance()->hasFolderIds( *this ) ) {
00970     invalidateFolder();
00971   }
00972 }
00973 
00974 void FolderStorage::invalidateFolder()
00975 {
00976   if ( !mExportsSernums ) return;
00977   unlink(QFile::encodeName( indexLocation()) + ".sorted");
00978   unlink(QFile::encodeName( indexLocation()) + ".ids");
00979   fillMessageDict();
00980   KMMsgDict::mutableInstance()->writeFolderIds( *this );
00981   emit invalidated( folder() );
00982 }
00983 
00984 
00985 //-----------------------------------------------------------------------------
00986 int FolderStorage::writeFolderIdsFile() const
00987 {
00988   if ( !mExportsSernums ) return -1;
00989   return KMMsgDict::mutableInstance()->writeFolderIds( *this );
00990 }
00991 
00992 //-----------------------------------------------------------------------------
00993 int FolderStorage::touchFolderIdsFile()
00994 {
00995   if ( !mExportsSernums ) return -1;
00996   return KMMsgDict::mutableInstance()->touchFolderIds( *this );
00997 }
00998 
00999 //-----------------------------------------------------------------------------
01000 int FolderStorage::appendToFolderIdsFile( int idx )
01001 {
01002   if ( !mExportsSernums ) return -1;
01003   int ret = 0;
01004   if ( count() == 1 ) {
01005     ret = KMMsgDict::mutableInstance()->writeFolderIds( *this );
01006   } else {
01007     ret = KMMsgDict::mutableInstance()->appendToFolderIds( *this, idx );
01008   }
01009   return ret;
01010 }
01011 
01012 void FolderStorage::replaceMsgSerNum( unsigned long sernum, KMMsgBase* msg, int idx )
01013 {
01014   if ( !mExportsSernums ) return;
01015   KMMsgDict::mutableInstance()->replace( sernum, msg, idx );
01016 }
01017 
01018 void FolderStorage::setRDict( KMMsgDictREntry *rentry ) const
01019 {
01020   if ( ! mExportsSernums )
01021     kdDebug(5006) << "WTF, this FolderStorage should be invisible to the msgdict, who is calling us?" << kdBacktrace() << endl;
01022   assert( mExportsSernums ); // otherwise things are very wrong
01023   if ( rentry == mRDict )
01024     return;
01025   KMMsgDict::deleteRentry( mRDict );
01026   mRDict = rentry;
01027 }
01028 
01029 //-----------------------------------------------------------------------------
01030 void FolderStorage::setStatus(int idx, KMMsgStatus status, bool toggle)
01031 {
01032   KMMsgBase *msg = getMsgBase(idx);
01033   if ( msg ) {
01034     if (toggle)
01035       msg->toggleStatus(status, idx);
01036     else
01037       msg->setStatus(status, idx);
01038   }
01039 }
01040 
01041 
01042 //-----------------------------------------------------------------------------
01043 void FolderStorage::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01044 {
01045   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01046   {
01047     FolderStorage::setStatus(*it, status, toggle);
01048   }
01049 }
01050 
01051 void FolderStorage::ignoreJobsForMessage( KMMessage *msg )
01052 {
01053   if ( !msg || msg->transferInProgress() )
01054     return;
01055 
01056   QPtrListIterator<FolderJob> it( mJobList );
01057   while ( it.current() )
01058   {
01059     //FIXME: the questions is : should we iterate through all
01060     //messages in jobs? I don't think so, because it would
01061     //mean canceling the jobs that work with other messages
01062     if ( it.current()->msgList().first() == msg )
01063     {
01064       FolderJob* job = it.current();
01065       mJobList.remove( job );
01066       delete job;
01067     } else
01068       ++it;
01069   }
01070 }
01071 
01072 //-----------------------------------------------------------------------------
01073 void FolderStorage::removeJobs()
01074 {
01075   mJobList.setAutoDelete( true );
01076   mJobList.clear();
01077   mJobList.setAutoDelete( false );
01078 }
01079 
01080 
01081 
01082 //-----------------------------------------------------------------------------
01083 void FolderStorage::updateChildrenState()
01084 {
01085   if ( folder() && folder()->child() )
01086   {
01087     if ( kmkernel->folderMgr()->folderCount( folder()->child() ) > 0 )
01088       setHasChildren( HasChildren );
01089     else
01090       setHasChildren( HasNoChildren );
01091   }
01092 }
01093 
01094 //-----------------------------------------------------------------------------
01095 void FolderStorage::setNoChildren( bool aNoChildren )
01096 {
01097   mNoChildren = aNoChildren;
01098   if ( aNoChildren )
01099     setHasChildren( HasNoChildren );
01100 }
01101 
01102 //-----------------------------------------------------------------------------
01103 void FolderStorage::setContentsType( KMail::FolderContentsType type, bool quiet )
01104 {
01105   if ( type != mContentsType ) {
01106     mContentsType = type;
01107     if ( !quiet )
01108        emit contentsTypeChanged( type );
01109   }
01110 }
01111 
01112 //-----------------------------------------------------------------------------
01113 void FolderStorage::search( const KMSearchPattern* pattern )
01114 {
01115   mSearchPattern = pattern;
01116   mCurrentSearchedMsg = 0;
01117   if ( pattern )
01118     slotProcessNextSearchBatch();
01119 }
01120 
01121 void FolderStorage::slotProcessNextSearchBatch()
01122 {
01123   if ( !mSearchPattern )
01124     return;
01125   QValueList<Q_UINT32> matchingSerNums;
01126   const int end = QMIN( mCurrentSearchedMsg + 15, count() );
01127   for ( int i = mCurrentSearchedMsg; i < end; ++i )
01128   {
01129     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), i );
01130     if ( mSearchPattern->matches( serNum ) )
01131       matchingSerNums.append( serNum );
01132   }
01133   mCurrentSearchedMsg = end;
01134   bool complete = ( end >= count() );
01135   emit searchResult( folder(), matchingSerNums, mSearchPattern, complete );
01136   if ( !complete )
01137     QTimer::singleShot( 0, this, SLOT(slotProcessNextSearchBatch()) );
01138 }
01139 
01140 //-----------------------------------------------------------------------------
01141 void FolderStorage::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
01142 {
01143   bool matches = pattern && pattern->matches( serNum );
01144 
01145   emit searchDone( folder(), serNum, pattern, matches );
01146 }
01147 
01148 //-----------------------------------------------------------------------------
01149 int FolderStorage::addMsg( QPtrList<KMMessage>& msgList, QValueList<int>& index_ret )
01150 {
01151   int ret = 0;
01152   int index;
01153   for ( QPtrListIterator<KMMessage> it( msgList ); *it; ++it )
01154   {
01155     int aret = addMsg( *it, &index );
01156     index_ret << index;
01157     if ( aret != 0 ) // error condition
01158       ret = aret;
01159   }
01160   return ret;
01161 }
01162 
01163 //-----------------------------------------------------------------------------
01164 bool FolderStorage::isMoveable() const
01165 {
01166   return ( folder()->isSystemFolder() ) ? false : true;
01167 }
01168 
01169 
01170 /*virtual*/
01171 KMAccount* FolderStorage::account() const
01172 {
01173     return 0;
01174 }
01175 
01176 #include "folderstorage.moc"