kmail

folderstorage.cpp

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