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