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 
00089 //-----------------------------------------------------------------------------
00090 FolderStorage::~FolderStorage()
00091 {
00092   mJobList.setAutoDelete( true );
00093   QObject::disconnect( SIGNAL(destroyed(QObject*)), this, 0 );
00094   mJobList.clear();
00095   KMMsgDict::deleteRentry(mRDict);
00096 }
00097 
00098 
00099 //-----------------------------------------------------------------------------
00100 QString FolderStorage::dotEscape(const QString& aStr)
00101 {
00102   if (aStr[0] != '.') return aStr;
00103   return aStr.left(aStr.find(QRegExp("[^\\.]"))) + aStr;
00104 }
00105 
00106 void FolderStorage::addJob( FolderJob* job ) const
00107 {
00108   QObject::connect( job, SIGNAL(destroyed(QObject*)),
00109                     SLOT(removeJob(QObject*)) );
00110   mJobList.append( job );
00111 }
00112 
00113 void FolderStorage::removeJob( QObject* job )
00114 {
00115   mJobList.remove( static_cast<FolderJob*>( job ) );
00116 }
00117 
00118 
00119 //-----------------------------------------------------------------------------
00120 QString FolderStorage::location() const
00121 {
00122   QString sLocation(const_cast<FolderStorage*>(this)->folder()->path());
00123 
00124   if (!sLocation.isEmpty()) sLocation += '/';
00125   sLocation += dotEscape(fileName());
00126 
00127   return sLocation;
00128 }
00129 
00130 QString FolderStorage::fileName() const
00131 {
00132   return mFolder->name();
00133 }
00134 
00135 
00136 
00137 //-----------------------------------------------------------------------------
00138 void FolderStorage::setAutoCreateIndex(bool autoIndex)
00139 {
00140   mAutoCreateIndex = autoIndex;
00141 }
00142 
00143 //-----------------------------------------------------------------------------
00144 void FolderStorage::setDirty(bool f)
00145 {
00146   mDirty = f;
00147   if (mDirty  && mAutoCreateIndex)
00148     mDirtyTimer->changeInterval( mDirtyTimerInterval );
00149   else
00150     mDirtyTimer->stop();
00151 }
00152 
00153 //-----------------------------------------------------------------------------
00154 void FolderStorage::markNewAsUnread()
00155 {
00156   KMMsgBase* msgBase;
00157   int i;
00158 
00159   for (i=0; i< count(); ++i)
00160   {
00161     if (!(msgBase = getMsgBase(i))) continue;
00162     if (msgBase->isNew())
00163     {
00164       msgBase->setStatus(KMMsgStatusUnread);
00165       msgBase->setDirty(true);
00166     }
00167   }
00168 }
00169 
00170 void FolderStorage::markUnreadAsRead()
00171 {
00172   KMMsgBase* msgBase;
00173   SerNumList serNums;
00174 
00175   for (int i=count()-1; i>=0; --i)
00176   {
00177     msgBase = getMsgBase(i);
00178     assert(msgBase);
00179     if (msgBase->isNew() || msgBase->isUnread())
00180     {
00181       serNums.append( msgBase->getMsgSerNum() );
00182     }
00183   }
00184   if (serNums.empty())
00185     return;
00186 
00187   KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums );
00188   command->start();
00189 }
00190 
00191 //-----------------------------------------------------------------------------
00192 void FolderStorage::quiet(bool beQuiet)
00193 {
00194 
00195   if (beQuiet)
00196   {
00197     /* Allocate the timer here to don't have one timer for each folder. BTW,
00198      * a timer is created when a folder is checked
00199      */
00200     if ( !mEmitChangedTimer) {
00201       mEmitChangedTimer= new QTimer( this );
00202       connect( mEmitChangedTimer, SIGNAL( timeout() ),
00203       this, SLOT( slotEmitChangedTimer() ) );
00204     }
00205     mQuiet++;
00206   } else {
00207     mQuiet--;
00208     if (mQuiet <= 0)
00209     {
00210       delete mEmitChangedTimer;
00211       mEmitChangedTimer=0L;
00212 
00213       mQuiet = 0;
00214       if (mChanged) {
00215        emit changed();
00216        // Don't hurt emit this if the mUnreadMsg really don't change
00217        // We emit it here, because this signal is delayed if mQuiet >0
00218        emit numUnreadMsgsChanged( folder() );
00219       }
00220       mChanged = false;
00221     }
00222   }
00223 }
00224 
00225 //-----------------------------------------------------------------------------
00226 
00228 int operator<( KMMsgBase & m1, KMMsgBase & m2 )
00229 {
00230   return (m1.date() < m2.date());
00231 }
00232 
00234 int operator==( KMMsgBase & m1, KMMsgBase & m2 )
00235 {
00236   return (m1.date() == m2.date());
00237 }
00238 
00239 
00240 //-----------------------------------------------------------------------------
00241 int FolderStorage::expungeOldMsg(int days)
00242 {
00243   int i, msgnb=0;
00244   time_t msgTime, maxTime;
00245   const KMMsgBase* mb;
00246   QValueList<int> rmvMsgList;
00247 
00248   maxTime = time(0) - days * 3600 * 24;
00249 
00250   for (i=count()-1; i>=0; i--) {
00251     mb = getMsgBase(i);
00252     assert(mb);
00253     msgTime = mb->date();
00254 
00255     if (msgTime < maxTime) {
00256       //kdDebug(5006) << "deleting msg " << i << " : " << mb->subject() << " - " << mb->dateStr(); // << endl;
00257       removeMsg( i );
00258       msgnb++;
00259     }
00260   }
00261   return msgnb;
00262 }
00263 
00264 //------------------------------------------
00265 void FolderStorage::slotEmitChangedTimer()
00266 {
00267   emit changed();
00268   mChanged=false;
00269 }
00270 //-----------------------------------------------------------------------------
00271 void FolderStorage::emitMsgAddedSignals(int idx)
00272 {
00273   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder() , idx );
00274   if (!mQuiet) {
00275     emit msgAdded(idx);
00276   } else {
00279     if ( !mEmitChangedTimer->isActive() ) {
00280       mEmitChangedTimer->start( 3000 );
00281     }
00282     mChanged=true;
00283   }
00284   emit msgAdded( folder(), serNum );
00285 }
00286 
00287 //-----------------------------------------------------------------------------
00288 bool FolderStorage::canAddMsgNow(KMMessage* aMsg, int* aIndex_ret)
00289 {
00290   if (aIndex_ret) *aIndex_ret = -1;
00291   KMFolder *msgParent = aMsg->parent();
00292   // If the message has a parent and is in transfer, bail out. If it does not
00293   // have a parent we want to be able to add it even if it is in transfer.
00294   if (aMsg->transferInProgress() && msgParent)
00295       return false;
00296   if (!aMsg->isComplete() && msgParent && msgParent->folderType() == KMFolderTypeImap)
00297   {
00298     FolderJob *job = msgParent->createJob(aMsg);
00299     connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00300             SLOT(reallyAddMsg(KMMessage*)));
00301     job->start();
00302     aMsg->setTransferInProgress( true );
00303     return false;
00304   }
00305   return true;
00306 }
00307 
00308 
00309 //-----------------------------------------------------------------------------
00310 void FolderStorage::reallyAddMsg(KMMessage* aMsg)
00311 {
00312   if (!aMsg) // the signal that is connected can call with aMsg=0
00313     return;
00314   aMsg->setTransferInProgress( false );
00315   aMsg->setComplete( true );
00316   KMFolder *aFolder = aMsg->parent();
00317   int index;
00318   ulong serNum = aMsg->getMsgSerNum();
00319   bool undo = aMsg->enableUndo();
00320   addMsg(aMsg, &index);
00321   if (index < 0) return;
00322   unGetMsg(index);
00323   if (undo)
00324   {
00325     kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() );
00326   }
00327 }
00328 
00329 
00330 //-----------------------------------------------------------------------------
00331 void FolderStorage::reallyAddCopyOfMsg(KMMessage* aMsg)
00332 {
00333   if ( !aMsg ) return; // messageRetrieved(0) is always possible
00334   aMsg->setParent( 0 );
00335   aMsg->setTransferInProgress( false );
00336   addMsg( aMsg );
00337   unGetMsg( count() - 1 );
00338 }
00339 
00340 int FolderStorage::find( const KMMessage * msg ) const {
00341   return find( &msg->toMsgBase() );
00342 }
00343 
00344 //-----------------------------------------------------------------------------
00345 void FolderStorage::removeMsg(const QPtrList<KMMsgBase>& msgList, bool imapQuiet)
00346 {
00347   for( QPtrListIterator<KMMsgBase> it( msgList ); *it; ++it )
00348   {
00349     int idx = find(it.current());
00350     assert( idx != -1);
00351     removeMsg(idx, imapQuiet);
00352   }
00353 }
00354 
00355 //-----------------------------------------------------------------------------
00356 void FolderStorage::removeMsg(const QPtrList<KMMessage>& msgList, bool imapQuiet)
00357 {
00358   for( QPtrListIterator<KMMessage> 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(int idx, bool)
00368 {
00369   //assert(idx>=0);
00370   if(idx < 0)
00371   {
00372     kdDebug(5006) << "FolderStorage::removeMsg() : idx < 0\n" << endl;
00373     return;
00374   }
00375 
00376   KMMsgBase* mb = getMsgBase(idx);
00377 
00378   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx );
00379   if (serNum != 0)
00380     emit msgRemoved( folder(), serNum );
00381   mb = takeIndexEntry( idx );
00382 
00383   setDirty( true );
00384   needsCompact=true; // message is taken from here - needs to be compacted
00385 
00386   if (mb->isUnread() || mb->isNew() ||
00387       (folder() == kmkernel->outboxFolder())) {
00388     --mUnreadMsgs;
00389     if ( !mQuiet ) {
00390 //      kdDebug( 5006 ) << "FolderStorage::msgStatusChanged" << endl;
00391       emit numUnreadMsgsChanged( folder() );
00392     }else{
00393       if ( !mEmitChangedTimer->isActive() ) {
00394 //        kdDebug( 5006 )<< "EmitChangedTimer started" << endl;
00395         mEmitChangedTimer->start( 3000 );
00396       }
00397       mChanged = true;
00398     }
00399   }
00400   --mTotalMsgs;
00401 
00402   QString msgIdMD5 = mb->msgIdMD5();
00403   emit msgRemoved( idx, msgIdMD5 );
00404   emit msgRemoved( folder() );
00405 }
00406 
00407 
00408 //-----------------------------------------------------------------------------
00409 KMMessage* FolderStorage::take(int idx)
00410 {
00411   KMMsgBase* mb;
00412   KMMessage* msg;
00413 
00414   assert(idx>=0 && idx<=count());
00415 
00416   mb = getMsgBase(idx);
00417   if (!mb) return 0;
00418   if (!mb->isMessage()) readMsg(idx);
00419   Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx );
00420   emit msgRemoved( folder(), serNum );
00421 
00422   msg = (KMMessage*)takeIndexEntry(idx);
00423 
00424   if (msg->isUnread() || msg->isNew() ||
00425       ( folder() == kmkernel->outboxFolder() )) {
00426     --mUnreadMsgs;
00427     if ( !mQuiet ) {
00428       emit numUnreadMsgsChanged( folder() );
00429     }else{
00430       if ( !mEmitChangedTimer->isActive() ) {
00431         mEmitChangedTimer->start( 3000 );
00432       }
00433       mChanged = true;
00434     }
00435   }
00436   --mTotalMsgs;
00437   msg->setParent(0);
00438   setDirty( true );
00439   needsCompact=true; // message is taken from here - needs to be compacted
00440   QString msgIdMD5 = msg->msgIdMD5();
00441   emit msgRemoved( idx, msgIdMD5 );
00442   emit msgRemoved( folder() );
00443 
00444   return msg;
00445 }
00446 
00447 void FolderStorage::take(QPtrList<KMMessage> msgList)
00448 {
00449   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00450   {
00451     if (msg->parent())
00452     {
00453       int idx = msg->parent()->find(msg);
00454       if ( idx >= 0 )
00455         take(idx);
00456     }
00457   }
00458 }
00459 
00460 
00461 //-----------------------------------------------------------------------------
00462 KMMessage* FolderStorage::getMsg(int idx)
00463 {
00464   if ( idx < 0 || idx >= count() )
00465     return 0;
00466 
00467   KMMsgBase* mb = getMsgBase(idx);
00468   if (!mb) return 0;
00469 
00470   KMMessage *msg = 0;
00471   bool undo = mb->enableUndo();
00472   if (mb->isMessage()) {
00473       msg = ((KMMessage*)mb);
00474   } else {
00475       QString mbSubject = mb->subject();
00476       msg = readMsg(idx);
00477       // sanity check
00478       if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) {
00479         kdDebug(5006) << "Error: " << location() <<
00480           " Index file is inconsistent with folder file. This should never happen." << endl;
00481         mCompactable = false; // Don't compact
00482         writeConfig();
00483       }
00484 
00485   }
00486   // Either isMessage and we had a sernum, or readMsg gives us one
00487   // (via insertion into mMsgList). sernum == 0 may still occur due to
00488   // an outdated or corrupt IMAP cache.
00489   if ( msg->getMsgSerNum() == 0 )
00490     return 0;
00491   msg->setEnableUndo(undo);
00492   msg->setComplete( true );
00493   return msg;
00494 }
00495 
00496 //-----------------------------------------------------------------------------
00497 KMMessage* FolderStorage::readTemporaryMsg(int idx)
00498 {
00499   if(!(idx >= 0 && idx <= count()))
00500     return 0;
00501 
00502   KMMsgBase* mb = getMsgBase(idx);
00503   if (!mb) return 0;
00504 
00505   unsigned long sernum = mb->getMsgSerNum();
00506 
00507   KMMessage *msg = 0;
00508   bool undo = mb->enableUndo();
00509   if (mb->isMessage()) {
00510     // the caller will delete it, so we must make a copy it
00511     msg = new KMMessage(*(KMMessage*)mb);
00512     msg->setMsgSerNum(sernum);
00513     msg->setComplete( true );
00514   } else {
00515     // ## Those two lines need to be moved to a virtual method for KMFolderSearch, like readMsg
00516     msg = new KMMessage(*(KMMsgInfo*)mb);
00517     msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum
00518     msg->setComplete( true );
00519     msg->fromDwString(getDwString(idx));
00520   }
00521   msg->setEnableUndo(undo);
00522   return msg;
00523 }
00524 
00525 
00526 //-----------------------------------------------------------------------------
00527 KMMsgInfo* FolderStorage::unGetMsg(int idx)
00528 {
00529   KMMsgBase* mb;
00530 
00531   if(!(idx >= 0 && idx <= count()))
00532     return 0;
00533 
00534   mb = getMsgBase(idx);
00535   if (!mb) return 0;
00536 
00537 
00538   if (mb->isMessage()) {
00539     // Remove this message from all jobs' list it might still be on.
00540     // setIndexEntry deletes the message.
00541     KMMessage *msg = static_cast<KMMessage*>(mb);
00542     if ( msg->transferInProgress() ) return 0;
00543     ignoreJobsForMessage( msg );
00544     return setIndexEntry( idx, msg );
00545   }
00546 
00547   return 0;
00548 }
00549 
00550 
00551 //-----------------------------------------------------------------------------
00552 bool FolderStorage::isMessage(int idx)
00553 {
00554   KMMsgBase* mb;
00555   if (!(idx >= 0 && idx <= count())) return false;
00556   mb = getMsgBase(idx);
00557   return (mb && mb->isMessage());
00558 }
00559 
00560 //-----------------------------------------------------------------------------
00561 FolderJob* FolderStorage::createJob( KMMessage *msg, FolderJob::JobType jt,
00562                                 KMFolder *folder, QString partSpecifier,
00563                                 const AttachmentStrategy *as ) const
00564 {
00565   FolderJob * job = doCreateJob( msg, jt, folder, partSpecifier, as );
00566   if ( job )
00567     addJob( job );
00568   return job;
00569 }
00570 
00571 //-----------------------------------------------------------------------------
00572 FolderJob* FolderStorage::createJob( QPtrList<KMMessage>& msgList, const QString& sets,
00573                                 FolderJob::JobType jt, KMFolder *folder ) const
00574 {
00575   FolderJob * job = doCreateJob( msgList, sets, jt, folder );
00576   if ( job )
00577     addJob( job );
00578   return job;
00579 }
00580 
00581 //-----------------------------------------------------------------------------
00582 int FolderStorage::moveMsg(KMMessage* aMsg, int* aIndex_ret)
00583 {
00584   assert(aMsg != 0);
00585   KMFolder* msgParent = aMsg->parent();
00586 
00587   if (msgParent)
00588     msgParent->open();
00589 
00590   open();
00591   int rc = addMsg(aMsg, aIndex_ret);
00592   close();
00593 
00594   if (msgParent)
00595     msgParent->close();
00596 
00597   return rc;
00598 }
00599 
00600 //-----------------------------------------------------------------------------
00601 int FolderStorage::moveMsg(QPtrList<KMMessage> msglist, int* aIndex_ret)
00602 {
00603   KMMessage* aMsg = msglist.first();
00604   assert(aMsg != 0);
00605   KMFolder* msgParent = aMsg->parent();
00606 
00607   if (msgParent)
00608     msgParent->open();
00609 
00610   QValueList<int> index;
00611   open();
00612   int rc = addMsg(msglist, index);
00613   close();
00614   // FIXME: we want to have a QValueList to pass it back, so change this method
00615   if ( !index.isEmpty() )
00616     aIndex_ret = &index.first();
00617 
00618   if (msgParent)
00619     msgParent->close();
00620 
00621   return rc;
00622 }
00623 
00624 
00625 //-----------------------------------------------------------------------------
00626 int FolderStorage::rename(const QString& newName, KMFolderDir *newParent)
00627 {
00628   QString oldLoc, oldIndexLoc, oldIdsLoc, newLoc, newIndexLoc, newIdsLoc;
00629   QString oldSubDirLoc, newSubDirLoc;
00630   QString oldName;
00631   int rc=0, openCount=mOpenCount;
00632   KMFolderDir *oldParent;
00633 
00634   assert(!newName.isEmpty());
00635 
00636   oldLoc = location();
00637   oldIndexLoc = indexLocation();
00638   oldSubDirLoc = folder()->subdirLocation();
00639   oldIdsLoc =  KMMsgDict::instance()->getFolderIdsLocation( *this );
00640   QString oldConfigString = "Folder-" + folder()->idString();
00641 
00642   close(true);
00643 
00644   oldName = folder()->fileName();
00645   oldParent = folder()->parent();
00646   if (newParent)
00647     folder()->setParent( newParent );
00648 
00649   folder()->setName(newName);
00650   newLoc = location();
00651   newIndexLoc = indexLocation();
00652   newSubDirLoc = folder()->subdirLocation();
00653   newIdsLoc = KMMsgDict::instance()->getFolderIdsLocation( *this );
00654 
00655   if (::rename(QFile::encodeName(oldLoc), QFile::encodeName(newLoc))) {
00656     folder()->setName(oldName);
00657     folder()->setParent(oldParent);
00658     rc = errno;
00659   }
00660   else {
00661     // rename/move index file and index.sorted file
00662     if (!oldIndexLoc.isEmpty()) {
00663       ::rename(QFile::encodeName(oldIndexLoc), QFile::encodeName(newIndexLoc));
00664       ::rename(QFile::encodeName(oldIndexLoc) + ".sorted",
00665                QFile::encodeName(newIndexLoc) + ".sorted");
00666     }
00667 
00668     // rename/move serial number file
00669     if (!oldIdsLoc.isEmpty())
00670       ::rename(QFile::encodeName(oldIdsLoc), QFile::encodeName(newIdsLoc));
00671 
00672     // rename/move the subfolder directory
00673     KMFolderDir* child = 0;
00674     if( folder() )
00675       child = folder()->child();
00676 
00677     if (!::rename(QFile::encodeName(oldSubDirLoc), QFile::encodeName(newSubDirLoc) )) {
00678       // now that the subfolder directory has been renamed and/or moved also
00679       // change the name that is stored in the corresponding KMFolderNode
00680       // (provide that the name actually changed)
00681       if( child && ( oldName != newName ) ) {
00682         child->setName( "." + QFile::encodeName(newName) + ".directory" );
00683       }
00684     }
00685 
00686     // if the folder is being moved then move its node and, if necessary, also
00687     // the associated subfolder directory node to the new parent
00688     if (newParent) {
00689       if (oldParent->findRef( folder() ) != -1)
00690         oldParent->take();
00691       newParent->inSort( folder() );
00692       if ( child ) {
00693         if ( child->parent()->findRef( child ) != -1 )
00694           child->parent()->take();
00695         newParent->inSort( child );
00696         child->setParent( newParent );
00697       }
00698     }
00699   }
00700 
00701   if (openCount > 0)
00702   {
00703     open();
00704     mOpenCount = openCount;
00705   }
00706   writeConfig();
00707 
00708   // delete the old entry as we get two entries with the same ID otherwise
00709   if ( oldConfigString != "Folder-" + folder()->idString() )
00710     KMKernel::config()->deleteGroup( oldConfigString );
00711 
00712   emit locationChanged( oldLoc, newLoc );
00713   emit nameChanged();
00714   kmkernel->folderMgr()->contentsChanged();
00715   return rc;
00716 }
00717 
00718 
00719 //-----------------------------------------------------------------------------
00720 void FolderStorage::remove()
00721 {
00722   assert(!folder()->name().isEmpty());
00723 
00724   clearIndex( true, mExportsSernums ); // delete and remove from dict if necessary
00725   close(true);
00726 
00727   if ( mExportsSernums )
00728     KMMsgDict::mutableInstance()->removeFolderIds( *this );
00729   unlink(QFile::encodeName(indexLocation()) + ".sorted");
00730   unlink(QFile::encodeName(indexLocation()));
00731 
00732   int rc = removeContents();
00733 
00734   needsCompact = false; //we are dead - no need to compact us
00735 
00736   // Erase settings, otherwise they might interfer when recreating the folder
00737   KConfig* config = KMKernel::config();
00738   config->deleteGroup( "Folder-" + folder()->idString() );
00739 
00740   emit removed(folder(), (rc ? false : true));
00741 }
00742 
00743 
00744 //-----------------------------------------------------------------------------
00745 int FolderStorage::expunge()
00746 {
00747   int openCount = mOpenCount;
00748 
00749   assert(!folder()->name().isEmpty());
00750 
00751   clearIndex( true, mExportsSernums );   // delete and remove from dict, if needed
00752   close( true );
00753 
00754   if ( mExportsSernums )
00755     KMMsgDict::mutableInstance()->removeFolderIds( *this );
00756   if ( mAutoCreateIndex )
00757     truncateIndex();
00758   else unlink(QFile::encodeName(indexLocation()));
00759 
00760   int rc = expungeContents();
00761   if (rc) return rc;
00762 
00763   mDirty = false;
00764   needsCompact = false; //we're cleared and truncated no need to compact
00765 
00766   if (openCount > 0)
00767   {
00768     open();
00769     mOpenCount = openCount;
00770   }
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(); // will update unreadMsgs
00811   int unread = mUnreadMsgs;
00812   close();
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();
00910   close();
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 )
01067 {
01068   if ( type != mContentsType ) {
01069     mContentsType = type;
01070     emit contentsTypeChanged( type );
01071   }
01072 }
01073 
01074 //-----------------------------------------------------------------------------
01075 void FolderStorage::search( const KMSearchPattern* pattern )
01076 {
01077   mSearchPattern = pattern;
01078   mCurrentSearchedMsg = 0;
01079   if ( pattern )
01080     slotProcessNextSearchBatch();
01081 }
01082 
01083 void FolderStorage::slotProcessNextSearchBatch()
01084 {
01085   if ( !mSearchPattern ) return;
01086   QValueList<Q_UINT32> matchingSerNums;
01087   int end = ( count() - mCurrentSearchedMsg > 100 ) ? 100+mCurrentSearchedMsg : count();
01088   for ( int i = mCurrentSearchedMsg; i < end; ++i )
01089   {
01090     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), i );
01091     if ( mSearchPattern->matches( serNum ) )
01092       matchingSerNums.append( serNum );
01093   }
01094   mCurrentSearchedMsg = end;
01095   bool complete = ( end == count() ) ? true : false;
01096   emit searchResult( folder(), matchingSerNums, mSearchPattern, complete );
01097   if ( !complete )
01098     QTimer::singleShot( 0, this, SLOT(slotProcessNextSearchBatch()) );
01099 }
01100 
01101 //-----------------------------------------------------------------------------
01102 void FolderStorage::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
01103 {
01104   bool matches = pattern && pattern->matches( serNum );
01105 
01106   emit searchDone( folder(), serNum, pattern, matches );
01107 }
01108 
01109 //-----------------------------------------------------------------------------
01110 int FolderStorage::addMsg( QPtrList<KMMessage>& msgList, QValueList<int>& index_ret )
01111 {
01112   int ret = 0;
01113   int index;
01114   for ( QPtrListIterator<KMMessage> it( msgList ); *it; ++it )
01115   {
01116     int aret = addMsg( *it, &index );
01117     index_ret << index;
01118     if ( aret != 0 ) // error condition
01119       ret = aret;
01120   }
01121   return ret;
01122 }
01123 
01124 //-----------------------------------------------------------------------------
01125 bool FolderStorage::isMoveable() const
01126 {
01127   return ( folder()->isSystemFolder() ) ? false : true;
01128 }
01129 
01130 #include "folderstorage.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys