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