kmail

kmfoldermbox.cpp

00001 /* -*- c-basic-offset: 2 -*-
00002  * kmail: KDE mail client
00003  * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  */
00020 #include <config.h>
00021 #include <qfileinfo.h>
00022 #include <qregexp.h>
00023 
00024 #include "kmfoldermbox.h"
00025 #include "folderstorage.h"
00026 #include "kmfolder.h"
00027 #include "kmkernel.h"
00028 #include "kmmsgdict.h"
00029 #include "undostack.h"
00030 #include "kcursorsaver.h"
00031 #include "jobscheduler.h"
00032 #include "compactionjob.h"
00033 #include "util.h"
00034 
00035 #include <kdebug.h>
00036 #include <klocale.h>
00037 #include <kmessagebox.h>
00038 #include <knotifyclient.h>
00039 #include <kprocess.h>
00040 #include <kconfig.h>
00041 
00042 #include <ctype.h>
00043 #include <stdio.h>
00044 #include <errno.h>
00045 #include <assert.h>
00046 #include <unistd.h>
00047 
00048 #ifdef HAVE_FCNTL_H
00049 #include <fcntl.h>
00050 #endif
00051 
00052 #include <stdlib.h>
00053 #include <sys/types.h>
00054 #include <sys/stat.h>
00055 #include <sys/file.h>
00056 #include "broadcaststatus.h"
00057 using KPIM::BroadcastStatus;
00058 
00059 #ifndef MAX_LINE
00060 #define MAX_LINE 4096
00061 #endif
00062 #ifndef INIT_MSGS
00063 #define INIT_MSGS 8
00064 #endif
00065 
00066 // Regular expression to find the line that seperates messages in a mail
00067 // folder:
00068 #define MSG_SEPERATOR_START "From "
00069 #define MSG_SEPERATOR_START_LEN (sizeof(MSG_SEPERATOR_START) - 1)
00070 #define MSG_SEPERATOR_REGEX "^From .*[0-9][0-9]:[0-9][0-9]"
00071 
00072 
00073 //-----------------------------------------------------------------------------
00074 KMFolderMbox::KMFolderMbox(KMFolder* folder, const char* name)
00075   : KMFolderIndex(folder, name)
00076 {
00077   mStream         = 0;
00078   mFilesLocked    = false;
00079   mReadOnly       = false;
00080   mLockType       = lock_none;
00081 }
00082 
00083 
00084 //-----------------------------------------------------------------------------
00085 KMFolderMbox::~KMFolderMbox()
00086 {
00087   if (mOpenCount>0) close(true);
00088   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00089 }
00090 
00091 //-----------------------------------------------------------------------------
00092 int KMFolderMbox::open()
00093 {
00094   int rc = 0;
00095 
00096   mOpenCount++;
00097   kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00098 
00099   if (mOpenCount > 1) return 0;  // already open
00100 
00101   assert(!folder()->name().isEmpty());
00102 
00103   mFilesLocked = false;
00104   mStream = fopen(QFile::encodeName(location()), "r+"); // messages file
00105   if (!mStream)
00106   {
00107     KNotifyClient::event( 0, "warning",
00108     i18n("Cannot open file \"%1\":\n%2").arg(location()).arg(strerror(errno)));
00109     kdDebug(5006) << "Cannot open folder `" << location() << "': " << strerror(errno) << endl;
00110     mOpenCount = 0;
00111     return errno;
00112   }
00113 
00114   lock();
00115 
00116   if (!folder()->path().isEmpty())
00117   {
00118      KMFolderIndex::IndexStatus index_status = indexStatus();
00119      // test if index file exists and is up-to-date
00120      if (KMFolderIndex::IndexOk != index_status)
00121      {
00122        // only show a warning if the index file exists, otherwise it can be
00123        // silently regenerated
00124        if (KMFolderIndex::IndexTooOld == index_status) {
00125         QString msg = i18n("<qt><p>The index of folder '%2' seems "
00126                            "to be out of date. To prevent message "
00127                            "corruption the index will be "
00128                            "regenerated. As a result deleted "
00129                            "messages might reappear and status "
00130                            "flags might be lost.</p>"
00131                            "<p>Please read the corresponding entry "
00132                            "in the <a href=\"%1\">FAQ section of the manual "
00133                            "of KMail</a> for "
00134                            "information about how to prevent this "
00135                            "problem from happening again.</p></qt>")
00136                       .arg("help:/kmail/faq.html#faq-index-regeneration")
00137                       .arg(name());
00138         // When KMail is starting up we have to show a non-blocking message
00139         // box so that the initialization can continue. We don't show a
00140         // queued message box when KMail isn't starting up because queued
00141         // message boxes don't have a "Don't ask again" checkbox.
00142         if (kmkernel->startingUp())
00143         {
00144           KConfigGroup configGroup( KMKernel::config(), "Notification Messages" );
00145           bool showMessage =
00146             configGroup.readBoolEntry( "showIndexRegenerationMessage", true );
00147           if (showMessage)
00148             KMessageBox::queuedMessageBox( 0, KMessageBox::Information,
00149                                            msg, i18n("Index Out of Date"),
00150                                            KMessageBox::AllowLink );
00151         }
00152         else
00153         {
00154             KCursorSaver idle(KBusyPtr::idle());
00155             KMessageBox::information( 0, msg, i18n("Index Out of Date"),
00156                                       "showIndexRegenerationMessage",
00157                                       KMessageBox::AllowLink );
00158         }
00159        }
00160        QString str;
00161        mIndexStream = 0;
00162        str = i18n("Folder `%1' changed. Recreating index.")
00163              .arg(name());
00164        emit statusMsg(str);
00165      } else {
00166        mIndexStream = fopen(QFile::encodeName(indexLocation()), "r+"); // index file
00167        if ( mIndexStream ) {
00168          fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00169          updateIndexStreamPtr();
00170        }
00171      }
00172 
00173      if (!mIndexStream)
00174        rc = createIndexFromContents();
00175      else
00176        if (!readIndex())
00177          rc = createIndexFromContents();
00178   }
00179   else
00180   {
00181     mAutoCreateIndex = false;
00182     rc = createIndexFromContents();
00183   }
00184 
00185   mChanged = false;
00186 
00187   fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
00188   if (mIndexStream)
00189      fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00190 
00191   return rc;
00192 }
00193 
00194 //----------------------------------------------------------------------------
00195 int KMFolderMbox::canAccess()
00196 {
00197   assert(!folder()->name().isEmpty());
00198 
00199   if (access(QFile::encodeName(location()), R_OK | W_OK) != 0) {
00200     kdDebug(5006) << "KMFolderMbox::access call to access function failed" << endl;
00201       return 1;
00202   }
00203   return 0;
00204 }
00205 
00206 //-----------------------------------------------------------------------------
00207 int KMFolderMbox::create()
00208 {
00209   int rc;
00210   int old_umask;
00211 
00212   assert(!folder()->name().isEmpty());
00213   assert(mOpenCount == 0);
00214 
00215   kdDebug(5006) << "Creating folder " << name() << endl;
00216   if (access(QFile::encodeName(location()), F_OK) == 0) {
00217     kdDebug(5006) << "KMFolderMbox::create call to access function failed." << endl;
00218     kdDebug(5006) << "File:: " << endl;
00219     kdDebug(5006) << "Error " << endl;
00220     return EEXIST;
00221   }
00222 
00223   old_umask = umask(077);
00224   mStream = fopen(QFile::encodeName(location()), "w+"); //sven; open RW
00225   umask(old_umask);
00226 
00227   if (!mStream) return errno;
00228 
00229   fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
00230 
00231   if (!folder()->path().isEmpty())
00232   {
00233     old_umask = umask(077);
00234     mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+"); //sven; open RW
00235     updateIndexStreamPtr(true);
00236     umask(old_umask);
00237 
00238     if (!mIndexStream) return errno;
00239     fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00240   }
00241   else
00242   {
00243     mAutoCreateIndex = false;
00244   }
00245 
00246   mOpenCount++;
00247   mChanged = false;
00248 
00249   rc = writeIndex();
00250   if (!rc) lock();
00251   return rc;
00252 }
00253 
00254 
00255 //-----------------------------------------------------------------------------
00256 void KMFolderMbox::close(bool aForced)
00257 {
00258   if (mOpenCount <= 0 || !mStream) return;
00259   if (mOpenCount > 0) mOpenCount--;
00260   if (mOpenCount > 0 && !aForced) return;
00261 #if 0 // removed hack that prevented closing system folders (see kmail-devel discussion about mail expiring)
00262   if ( (folder() != kmkernel->inboxFolder())
00263         && folder()->isSystemFolder() && !aForced )
00264   {
00265       mOpenCount = 1;
00266       return;
00267   }
00268 #endif
00269 
00270   if (mAutoCreateIndex)
00271   {
00272       if (KMFolderIndex::IndexOk != indexStatus()) {
00273           kdDebug(5006) << "Critical error: " << location() <<
00274               " has been modified by an external application while KMail was running." << endl;
00275           //      exit(1); backed out due to broken nfs
00276       }
00277 
00278       updateIndex();
00279       writeConfig();
00280   }
00281 
00282   if (!noContent()) {
00283     if (mStream) unlock();
00284     mMsgList.clear(true);
00285 
00286     if (mStream) fclose(mStream);
00287     if (mIndexStream) {
00288       fclose(mIndexStream);
00289       updateIndexStreamPtr(true);
00290     }
00291   }
00292   mOpenCount   = 0;
00293   mStream      = 0;
00294   mIndexStream = 0;
00295   mFilesLocked = false;
00296   mUnreadMsgs  = -1;
00297 
00298   mMsgList.reset(INIT_MSGS);
00299 }
00300 
00301 //-----------------------------------------------------------------------------
00302 void KMFolderMbox::sync()
00303 {
00304   if (mOpenCount > 0)
00305     if (!mStream || fsync(fileno(mStream)) ||
00306         !mIndexStream || fsync(fileno(mIndexStream))) {
00307     kmkernel->emergencyExit( i18n("Could not sync index file <b>%1</b>: %2").arg( indexLocation() ).arg(errno ? QString::fromLocal8Bit(strerror(errno)) : i18n("Internal error. Please copy down the details and report a bug.")));
00308     }
00309 }
00310 
00311 //-----------------------------------------------------------------------------
00312 int KMFolderMbox::lock()
00313 {
00314   int rc;
00315   struct flock fl;
00316   fl.l_type=F_WRLCK;
00317   fl.l_whence=0;
00318   fl.l_start=0;
00319   fl.l_len=0;
00320   fl.l_pid=-1;
00321   QCString cmd_str;
00322   assert(mStream != 0);
00323   mFilesLocked = false;
00324   mReadOnly = false;
00325 
00326   switch( mLockType )
00327   {
00328     case FCNTL:
00329       rc = fcntl(fileno(mStream), F_SETLKW, &fl);
00330 
00331       if (rc < 0)
00332       {
00333         kdDebug(5006) << "Cannot lock folder `" << location() << "': "
00334                   << strerror(errno) << " (" << errno << ")" << endl;
00335         mReadOnly = true;
00336         return errno;
00337       }
00338 
00339       if (mIndexStream)
00340       {
00341         rc = fcntl(fileno(mIndexStream), F_SETLK, &fl);
00342 
00343         if (rc < 0)
00344         {
00345           kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
00346                     << strerror(errno) << " (" << errno << ")" << endl;
00347           rc = errno;
00348           fl.l_type = F_UNLCK;
00349           /*rc =*/ fcntl(fileno(mIndexStream), F_SETLK, &fl);
00350           mReadOnly = true;
00351           return rc;
00352         }
00353       }
00354       break;
00355 
00356     case procmail_lockfile:
00357       cmd_str = "lockfile -l20 -r5 ";
00358       if (!mProcmailLockFileName.isEmpty())
00359         cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
00360       else
00361         cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock"));
00362 
00363       rc = system( cmd_str.data() );
00364       if( rc != 0 )
00365       {
00366         kdDebug(5006) << "Cannot lock folder `" << location() << "': "
00367                   << strerror(rc) << " (" << rc << ")" << endl;
00368         mReadOnly = true;
00369         return rc;
00370       }
00371       if( mIndexStream )
00372       {
00373         cmd_str = "lockfile -l20 -r5 " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock"));
00374         rc = system( cmd_str.data() );
00375         if( rc != 0 )
00376         {
00377           kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
00378                     << strerror(rc) << " (" << rc << ")" << endl;
00379           mReadOnly = true;
00380           return rc;
00381         }
00382       }
00383       break;
00384 
00385     case mutt_dotlock:
00386       cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(location()));
00387       rc = system( cmd_str.data() );
00388       if( rc != 0 )
00389       {
00390         kdDebug(5006) << "Cannot lock folder `" << location() << "': "
00391                   << strerror(rc) << " (" << rc << ")" << endl;
00392         mReadOnly = true;
00393         return rc;
00394       }
00395       if( mIndexStream )
00396       {
00397         cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(indexLocation()));
00398         rc = system( cmd_str.data() );
00399         if( rc != 0 )
00400         {
00401           kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
00402                     << strerror(rc) << " (" << rc << ")" << endl;
00403           mReadOnly = true;
00404           return rc;
00405         }
00406       }
00407       break;
00408 
00409     case mutt_dotlock_privileged:
00410       cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(location()));
00411       rc = system( cmd_str.data() );
00412       if( rc != 0 )
00413       {
00414         kdDebug(5006) << "Cannot lock folder `" << location() << "': "
00415                   << strerror(rc) << " (" << rc << ")" << endl;
00416         mReadOnly = true;
00417         return rc;
00418       }
00419       if( mIndexStream )
00420       {
00421         cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(indexLocation()));
00422         rc = system( cmd_str.data() );
00423         if( rc != 0 )
00424         {
00425           kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
00426                     << strerror(rc) << " (" << rc << ")" << endl;
00427           mReadOnly = true;
00428           return rc;
00429         }
00430       }
00431       break;
00432 
00433     case lock_none:
00434     default:
00435       break;
00436   }
00437 
00438 
00439   mFilesLocked = true;
00440   return 0;
00441 }
00442 
00443 //-------------------------------------------------------------
00444 FolderJob*
00445 KMFolderMbox::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
00446                            KMFolder *folder, QString, const AttachmentStrategy* ) const
00447 {
00448   MboxJob *job = new MboxJob( msg, jt, folder );
00449   job->setParent( this );
00450   return job;
00451 }
00452 
00453 //-------------------------------------------------------------
00454 FolderJob*
00455 KMFolderMbox::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
00456                            FolderJob::JobType jt, KMFolder *folder ) const
00457 {
00458   MboxJob *job = new MboxJob( msgList, sets, jt, folder );
00459   job->setParent( this );
00460   return job;
00461 }
00462 
00463 //-----------------------------------------------------------------------------
00464 int KMFolderMbox::unlock()
00465 {
00466   int rc;
00467   struct flock fl;
00468   fl.l_type=F_UNLCK;
00469   fl.l_whence=0;
00470   fl.l_start=0;
00471   fl.l_len=0;
00472   QCString cmd_str;
00473 
00474   assert(mStream != 0);
00475   mFilesLocked = false;
00476 
00477   switch( mLockType )
00478   {
00479     case FCNTL:
00480       if (mIndexStream) fcntl(fileno(mIndexStream), F_SETLK, &fl);
00481       fcntl(fileno(mStream), F_SETLK, &fl);
00482       rc = errno;
00483       break;
00484 
00485     case procmail_lockfile:
00486       cmd_str = "rm -f ";
00487       if (!mProcmailLockFileName.isEmpty())
00488         cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
00489       else
00490         cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock"));
00491 
00492       rc = system( cmd_str.data() );
00493       if( mIndexStream )
00494       {
00495         cmd_str = "rm -f " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock"));
00496         rc = system( cmd_str.data() );
00497       }
00498       break;
00499 
00500     case mutt_dotlock:
00501       cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(location()));
00502       rc = system( cmd_str.data() );
00503       if( mIndexStream )
00504       {
00505         cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(indexLocation()));
00506         rc = system( cmd_str.data() );
00507       }
00508       break;
00509 
00510     case mutt_dotlock_privileged:
00511       cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(location()));
00512       rc = system( cmd_str.data() );
00513       if( mIndexStream )
00514       {
00515         cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(indexLocation()));
00516         rc = system( cmd_str.data() );
00517       }
00518       break;
00519 
00520     case lock_none:
00521     default:
00522       rc = 0;
00523       break;
00524   }
00525 
00526   return rc;
00527 }
00528 
00529 
00530 //-----------------------------------------------------------------------------
00531 KMFolderIndex::IndexStatus KMFolderMbox::indexStatus()
00532 {
00533   QFileInfo contInfo(location());
00534   QFileInfo indInfo(indexLocation());
00535 
00536   if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00537   if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00538 
00539   // Check whether the mbox file is more than 5 seconds newer than the index
00540   // file. The 5 seconds are added to reduce the number of false alerts due
00541   // to slightly out of sync clocks of the NFS server and the local machine.
00542   return ( contInfo.lastModified() > indInfo.lastModified().addSecs(5) )
00543       ? KMFolderIndex::IndexTooOld
00544       : KMFolderIndex::IndexOk;
00545 }
00546 
00547 
00548 //-----------------------------------------------------------------------------
00549 int KMFolderMbox::createIndexFromContents()
00550 {
00551   char line[MAX_LINE];
00552   char status[8], xstatus[8];
00553   QCString subjStr, dateStr, fromStr, toStr, xmarkStr, *lastStr=0;
00554   QCString replyToIdStr, replyToAuxIdStr, referencesStr, msgIdStr;
00555   QCString sizeServerStr, uidStr;
00556   QCString contentTypeStr, charset;
00557   bool atEof = false;
00558   bool inHeader = true;
00559   KMMsgInfo* mi;
00560   QString msgStr;
00561   QRegExp regexp(MSG_SEPERATOR_REGEX);
00562   int i, num, numStatus;
00563   short needStatus;
00564 
00565   assert(mStream != 0);
00566   rewind(mStream);
00567 
00568   mMsgList.clear();
00569 
00570   num     = -1;
00571   numStatus= 11;
00572   off_t offs = 0;
00573   size_t size = 0;
00574   dateStr = "";
00575   fromStr = "";
00576   toStr = "";
00577   subjStr = "";
00578   *status = '\0';
00579   *xstatus = '\0';
00580   xmarkStr = "";
00581   replyToIdStr = "";
00582   replyToAuxIdStr = "";
00583   referencesStr = "";
00584   msgIdStr = "";
00585   needStatus = 3;
00586   size_t sizeServer = 0;
00587   ulong uid = 0;
00588 
00589 
00590   while (!atEof)
00591   {
00592     off_t pos = ftell(mStream);
00593     if (!fgets(line, MAX_LINE, mStream)) atEof = true;
00594 
00595     if (atEof ||
00596         (memcmp(line, MSG_SEPERATOR_START, MSG_SEPERATOR_START_LEN)==0 &&
00597          regexp.search(line) >= 0))
00598     {
00599       size = pos - offs;
00600       pos = ftell(mStream);
00601 
00602       if (num >= 0)
00603       {
00604         if (numStatus <= 0)
00605         {
00606           msgStr = i18n("Creating index file: one message done", "Creating index file: %n messages done", num);
00607           emit statusMsg(msgStr);
00608           numStatus = 10;
00609         }
00610 
00611         if (size > 0)
00612         {
00613           msgIdStr = msgIdStr.stripWhiteSpace();
00614           if( !msgIdStr.isEmpty() ) {
00615             int rightAngle;
00616             rightAngle = msgIdStr.find( '>' );
00617             if( rightAngle != -1 )
00618               msgIdStr.truncate( rightAngle + 1 );
00619           }
00620 
00621           replyToIdStr = replyToIdStr.stripWhiteSpace();
00622           if( !replyToIdStr.isEmpty() ) {
00623             int rightAngle;
00624             rightAngle = replyToIdStr.find( '>' );
00625             if( rightAngle != -1 )
00626               replyToIdStr.truncate( rightAngle + 1 );
00627           }
00628 
00629           referencesStr = referencesStr.stripWhiteSpace();
00630           if( !referencesStr.isEmpty() ) {
00631             int leftAngle, rightAngle;
00632             leftAngle = referencesStr.findRev( '<' );
00633             if( ( leftAngle != -1 )
00634                 && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) {
00635               // use the last reference, instead of missing In-Reply-To
00636               replyToIdStr = referencesStr.mid( leftAngle );
00637             }
00638 
00639             // find second last reference
00640             leftAngle = referencesStr.findRev( '<', leftAngle - 1 );
00641             if( leftAngle != -1 )
00642               referencesStr = referencesStr.mid( leftAngle );
00643             rightAngle = referencesStr.findRev( '>' );
00644             if( rightAngle != -1 )
00645               referencesStr.truncate( rightAngle + 1 );
00646 
00647             // Store the second to last reference in the replyToAuxIdStr
00648             // It is a good candidate for threading the message below if the
00649             // message In-Reply-To points to is not kept in this folder,
00650             // but e.g. in an Outbox
00651             replyToAuxIdStr = referencesStr;
00652             rightAngle = referencesStr.find( '>' );
00653             if( rightAngle != -1 )
00654               replyToAuxIdStr.truncate( rightAngle + 1 );
00655           }
00656 
00657           contentTypeStr = contentTypeStr.stripWhiteSpace();
00658           charset = "";
00659           if ( !contentTypeStr.isEmpty() )
00660           {
00661             int cidx = contentTypeStr.find( "charset=" );
00662             if ( cidx != -1 ) {
00663               charset = contentTypeStr.mid( cidx + 8 );
00664               if ( charset[0] == '"' ) {
00665                 charset = charset.mid( 1 );
00666               }
00667               cidx = 0;
00668               while ( (unsigned int) cidx < charset.length() ) {
00669                 if ( charset[cidx] == '"' || ( !isalnum(charset[cidx]) &&
00670                     charset[cidx] != '-' && charset[cidx] != '_' ) )
00671                   break;
00672                 ++cidx;
00673               }
00674               charset.truncate( cidx );
00675               // kdDebug() << "KMFolderMaildir::readFileHeaderIntern() charset found: " <<
00676               //              charset << " from " << contentTypeStr << endl;
00677             }
00678           }
00679 
00680           mi = new KMMsgInfo(folder());
00681           mi->init( subjStr.stripWhiteSpace(),
00682                     fromStr.stripWhiteSpace(),
00683                     toStr.stripWhiteSpace(),
00684                     0, KMMsgStatusNew,
00685                     xmarkStr.stripWhiteSpace(),
00686                     replyToIdStr, replyToAuxIdStr, msgIdStr,
00687                     KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
00688                     KMMsgMDNStateUnknown, charset, offs, size, sizeServer, uid );
00689           mi->setStatus(status, xstatus);
00690           mi->setDate( dateStr.stripWhiteSpace() );
00691           mi->setDirty(false);
00692           mMsgList.append(mi, mExportsSernums );
00693 
00694           *status = '\0';
00695           *xstatus = '\0';
00696           needStatus = 3;
00697           xmarkStr = "";
00698           replyToIdStr = "";
00699           replyToAuxIdStr = "";
00700           referencesStr = "";
00701           msgIdStr = "";
00702           dateStr = "";
00703           fromStr = "";
00704           subjStr = "";
00705           sizeServer = 0;
00706           uid = 0;
00707         }
00708         else num--,numStatus++;
00709       }
00710 
00711       offs = ftell(mStream);
00712       num++;
00713       numStatus--;
00714       inHeader = true;
00715       continue;
00716     }
00717     // Is this a long header line?
00718     if (inHeader && (line[0]=='\t' || line[0]==' '))
00719     {
00720       i = 0;
00721       while (line [i]=='\t' || line [i]==' ') i++;
00722       if (line [i] < ' ' && line [i]>0) inHeader = false;
00723       else if (lastStr) *lastStr += line + i;
00724     }
00725     else lastStr = 0;
00726 
00727     if (inHeader && (line [0]=='\n' || line [0]=='\r'))
00728       inHeader = false;
00729     if (!inHeader) continue;
00730 
00731     /* -sanders Make all messages read when auto-recreating index */
00732     /* Reverted, as it breaks reading the sent mail status, for example.
00733        -till */
00734     if ((needStatus & 1) && strncasecmp(line, "Status:", 7) == 0)
00735     {
00736       for(i=0; i<4 && line[i+8] > ' '; i++)
00737         status[i] = line[i+8];
00738       status[i] = '\0';
00739       needStatus &= ~1;
00740     }
00741     else if ((needStatus & 2) && strncasecmp(line, "X-Status:", 9)==0)
00742     {
00743       for(i=0; i<4 && line[i+10] > ' '; i++)
00744         xstatus[i] = line[i+10];
00745       xstatus[i] = '\0';
00746       needStatus &= ~2;
00747     }
00748     else if (strncasecmp(line,"X-KMail-Mark:",13)==0)
00749         xmarkStr = QCString(line+13);
00750     else if (strncasecmp(line,"In-Reply-To:",12)==0) {
00751       replyToIdStr = QCString(line+12);
00752       lastStr = &replyToIdStr;
00753     }
00754     else if (strncasecmp(line,"References:",11)==0) {
00755       referencesStr = QCString(line+11);
00756       lastStr = &referencesStr;
00757     }
00758     else if (strncasecmp(line,"Message-Id:",11)==0) {
00759       msgIdStr = QCString(line+11);
00760       lastStr = &msgIdStr;
00761     }
00762     else if (strncasecmp(line,"Date:",5)==0)
00763     {
00764       dateStr = QCString(line+5);
00765       lastStr = &dateStr;
00766     }
00767     else if (strncasecmp(line,"From:", 5)==0)
00768     {
00769       fromStr = QCString(line+5);
00770       lastStr = &fromStr;
00771     }
00772     else if (strncasecmp(line,"To:", 3)==0)
00773     {
00774       toStr = QCString(line+3);
00775       lastStr = &toStr;
00776     }
00777     else if (strncasecmp(line,"Subject:",8)==0)
00778     {
00779       subjStr = QCString(line+8);
00780       lastStr = &subjStr;
00781     }
00782     else if (strncasecmp(line,"X-Length:",9)==0)
00783     {
00784       sizeServerStr = QCString(line+9);
00785       sizeServer = sizeServerStr.toULong();
00786       lastStr = &sizeServerStr;
00787     }
00788     else if (strncasecmp(line,"X-UID:",6)==0)
00789     {
00790       uidStr = QCString(line+6);
00791       uid = uidStr.toULong();
00792       lastStr = &uidStr;
00793     }
00794     else if (strncasecmp(line, "Content-Type:", 13) == 0)
00795     {
00796       contentTypeStr = QCString(line+13);
00797       lastStr = &contentTypeStr;
00798     }
00799   }
00800 
00801   if (mAutoCreateIndex)
00802   {
00803     emit statusMsg(i18n("Writing index file"));
00804     writeIndex();
00805   }
00806   else mHeaderOffset = 0;
00807 
00808   correctUnreadMsgsCount();
00809 
00810   if (kmkernel->outboxFolder() == folder() && count() > 0)
00811     KMessageBox::queuedMessageBox(0, KMessageBox::Information,
00812                                   i18n("Your outbox contains messages which were "
00813     "most-likely not created by KMail;\nplease remove them from there if you "
00814     "do not want KMail to send them."));
00815 
00816   invalidateFolder();
00817   return 0;
00818 }
00819 
00820 
00821 //-----------------------------------------------------------------------------
00822 KMMessage* KMFolderMbox::readMsg(int idx)
00823 {
00824   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00825 
00826   assert(mi!=0 && !mi->isMessage());
00827   assert(mStream != 0);
00828 
00829   KMMessage *msg = new KMMessage(*mi); // note that mi is deleted by the line below
00830   mMsgList.set(idx,&msg->toMsgBase()); // done now so that the serial number can be computed
00831   msg->fromDwString(getDwString(idx));
00832   return msg;
00833 }
00834 
00835 
00836 #define STRDIM(x) (sizeof(x)/sizeof(*x)-1)
00837 // performs (\n|^)>{n}From_ -> \1>{n-1}From_ conversion
00838 static size_t unescapeFrom( char* str, size_t strLen ) {
00839   if ( !str )
00840     return 0;
00841   if ( strLen <= STRDIM(">From ") )
00842     return strLen;
00843 
00844   // yes, *d++ = *s++ is a no-op as long as d == s (until after the
00845   // first >From_), but writes are cheap compared to reads and the
00846   // data is already in the cache from the read, so special-casing
00847   // might even be slower...
00848   const char * s = str;
00849   char * d = str;
00850   const char * const e = str + strLen - STRDIM(">From ");
00851 
00852   while ( s < e ) {
00853     if ( *s == '\n' && *(s+1) == '>' ) { // we can do the lookahead, since e is 6 chars from the end!
00854       *d++ = *s++;  // == '\n'
00855       *d++ = *s++;  // == '>'
00856       while ( s < e && *s == '>' )
00857         *d++ = *s++;
00858       if ( qstrncmp( s, "From ", STRDIM("From ") ) == 0 )
00859         --d;
00860     }
00861     *d++ = *s++; // yes, s might be e here, but e is not the end :-)
00862   }
00863   // copy the rest:
00864   while ( s < str + strLen )
00865     *d++ = *s++;
00866   if ( d < s ) // only NUL-terminate if it's shorter
00867     *d = 0;
00868 
00869   return d - str;
00870 }
00871 
00872 //static
00873 QByteArray KMFolderMbox::escapeFrom( const DwString & str ) {
00874   const unsigned int strLen = str.length();
00875   if ( strLen <= STRDIM("From ") )
00876     return KMail::Util::ByteArray( str );
00877   // worst case: \nFrom_\nFrom_\nFrom_... => grows to 7/6
00878   QByteArray result( int( strLen + 5 ) / 6 * 7 + 1 );
00879 
00880   const char * s = str.data();
00881   const char * const e = s + strLen - STRDIM("From ");
00882   char * d = result.data();
00883 
00884   bool onlyAnglesAfterLF = false; // dont' match ^From_
00885   while ( s < e ) {
00886     switch ( *s ) {
00887     case '\n':
00888       onlyAnglesAfterLF = true;
00889       break;
00890     case '>':
00891       break;
00892     case 'F':
00893       if ( onlyAnglesAfterLF && qstrncmp( s+1, "rom ", STRDIM("rom ") ) == 0 )
00894         *d++ = '>';
00895       // fall through
00896     default:
00897       onlyAnglesAfterLF = false;
00898       break;
00899     }
00900     *d++ = *s++;
00901   }
00902   while ( s < str.data() + strLen )
00903     *d++ = *s++;
00904 
00905   result.truncate( d - result.data() );
00906   return result;
00907 }
00908 
00909 #undef STRDIM
00910 
00911 //-----------------------------------------------------------------------------
00912 QCString& KMFolderMbox::getMsgString(int idx, QCString &mDest)
00913 {
00914   unsigned long msgSize;
00915   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00916 
00917   assert(mi!=0);
00918   assert(mStream != 0);
00919 
00920   msgSize = mi->msgSize();
00921   mDest.resize(msgSize+2);
00922 
00923   fseek(mStream, mi->folderOffset(), SEEK_SET);
00924   fread(mDest.data(), msgSize, 1, mStream);
00925   mDest[msgSize] = '\0';
00926 
00927   size_t newMsgSize = unescapeFrom( mDest.data(), msgSize );
00928   newMsgSize = KMail::Util::crlf2lf( mDest.data(), newMsgSize );
00929 
00930   return mDest;
00931 }
00932 
00933 
00934 //-----------------------------------------------------------------------------
00935 DwString KMFolderMbox::getDwString(int idx)
00936 {
00937   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00938 
00939   assert(mi!=0);
00940   assert(mStream != 0);
00941 
00942   size_t msgSize = mi->msgSize();
00943   char* msgText = new char[ msgSize + 1 ];
00944 
00945   fseek(mStream, mi->folderOffset(), SEEK_SET);
00946   fread(msgText, msgSize, 1, mStream);
00947   msgText[msgSize] = '\0';
00948 
00949   size_t newMsgSize = unescapeFrom( msgText, msgSize );
00950   newMsgSize = KMail::Util::crlf2lf( msgText, newMsgSize );
00951 
00952   DwString msgStr;
00953   // the DwString takes possession of msgText, so we must not delete msgText
00954   msgStr.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
00955   return msgStr;
00956 }
00957 
00958 
00959 //-----------------------------------------------------------------------------
00960 int KMFolderMbox::addMsg( KMMessage* aMsg, int* aIndex_ret )
00961 {
00962   if (!canAddMsgNow(aMsg, aIndex_ret)) return 0;
00963   bool opened = false;
00964   QByteArray msgText;
00965   char endStr[3];
00966   int idx = -1, rc;
00967   KMFolder* msgParent;
00968   bool editing = false;
00969   int growth = 0;
00970 
00971   if (!mStream)
00972   {
00973     opened = true;
00974     rc = open();
00975     kdDebug(5006) << "KMFolderMBox::addMsg-open: " << rc << " of folder: " << label() << endl;
00976     if (rc) return rc;
00977   }
00978 
00979   // take message out of the folder it is currently in, if any
00980   msgParent = aMsg->parent();
00981   if (msgParent)
00982   {
00983     if ( msgParent== folder() )
00984     {
00985         if (kmkernel->folderIsDraftOrOutbox( folder() ))
00986           //special case for Edit message.
00987           {
00988             kdDebug(5006) << "Editing message in outbox or drafts" << endl;
00989             editing = true;
00990           }
00991         else
00992           return 0;
00993       }
00994 
00995     idx = msgParent->find(aMsg);
00996     msgParent->getMsg( idx );
00997   }
00998 
00999   if (folderType() != KMFolderTypeImap)
01000   {
01001 /*
01002 QFile fileD0( "testdat_xx-kmfoldermbox-0" );
01003 if( fileD0.open( IO_WriteOnly ) ) {
01004     QDataStream ds( &fileD0 );
01005     ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
01006     fileD0.close();  // If data is 0 we just create a zero length file.
01007 }
01008 */
01009     aMsg->setStatusFields();
01010 /*
01011 QFile fileD1( "testdat_xx-kmfoldermbox-1" );
01012 if( fileD1.open( IO_WriteOnly ) ) {
01013     QDataStream ds( &fileD1 );
01014     ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
01015     fileD1.close();  // If data is 0 we just create a zero length file.
01016 }
01017 */
01018     if (aMsg->headerField("Content-Type").isEmpty())  // This might be added by
01019       aMsg->removeHeaderField("Content-Type");        // the line above
01020   }
01021   msgText = escapeFrom( aMsg->asDwString() );
01022   size_t len = msgText.size();
01023 
01024   assert(mStream != 0);
01025   clearerr(mStream);
01026   if (len <= 0)
01027   {
01028     kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl;
01029     if (opened) close();
01030     return 0;
01031   }
01032 
01033   // Make sure the file is large enough to check for an end
01034   // character
01035   fseek(mStream, 0, SEEK_END);
01036   off_t revert = ftell(mStream);
01037   if (ftell(mStream) >= 2) {
01038       // write message to folder file
01039       fseek(mStream, -2, SEEK_END);
01040       fread(endStr, 1, 2, mStream); // ensure separating empty line
01041       if (ftell(mStream) > 0 && endStr[0]!='\n') {
01042           ++growth;
01043           if (endStr[1]!='\n') {
01044               //printf ("****endStr[1]=%c\n", endStr[1]);
01045               fwrite("\n\n", 1, 2, mStream);
01046               ++growth;
01047           }
01048           else fwrite("\n", 1, 1, mStream);
01049       }
01050   }
01051   fseek(mStream,0,SEEK_END); // this is needed on solaris and others
01052   int error = ferror(mStream);
01053   if (error)
01054   {
01055     if (opened) close();
01056     return error;
01057   }
01058 
01059   QCString messageSeparator( aMsg->mboxMessageSeparator() );
01060   fwrite( messageSeparator.data(), messageSeparator.length(), 1, mStream );
01061   off_t offs = ftell(mStream);
01062   fwrite(msgText.data(), len, 1, mStream);
01063   if (msgText[(int)len-1]!='\n') fwrite("\n\n", 1, 2, mStream);
01064   fflush(mStream);
01065   size_t size = ftell(mStream) - offs;
01066 
01067   error = ferror(mStream);
01068   if (error) {
01069     kdDebug(5006) << "Error: Could not add message to folder: " << strerror(errno) << endl;
01070     if (ftell(mStream) > revert) {
01071       kdDebug(5006) << "Undoing changes" << endl;
01072       truncate( QFile::encodeName(location()), revert );
01073     }
01074     kmkernel->emergencyExit( i18n("Could not add message to folder: ") + QString::fromLocal8Bit(strerror(errno)));
01075 
01076     /* This code is not 100% reliable
01077     bool busy = kmkernel->kbp()->isBusy();
01078     if (busy) kmkernel->kbp()->idle();
01079     KMessageBox::sorry(0,
01080           i18n("Unable to add message to folder.\n"
01081                "(No space left on device or insufficient quota?)\n"
01082                "Free space and sufficient quota are required to continue safely."));
01083     if (busy) kmkernel->kbp()->busy();
01084     if (opened) close();
01085     kmkernel->kbp()->idle();
01086     */
01087     return error;
01088   }
01089 
01090   if (msgParent) {
01091     if (idx >= 0) msgParent->take(idx);
01092   }
01093 //  if (mAccount) aMsg->removeHeaderField("X-UID");
01094 
01095   if (aMsg->isUnread() || aMsg->isNew() ||
01096       (folder() == kmkernel->outboxFolder())) {
01097     if (mUnreadMsgs == -1) mUnreadMsgs = 1;
01098     else ++mUnreadMsgs;
01099     if ( !mQuiet )
01100       emit numUnreadMsgsChanged( folder() );
01101   }
01102   ++mTotalMsgs;
01103 
01104   if ( aMsg->attachmentState() == KMMsgAttachmentUnknown &&
01105        aMsg->readyToShow() )
01106     aMsg->updateAttachmentState();
01107 
01108   // store information about the position in the folder file in the message
01109   aMsg->setParent(folder());
01110   aMsg->setFolderOffset(offs);
01111   aMsg->setMsgSize(size);
01112   idx = mMsgList.append(&aMsg->toMsgBase(), mExportsSernums );
01113   if ( aMsg->getMsgSerNum() <= 0 )
01114     aMsg->setMsgSerNum();
01115   else
01116     replaceMsgSerNum( aMsg->getMsgSerNum(), &aMsg->toMsgBase(), idx );
01117 
01118   // change the length of the previous message to encompass white space added
01119   if ((idx > 0) && (growth > 0)) {
01120     // don't grow if a deleted message claims space at the end of the file
01121     if ((ulong)revert == mMsgList[idx - 1]->folderOffset() + mMsgList[idx - 1]->msgSize() )
01122       mMsgList[idx - 1]->setMsgSize( mMsgList[idx - 1]->msgSize() + growth );
01123   }
01124 
01125   // write index entry if desired
01126   if (mAutoCreateIndex)
01127   {
01128     assert(mIndexStream != 0);
01129     clearerr(mIndexStream);
01130     fseek(mIndexStream, 0, SEEK_END);
01131     revert = ftell(mIndexStream);
01132 
01133     KMMsgBase * mb = &aMsg->toMsgBase();
01134         int len;
01135         const uchar *buffer = mb->asIndexString(len);
01136         fwrite(&len,sizeof(len), 1, mIndexStream);
01137         mb->setIndexOffset( ftell(mIndexStream) );
01138         mb->setIndexLength( len );
01139         if(fwrite(buffer, len, 1, mIndexStream) != 1)
01140             kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
01141 
01142     fflush(mIndexStream);
01143     error = ferror(mIndexStream);
01144 
01145     if ( mExportsSernums )
01146       error |= appendToFolderIdsFile( idx );
01147 
01148     if (error) {
01149       kdWarning(5006) << "Error: Could not add message to folder (No space left on device?)" << endl;
01150       if (ftell(mIndexStream) > revert) {
01151         kdWarning(5006) << "Undoing changes" << endl;
01152         truncate( QFile::encodeName(indexLocation()), revert );
01153       }
01154       if ( errno )
01155         kmkernel->emergencyExit( i18n("Could not add message to folder:") + QString::fromLocal8Bit(strerror(errno)));
01156       else
01157         kmkernel->emergencyExit( i18n("Could not add message to folder (No space left on device?)") );
01158 
01159       /* This code may not be 100% reliable
01160       bool busy = kmkernel->kbp()->isBusy();
01161       if (busy) kmkernel->kbp()->idle();
01162       KMessageBox::sorry(0,
01163         i18n("Unable to add message to folder.\n"
01164              "(No space left on device or insufficient quota?)\n"
01165              "Free space and sufficient quota are required to continue safely."));
01166       if (busy) kmkernel->kbp()->busy();
01167       if (opened) close();
01168       */
01169       return error;
01170     }
01171   }
01172 
01173   // some "paper work"
01174   if (aIndex_ret) *aIndex_ret = idx;
01175   emitMsgAddedSignals(idx);
01176   if (opened) close();
01177 
01178   // All streams have been flushed without errors if we arrive here
01179   // Return success!
01180   // (Don't return status of stream, it may have been closed already.)
01181   return 0;
01182 }
01183 
01184 int KMFolderMbox::compact( unsigned int startIndex, int nbMessages, FILE* tmpfile, off_t& offs, bool& done )
01185 {
01186   int rc = 0;
01187   QCString mtext;
01188   unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() :
01189                            QMIN( mMsgList.count(), startIndex + nbMessages );
01190   //kdDebug(5006) << "KMFolderMbox: compacting from " << startIndex << " to " << stopIndex << endl;
01191   for(unsigned int idx = startIndex; idx < stopIndex; ++idx) {
01192     KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx);
01193     size_t msize = mi->msgSize();
01194     if (mtext.size() < msize + 2)
01195       mtext.resize(msize+2);
01196     off_t folder_offset = mi->folderOffset();
01197 
01198     //now we need to find the separator! grr...
01199     for(off_t i = folder_offset-25; true; i -= 20) {
01200       off_t chunk_offset = i <= 0 ? 0 : i;
01201       if(fseek(mStream, chunk_offset, SEEK_SET) == -1) {
01202         rc = errno;
01203         break;
01204       }
01205       if (mtext.size() < 20)
01206         mtext.resize(20);
01207       fread(mtext.data(), 20, 1, mStream);
01208       if(i <= 0) { //woops we've reached the top of the file, last try..
01209         if ( mtext.contains( "from ", false ) ) {
01210           if (mtext.size() < (size_t)folder_offset)
01211               mtext.resize(folder_offset);
01212           if(fseek(mStream, chunk_offset, SEEK_SET) == -1 ||
01213              !fread(mtext.data(), folder_offset, 1, mStream) ||
01214              !fwrite(mtext.data(), folder_offset, 1, tmpfile)) {
01215               rc = errno;
01216               break;
01217           }
01218           offs += folder_offset;
01219         } else {
01220           rc = 666;
01221         }
01222         break;
01223       } else {
01224         int last_crlf = -1;
01225         for(int i2 = 0; i2 < 20; i2++) {
01226           if(*(mtext.data()+i2) == '\n')
01227             last_crlf = i2;
01228         }
01229         if(last_crlf != -1) {
01230           int size = folder_offset - (i + last_crlf+1);
01231           if ((int)mtext.size() < size)
01232               mtext.resize(size);
01233           if(fseek(mStream, i + last_crlf+1, SEEK_SET) == -1 ||
01234              !fread(mtext.data(), size, 1, mStream) ||
01235              !fwrite(mtext.data(), size, 1, tmpfile)) {
01236               rc = errno;
01237               break;
01238           }
01239           offs += size;
01240           break;
01241         }
01242       }
01243     }
01244     if (rc)
01245       break;
01246 
01247     //now actually write the message
01248     if(fseek(mStream, folder_offset, SEEK_SET) == -1 ||
01249        !fread(mtext.data(), msize, 1, mStream) || !fwrite(mtext.data(), msize, 1, tmpfile)) {
01250         rc = errno;
01251         break;
01252     }
01253     mi->setFolderOffset(offs);
01254     offs += msize;
01255   }
01256   done = ( !rc && stopIndex == mMsgList.count() ); // finished without errors
01257   return rc;
01258 }
01259 
01260 //-----------------------------------------------------------------------------
01261 int KMFolderMbox::compact( bool silent )
01262 {
01263   // This is called only when the user explicitely requests compaction,
01264   // so we don't check needsCompact.
01265   int openCount = mOpenCount;
01266 
01267   KMail::MboxCompactionJob* job = new KMail::MboxCompactionJob( folder(), true /*immediate*/ );
01268   int rc = job->executeNow( silent );
01269   // Note that job autodeletes itself.
01270 
01271   if (openCount > 0)
01272   {
01273     open();
01274     mOpenCount = openCount;
01275   }
01276   // If this is the current folder, the changed signal will ultimately call
01277   // KMHeaders::setFolderInfoStatus which will override the message, so save/restore it
01278   QString statusMsg = BroadcastStatus::instance()->statusMsg();
01279   emit changed();
01280   BroadcastStatus::instance()->setStatusMsg( statusMsg );
01281   return rc;
01282 }
01283 
01284 
01285 //-----------------------------------------------------------------------------
01286 void KMFolderMbox::setLockType( LockType ltype )
01287 {
01288   mLockType = ltype;
01289 }
01290 
01291 //-----------------------------------------------------------------------------
01292 void KMFolderMbox::setProcmailLockFileName( const QString &fname )
01293 {
01294   mProcmailLockFileName = fname;
01295 }
01296 
01297 //-----------------------------------------------------------------------------
01298 int KMFolderMbox::removeContents()
01299 {
01300   int rc = 0;
01301   rc = unlink(QFile::encodeName(location()));
01302   return rc;
01303 }
01304 
01305 //-----------------------------------------------------------------------------
01306 int KMFolderMbox::expungeContents()
01307 {
01308   int rc = 0;
01309   if (truncate(QFile::encodeName(location()), 0))
01310     rc = errno;
01311   return rc;
01312 }
01313 
01314 //-----------------------------------------------------------------------------
01315 #include "kmfoldermbox.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys