kmail Library API Documentation

kmfolder.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmfolder.cpp
00003 // Author: Stefan Taferner <taferner@alpin.or.at>
00004 
00005 #include <config.h>
00006 
00007 #include "kmfolder.h"
00008 
00009 #include "kmfolderimap.h" //for the nasty imap hacks, FIXME
00010 #include "undostack.h"
00011 #include "kmmsgdict.h"
00012 #include "identitymanager.h"
00013 #include "kmidentity.h"
00014 #include "kmfoldermgr.h"
00015 #include "kmkernel.h"
00016 #include "kmcommands.h"
00017 
00018 #include <kmessagebox.h>
00019 #include <klocale.h>
00020 #include <kconfig.h>
00021 #include <kdebug.h>
00022 
00023 #include <qfile.h>
00024 #include <qregexp.h>
00025 
00026 #include <mimelib/mimepp.h>
00027 #include <errno.h>
00028 
00029 //-----------------------------------------------------------------------------
00030 
00031 KMFolder :: KMFolder(KMFolderDir* aParent, const QString& aName) :
00032   KMFolderNode(aParent, aName)
00033 {
00034   mOpenCount      = 0;
00035   mQuiet      = 0;
00036   mChanged        = FALSE;
00037   mAutoCreateIndex= TRUE;
00038   mIsSystemFolder = FALSE;
00039   mType           = "plain";
00040   mAcctList       = 0;
00041   mDirty          = FALSE;
00042   mUnreadMsgs      = -1;
00043   mGuessedUnreadMsgs = -1;
00044   mTotalMsgs      = -1;
00045   needsCompact    = FALSE;
00046   mChild          = 0;
00047   mConvertToUtf8  = FALSE;
00048   mMailingListEnabled = FALSE;
00049   mCompactable     = TRUE;
00050   mNoContent      = FALSE;
00051   expireMessages = FALSE;
00052   unreadExpireAge = 28;
00053   unreadExpireUnits = expireNever;
00054   readExpireAge = 14;
00055   readExpireUnits = expireNever;
00056   mRDict = 0;
00057   mUseCustomIcons = false;
00058   mDirtyTimer = new QTimer(this);
00059   connect(mDirtyTimer, SIGNAL(timeout()),
00060       this, SLOT(updateIndex()));
00061 
00062   if ( aParent ) {
00063       connect(this, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
00064               parent()->manager(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)));
00065       connect(this, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00066               parent()->manager(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)));
00067       connect(this, SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)),
00068               parent()->manager(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)));
00069       connect(this, SIGNAL(msgHeaderChanged(KMFolder*,  int)),
00070               parent()->manager(), SIGNAL(msgHeaderChanged(KMFolder*, int)));
00071   }
00072   //FIXME: Centralize all the readConfig calls somehow - Zack
00073   readConfig();
00074 }
00075 
00076 
00077 //-----------------------------------------------------------------------------
00078 KMFolder :: ~KMFolder()
00079 {
00080   delete mAcctList;
00081   mJobList.setAutoDelete( true );
00082   QObject::disconnect( SIGNAL(destroyed(QObject*)), this, 0 );
00083   mJobList.clear();
00084   KMMsgDict::deleteRentry(mRDict);
00085 }
00086 
00087 
00088 //-----------------------------------------------------------------------------
00089 QString KMFolder::dotEscape(const QString& aStr) const
00090 {
00091   if (aStr[0] != '.') return aStr;
00092   return aStr.left(aStr.find(QRegExp("[^\\.]"))) + aStr;
00093 }
00094 
00095 void KMFolder::addJob( FolderJob* job ) const
00096 {
00097   QObject::connect( job, SIGNAL(destroyed(QObject*)),
00098                     SLOT(removeJob(QObject*)) );
00099   mJobList.append( job );
00100 }
00101 
00102 void KMFolder::removeJob( QObject* job )
00103 {
00104   mJobList.remove( static_cast<FolderJob*>( job ) );
00105 }
00106 
00107 
00108 //-----------------------------------------------------------------------------
00109 QString KMFolder::location() const
00110 {
00111   QString sLocation(path());
00112 
00113   if (!sLocation.isEmpty()) sLocation += '/';
00114   sLocation += dotEscape(fileName());
00115 
00116   return sLocation;
00117 }
00118 
00119 
00120 
00121 //-----------------------------------------------------------------------------
00122 QString KMFolder::subdirLocation() const
00123 {
00124   QString sLocation(path());
00125 
00126   if (!sLocation.isEmpty()) sLocation += '/';
00127   sLocation += '.';
00128   sLocation += dotEscape(fileName());
00129   sLocation += ".directory";
00130 
00131   return sLocation;
00132 }
00133 
00134 //-----------------------------------------------------------------------------
00135 KMFolderDir* KMFolder::createChildFolder()
00136 {
00137   QString childName = "." + fileName() + ".directory";
00138   QString childDir = path() + "/" + childName;
00139   bool ok = true;
00140 
00141   if (mChild)
00142     return mChild;
00143 
00144   if (access(QFile::encodeName(childDir), W_OK) != 0) // Not there or not writable
00145   {
00146     if (mkdir(QFile::encodeName(childDir), S_IRWXU) != 0
00147       && chmod(QFile::encodeName(childDir), S_IRWXU) != 0)
00148         ok=false; //failed create new or chmod existing tmp/
00149   }
00150 
00151   if (!ok) {
00152     QString wmsg = QString(" '%1': %2").arg(childDir).arg(strerror(errno));
00153     KMessageBox::information(0,i18n("Failed to create folder") + wmsg);
00154     return 0;
00155   }
00156 
00157   KMFolderDir* folderDir = new KMFolderDir(parent(), childName,
00158     (folderType() == KMFolderTypeImap) ? KMImapDir : KMStandardDir);
00159   if (!folderDir)
00160     return 0;
00161   folderDir->reload();
00162   parent()->append(folderDir);
00163   mChild = folderDir;
00164   return folderDir;
00165 }
00166 
00167 //-----------------------------------------------------------------------------
00168 void KMFolder::setAutoCreateIndex(bool autoIndex)
00169 {
00170   mAutoCreateIndex = autoIndex;
00171 }
00172 
00173 //-----------------------------------------------------------------------------
00174 void KMFolder::setDirty(bool f)
00175 {
00176   mDirty = f;
00177   if (mDirty  && mAutoCreateIndex)
00178     mDirtyTimer->changeInterval( mDirtyTimerInterval );
00179   else
00180     mDirtyTimer->stop();
00181 }
00182 
00183 //-----------------------------------------------------------------------------
00184 void KMFolder::setIdentity( uint identity ) {
00185   mIdentity = identity;
00186   kmkernel->slotRequestConfigSync();
00187 }
00188 
00189 //-----------------------------------------------------------------------------
00190 void KMFolder::markNewAsUnread()
00191 {
00192   KMMsgBase* msgBase;
00193   int i;
00194 
00195   for (i=0; i< count(); ++i)
00196   {
00197     if (!(msgBase = getMsgBase(i))) continue;
00198     if (msgBase->isNew())
00199     {
00200       msgBase->setStatus(KMMsgStatusUnread);
00201       msgBase->setDirty(TRUE);
00202     }
00203   }
00204 }
00205 
00206 void KMFolder::markUnreadAsRead()
00207 {
00208   KMMsgBase* msgBase;
00209   SerNumList serNums;
00210 
00211   for (int i=count()-1; i>=0; --i)
00212   {
00213     msgBase = getMsgBase(i);
00214     assert(msgBase);
00215     if (msgBase->isNew() || msgBase->isUnread())
00216     {
00217       serNums.append( msgBase->getMsgSerNum() );
00218     }
00219   }
00220   if (serNums.empty())
00221     return;
00222 
00223   KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums );
00224   command->start();
00225 }
00226 
00227 //-----------------------------------------------------------------------------
00228 void KMFolder::quiet(bool beQuiet)
00229 {
00230   if (beQuiet)
00231     mQuiet++;
00232   else {
00233     mQuiet--;
00234     if (mQuiet <= 0)
00235     {
00236       mQuiet = 0;
00237       if (mChanged)
00238        emit changed();
00239       mChanged = FALSE;
00240     }
00241   }
00242 }
00243 
00244 //-----------------------------------------------------------------------------
00245 
00246 // Needed to use QSortedList in reduceSize()
00247 
00249 int operator<( KMMsgBase & m1, KMMsgBase & m2 )
00250 {
00251   return (m1.date() < m2.date());
00252 }
00253 
00255 int operator==( KMMsgBase & m1, KMMsgBase & m2 )
00256 {
00257   return (m1.date() == m2.date());
00258 }
00259 
00260 
00261 //-----------------------------------------------------------------------------
00262 int KMFolder::expungeOldMsg(int days)
00263 {
00264   int i, msgnb=0;
00265   time_t msgTime, maxTime;
00266   const KMMsgBase* mb;
00267   QValueList<int> rmvMsgList;
00268 
00269   maxTime = time(0) - days * 3600 * 24;
00270 
00271   for (i=count()-1; i>=0; i--) {
00272     mb = getMsgBase(i);
00273     assert(mb);
00274     msgTime = mb->date();
00275 
00276     if (msgTime < maxTime) {
00277       //kdDebug(5006) << "deleting msg " << i << " : " << mb->subject() << " - " << mb->dateStr(); // << endl;
00278       removeMsg( i );
00279       msgnb++;
00280     }
00281   }
00282   return msgnb;
00283 }
00284 
00285 
00286 //-----------------------------------------------------------------------------
00291 int
00292 KMFolder::daysToExpire(int number, ExpireUnits units) {
00293   switch (units) {
00294   case expireDays: // Days
00295     return number;
00296   case expireWeeks: // Weeks
00297     return number * 7;
00298   case expireMonths: // Months - this could be better rather than assuming 31day months.
00299     return number * 31;
00300   default: // this avoids a compiler warning (not handled enumeration values)
00301     ;
00302   }
00303 
00304   return -1;
00305 
00306 }
00307 
00308 //-----------------------------------------------------------------------------
00314 void KMFolder::expireOldMessages() {
00315   FolderJob *job = createJob( 0, FolderJob::tExpireMessages );
00316   job->start();
00317 }
00318 
00319 
00320 //-----------------------------------------------------------------------------
00321 void KMFolder::emitMsgAddedSignals(int idx)
00322 {
00323   Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(this, idx);
00324   if (!mQuiet) {
00325     emit msgAdded(idx);
00326   } else {
00327     mChanged=true;
00328   }
00329   emit msgAdded(this, serNum);
00330 }
00331 
00332 //-----------------------------------------------------------------------------
00333 bool KMFolder::canAddMsgNow(KMMessage* aMsg, int* aIndex_ret)
00334 {
00335   if (aIndex_ret) *aIndex_ret = -1;
00336   KMFolder *msgParent = aMsg->parent();
00337   // If the message has a parent and is in transfer, bail out. If it does not
00338   // have a parent we want to be able to add it even if it is in transfer.
00339   if (aMsg->transferInProgress() && msgParent)
00340       return false;
00341   if (!aMsg->isComplete() && msgParent && msgParent->folderType() == KMFolderTypeImap)
00342   {
00343     FolderJob *job = msgParent->createJob(aMsg);
00344     connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00345             SLOT(reallyAddMsg(KMMessage*)));
00346     job->start();
00347     aMsg->setTransferInProgress(TRUE);
00348     return FALSE;
00349   }
00350   return TRUE;
00351 }
00352 
00353 
00354 //-----------------------------------------------------------------------------
00355 void KMFolder::reallyAddMsg(KMMessage* aMsg)
00356 {
00357   if (!aMsg) // the signal that is connected can call with aMsg=0
00358     return;
00359   aMsg->setTransferInProgress(FALSE);
00360   KMFolder *folder = aMsg->parent();
00361   int index;
00362   ulong serNum = aMsg->getMsgSerNum();
00363   bool undo = aMsg->enableUndo();
00364   addMsg(aMsg, &index);
00365   if (index < 0) return;
00366   unGetMsg(index);
00367   if (undo)
00368   {
00369     kmkernel->undoStack()->pushSingleAction( serNum, folder, this );
00370   }
00371 }
00372 
00373 
00374 //-----------------------------------------------------------------------------
00375 void KMFolder::reallyAddCopyOfMsg(KMMessage* aMsg)
00376 {
00377   aMsg->setParent( 0 );
00378   aMsg->setTransferInProgress( false );
00379   addMsg( aMsg );
00380   unGetMsg( count() - 1 );
00381 }
00382 
00383 int KMFolder::find( const KMMessage * msg ) const {
00384   return find( &msg->toMsgBase() );
00385 }
00386 
00387 //-----------------------------------------------------------------------------
00388 void KMFolder::removeMsg(QPtrList<KMMessage> msgList, bool imapQuiet)
00389 {
00390   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00391   {
00392     int idx = find(msg);
00393     assert( idx != -1);
00394     removeMsg(idx, imapQuiet);
00395   }
00396 }
00397 
00398 //-----------------------------------------------------------------------------
00399 void KMFolder::removeMsg(int idx, bool)
00400 {
00401   //assert(idx>=0);
00402   if(idx < 0)
00403   {
00404     kdDebug(5006) << "KMFolder::removeMsg() : idx < 0\n" << endl;
00405     return;
00406   }
00407 
00408   KMMsgBase* mb = getMsgBase(idx);
00409 
00410   Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(this, idx);
00411   if (serNum != 0)
00412     emit msgRemoved(this, 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       (this == kmkernel->outboxFolder())) {
00420     --mUnreadMsgs;
00421     emit numUnreadMsgsChanged( this );
00422   }
00423   --mTotalMsgs;
00424 
00425   QString msgIdMD5 = mb->msgIdMD5();
00426   QString strippedSubjMD5 = mb->strippedSubjectMD5();
00427   if (strippedSubjMD5.isEmpty()) {
00428      mb->initStrippedSubjectMD5();
00429      strippedSubjMD5 = mb->strippedSubjectMD5();
00430   }
00431   emit msgRemoved(idx, msgIdMD5, strippedSubjMD5);
00432   emit msgRemoved(this);
00433 }
00434 
00435 
00436 //-----------------------------------------------------------------------------
00437 KMMessage* KMFolder::take(int idx)
00438 {
00439   KMMsgBase* mb;
00440   KMMessage* msg;
00441 
00442   assert(idx>=0 && idx<=count());
00443 
00444   mb = getMsgBase(idx);
00445   if (!mb) return 0;
00446   if (!mb->isMessage()) readMsg(idx);
00447   Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(this, idx);
00448   emit msgRemoved(this,serNum);
00449 
00450   msg = (KMMessage*)takeIndexEntry(idx);
00451 
00452   if (msg->isUnread() || msg->isNew() ||
00453       (this == kmkernel->outboxFolder())) {
00454     --mUnreadMsgs;
00455     emit numUnreadMsgsChanged( this );
00456   }
00457   --mTotalMsgs;
00458   msg->setParent(0);
00459   setDirty( true );
00460   needsCompact=true; // message is taken from here - needs to be compacted
00461   QString msgIdMD5 = msg->msgIdMD5();
00462   QString strippedSubjMD5 = msg->strippedSubjectMD5();
00463   if (strippedSubjMD5.isEmpty()) {
00464      msg->initStrippedSubjectMD5();
00465      strippedSubjMD5 = msg->strippedSubjectMD5();
00466   }
00467   emit msgRemoved(idx, msgIdMD5, strippedSubjMD5);
00468   emit msgRemoved(this);
00469 
00470   return msg;
00471 }
00472 
00473 void KMFolder::take(QPtrList<KMMessage> msgList)
00474 {
00475   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00476   {
00477     if (msg->parent())
00478     {
00479       int idx = msg->parent()->find(msg);
00480       assert( idx != -1);
00481       KMFolder::take(idx);
00482     }
00483   }
00484 }
00485 
00486 
00487 //-----------------------------------------------------------------------------
00488 KMMessage* KMFolder::getMsg(int idx)
00489 {
00490   KMMsgBase* mb;
00491 
00492   if(!(idx >= 0 && idx <= count()))
00493     return 0;
00494 
00495   mb = getMsgBase(idx);
00496   if (!mb) return 0;
00497 
00498 #if 0
00499   if (mb->isMessage()) return ((KMMessage*)mb);
00500   return readMsg(idx);
00501 #else
00502   KMMessage *msg = 0;
00503   bool undo = mb->enableUndo();
00504   if (mb->isMessage()) {
00505       msg = ((KMMessage*)mb);
00506   } else {
00507       QString mbSubject = mb->subject();
00508       msg = readMsg(idx);
00509       // sanity check
00510       if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) {
00511       kdDebug(5006) << "Error: " << location() <<
00512       " Index file is inconsistent with folder file. This should never happen." << endl;
00513       mCompactable = FALSE; // Don't compact
00514       writeConfig();
00515       }
00516   }
00517   msg->setEnableUndo(undo);
00518 
00519   if (msg->getMsgSerNum() == 0) {
00520     msg->setMsgSerNum(kmkernel->msgDict()->insert(0, msg, idx));
00521     kdDebug(5006) << "Serial number generated for message in folder " << label() << endl;
00522   }
00523   msg->setComplete( true );
00524   return msg;
00525 #endif
00526 
00527 
00528 }
00529 
00530 
00531 //-----------------------------------------------------------------------------
00532 KMMsgInfo* KMFolder::unGetMsg(int idx)
00533 {
00534   KMMsgBase* mb;
00535 
00536   if(!(idx >= 0 && idx <= count()))
00537     return 0;
00538 
00539   mb = getMsgBase(idx);
00540   if (!mb) return 0;
00541 
00542 
00543   if (mb->isMessage()) {
00544     // Remove this message from all jobs' list it might still be on.
00545     // setIndexEntry deletes the message.
00546     KMMessage *msg = static_cast<KMMessage*>(mb);
00547     if ( msg->transferInProgress() ) return 0;
00548     ignoreJobsForMessage( msg );
00549     return setIndexEntry( idx, msg );
00550   }
00551 
00552   return 0;
00553 }
00554 
00555 
00556 //-----------------------------------------------------------------------------
00557 bool KMFolder::isMessage(int idx)
00558 {
00559   KMMsgBase* mb;
00560   if (!(idx >= 0 && idx <= count())) return FALSE;
00561   mb = getMsgBase(idx);
00562   return (mb && mb->isMessage());
00563 }
00564 
00565 //-----------------------------------------------------------------------------
00566 FolderJob* KMFolder::createJob( KMMessage *msg, FolderJob::JobType jt,
00567                                 KMFolder *folder, QString partSpecifier, 
00568                                 const AttachmentStrategy *as ) const
00569 {
00570   FolderJob * job = doCreateJob( msg, jt, folder, partSpecifier, as );
00571   if ( job )
00572     addJob( job );
00573   return job;
00574 }
00575 
00576 //-----------------------------------------------------------------------------
00577 FolderJob* KMFolder::createJob( QPtrList<KMMessage>& msgList, const QString& sets,
00578                                 FolderJob::JobType jt, KMFolder *folder ) const
00579 {
00580   FolderJob * job = doCreateJob( msgList, sets, jt, folder );
00581   if ( job )
00582     addJob( job );
00583   return job;
00584 }
00585 
00586 //-----------------------------------------------------------------------------
00587 int KMFolder::moveMsg(KMMessage* aMsg, int* aIndex_ret)
00588 {
00589   KMFolder* msgParent;
00590   int rc;
00591 
00592   assert(aMsg != 0);
00593   msgParent = aMsg->parent();
00594 
00595   if (msgParent)
00596     msgParent->open();
00597 
00598   open();
00599   rc = addMsg(aMsg, aIndex_ret);
00600   close();
00601 
00602   if (msgParent)
00603     msgParent->close();
00604 
00605   return rc;
00606 }
00607 
00608 //-----------------------------------------------------------------------------
00609 int KMFolder::moveMsg(QPtrList<KMMessage> msglist, int* aIndex_ret)
00610 {
00611   KMFolder* msgParent;
00612   int rc;
00613 
00614   KMMessage* aMsg = msglist.first();
00615   assert(aMsg != 0);
00616   msgParent = aMsg->parent();
00617 
00618   if (msgParent)
00619     msgParent->open();
00620 
00621   open();
00622   //FIXME : is it always imap ?
00623   rc = static_cast<KMFolderImap*>(this)->addMsg(msglist, aIndex_ret); //yuck: Don
00624   close();
00625 
00626   if (msgParent)
00627     msgParent->close();
00628 
00629   return rc;
00630 }
00631 
00632 
00633 //-----------------------------------------------------------------------------
00634 int KMFolder::rename(const QString& newName, KMFolderDir *newParent)
00635 {
00636   QString oldLoc, oldIndexLoc, oldIdsLoc, newLoc, newIndexLoc, newIdsLoc;
00637   QString oldSubDirLoc, newSubDirLoc;
00638   QString oldName;
00639   int rc=0, openCount=mOpenCount;
00640   KMFolderDir *oldParent;
00641 
00642   assert(!newName.isEmpty());
00643 
00644   oldLoc = location();
00645   oldIndexLoc = indexLocation();
00646   oldSubDirLoc = subdirLocation();
00647   if (kmkernel->msgDict())
00648     oldIdsLoc = kmkernel->msgDict()->getFolderIdsLocation(this);
00649 
00650   close(TRUE);
00651 
00652   oldName = fileName();
00653   oldParent = parent();
00654   if (newParent)
00655     setParent( newParent );
00656 
00657   setName(newName);
00658   newLoc = location();
00659   newIndexLoc = indexLocation();
00660   newSubDirLoc = subdirLocation();
00661   if (kmkernel->msgDict())
00662     newIdsLoc = kmkernel->msgDict()->getFolderIdsLocation(this);
00663 
00664   if (::rename(QFile::encodeName(oldLoc), QFile::encodeName(newLoc))) {
00665     setName(oldName);
00666     setParent(oldParent);
00667     rc = errno;
00668   }
00669   else {
00670     // rename/move index file and index.sorted file
00671     if (!oldIndexLoc.isEmpty()) {
00672       ::rename(QFile::encodeName(oldIndexLoc), QFile::encodeName(newIndexLoc));
00673       ::rename(QFile::encodeName(oldIndexLoc) + ".sorted",
00674                QFile::encodeName(newIndexLoc) + ".sorted");
00675     }
00676 
00677     // rename/move serial number file
00678     if (!oldIdsLoc.isEmpty())
00679       ::rename(QFile::encodeName(oldIdsLoc), QFile::encodeName(newIdsLoc));
00680 
00681     // rename/move the subfolder directory
00682     if (!::rename(QFile::encodeName(oldSubDirLoc), QFile::encodeName(newSubDirLoc) )) {
00683       // now that the subfolder directory has been renamed and/or moved also
00684       // change the name that is stored in the corresponding KMFolderNode
00685       // (provide that the name actually changed)
00686       if( mChild && ( oldName != newName ) ) {
00687         mChild->setName( "." + QFile::encodeName(newName) + ".directory" );
00688       }
00689     }
00690 
00691     // if the folder is being moved then move its node and, if necessary, also
00692     // the associated subfolder directory node to the new parent
00693     if (newParent) {
00694       if (oldParent->findRef( this ) != -1)
00695         oldParent->take();
00696       newParent->inSort( this );
00697       if (mChild) {
00698         if (mChild->parent()->findRef( mChild ) != -1)
00699           mChild->parent()->take();
00700         newParent->inSort( mChild );
00701         mChild->setParent( newParent );
00702       }
00703     }
00704   }
00705 
00706   if (openCount > 0)
00707   {
00708     open();
00709     mOpenCount = openCount;
00710   }
00711 
00712   emit nameChanged();
00713   return rc;
00714 }
00715 
00716 
00717 //-----------------------------------------------------------------------------
00718 int KMFolder::remove()
00719 {
00720   assert(!name().isEmpty());
00721 
00722   mChild = 0;
00723   clearIndex(true, true); // delete and remove from dict
00724   close(TRUE);
00725 
00726   if (kmkernel->msgDict()) kmkernel->msgDict()->removeFolderIds(this);
00727   unlink(QFile::encodeName(indexLocation()) + ".sorted");
00728   unlink(QFile::encodeName(indexLocation()));
00729 
00730   int rc = removeContents();
00731   if (rc) return rc;
00732 
00733   needsCompact = false; //we are dead - no need to compact us
00734   return 0;
00735 }
00736 
00737 
00738 //-----------------------------------------------------------------------------
00739 int KMFolder::expunge()
00740 {
00741   int openCount = mOpenCount;
00742 
00743   assert(!name().isEmpty());
00744 
00745   clearIndex(true, true);   // delete and remove from dict
00746   close(TRUE);
00747 
00748   kmkernel->msgDict()->removeFolderIds(this);
00749   if (mAutoCreateIndex)
00750     truncateIndex();
00751   else unlink(QFile::encodeName(indexLocation()));
00752 
00753   int rc = expungeContents();
00754   if (rc) return rc;
00755 
00756   mDirty = FALSE;
00757   needsCompact = false; //we're cleared and truncated no need to compact
00758 
00759   if (openCount > 0)
00760   {
00761     open();
00762     mOpenCount = openCount;
00763   }
00764 
00765   mUnreadMsgs = 0;
00766   mTotalMsgs = 0;
00767   emit numUnreadMsgsChanged( this );
00768   if (mAutoCreateIndex)
00769     writeConfig();
00770   emit changed();
00771   emit expunged();
00772 
00773   return 0;
00774 }
00775 
00776 
00777 //-----------------------------------------------------------------------------
00778 const char* KMFolder::type() const
00779 {
00780   if (mAcctList) return "In";
00781   return KMFolderNode::type();
00782 }
00783 
00784 
00785 //-----------------------------------------------------------------------------
00786 QString KMFolder::label() const
00787 {
00788   if (mIsSystemFolder && !mLabel.isEmpty()) return mLabel;
00789   if (mIsSystemFolder) return i18n(name().latin1());
00790   return name();
00791 }
00792 
00793 int KMFolder::count(bool cache) const
00794 {
00795   if (cache && mTotalMsgs != -1)
00796     return mTotalMsgs;
00797   else
00798     return -1;
00799 }
00800 
00801 //-----------------------------------------------------------------------------
00802 int KMFolder::countUnread()
00803 {
00804   if (mGuessedUnreadMsgs > -1)
00805     return mGuessedUnreadMsgs;
00806   if (mUnreadMsgs > -1)
00807     return mUnreadMsgs;
00808 
00809   readConfig();
00810 
00811   if (mUnreadMsgs > -1)
00812     return mUnreadMsgs;
00813 
00814   open(); // will update unreadMsgs
00815   int unread = mUnreadMsgs;
00816   close();
00817   return (unread > 0) ? unread : 0;
00818 }
00819 
00820 //-----------------------------------------------------------------------------
00821 int KMFolder::countUnreadRecursive()
00822 {
00823   KMFolder *folder;
00824   int count = countUnread();
00825   KMFolderDir *dir = child();
00826   if (!dir)
00827     return count;
00828 
00829   QPtrListIterator<KMFolderNode> it(*dir);
00830   for ( ; it.current(); ++it )
00831     if (!it.current()->isDir()) {
00832       folder = static_cast<KMFolder*>(it.current());
00833       count += folder->countUnreadRecursive();
00834     }
00835 
00836   return count;
00837 }
00838 
00839 //-----------------------------------------------------------------------------
00840 void KMFolder::msgStatusChanged(const KMMsgStatus oldStatus,
00841   const KMMsgStatus newStatus, int idx)
00842 {
00843   int oldUnread = 0;
00844   int newUnread = 0;
00845 
00846   if (oldStatus & KMMsgStatusUnread || oldStatus & KMMsgStatusNew ||
00847       (this == kmkernel->outboxFolder()))
00848     oldUnread = 1;
00849   if (newStatus & KMMsgStatusUnread || newStatus & KMMsgStatusNew ||
00850       (this == kmkernel->outboxFolder()))
00851     newUnread = 1;
00852   int deltaUnread = newUnread - oldUnread;
00853 
00854   mDirtyTimer->changeInterval(mDirtyTimerInterval);
00855   if (deltaUnread != 0) {
00856     if (mUnreadMsgs < 0) mUnreadMsgs = 0;
00857     mUnreadMsgs += deltaUnread;
00858     emit numUnreadMsgsChanged( this );
00859 
00860     Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(this, idx);
00861     emit msgChanged( this, serNum, deltaUnread );
00862   }
00863 }
00864 
00865 //-----------------------------------------------------------------------------
00866 void KMFolder::headerOfMsgChanged(const KMMsgBase* aMsg, int idx)
00867 {
00868   if (idx < 0)
00869     idx = aMsg->parent()->find( aMsg );
00870   if (idx >= 0 && !mQuiet)
00871     emit msgHeaderChanged(this, idx);
00872    else
00873      mChanged = TRUE;
00874 }
00875 
00876 //-----------------------------------------------------------------------------
00877 QString KMFolder::idString() const
00878 {
00879   KMFolderNode* folderNode = parent();
00880   if (!folderNode)
00881     return "";
00882   while (folderNode->parent())
00883     folderNode = folderNode->parent();
00884   int pathLen = path().length() - folderNode->path().length();
00885   QString relativePath = path().right( pathLen );
00886   if (!relativePath.isEmpty())
00887     relativePath = relativePath.right( relativePath.length() - 1 ) + "/";
00888   QString escapedName = QString( name() );
00889   /* Escape [ and ] as they are disallowed for kconfig sections and that is
00890      what the idString is primarily used for. */
00891   escapedName.replace( "[", "%(" );
00892   escapedName.replace( "]", "%)" );
00893   return relativePath + escapedName;
00894 }
00895 
00896 //-----------------------------------------------------------------------------
00897 void KMFolder::readConfig()
00898 {
00899   //kdDebug(5006)<<"#### READING CONFIG  = "<< name() <<endl;
00900   KConfig* config = KMKernel::config();
00901   KConfigGroupSaver saver(config, "Folder-" + idString());
00902   if (mUnreadMsgs == -1)
00903     mUnreadMsgs = config->readNumEntry("UnreadMsgs", -1);
00904   if (mTotalMsgs == -1)
00905     mTotalMsgs = config->readNumEntry("TotalMsgs", -1);
00906   mMailingListEnabled = config->readBoolEntry("MailingListEnabled");
00907   mMailingListPostingAddress = config->readEntry("MailingListPostingAddress");
00908   mMailingListAdminAddress = config->readEntry("MailingListAdminAddress");
00909   mIdentity = config->readUnsignedNumEntry("Identity",0);
00910   mCompactable = config->readBoolEntry("Compactable", TRUE);
00911 
00912   expireMessages = config->readBoolEntry("ExpireMessages", FALSE);
00913   readExpireAge = config->readNumEntry("ReadExpireAge", 3);
00914   readExpireUnits = (ExpireUnits)config->readNumEntry("ReadExpireUnits", expireMonths);
00915   unreadExpireAge = config->readNumEntry("UnreadExpireAge", 12);
00916   unreadExpireUnits = (ExpireUnits)config->readNumEntry("UnreadExpireUnits", expireNever);
00917   setUserWhoField( config->readEntry("WhoField"), false );
00918   mUseCustomIcons = config->readBoolEntry("UseCustomIcons", false );
00919   mNormalIconPath = config->readEntry("NormalIconPath" );
00920   mUnreadIconPath = config->readEntry("UnreadIconPath" );
00921   if ( mUseCustomIcons )
00922       emit iconsChanged();
00923 }
00924 
00925 //-----------------------------------------------------------------------------
00926 void KMFolder::writeConfig()
00927 {
00928   KConfig* config = KMKernel::config();
00929   KConfigGroupSaver saver(config, "Folder-" + idString());
00930   config->writeEntry("UnreadMsgs", countUnread());
00931   config->writeEntry("TotalMsgs", mTotalMsgs);
00932   config->writeEntry("MailingListEnabled", mMailingListEnabled);
00933   config->writeEntry("MailingListPostingAddress", mMailingListPostingAddress);
00934   config->writeEntry("MailingListAdminAddress", mMailingListAdminAddress);
00935   config->writeEntry("Identity", mIdentity);
00936   config->writeEntry("Compactable", mCompactable);
00937   config->writeEntry("ExpireMessages", expireMessages);
00938   config->writeEntry("ReadExpireAge", readExpireAge);
00939   config->writeEntry("ReadExpireUnits", readExpireUnits);
00940   config->writeEntry("UnreadExpireAge", unreadExpireAge);
00941   config->writeEntry("UnreadExpireUnits", unreadExpireUnits);
00942   config->writeEntry("WhoField", mUserWhoField);
00943 
00944   config->writeEntry("UseCustomIcons", mUseCustomIcons);
00945   config->writeEntry("NormalIconPath", mNormalIconPath);
00946   config->writeEntry("UnreadIconPath", mUnreadIconPath);
00947 }
00948 
00949 //-----------------------------------------------------------------------------
00950 void KMFolder::correctUnreadMsgsCount()
00951 {
00952   open();
00953   close();
00954   emit numUnreadMsgsChanged( this );
00955 }
00956 
00957 //-----------------------------------------------------------------------------
00958 void KMFolder::fillMsgDict(KMMsgDict *dict)
00959 {
00960   fillDictFromIndex(dict);
00961 }
00962 
00963 //-----------------------------------------------------------------------------
00964 int KMFolder::writeMsgDict(KMMsgDict *dict)
00965 {
00966   int ret = 0;
00967   if (!dict)
00968     dict = kmkernel->msgDict();
00969   if (dict)
00970     ret = dict->writeFolderIds(this);
00971   return ret;
00972 }
00973 
00974 //-----------------------------------------------------------------------------
00975 int KMFolder::touchMsgDict()
00976 {
00977   int ret = 0;
00978   KMMsgDict *dict = kmkernel->msgDict();
00979   if (dict)
00980     ret = dict->touchFolderIds(this);
00981   return ret;
00982 }
00983 
00984 //-----------------------------------------------------------------------------
00985 int KMFolder::appendtoMsgDict(int idx)
00986 {
00987   int ret = 0;
00988   KMMsgDict *dict = kmkernel->msgDict();
00989   if (dict) {
00990     if (count() == 1) {
00991       ret = dict->writeFolderIds(this);
00992     } else {
00993       ret = dict->appendtoFolderIds(this, idx);
00994     }
00995   }
00996   return ret;
00997 }
00998 
00999 //-----------------------------------------------------------------------------
01000 void KMFolder::setStatus(int idx, KMMsgStatus status, bool toggle)
01001 {
01002   KMMsgBase *msg = getMsgBase(idx);
01003   if ( msg ) {
01004     if (toggle)
01005       msg->toggleStatus(status, idx);
01006     else
01007       msg->setStatus(status, idx);
01008   }
01009 }
01010 
01011 void KMFolder::setRDict(KMMsgDictREntry *rentry) {
01012   if (rentry == mRDict)
01013     return;
01014   KMMsgDict::deleteRentry(mRDict);
01015   mRDict = rentry;
01016 }
01017 
01018 //-----------------------------------------------------------------------------
01019 void KMFolder::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01020 {
01021   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01022   {
01023     KMFolder::setStatus(*it, status, toggle);
01024   }
01025 }
01026 
01027 //-----------------------------------------------------------------------------
01028 void KMFolder::setUserWhoField(const QString &whoField, bool aWriteConfig)
01029 {
01030   mUserWhoField = whoField;
01031   if ( whoField.isEmpty() )
01032   {
01033     // default setting
01034     const KMIdentity & identity =
01035       kmkernel->identityManager()->identityForUoidOrDefault( mIdentity );
01036 
01037     if ( mIsSystemFolder && folderType() != KMFolderTypeImap )
01038     {
01039       // local system folders
01040       if ( this == kmkernel->inboxFolder() || this == kmkernel->trashFolder() ) mWhoField = "From";
01041       if ( this == kmkernel->outboxFolder() || this == kmkernel->sentFolder() || this == kmkernel->draftsFolder() ) mWhoField = "To";
01042 
01043     } else if ( identity.drafts() == idString() || identity.fcc() == idString() ) {
01044       // drafts or sent of the identity
01045       mWhoField = "To";
01046     } else {
01047       mWhoField = "From";
01048     }
01049 
01050   } else if ( whoField == "From" || whoField == "To" ) {
01051 
01052     // set the whoField according to the user-setting
01053     mWhoField = whoField;
01054 
01055   } else {
01056     // this should not happen...
01057     kdDebug(5006) << "Illegal setting " << whoField << " for userWhoField!" << endl;
01058   }
01059 
01060   if (aWriteConfig)
01061     writeConfig();
01062 }
01063 
01064 void KMFolder::ignoreJobsForMessage( KMMessage *msg )
01065 {
01066   if ( !msg || msg->transferInProgress() )
01067     return;
01068 
01069   QPtrListIterator<FolderJob> it( mJobList );
01070   while ( it.current() )
01071   {
01072     //FIXME: the questions is : should we iterate through all
01073     //messages in jobs? I don't think so, because it would
01074     //mean canceling the jobs that work with other messages
01075     if ( it.current()->msgList().first() == msg )
01076     {
01077       FolderJob* job = it.current();
01078       mJobList.remove( job );
01079       delete job;
01080     } else
01081       ++it;
01082   }
01083 }
01084 
01085 void KMFolder::setIconPaths(const QString &normalPath, const QString &unreadPath)
01086 {
01087   mNormalIconPath = normalPath;
01088   mUnreadIconPath = unreadPath;
01089   writeConfig();
01090   emit iconsChanged();
01091 }
01092 
01093 //-----------------------------------------------------------------------------
01094 void KMFolder::removeJobs()
01095 {
01096   mJobList.setAutoDelete( true );
01097   mJobList.clear();
01098   mJobList.setAutoDelete( false );
01099 }
01100 
01101 //-----------------------------------------------------------------------------
01102 size_t KMFolder::crlf2lf( char* str, const size_t strLen )
01103 {
01104   if ( !str || strLen == 0 ) return 0;
01105   
01106   const char* source = str;
01107   const char* sourceEnd = source + strLen;
01108 
01109   // search the first occurrence of "\r\n"
01110   for ( ; source < sourceEnd - 1; ++source ) {
01111     if ( *source == '\r' && *( source + 1 ) == '\n' )
01112       break;
01113   }
01114 
01115   if ( source == sourceEnd - 1 ) {
01116     // no "\r\n" found
01117     return strLen;
01118   }
01119 
01120   // replace all occurrences of "\r\n" with "\n" (in place)
01121   char* target = const_cast<char*>( source ); // target points to '\r'
01122   ++source; // source points to '\n'
01123   for ( ; source < sourceEnd; ++source ) {
01124     if ( *source != '\r' || *( source + 1 ) != '\n' )
01125       *target++ = *source;
01126   }
01127   *target = '\0'; // terminate result
01128   return target - str;
01129 }
01130 
01131 #include "kmfolder.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:26 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003