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