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       take(idx);
00455     }
00456   }
00457 }
00458 
00459 
00460 //-----------------------------------------------------------------------------
00461 KMMessage* FolderStorage::getMsg(int idx)
00462 {
00463   if ( idx < 0 || idx >= count() )
00464     return 0;
00465 
00466   KMMsgBase* mb = getMsgBase(idx);
00467   if (!mb) return 0;
00468 
00469   KMMessage *msg = 0;
00470   bool undo = mb->enableUndo();
00471   if (mb->isMessage()) {
00472       msg = ((KMMessage*)mb);
00473   } else {
00474       QString mbSubject = mb->subject();
00475       msg = readMsg(idx);
00476       // sanity check
00477       if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) {
00478         kdDebug(5006) << "Error: " << location() <<
00479           " Index file is inconsistent with folder file. This should never happen." << endl;
00480         mCompactable = false; // Don't compact
00481         writeConfig();
00482       }
00483 
00484   }
00485   // Either isMessage and we had a sernum, or readMsg gives us one
00486   // (via insertion into mMsgList). sernum == 0 may still occur due to
00487   // an outdated or corrupt IMAP cache.
00488   if ( msg->getMsgSerNum() == 0 )
00489     return 0;
00490   msg->setEnableUndo(undo);
00491   msg->setComplete( true );
00492   return msg;
00493 }
00494 
00495 //-----------------------------------------------------------------------------
00496 KMMessage* FolderStorage::readTemporaryMsg(int idx)
00497 {
00498   if(!(idx >= 0 && idx <= count()))
00499     return 0;
00500 
00501   KMMsgBase* mb = getMsgBase(idx);
00502   if (!mb) return 0;
00503 
00504   unsigned long sernum = mb->getMsgSerNum();
00505 
00506   KMMessage *msg = 0;
00507   bool undo = mb->enableUndo();
00508   if (mb->isMessage()) {
00509     // the caller will delete it, so we must make a copy it
00510     msg = new KMMessage(*(KMMessage*)mb);
00511     msg->setMsgSerNum(sernum);
00512     msg->setComplete( true );
00513   } else {
00514     // ## Those two lines need to be moved to a virtual method for KMFolderSearch, like readMsg
00515     msg = new KMMessage(*(KMMsgInfo*)mb);
00516     msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum
00517     msg->setComplete( true );
00518     msg->fromDwString(getDwString(idx));
00519   }
00520   msg->setEnableUndo(undo);
00521   return msg;
00522 }
00523 
00524 
00525 //-----------------------------------------------------------------------------
00526 KMMsgInfo* FolderStorage::unGetMsg(int idx)
00527 {
00528   KMMsgBase* mb;
00529 
00530   if(!(idx >= 0 && idx <= count()))
00531     return 0;
00532 
00533   mb = getMsgBase(idx);
00534   if (!mb) return 0;
00535 
00536 
00537   if (mb->isMessage()) {
00538     // Remove this message from all jobs' list it might still be on.
00539     // setIndexEntry deletes the message.
00540     KMMessage *msg = static_cast<KMMessage*>(mb);
00541     if ( msg->transferInProgress() ) return 0;
00542     ignoreJobsForMessage( msg );
00543     return setIndexEntry( idx, msg );
00544   }
00545 
00546   return 0;
00547 }
00548 
00549 
00550 //-----------------------------------------------------------------------------
00551 bool FolderStorage::isMessage(int idx)
00552 {
00553   KMMsgBase* mb;
00554   if (!(idx >= 0 && idx <= count())) return false;
00555   mb = getMsgBase(idx);
00556   return (mb && mb->isMessage());
00557 }
00558 
00559 //-----------------------------------------------------------------------------
00560 FolderJob* FolderStorage::createJob( KMMessage *msg, FolderJob::JobType jt,
00561                                 KMFolder *folder, QString partSpecifier,
00562                                 const AttachmentStrategy *as ) const
00563 {
00564   FolderJob * job = doCreateJob( msg, jt, folder, partSpecifier, as );
00565   if ( job )
00566     addJob( job );
00567   return job;
00568 }
00569 
00570 //-----------------------------------------------------------------------------
00571 FolderJob* FolderStorage::createJob( QPtrList<KMMessage>& msgList, const QString& sets,
00572                                 FolderJob::JobType jt, KMFolder *folder ) const
00573 {
00574   FolderJob * job = doCreateJob( msgList, sets, jt, folder );
00575   if ( job )
00576     addJob( job );
00577   return job;
00578 }
00579 
00580 //-----------------------------------------------------------------------------
00581 int FolderStorage::moveMsg(KMMessage* aMsg, int* aIndex_ret)
00582 {
00583   assert(aMsg != 0);
00584   KMFolder* msgParent = aMsg->parent();
00585 
00586   if (msgParent)
00587     msgParent->open();
00588 
00589   open();
00590   int rc = addMsg(aMsg, aIndex_ret);
00591   close();
00592 
00593   if (msgParent)
00594     msgParent->close();
00595 
00596   return rc;
00597 }
00598 
00599 //-----------------------------------------------------------------------------
00600 int FolderStorage::moveMsg(QPtrList<KMMessage> msglist, int* aIndex_ret)
00601 {
00602   KMMessage* aMsg = msglist.first();
00603   assert(aMsg != 0);
00604   KMFolder* msgParent = aMsg->parent();
00605 
00606   if (msgParent)
00607     msgParent->open();
00608 
00609   QValueList<int> index;
00610   open();
00611   int rc = addMsg(msglist, index);
00612   close();
00613   // FIXME: we want to have a QValueList to pass it back, so change this method
00614   if ( !index.isEmpty() )
00615     aIndex_ret = &index.first();
00616 
00617   if (msgParent)
00618     msgParent->close();
00619 
00620   return rc;
00621 }
00622 
00623 
00624 //-----------------------------------------------------------------------------
00625 int FolderStorage::rename(const QString& newName, KMFolderDir *newParent)
00626 {
00627   QString oldLoc, oldIndexLoc, oldIdsLoc, newLoc, newIndexLoc, newIdsLoc;
00628   QString oldSubDirLoc, newSubDirLoc;
00629   QString oldName;
00630   int rc=0, openCount=mOpenCount;
00631   KMFolderDir *oldParent;
00632 
00633   assert(!newName.isEmpty());
00634 
00635   oldLoc = location();
00636   oldIndexLoc = indexLocation();
00637   oldSubDirLoc = folder()->subdirLocation();
00638   oldIdsLoc =  KMMsgDict::instance()->getFolderIdsLocation( *this );
00639   QString oldConfigString = "Folder-" + folder()->idString();
00640 
00641   close(true);
00642 
00643   oldName = folder()->fileName();
00644   oldParent = folder()->parent();
00645   if (newParent)
00646     folder()->setParent( newParent );
00647 
00648   folder()->setName(newName);
00649   newLoc = location();
00650   newIndexLoc = indexLocation();
00651   newSubDirLoc = folder()->subdirLocation();
00652   newIdsLoc = KMMsgDict::instance()->getFolderIdsLocation( *this );
00653 
00654   if (::rename(QFile::encodeName(oldLoc), QFile::encodeName(newLoc))) {
00655     folder()->setName(oldName);
00656     folder()->setParent(oldParent);
00657     rc = errno;
00658   }
00659   else {
00660     // rename/move index file and index.sorted file
00661     if (!oldIndexLoc.isEmpty()) {
00662       ::rename(QFile::encodeName(oldIndexLoc), QFile::encodeName(newIndexLoc));
00663       ::rename(QFile::encodeName(oldIndexLoc) + ".sorted",
00664                QFile::encodeName(newIndexLoc) + ".sorted");
00665     }
00666 
00667     // rename/move serial number file
00668     if (!oldIdsLoc.isEmpty())
00669       ::rename(QFile::encodeName(oldIdsLoc), QFile::encodeName(newIdsLoc));
00670 
00671     // rename/move the subfolder directory
00672     KMFolderDir* child = 0;
00673     if( folder() )
00674       child = folder()->child();
00675 
00676     if (!::rename(QFile::encodeName(oldSubDirLoc), QFile::encodeName(newSubDirLoc) )) {
00677       // now that the subfolder directory has been renamed and/or moved also
00678       // change the name that is stored in the corresponding KMFolderNode
00679       // (provide that the name actually changed)
00680       if( child && ( oldName != newName ) ) {
00681         child->setName( "." + QFile::encodeName(newName) + ".directory" );
00682       }
00683     }
00684 
00685     // if the folder is being moved then move its node and, if necessary, also
00686     // the associated subfolder directory node to the new parent
00687     if (newParent) {
00688       if (oldParent->findRef( folder() ) != -1)
00689         oldParent->take();
00690       newParent->inSort( folder() );
00691       if ( child ) {
00692         if ( child->parent()->findRef( child ) != -1 )
00693           child->parent()->take();
00694         newParent->inSort( child );
00695         child->setParent( newParent );
00696       }
00697     }
00698   }
00699 
00700   if (openCount > 0)
00701   {
00702     open();
00703     mOpenCount = openCount;
00704   }
00705   writeConfig();
00706 
00707   // delete the old entry as we get two entries with the same ID otherwise
00708   if ( oldConfigString != "Folder-" + folder()->idString() )
00709     KMKernel::config()->deleteGroup( oldConfigString );
00710 
00711   emit locationChanged( oldLoc, newLoc );
00712   emit nameChanged();
00713   kmkernel->folderMgr()->contentsChanged();
00714   return rc;
00715 }
00716 
00717 
00718 //-----------------------------------------------------------------------------
00719 void FolderStorage::remove()
00720 {
00721   assert(!folder()->name().isEmpty());
00722 
00723   clearIndex( true, mExportsSernums ); // delete and remove from dict if necessary
00724   close(true);
00725 
00726   if ( mExportsSernums )
00727     KMMsgDict::mutableInstance()->removeFolderIds( *this );
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 removed(folder(), (rc ? false : true));
00740 }
00741 
00742 
00743 //-----------------------------------------------------------------------------
00744 int FolderStorage::expunge()
00745 {
00746   int openCount = mOpenCount;
00747 
00748   assert(!folder()->name().isEmpty());
00749 
00750   clearIndex( true, mExportsSernums );   // delete and remove from dict, if needed
00751   close( true );
00752 
00753   if ( mExportsSernums )
00754     KMMsgDict::mutableInstance()->removeFolderIds( *this );
00755   if ( mAutoCreateIndex )
00756     truncateIndex();
00757   else unlink(QFile::encodeName(indexLocation()));
00758 
00759   int rc = expungeContents();
00760   if (rc) return rc;
00761 
00762   mDirty = false;
00763   needsCompact = false; //we're cleared and truncated no need to compact
00764 
00765   if (openCount > 0)
00766   {
00767     open();
00768     mOpenCount = openCount;
00769   }
00770 
00771   mUnreadMsgs = 0;
00772   mTotalMsgs = 0;
00773   emit numUnreadMsgsChanged( folder() );
00774   if ( mAutoCreateIndex ) // FIXME Heh? - Till
00775     writeConfig();
00776   emit changed();
00777   emit expunged( folder() );
00778 
00779   return 0;
00780 }
00781 
00782 //-----------------------------------------------------------------------------
00783 QString FolderStorage::label() const
00784 {
00785   return folder()->label();
00786 }
00787 
00788 int FolderStorage::count(bool cache) const
00789 {
00790   if (cache && mTotalMsgs != -1)
00791     return mTotalMsgs;
00792   else
00793     return -1;
00794 }
00795 
00796 //-----------------------------------------------------------------------------
00797 int FolderStorage::countUnread()
00798 {
00799   if (mGuessedUnreadMsgs > -1)
00800     return mGuessedUnreadMsgs;
00801   if (mUnreadMsgs > -1)
00802     return mUnreadMsgs;
00803 
00804   readConfig();
00805 
00806   if (mUnreadMsgs > -1)
00807     return mUnreadMsgs;
00808 
00809   open(); // will update unreadMsgs
00810   int unread = mUnreadMsgs;
00811   close();
00812   return (unread > 0) ? unread : 0;
00813 }
00814 
00815 //-----------------------------------------------------------------------------
00816 void FolderStorage::msgStatusChanged(const KMMsgStatus oldStatus,
00817   const KMMsgStatus newStatus, int idx)
00818 {
00819   int oldUnread = 0;
00820   int newUnread = 0;
00821 
00822   if (((oldStatus & KMMsgStatusUnread || oldStatus & KMMsgStatusNew) &&
00823       !(oldStatus & KMMsgStatusIgnored)) ||
00824       (folder() == kmkernel->outboxFolder()))
00825     oldUnread = 1;
00826   if (((newStatus & KMMsgStatusUnread || newStatus & KMMsgStatusNew) &&
00827       !(newStatus & KMMsgStatusIgnored)) ||
00828       (folder() == kmkernel->outboxFolder()))
00829     newUnread = 1;
00830   int deltaUnread = newUnread - oldUnread;
00831 
00832   mDirtyTimer->changeInterval(mDirtyTimerInterval);
00833   if (deltaUnread != 0) {
00834     if (mUnreadMsgs < 0) mUnreadMsgs = 0;
00835     mUnreadMsgs += deltaUnread;
00836     if ( !mQuiet ) {
00837       emit numUnreadMsgsChanged( folder() );
00838     }else{
00839       if ( !mEmitChangedTimer->isActive() ) {
00840         mEmitChangedTimer->start( 3000 );
00841       }
00842       mChanged = true;
00843     }
00844     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(folder(), idx);
00845     emit msgChanged( folder(), serNum, deltaUnread );
00846   }
00847 }
00848 
00849 //-----------------------------------------------------------------------------
00850 void FolderStorage::headerOfMsgChanged(const KMMsgBase* aMsg, int idx)
00851 {
00852   if (idx < 0)
00853     idx = aMsg->parent()->find( aMsg );
00854 
00855   if (idx >= 0 )
00856   {
00857     if ( !mQuiet )
00858       emit msgHeaderChanged(folder(), idx);
00859     else{
00860       if ( !mEmitChangedTimer->isActive() ) {
00861         mEmitChangedTimer->start( 3000 );
00862       }
00863       mChanged = true;
00864     }
00865   } else
00866     mChanged = true;
00867 }
00868 
00869 //-----------------------------------------------------------------------------
00870 void FolderStorage::readConfig()
00871 {
00872   //kdDebug(5006)<<"#### READING CONFIG  = "<< name() <<endl;
00873   KConfig* config = KMKernel::config();
00874   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00875   if (mUnreadMsgs == -1)
00876     mUnreadMsgs = config->readNumEntry("UnreadMsgs", -1);
00877   if (mTotalMsgs == -1)
00878     mTotalMsgs = config->readNumEntry("TotalMsgs", -1);
00879   mCompactable = config->readBoolEntry("Compactable", true);
00880 
00881   int type = config->readNumEntry( "ContentsType", 0 );
00882   if ( type < 0 || type > KMail::ContentsTypeLast ) type = 0;
00883   setContentsType( static_cast<KMail::FolderContentsType>( type ) );
00884 
00885   if( folder() ) folder()->readConfig( config );
00886 }
00887 
00888 //-----------------------------------------------------------------------------
00889 void FolderStorage::writeConfig()
00890 {
00891   KConfig* config = KMKernel::config();
00892   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00893   config->writeEntry("UnreadMsgs",
00894       mGuessedUnreadMsgs == -1 ? mUnreadMsgs : mGuessedUnreadMsgs);
00895   config->writeEntry("TotalMsgs", mTotalMsgs);
00896   config->writeEntry("Compactable", mCompactable);
00897   config->writeEntry("ContentsType", mContentsType);
00898 
00899   // Write the KMFolder parts
00900   if( folder() ) folder()->writeConfig( config );
00901 
00902   GlobalSettings::self()->requestSync();
00903 }
00904 
00905 //-----------------------------------------------------------------------------
00906 void FolderStorage::correctUnreadMsgsCount()
00907 {
00908   open();
00909   close();
00910   emit numUnreadMsgsChanged( folder() );
00911 }
00912 
00913 void FolderStorage::registerWithMessageDict()
00914 {
00915   mExportsSernums = true;
00916   readFolderIdsFile();
00917 }
00918 
00919 void FolderStorage::deregisterFromMessageDict()
00920 {
00921   writeFolderIdsFile();
00922   mExportsSernums = false;
00923 }
00924 
00925 void FolderStorage::readFolderIdsFile()
00926 {
00927   if ( !mExportsSernums ) return;
00928   if ( KMMsgDict::mutableInstance()->readFolderIds( *this ) == -1 ) {
00929     invalidateFolder();
00930   }
00931   if ( !KMMsgDict::mutableInstance()->hasFolderIds( *this ) ) {
00932     invalidateFolder();
00933   }
00934 }
00935 
00936 void FolderStorage::invalidateFolder()
00937 {
00938   if ( !mExportsSernums ) return;
00939   unlink(QFile::encodeName( indexLocation()) + ".sorted");
00940   unlink(QFile::encodeName( indexLocation()) + ".ids");
00941   fillMessageDict();
00942   KMMsgDict::mutableInstance()->writeFolderIds( *this );
00943   emit invalidated( folder() );
00944 }
00945 
00946 
00947 //-----------------------------------------------------------------------------
00948 int FolderStorage::writeFolderIdsFile() const
00949 {
00950   if ( !mExportsSernums ) return -1;
00951   return KMMsgDict::mutableInstance()->writeFolderIds( *this );
00952 }
00953 
00954 //-----------------------------------------------------------------------------
00955 int FolderStorage::touchFolderIdsFile()
00956 {
00957   if ( !mExportsSernums ) return -1;
00958   return KMMsgDict::mutableInstance()->touchFolderIds( *this );
00959 }
00960 
00961 //-----------------------------------------------------------------------------
00962 int FolderStorage::appendToFolderIdsFile( int idx )
00963 {
00964   if ( !mExportsSernums ) return -1;
00965   int ret = 0;
00966   if ( count() == 1 ) {
00967     ret = KMMsgDict::mutableInstance()->writeFolderIds( *this );
00968   } else {
00969     ret = KMMsgDict::mutableInstance()->appendToFolderIds( *this, idx );
00970   }
00971   return ret;
00972 }
00973 
00974 void FolderStorage::replaceMsgSerNum( unsigned long sernum, KMMsgBase* msg, int idx )
00975 {
00976   if ( !mExportsSernums ) return;
00977   KMMsgDict::mutableInstance()->replace( sernum, msg, idx );
00978 }
00979 
00980 void FolderStorage::setRDict( KMMsgDictREntry *rentry ) const
00981 {
00982   if ( ! mExportsSernums )
00983     kdDebug(5006) << "WTF, this FolderStorage should be invisible to the msgdict, who is calling us?" << kdBacktrace() << endl;
00984   assert( mExportsSernums ); // otherwise things are very wrong
00985   if ( rentry == mRDict )
00986     return;
00987   KMMsgDict::deleteRentry( mRDict );
00988   mRDict = rentry;
00989 }
00990 
00991 //-----------------------------------------------------------------------------
00992 void FolderStorage::setStatus(int idx, KMMsgStatus status, bool toggle)
00993 {
00994   KMMsgBase *msg = getMsgBase(idx);
00995   if ( msg ) {
00996     if (toggle)
00997       msg->toggleStatus(status, idx);
00998     else
00999       msg->setStatus(status, idx);
01000   }
01001 }
01002 
01003 
01004 //-----------------------------------------------------------------------------
01005 void FolderStorage::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01006 {
01007   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01008   {
01009     FolderStorage::setStatus(*it, status, toggle);
01010   }
01011 }
01012 
01013 void FolderStorage::ignoreJobsForMessage( KMMessage *msg )
01014 {
01015   if ( !msg || msg->transferInProgress() )
01016     return;
01017 
01018   QPtrListIterator<FolderJob> it( mJobList );
01019   while ( it.current() )
01020   {
01021     //FIXME: the questions is : should we iterate through all
01022     //messages in jobs? I don't think so, because it would
01023     //mean canceling the jobs that work with other messages
01024     if ( it.current()->msgList().first() == msg )
01025     {
01026       FolderJob* job = it.current();
01027       mJobList.remove( job );
01028       delete job;
01029     } else
01030       ++it;
01031   }
01032 }
01033 
01034 //-----------------------------------------------------------------------------
01035 void FolderStorage::removeJobs()
01036 {
01037   mJobList.setAutoDelete( true );
01038   mJobList.clear();
01039   mJobList.setAutoDelete( false );
01040 }
01041 
01042 
01043 
01044 //-----------------------------------------------------------------------------
01045 void FolderStorage::updateChildrenState()
01046 {
01047   if ( folder() && folder()->child() )
01048   {
01049     if ( kmkernel->folderMgr()->folderCount( folder()->child() ) > 0 )
01050       setHasChildren( HasChildren );
01051     else
01052       setHasChildren( HasNoChildren );
01053   }
01054 }
01055 
01056 //-----------------------------------------------------------------------------
01057 void FolderStorage::setNoChildren( bool aNoChildren )
01058 {
01059   mNoChildren = aNoChildren;
01060   if ( aNoChildren )
01061     setHasChildren( HasNoChildren );
01062 }
01063 
01064 //-----------------------------------------------------------------------------
01065 void FolderStorage::setContentsType( KMail::FolderContentsType type )
01066 {
01067   if ( type != mContentsType ) {
01068     mContentsType = type;
01069     emit contentsTypeChanged( type );
01070   }
01071 }
01072 
01073 //-----------------------------------------------------------------------------
01074 void FolderStorage::search( const KMSearchPattern* pattern )
01075 {
01076   mSearchPattern = pattern;
01077   mCurrentSearchedMsg = 0;
01078   if ( pattern )
01079     slotProcessNextSearchBatch();
01080 }
01081 
01082 void FolderStorage::slotProcessNextSearchBatch()
01083 {
01084   if ( !mSearchPattern ) return;
01085   QValueList<Q_UINT32> matchingSerNums;
01086   int end = ( count() - mCurrentSearchedMsg > 100 ) ? 100+mCurrentSearchedMsg : count();
01087   for ( int i = mCurrentSearchedMsg; i < end; ++i )
01088   {
01089     Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), i );
01090     if ( mSearchPattern->matches( serNum ) )
01091       matchingSerNums.append( serNum );
01092   }
01093   mCurrentSearchedMsg = end;
01094   bool complete = ( end == count() ) ? true : false;
01095   emit searchResult( folder(), matchingSerNums, mSearchPattern, complete );
01096   if ( !complete )
01097     QTimer::singleShot( 0, this, SLOT(slotProcessNextSearchBatch()) );
01098 }
01099 
01100 //-----------------------------------------------------------------------------
01101 void FolderStorage::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
01102 {
01103   bool matches = pattern && pattern->matches( serNum );
01104 
01105   emit searchDone( folder(), serNum, pattern, matches );
01106 }
01107 
01108 //-----------------------------------------------------------------------------
01109 int FolderStorage::addMsg( QPtrList<KMMessage>& msgList, QValueList<int>& index_ret )
01110 {
01111   int ret = 0;
01112   int index;
01113   for ( QPtrListIterator<KMMessage> it( msgList ); *it; ++it )
01114   {
01115     int aret = addMsg( *it, &index );
01116     index_ret << index;
01117     if ( aret != 0 ) // error condition
01118       ret = aret;
01119   }
01120   return ret;
01121 }
01122 
01123 //-----------------------------------------------------------------------------
01124 bool FolderStorage::isMoveable() const
01125 {
01126   return ( folder()->isSystemFolder() ) ? false : true;
01127 }
01128 
01129 #include "folderstorage.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys