kmail Library API Documentation

kmfoldermaildir.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmfoldermaildir.cpp
00003 // Author: Kurt Granroth <granroth@kde.org>
00004 
00005 #ifdef HAVE_CONFIG_H
00006 #include <config.h>
00007 #endif
00008 
00009 #include <qdir.h>
00010 #include <qregexp.h>
00011 
00012 #include <libkdepim/kfileio.h>
00013 #include "kmfoldermaildir.h"
00014 #include "kmfoldermgr.h"
00015 #include "kmfolder.h"
00016 #include "undostack.h"
00017 #include "maildirjob.h"
00018 #include "kcursorsaver.h"
00019 #include "jobscheduler.h"
00020 using KMail::MaildirJob;
00021 #include "compactionjob.h"
00022 
00023 #include <kio/netaccess.h>
00024 #include <kapplication.h>
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 #include <kstaticdeleter.h>
00028 #include <kmessagebox.h>
00029 
00030 #include <dirent.h>
00031 #include <errno.h>
00032 #include <stdlib.h>
00033 #include <sys/stat.h>
00034 #include <sys/types.h>
00035 #include <unistd.h>
00036 #include <assert.h>
00037 #include <limits.h>
00038 #include <unistd.h>
00039 #include <fcntl.h>
00040 
00041 #ifndef MAX_LINE
00042 #define MAX_LINE 4096
00043 #endif
00044 #ifndef INIT_MSGS
00045 #define INIT_MSGS 8
00046 #endif
00047 
00048 
00049 //-----------------------------------------------------------------------------
00050 KMFolderMaildir::KMFolderMaildir(KMFolder* folder, const char* name)
00051   : KMFolderIndex(folder, name)
00052 {
00053 }
00054 
00055 
00056 //-----------------------------------------------------------------------------
00057 KMFolderMaildir::~KMFolderMaildir()
00058 {
00059   if (mOpenCount>0) close(TRUE);
00060   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00061 }
00062 
00063 //-----------------------------------------------------------------------------
00064 int KMFolderMaildir::canAccess()
00065 {
00066 
00067   assert(!folder()->name().isEmpty());
00068 
00069   QString sBadFolderName;
00070   if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0) {
00071     sBadFolderName = location();
00072   } else if (access(QFile::encodeName(location() + "/new"), R_OK | W_OK | X_OK) != 0) {
00073     sBadFolderName = location() + "/new";
00074   } else if (access(QFile::encodeName(location() + "/cur"), R_OK | W_OK | X_OK) != 0) {
00075     sBadFolderName = location() + "/cur";
00076   } else if (access(QFile::encodeName(location() + "/tmp"), R_OK | W_OK | X_OK) != 0) {
00077     sBadFolderName = location() + "/tmp";
00078   }
00079 
00080   if ( !sBadFolderName.isEmpty() ) {
00081     int nRetVal = QFile::exists(sBadFolderName) ? EPERM : ENOENT;
00082     KCursorSaver idle(KBusyPtr::idle());
00083     if ( nRetVal == ENOENT )
00084       KMessageBox::sorry(0, i18n("Error opening %1; this folder is missing.")
00085                          .arg(sBadFolderName));
00086     else
00087       KMessageBox::sorry(0, i18n("Error opening %1; either this is not a valid "
00088                                  "maildir folder, or you do not have sufficient access permissions.")
00089                          .arg(sBadFolderName));
00090     return nRetVal;
00091   }
00092 
00093   return 0;
00094 }
00095 
00096 //-----------------------------------------------------------------------------
00097 int KMFolderMaildir::open()
00098 {
00099   int rc = 0;
00100 
00101   mOpenCount++;
00102   kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00103 
00104   if (mOpenCount > 1) return 0;  // already open
00105 
00106   assert(!folder()->name().isEmpty());
00107 
00108   rc = canAccess();
00109   if ( rc != 0 ) {
00110       return rc;
00111   }
00112 
00113   if (!folder()->path().isEmpty())
00114   {
00115     if (KMFolderIndex::IndexOk != indexStatus()) // test if contents file has changed
00116     {
00117       QString str;
00118       mIndexStream = 0;
00119       str = i18n("Folder `%1' changed; recreating index.")
00120           .arg(name());
00121       emit statusMsg(str);
00122     } else {
00123       mIndexStream = fopen(QFile::encodeName(indexLocation()), "r+"); // index file
00124       if ( mIndexStream ) {
00125         fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00126         updateIndexStreamPtr();
00127       }
00128     }
00129 
00130     if (!mIndexStream)
00131       rc = createIndexFromContents();
00132     else
00133       readIndex();
00134   }
00135   else
00136   {
00137     mAutoCreateIndex = FALSE;
00138     rc = createIndexFromContents();
00139   }
00140 
00141   mChanged = FALSE;
00142 
00143   //readConfig();
00144 
00145   return rc;
00146 }
00147 
00148 
00149 //-----------------------------------------------------------------------------
00150 int KMFolderMaildir::create(bool imap)
00151 {
00152   int rc;
00153   int old_umask;
00154 
00155   assert(!folder()->name().isEmpty());
00156   assert(mOpenCount == 0);
00157 
00158   // Make sure that neither a new, cur or tmp subfolder exists already.
00159   QFileInfo dirinfo;
00160   dirinfo.setFile(location() + "/new");
00161   if (dirinfo.exists()) return 1;
00162   dirinfo.setFile(location() + "/cur");
00163   if (dirinfo.exists()) return 1;
00164   dirinfo.setFile(location() + "/tmp");
00165   if (dirinfo.exists()) return 1;
00166 
00167   // create the maildir directory structure
00168   if (::mkdir(QFile::encodeName(location()), S_IRWXU) > 0)
00169   {
00170     kdDebug(5006) << "Could not create " << location() << " maildir" << endl;
00171     return errno;
00172   }
00173   if (::mkdir(QFile::encodeName(location() + "/new"), S_IRWXU) > 0)
00174   {
00175     kdDebug(5006) << "Could not create " << location() << "/new" << endl;
00176     return errno;
00177   }
00178   if (::mkdir(QFile::encodeName(location() + "/cur"), S_IRWXU) > 0)
00179   {
00180     kdDebug(5006) << "Could not create " << location() << "/cur" << endl;
00181     return errno;
00182   }
00183   if (::mkdir(QFile::encodeName(location() + "/tmp"), S_IRWXU) > 0)
00184   {
00185     kdDebug(5006) << "Could not create " << location() << "/new" << endl;
00186     return errno;
00187   }
00188 
00189   if (!folder()->path().isEmpty())
00190   {
00191     old_umask = umask(077);
00192     mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+"); //sven; open RW
00193     updateIndexStreamPtr(TRUE);
00194     umask(old_umask);
00195 
00196     if (!mIndexStream) return errno;
00197     fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00198   }
00199   else
00200   {
00201     mAutoCreateIndex = FALSE;
00202   }
00203 
00204   mOpenCount++;
00205   mChanged = FALSE;
00206   if (imap) {
00207     readConfig();
00208     mUnreadMsgs = -1;
00209   }
00210 
00211   rc = writeIndex();
00212   return rc;
00213 }
00214 
00215 
00216 //-----------------------------------------------------------------------------
00217 void KMFolderMaildir::close(bool aForced)
00218 {
00219   if (mOpenCount <= 0) return;
00220   if (mOpenCount > 0) mOpenCount--;
00221 
00222   if (mOpenCount > 0 && !aForced) return;
00223 
00224 #if 0 // removed hack that prevented closing system folders (see kmail-devel discussion about mail expiring)
00225   if ( (folder() != kmkernel->inboxFolder())
00226        && folder()->isSystemFolder() && !aForced)
00227   {
00228      mOpenCount = 1;
00229      return;
00230   }
00231 #endif
00232 
00233   if (mAutoCreateIndex)
00234   {
00235       updateIndex();
00236       writeConfig();
00237   }
00238 
00239   mMsgList.clear(TRUE);
00240 
00241     if (mIndexStream) {
00242     fclose(mIndexStream);
00243     updateIndexStreamPtr(TRUE);
00244     }
00245 
00246   mOpenCount   = 0;
00247   mIndexStream = 0;
00248   mUnreadMsgs  = -1;
00249 
00250   mMsgList.reset(INIT_MSGS);
00251 }
00252 
00253 //-----------------------------------------------------------------------------
00254 void KMFolderMaildir::sync()
00255 {
00256   if (mOpenCount > 0)
00257     if (!mIndexStream || fsync(fileno(mIndexStream))) {
00258     kmkernel->emergencyExit( i18n("Could not sync maildir folder.") );
00259     }
00260 }
00261 
00262 //-----------------------------------------------------------------------------
00263 int KMFolderMaildir::expungeContents()
00264 {
00265   // nuke all messages in this folder now
00266   QDir d(location() + "/new");
00267   // d.setFilter(QDir::Files); coolo: QFile::remove returns false for non-files
00268   QStringList files(d.entryList());
00269   QStringList::ConstIterator it(files.begin());
00270   for ( ; it != files.end(); ++it)
00271     QFile::remove(d.filePath(*it));
00272 
00273   d.setPath(location() + "/cur");
00274   files = d.entryList();
00275   for (it = files.begin(); it != files.end(); ++it)
00276     QFile::remove(d.filePath(*it));
00277 
00278   return 0;
00279 }
00280 
00281 int KMFolderMaildir::compact( unsigned int startIndex, int nbMessages, const QStringList& entryList, bool& done )
00282 {
00283   QString subdirNew(location() + "/new/");
00284   QString subdirCur(location() + "/cur/");
00285 
00286   unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() :
00287                            QMIN( mMsgList.count(), startIndex + nbMessages );
00288   //kdDebug(5006) << "KMFolderMaildir: compacting from " << startIndex << " to " << stopIndex << endl;
00289   for(unsigned int idx = startIndex; idx < stopIndex; ++idx) {
00290     KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx);
00291     if (!mi)
00292       continue;
00293 
00294     QString filename(mi->fileName());
00295     if (filename.isEmpty())
00296       continue;
00297 
00298     // first, make sure this isn't in the 'new' subdir
00299     if ( entryList.contains( filename ) )
00300       moveInternal(subdirNew + filename, subdirCur + filename, mi);
00301 
00302     // construct a valid filename.  if it's already valid, then
00303     // nothing happens
00304     constructValidFileName(filename, mi->status());
00305 
00306     // if the name changed, then we need to update the actual filename
00307     if (filename != mi->fileName())
00308     {
00309       moveInternal(subdirCur + mi->fileName(), subdirCur + filename, mi);
00310       mi->setFileName(filename);
00311       setDirty( true );
00312     }
00313 
00314 #if 0
00315     // we can't have any New messages at this point
00316     if (mi->isNew())
00317     {
00318       mi->setStatus(KMMsgStatusUnread);
00319       setDirty( true );
00320     }
00321 #endif
00322   }
00323   done = ( stopIndex == mMsgList.count() );
00324   return 0;
00325 }
00326 
00327 //-----------------------------------------------------------------------------
00328 int KMFolderMaildir::compact( bool silent )
00329 {
00330   KMail::MaildirCompactionJob* job = new KMail::MaildirCompactionJob( folder(), true /*immediate*/ );
00331   int rc = job->executeNow( silent );
00332   // Note that job autodeletes itself.
00333   return rc;
00334 }
00335 
00336 //-------------------------------------------------------------
00337 FolderJob*
00338 KMFolderMaildir::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
00339                               KMFolder *folder, QString, const AttachmentStrategy* ) const
00340 {
00341   MaildirJob *job = new MaildirJob( msg, jt, folder );
00342   job->setParentFolder( this );
00343   return job;
00344 }
00345 
00346 //-------------------------------------------------------------
00347 FolderJob*
00348 KMFolderMaildir::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
00349                               FolderJob::JobType jt, KMFolder *folder ) const
00350 {
00351   MaildirJob *job = new MaildirJob( msgList, sets, jt, folder );
00352   job->setParentFolder( this );
00353   return job;
00354 }
00355 
00356 //-------------------------------------------------------------
00357 int KMFolderMaildir::addMsg(KMMessage* aMsg, int* index_return)
00358 {
00359 /*
00360 QFile fileD0( "testdat_xx-kmfoldermaildir-0" );
00361 if( fileD0.open( IO_WriteOnly ) ) {
00362     QDataStream ds( &fileD0 );
00363     ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
00364     fileD0.close();  // If data is 0 we just create a zero length file.
00365 }
00366 */
00367   if (!canAddMsgNow(aMsg, index_return)) return 0;
00368 
00369   long len;
00370   unsigned long size;
00371   bool opened = FALSE;
00372   KMFolder* msgParent;
00373   QCString msgText;
00374   int idx(-1);
00375   int rc;
00376 
00377   // take message out of the folder it is currently in, if any
00378   msgParent = aMsg->parent();
00379   if (msgParent)
00380   {
00381     if (msgParent==folder() && !kmkernel->folderIsDraftOrOutbox(folder()))
00382         return 0;
00383 
00384     idx = msgParent->find(aMsg);
00385     msgParent->getMsg( idx );
00386   }
00387 
00388   aMsg->setStatusFields();
00389   if (aMsg->headerField("Content-Type").isEmpty())  // This might be added by
00390     aMsg->removeHeaderField("Content-Type");        // the line above
00391   msgText = aMsg->asString();
00392   len = msgText.length();
00393 
00394   if (len <= 0)
00395   {
00396     kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl;
00397     return 0;
00398   }
00399 
00400   // make sure the filename has the correct extension
00401   QString filename(aMsg->fileName());
00402   constructValidFileName(filename, aMsg->status());
00403 
00404   QString tmp_file(location() + "/tmp/");
00405   tmp_file += filename;
00406 
00407   if (!KPIM::kCStringToFile(msgText, tmp_file, false, false, false))
00408     kmkernel->emergencyExit( "" ); // KPIM::kCStringToFile already showed an errormessage
00409 
00410   QFile file(tmp_file);
00411   size = msgText.length();
00412 
00413   if (!isOpened())
00414   {
00415     opened = TRUE;
00416     rc = open();
00417     kdDebug(5006) << "KMFolderMaildir::addMsg-open: " << rc << " of folder: " << label() << endl;
00418     if (rc) return rc;
00419   }
00420 
00421   // now move the file to the correct location
00422   QString new_loc(location() + "/cur/");
00423   new_loc += filename;
00424   if (moveInternal(tmp_file, new_loc, filename, aMsg->status()).isNull())
00425   {
00426     file.remove();
00427     if (opened) close();
00428     return -1;
00429   }
00430 
00431   if (msgParent)
00432     if (idx >= 0) msgParent->take(idx);
00433 
00434   if (filename != aMsg->fileName())
00435     aMsg->setFileName(filename);
00436 
00437   if (aMsg->isUnread() || aMsg->isNew() || folder() == kmkernel->outboxFolder())
00438   {
00439     if (mUnreadMsgs == -1)
00440       mUnreadMsgs = 1;
00441     else
00442       ++mUnreadMsgs;
00443     emit numUnreadMsgsChanged( folder() );
00444   }
00445   ++mTotalMsgs;
00446 
00447   // store information about the position in the folder file in the message
00448   aMsg->setParent(folder());
00449   aMsg->setMsgSize(size);
00450   idx = mMsgList.append(&aMsg->toMsgBase());
00451   if (aMsg->getMsgSerNum() <= 0)
00452     aMsg->setMsgSerNum();
00453 
00454   // write index entry if desired
00455   if (mAutoCreateIndex)
00456   {
00457     assert(mIndexStream != 0);
00458     clearerr(mIndexStream);
00459     fseek(mIndexStream, 0, SEEK_END);
00460     off_t revert = ftell(mIndexStream);
00461 
00462     int len;
00463     KMMsgBase * mb = &aMsg->toMsgBase();
00464     const uchar *buffer = mb->asIndexString(len);
00465     fwrite(&len,sizeof(len), 1, mIndexStream);
00466     mb->setIndexOffset( ftell(mIndexStream) );
00467     mb->setIndexLength( len );
00468     if(fwrite(buffer, len, 1, mIndexStream) != 1)
00469     kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
00470 
00471     fflush(mIndexStream);
00472     int error = ferror(mIndexStream);
00473 
00474     error |= appendtoMsgDict(idx);
00475 
00476     if (error) {
00477       kdDebug(5006) << "Error: Could not add message to folder (No space left on device?)" << endl;
00478       if (ftell(mIndexStream) > revert) {
00479     kdDebug(5006) << "Undoing changes" << endl;
00480     truncate( QFile::encodeName(indexLocation()), revert );
00481       }
00482       kmkernel->emergencyExit(i18n("KMFolderMaildir::addMsg: abnormally terminating to prevent data loss."));
00483       // exit(1); // don't ever use exit(), use the above!
00484 
00485       /* This code may not be 100% reliable
00486       bool busy = kmkernel->kbp()->isBusy();
00487       if (busy) kmkernel->kbp()->idle();
00488       KMessageBox::sorry(0,
00489         i18n("Unable to add message to folder.\n"
00490          "(No space left on device or insufficient quota?)\n"
00491          "Free space and sufficient quota are required to continue safely."));
00492       if (busy) kmkernel->kbp()->busy();
00493       if (opened) close();
00494       */
00495       return error;
00496     }
00497   }
00498 
00499   // some "paper work"
00500   if (index_return)
00501     *index_return = idx;
00502 
00503   emitMsgAddedSignals(idx);
00504   needsCompact = true;
00505 
00506   if (opened) close();
00507 /*
00508 QFile fileD1( "testdat_xx-kmfoldermaildir-1" );
00509 if( fileD1.open( IO_WriteOnly ) ) {
00510     QDataStream ds( &fileD1 );
00511     ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
00512     fileD1.close();  // If data is 0 we just create a zero length file.
00513 }
00514 */
00515   return 0;
00516 }
00517 
00518 KMMessage* KMFolderMaildir::readMsg(int idx)
00519 {
00520   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00521   KMMessage *msg = new KMMessage(*mi); // note that mi is deleted by the line below
00522   mMsgList.set(idx,&msg->toMsgBase()); // done now so that the serial number can be computed
00523   msg->fromDwString(getDwString(idx));
00524   return msg;
00525 }
00526 
00527 DwString KMFolderMaildir::getDwString(int idx)
00528 {
00529   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00530   QString abs_file(location() + "/cur/");
00531   abs_file += mi->fileName();
00532   QFileInfo fi( abs_file );
00533 
00534   if (fi.exists() && fi.isFile() && fi.isWritable() && fi.size() > 0)
00535   {
00536     FILE* stream = fopen(QFile::encodeName(abs_file), "r+");
00537     if (stream) {
00538       size_t msgSize = fi.size();
00539       char* msgText = new char[ msgSize + 1 ];
00540       fread(msgText, msgSize, 1, stream);
00541       fclose( stream );
00542       msgText[msgSize] = '\0';
00543       size_t newMsgSize = crlf2lf( msgText, msgSize );
00544       DwString str;
00545       // the DwString takes possession of msgText, so we must not delete it
00546       str.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
00547       return str;
00548     }
00549   }
00550   kdDebug(5006) << "Could not open file r+ " << abs_file << endl;
00551   return DwString();
00552 }
00553 
00554 
00555 QCString& KMFolderMaildir::getMsgString(int idx, QCString& mDest)
00556 {
00557   KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00558 
00559   assert(mi!=0);
00560 
00561   QString abs_file(location() + "/cur/");
00562   abs_file += mi->fileName();
00563 
00564   if (QFile::exists(abs_file) == false)
00565   {
00566     kdDebug(5006) << "The " << abs_file << " file doesn't exist!" << endl;
00567     return mDest;
00568   }
00569 
00570   QFileInfo fi( abs_file );
00571   mDest.resize(fi.size()+2);
00572   mDest = KPIM::kFileToString(abs_file, false, false);
00573   size_t newMsgSize = crlf2lf( mDest.data(), fi.size() );
00574   mDest[newMsgSize] = '\0';
00575   return mDest;
00576 }
00577 
00578 void KMFolderMaildir::readFileHeaderIntern(const QString& dir, const QString& file, KMMsgStatus status)
00579 {
00580   // we keep our current directory to restore it later
00581   char path_buffer[PATH_MAX];
00582   ::getcwd(path_buffer, PATH_MAX - 1);
00583   ::chdir(QFile::encodeName(dir));
00584 
00585   // messages in the 'cur' directory are Read by default.. but may
00586   // actually be some other state (but not New)
00587   if (status == KMMsgStatusRead)
00588   {
00589     if (file.find(":2,") == -1)
00590       status = KMMsgStatusUnread;
00591     else if (file.right(5) == ":2,RS")
00592       status |= KMMsgStatusReplied;
00593   }
00594 
00595   // open the file and get a pointer to it
00596   QFile f(file);
00597   if ( f.open( IO_ReadOnly ) == false ) {
00598     kdWarning(5006) << "The file '" << QFile::encodeName(dir) << "/" << file
00599                     << "' could not be opened for reading the message. "
00600                        "Please check ownership and permissions."
00601                     << endl;
00602     return;
00603   }
00604 
00605   char line[MAX_LINE];
00606   bool atEof    = false;
00607   bool inHeader = true;
00608   QCString *lastStr = 0;
00609 
00610   QCString dateStr, fromStr, toStr, subjStr;
00611   QCString xmarkStr, replyToIdStr, msgIdStr, referencesStr;
00612   QCString statusStr, replyToAuxIdStr, uidStr;
00613 
00614   // iterate through this file until done
00615   while (!atEof)
00616   {
00617     // if the end of the file has been reached or if there was an error
00618     if ( f.atEnd() || ( -1 == f.readLine(line, MAX_LINE) ) )
00619       atEof = true;
00620 
00621     // are we done with this file?  if so, compile our info and store
00622     // it in a KMMsgInfo object
00623     if (atEof || !inHeader)
00624     {
00625       msgIdStr = msgIdStr.stripWhiteSpace();
00626       if( !msgIdStr.isEmpty() ) {
00627         int rightAngle;
00628         rightAngle = msgIdStr.find( '>' );
00629         if( rightAngle != -1 )
00630           msgIdStr.truncate( rightAngle + 1 );
00631       }
00632 
00633       replyToIdStr = replyToIdStr.stripWhiteSpace();
00634       if( !replyToIdStr.isEmpty() ) {
00635         int rightAngle;
00636         rightAngle = replyToIdStr.find( '>' );
00637         if( rightAngle != -1 )
00638           replyToIdStr.truncate( rightAngle + 1 );
00639       }
00640 
00641       referencesStr = referencesStr.stripWhiteSpace();
00642       if( !referencesStr.isEmpty() ) {
00643         int leftAngle, rightAngle;
00644         leftAngle = referencesStr.findRev( '<' );
00645         if( ( leftAngle != -1 )
00646             && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) {
00647           // use the last reference, instead of missing In-Reply-To
00648           replyToIdStr = referencesStr.mid( leftAngle );
00649         }
00650 
00651         // find second last reference
00652         leftAngle = referencesStr.findRev( '<', leftAngle - 1 );
00653         if( leftAngle != -1 )
00654           referencesStr = referencesStr.mid( leftAngle );
00655         rightAngle = referencesStr.findRev( '>' );
00656         if( rightAngle != -1 )
00657           referencesStr.truncate( rightAngle + 1 );
00658 
00659         // Store the second to last reference in the replyToAuxIdStr
00660         // It is a good candidate for threading the message below if the
00661         // message In-Reply-To points to is not kept in this folder,
00662         // but e.g. in an Outbox
00663         replyToAuxIdStr = referencesStr;
00664         rightAngle = referencesStr.find( '>' );
00665         if( rightAngle != -1 )
00666           replyToAuxIdStr.truncate( rightAngle + 1 );
00667       }
00668 
00669       statusStr = statusStr.stripWhiteSpace();
00670       if (!statusStr.isEmpty())
00671       {
00672         // only handle those states not determined by the file suffix
00673         if (statusStr[0] == 'S')
00674           status |= KMMsgStatusSent;
00675         else if (statusStr[0] == 'F')
00676           status |= KMMsgStatusForwarded;
00677         else if (statusStr[0] == 'D')
00678           status |= KMMsgStatusDeleted;
00679         else if (statusStr[0] == 'Q')
00680           status |= KMMsgStatusQueued;
00681         else if (statusStr[0] == 'G')
00682           status |= KMMsgStatusFlag;
00683       }
00684 
00685       KMMsgInfo *mi = new KMMsgInfo(folder());
00686       mi->init( subjStr.stripWhiteSpace(),
00687                 fromStr.stripWhiteSpace(),
00688                 toStr.stripWhiteSpace(),
00689                 0, status,
00690                 xmarkStr.stripWhiteSpace(),
00691                 replyToIdStr, replyToAuxIdStr, msgIdStr,
00692                 file.local8Bit(),
00693                 KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
00694                 KMMsgMDNStateUnknown, f.size() );
00695 
00696       dateStr = dateStr.stripWhiteSpace();
00697       if (!dateStr.isEmpty())
00698         mi->setDate(dateStr);
00699       if ( !uidStr.isEmpty() )
00700          mi->setUID( uidStr.toULong() );
00701       mi->setDirty(false);
00702       mMsgList.append(mi);
00703 
00704       // if this is a New file and is in 'new', we move it to 'cur'
00705       if (status & KMMsgStatusNew)
00706       {
00707         QString newDir(location() + "/new/");
00708         QString curDir(location() + "/cur/");
00709         moveInternal(newDir + file, curDir + file, mi);
00710       }
00711 
00712       break;
00713     }
00714 
00715     // Is this a long header line?
00716     if (inHeader && line[0] == '\t' || line[0] == ' ')
00717     {
00718       int i = 0;
00719       while (line[i] == '\t' || line[i] == ' ')
00720         i++;
00721       if (line[i] < ' ' && line[i] > 0)
00722         inHeader = false;
00723       else
00724         if (lastStr)
00725           *lastStr += line + i;
00726     }
00727     else
00728       lastStr = 0;
00729 
00730     if (inHeader && (line[0] == '\n' || line[0] == '\r'))
00731       inHeader = false;
00732     if (!inHeader)
00733       continue;
00734 
00735     if (strncasecmp(line, "Date:", 5) == 0)
00736     {
00737       dateStr = QCString(line+5);
00738       lastStr = &dateStr;
00739     }
00740     else if (strncasecmp(line, "From:", 5) == 0)
00741     {
00742       fromStr = QCString(line+5);
00743       lastStr = &fromStr;
00744     }
00745     else if (strncasecmp(line, "To:", 3) == 0)
00746     {
00747       toStr = QCString(line+3);
00748       lastStr = &toStr;
00749     }
00750     else if (strncasecmp(line, "Subject:", 8) == 0)
00751     {
00752       subjStr = QCString(line+8);
00753       lastStr = &subjStr;
00754     }
00755     else if (strncasecmp(line, "References:", 11) == 0)
00756     {
00757       referencesStr = QCString(line+11);
00758       lastStr = &referencesStr;
00759     }
00760     else if (strncasecmp(line, "Message-Id:", 11) == 0)
00761     {
00762       msgIdStr = QCString(line+11);
00763       lastStr = &msgIdStr;
00764     }
00765     else if (strncasecmp(line, "X-KMail-Mark:", 13) == 0)
00766     {
00767       xmarkStr = QCString(line+13);
00768     }
00769     else if (strncasecmp(line, "X-Status:", 9) == 0)
00770     {
00771       statusStr = QCString(line+9);
00772     }
00773     else if (strncasecmp(line, "In-Reply-To:", 12) == 0)
00774     {
00775       replyToIdStr = QCString(line+12);
00776       lastStr = &replyToIdStr;
00777     }
00778     else if (strncasecmp(line, "X-UID:", 6) == 0)
00779     {
00780       uidStr = QCString(line+6);
00781       lastStr = &uidStr;
00782     }
00783 
00784   }
00785 
00786   if (status & KMMsgStatusNew || status & KMMsgStatusUnread ||
00787       (folder() == kmkernel->outboxFolder()))
00788   {
00789     mUnreadMsgs++;
00790    if (mUnreadMsgs == 0) ++mUnreadMsgs;
00791   }
00792 
00793   ::chdir(path_buffer);
00794 }
00795 
00796 int KMFolderMaildir::createIndexFromContents()
00797 {
00798   mUnreadMsgs = 0;
00799 
00800   mMsgList.clear(true);
00801   mMsgList.reset(INIT_MSGS);
00802 
00803   mChanged = false;
00804 
00805   // first, we make sure that all the directories are here as they
00806   // should be
00807   QFileInfo dirinfo;
00808 
00809   dirinfo.setFile(location() + "/new");
00810   if (!dirinfo.exists() || !dirinfo.isDir())
00811   {
00812     kdDebug(5006) << "Directory " << location() << "/new doesn't exist or is a file"<< endl;
00813     return 1;
00814   }
00815   QDir newDir(location() + "/new");
00816   newDir.setFilter(QDir::Files);
00817 
00818   dirinfo.setFile(location() + "/cur");
00819   if (!dirinfo.exists() || !dirinfo.isDir())
00820   {
00821     kdDebug(5006) << "Directory " << location() << "/cur doesn't exist or is a file"<< endl;
00822     return 1;
00823   }
00824   QDir curDir(location() + "/cur");
00825   curDir.setFilter(QDir::Files);
00826 
00827   // then, we look for all the 'cur' files
00828   const QFileInfoList *list = curDir.entryInfoList();
00829   QFileInfoListIterator it(*list);
00830   QFileInfo *fi;
00831 
00832   while ((fi = it.current()))
00833   {
00834     readFileHeaderIntern(curDir.path(), fi->fileName(), KMMsgStatusRead);
00835     ++it;
00836   }
00837 
00838   // then, we look for all the 'new' files
00839   list = newDir.entryInfoList();
00840   it = *list;
00841 
00842   while ((fi=it.current()))
00843   {
00844     readFileHeaderIntern(newDir.path(), fi->fileName(), KMMsgStatusNew);
00845     ++it;
00846   }
00847 
00848   if (autoCreateIndex())
00849   {
00850     emit statusMsg(i18n("Writing index file"));
00851     writeIndex();
00852   }
00853   else mHeaderOffset = 0;
00854 
00855   correctUnreadMsgsCount();
00856 
00857   if (kmkernel->outboxFolder() == folder() && count() > 0)
00858     KMessageBox::information(0, i18n("Your outbox contains messages which were "
00859     "most-likely not created by KMail;\nplease remove them from there if you "
00860     "do not want KMail to send them."));
00861 
00862   needsCompact = true;
00863 
00864   if (folder()->parent())
00865     folder()->parent()->manager()->invalidateFolder(kmkernel->msgDict(), folder());
00866   return 0;
00867 }
00868 
00869 KMFolderIndex::IndexStatus KMFolderMaildir::indexStatus()
00870 {
00871   QFileInfo new_info(location() + "/new");
00872   QFileInfo cur_info(location() + "/cur");
00873   QFileInfo index_info(indexLocation());
00874 
00875   if (!index_info.exists())
00876     return KMFolderIndex::IndexMissing;
00877 
00878   // Check whether the directories are more than 5 seconds newer than the index
00879   // file. The 5 seconds are added to reduce the number of false alerts due
00880   // to slightly out of sync clocks of the NFS server and the local machine.
00881   return ((new_info.lastModified() > index_info.lastModified().addSecs(5)) ||
00882           (cur_info.lastModified() > index_info.lastModified().addSecs(5)))
00883          ? KMFolderIndex::IndexTooOld
00884          : KMFolderIndex::IndexOk;
00885 }
00886 
00887 //-----------------------------------------------------------------------------
00888 void KMFolderMaildir::removeMsg(int idx, bool)
00889 {
00890   KMMsgBase* msg = mMsgList[idx];
00891   if (!msg || !msg->fileName()) return;
00892 
00893   removeFile(msg->fileName());
00894 
00895   KMFolderIndex::removeMsg(idx);
00896 }
00897 
00898 //-----------------------------------------------------------------------------
00899 KMMessage* KMFolderMaildir::take(int idx)
00900 {
00901   // first, we do the high-level stuff.. then delete later
00902   KMMessage *msg = KMFolderIndex::take(idx);
00903 
00904   if (!msg || !msg->fileName()) return 0;
00905 
00906   if (removeFile(msg->fileName()))
00907     return msg;
00908   else
00909     return 0;
00910 }
00911 
00912 bool KMFolderMaildir::removeFile(const QString& filename)
00913 {
00914   // we need to look in both 'new' and 'cur' since it's possible to
00915   // delete a message before the folder is compacted.  since the file
00916   // naming and moving is done in ::compact, we can't assume any
00917   // location at this point
00918   QCString abs_file(QFile::encodeName(location() + "/cur/"));
00919   abs_file += QFile::encodeName(filename);
00920 
00921   if (::unlink( abs_file ) == 0)
00922       return true;
00923 
00924   if (errno == ENOENT)  {// doesn't exist
00925 
00926     abs_file = QFile::encodeName(location() + "/new/");
00927     abs_file += QFile::encodeName(filename);
00928 
00929     if (::unlink( abs_file ) == 0)
00930         return true;
00931 
00932   }
00933 
00934   kdDebug(5006) << "Can't delete " << abs_file << " " << perror << endl;
00935   return false;
00936 }
00937 
00938 //-----------------------------------------------------------------------------
00939 int KMFolderMaildir::removeContents()
00940 {
00941     if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/new/"), 0))
00942         return 1;
00943     if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/cur/"), 0))
00944         return 1;
00945     if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/tmp/"), 0))
00946         return 1;
00947 
00948     /* The subdirs are removed now. Check if there is anything else in the dir
00949      * and only if not delete the dir itself. The user could have data stored
00950      * that would otherwise be deleted. */
00951     QDir dir(location());
00952     if ( dir.count() == 2 ) { // only . and ..
00953         if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()), 0))
00954             return 1;
00955     }
00956     return 0;
00957 }
00958 
00959 static QRegExp *suffix_regex = 0;
00960 static KStaticDeleter<QRegExp> suffix_regex_sd;
00961 
00962 //-----------------------------------------------------------------------------
00963 QString KMFolderMaildir::constructValidFileName(QString& aFileName, KMMsgStatus status)
00964 {
00965   if (aFileName.isEmpty())
00966   {
00967     aFileName.sprintf("%ld.%d.", (long)time(0), getpid());
00968     aFileName += KApplication::randomString(5);
00969   }
00970 
00971   if (!suffix_regex)
00972       suffix_regex_sd.setObject(suffix_regex, new QRegExp(":2,?R?S?$"));
00973 
00974   aFileName.truncate(aFileName.findRev(*suffix_regex));
00975 
00976   QString suffix;
00977   if (! ((status & KMMsgStatusNew) || (status & KMMsgStatusUnread)) )
00978   {
00979     suffix += ":2,";
00980     if (status & KMMsgStatusReplied)
00981       suffix += "RS";
00982     else
00983       suffix += "S";
00984   }
00985 
00986   aFileName += suffix;
00987 
00988   return aFileName;
00989 }
00990 
00991 //-----------------------------------------------------------------------------
00992 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, KMMsgInfo *mi)
00993 {
00994   QString filename(mi->fileName());
00995   QString ret(moveInternal(oldLoc, newLoc, filename, mi->status()));
00996 
00997   if (filename != mi->fileName())
00998     mi->setFileName(filename);
00999 
01000   return ret;
01001 }
01002 
01003 //-----------------------------------------------------------------------------
01004 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, QString& aFileName, KMMsgStatus status)
01005 {
01006   QString dest(newLoc);
01007   // make sure that our destination filename doesn't already exist
01008   while (QFile::exists(dest))
01009   {
01010     aFileName = "";
01011     constructValidFileName(aFileName, status);
01012 
01013     QFileInfo fi(dest);
01014     dest = fi.dirPath(true) + "/" + aFileName;
01015     setDirty( true );
01016   }
01017 
01018   QDir d;
01019   if (d.rename(oldLoc, dest) == false)
01020     return QString::null;
01021   else
01022     return dest;
01023 }
01024 
01025 //-----------------------------------------------------------------------------
01026 void KMFolderMaildir::msgStatusChanged(const KMMsgStatus oldStatus,
01027   const KMMsgStatus newStatus, int idx)
01028 {
01029   // if the status of any message changes, then we need to compact
01030   needsCompact = true;
01031 
01032   KMFolderIndex::msgStatusChanged(oldStatus, newStatus, idx);
01033 }
01034 
01035 #include "kmfoldermaildir.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 23 22:43:47 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003