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