kmail

folderstorage.cpp

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