kmail Library API Documentation

kmfolderindex.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // Author:: Don Sanders <sanders@kde.org>
00003 // License GPL
00004 
00005 #include "kmfolderindex.h"
00006 #include <config.h>
00007 #include <qfileinfo.h>
00008 #include <qtimer.h>
00009 #include <kdebug.h>
00010 
00011 
00012 #define HAVE_MMAP //need to get this into autoconf FIXME  --Sam
00013 #include <unistd.h>
00014 #ifdef HAVE_MMAP
00015 #include <sys/mman.h>
00016 #endif
00017 
00018 // Current version of the table of contents (index) files
00019 #define INDEX_VERSION 1506
00020 
00021 #ifndef MAX_LINE
00022 #define MAX_LINE 4096
00023 #endif
00024 
00025 #ifndef INIT_MSGS
00026 #define INIT_MSGS 8
00027 #endif
00028 
00029 #include <errno.h>
00030 #include <assert.h>
00031 #include <utime.h>
00032 
00033 #ifdef HAVE_BYTESWAP_H
00034 #include <byteswap.h>
00035 #endif
00036 #include <kapplication.h>
00037 #include <kcursor.h>
00038 #include <kmessagebox.h>
00039 #include <klocale.h>
00040 #include "kmmsgdict.h"
00041 
00042 // We define functions as kmail_swap_NN so that we don't get compile errors
00043 // on platforms where bswap_NN happens to be a function instead of a define.
00044 
00045 /* Swap bytes in 32 bit value.  */
00046 #ifdef bswap_32
00047 #define kmail_swap_32(x) bswap_32(x)
00048 #else
00049 #define kmail_swap_32(x) \
00050      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |           \
00051       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
00052 #endif
00053 
00054 #include <stdlib.h>
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include <sys/file.h>
00058 
00059 KMFolderIndex::KMFolderIndex(KMFolderDir* parent, const QString& name)
00060   : KMFolder(parent, name), mMsgList(INIT_MSGS)
00061 {
00062     mIndexStream = 0;
00063     mIndexStreamPtr = 0;
00064     mIndexStreamPtrLength = 0;
00065     mIndexSwapByteOrder = false;
00066     mIndexSizeOfLong = sizeof(long);
00067     mIndexId = 0;
00068     mHeaderOffset   = 0;
00069 }
00070 
00071 
00072 KMFolderIndex::~KMFolderIndex()
00073 {
00074 }
00075 
00076 
00077 QString KMFolderIndex::indexLocation() const
00078 {
00079   QString sLocation(path());
00080 
00081   if (!sLocation.isEmpty()) sLocation += '/';
00082   sLocation += '.';
00083   sLocation += dotEscape(fileName());
00084   sLocation += ".index";
00085 
00086   return sLocation;
00087 }
00088 
00089 int KMFolderIndex::updateIndex()
00090 {
00091   if (!mAutoCreateIndex)
00092     return 0;
00093   bool dirty = mDirty;
00094   mDirtyTimer->stop();
00095   for (unsigned int i=0; !dirty && i<mMsgList.high(); i++)
00096     if (mMsgList.at(i))
00097       dirty = !mMsgList.at(i)->syncIndexString();
00098   if (!dirty) { // Update successful
00099       touchMsgDict();
00100       return 0;
00101   }
00102   return writeIndex();
00103 }
00104 
00105 int KMFolderIndex::writeIndex( bool createEmptyIndex )
00106 {
00107   QString tempName;
00108   QString indexName;
00109   mode_t old_umask;
00110   int len;
00111   const uchar *buffer = 0;
00112 
00113   indexName = indexLocation();
00114   tempName = indexName + ".temp";
00115   unlink(QFile::encodeName(tempName));
00116 
00117   // We touch the folder, otherwise the index is regenerated, if KMail is
00118   // running, while the clock switches from daylight savings time to normal time
00119   utime(QFile::encodeName(location()), 0);
00120 
00121   old_umask = umask(077);
00122   FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00123   umask(old_umask);
00124   if (!tmpIndexStream)
00125     return errno;
00126 
00127   fprintf(tmpIndexStream, "# KMail-Index V%d\n", INDEX_VERSION);
00128 
00129   // Header
00130   Q_UINT32 byteOrder = 0x12345678;
00131   Q_UINT32 sizeOfLong = sizeof(long);
00132 
00133   Q_UINT32 header_length = sizeof(byteOrder)+sizeof(sizeOfLong);
00134   char pad_char = '\0';
00135   fwrite(&pad_char, sizeof(pad_char), 1, tmpIndexStream);
00136   fwrite(&header_length, sizeof(header_length), 1, tmpIndexStream);
00137 
00138   // Write header
00139   fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00140   fwrite(&sizeOfLong, sizeof(sizeOfLong), 1, tmpIndexStream);
00141 
00142   off_t nho = ftell(tmpIndexStream);
00143 
00144   if ( !createEmptyIndex ) {
00145     KMMsgBase* msgBase;
00146     for (unsigned int i=0; i<mMsgList.high(); i++)
00147     {
00148       if (!(msgBase = mMsgList.at(i))) continue;
00149       buffer = msgBase->asIndexString(len);
00150       fwrite(&len,sizeof(len), 1, tmpIndexStream);
00151 
00152       off_t tmp = ftell(tmpIndexStream);
00153       msgBase->setIndexOffset(tmp);
00154       msgBase->setIndexLength(len);
00155       if(fwrite(buffer, len, 1, tmpIndexStream) != 1)
00156     kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
00157     }
00158   }
00159 
00160   int fError = ferror( tmpIndexStream );
00161   if( fError != 0 ) {
00162     fclose( tmpIndexStream );
00163     return fError;
00164   }
00165   if(    ( fflush( tmpIndexStream ) != 0 )
00166       || ( fsync( fileno( tmpIndexStream ) ) != 0 ) ) {
00167     int errNo = errno;
00168     fclose( tmpIndexStream );
00169     return errNo;
00170   }
00171   if( fclose( tmpIndexStream ) != 0 )
00172     return errno;
00173 
00174   ::rename(QFile::encodeName(tempName), QFile::encodeName(indexName));
00175   mHeaderOffset = nho;
00176   if (mIndexStream)
00177       fclose(mIndexStream);
00178 
00179   if ( createEmptyIndex )
00180     return 0;
00181 
00182   mIndexStream = fopen(QFile::encodeName(indexName), "r+"); // index file
00183   assert( mIndexStream );
00184   updateIndexStreamPtr();
00185 
00186   writeMsgDict();
00187 
00188   setDirty( false );
00189   return 0;
00190 }
00191 
00192 
00193 bool KMFolderIndex::readIndex()
00194 {
00195   Q_INT32 len;
00196   KMMsgInfo* mi;
00197 
00198   assert(mIndexStream != 0);
00199   rewind(mIndexStream);
00200 
00201   clearIndex();
00202   int version;
00203 
00204   setDirty( false );
00205 
00206   if (!readIndexHeader(&version)) return false;
00207 
00208   mUnreadMsgs = 0;
00209   mTotalMsgs = 0;
00210   mHeaderOffset = ftell(mIndexStream);
00211 
00212   clearIndex();
00213   while (!feof(mIndexStream))
00214   {
00215     mi = 0;
00216     if(version >= 1505) {
00217       if(!fread(&len, sizeof(len), 1, mIndexStream))
00218         break;
00219 
00220       if (mIndexSwapByteOrder)
00221         len = kmail_swap_32(len);
00222 
00223       off_t offs = ftell(mIndexStream);
00224       if(fseek(mIndexStream, len, SEEK_CUR))
00225         break;
00226       mi = new KMMsgInfo(this, offs, len);
00227     }
00228     else
00229     {
00230       QCString line(MAX_LINE);
00231       fgets(line.data(), MAX_LINE, mIndexStream);
00232       if (feof(mIndexStream)) break;
00233       if (*line.data() == '\0') {
00234       fclose(mIndexStream);
00235       mIndexStream = 0;
00236       clearIndex();
00237       return false;
00238       }
00239       mi = new KMMsgInfo(this);
00240       mi->compat_fromOldIndexString(line, mConvertToUtf8);
00241     }
00242     if(!mi)
00243       break;
00244 
00245     if (mi->isDeleted())
00246     {
00247       delete mi;  // skip messages that are marked as deleted
00248       setDirty( true );
00249       needsCompact = true;  //We have deleted messages - needs to be compacted
00250       continue;
00251     }
00252 #ifdef OBSOLETE
00253     else if (mi->isNew())
00254     {
00255       mi->setStatus(KMMsgStatusUnread);
00256       mi->setDirty(FALSE);
00257     }
00258 #endif
00259     if ((mi->isNew()) || (mi->isUnread()) ||
00260         (this == kmkernel->outboxFolder()))
00261     {
00262       ++mUnreadMsgs;
00263       if (mUnreadMsgs == 0) ++mUnreadMsgs;
00264     }
00265     mMsgList.append(mi, false);
00266   }
00267   if( version < 1505)
00268   {
00269     mConvertToUtf8 = FALSE;
00270     setDirty( true );
00271     writeIndex();
00272   }
00273   mTotalMsgs = mMsgList.count();
00274   return true;
00275 }
00276 
00277 
00278 int KMFolderIndex::count(bool cache) const
00279 {
00280   int res = KMFolder::count(cache);
00281   if (res == -1)
00282     res = mMsgList.count();
00283   return res;
00284 }
00285 
00286 
00287 bool KMFolderIndex::readIndexHeader(int *gv)
00288 {
00289   int indexVersion;
00290   assert(mIndexStream != 0);
00291   mIndexSwapByteOrder = false;
00292   mIndexSizeOfLong = sizeof(long);
00293 
00294   int ret = fscanf(mIndexStream, "# KMail-Index V%d\n", &indexVersion);
00295   if ( ret == EOF || ret == 0 )
00296       return false; // index file has invalid header
00297   if(gv)
00298       *gv = indexVersion;
00299   if (indexVersion < 1505 ) {
00300       if(indexVersion == 1503) {
00301       kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl;
00302       mConvertToUtf8 = TRUE;
00303       }
00304       return TRUE;
00305   } else if (indexVersion == 1505) {
00306   } else if (indexVersion < INDEX_VERSION) {
00307       kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl;
00308       createIndexFromContents();
00309       return FALSE;
00310   } else if(indexVersion > INDEX_VERSION) {
00311       kapp->setOverrideCursor(KCursor::arrowCursor());
00312       int r = KMessageBox::questionYesNo(0,
00313                      i18n(
00314                         "The mail index for '%1' is from an unknown version of KMail (%2).\n"
00315                         "This index can be regenerated from your mail folder, but some "
00316                         "information, including status flags, may be lost. Do you wish "
00317                         "to downgrade your index file?") .arg(name()) .arg(indexVersion) );
00318       kapp->restoreOverrideCursor();
00319       if (r == KMessageBox::Yes)
00320       createIndexFromContents();
00321       return FALSE;
00322   }
00323   else {
00324       // Header
00325       Q_UINT32 byteOrder = 0;
00326       Q_UINT32 sizeOfLong = sizeof(long); // default
00327 
00328       Q_UINT32 header_length = 0;
00329       fseek(mIndexStream, sizeof(char), SEEK_CUR );
00330       fread(&header_length, sizeof(header_length), 1, mIndexStream);
00331       if (header_length > 0xFFFF)
00332          header_length = kmail_swap_32(header_length);
00333 
00334       off_t endOfHeader = ftell(mIndexStream) + header_length;
00335 
00336       bool needs_update = true;
00337       // Process available header parts
00338       if (header_length >= sizeof(byteOrder))
00339       {
00340          fread(&byteOrder, sizeof(byteOrder), 1, mIndexStream);
00341          mIndexSwapByteOrder = (byteOrder == 0x78563412);
00342          header_length -= sizeof(byteOrder);
00343 
00344          if (header_length >= sizeof(sizeOfLong))
00345          {
00346             fread(&sizeOfLong, sizeof(sizeOfLong), 1, mIndexStream);
00347             if (mIndexSwapByteOrder)
00348                sizeOfLong = kmail_swap_32(sizeOfLong);
00349             mIndexSizeOfLong = sizeOfLong;
00350             header_length -= sizeof(sizeOfLong);
00351             needs_update = false;
00352          }
00353       }
00354       if (needs_update || mIndexSwapByteOrder || (mIndexSizeOfLong != sizeof(long)))
00355     setDirty( true );
00356       // Seek to end of header
00357       fseek(mIndexStream, endOfHeader, SEEK_SET );
00358 
00359       if (mIndexSwapByteOrder)
00360          kdDebug(5006) << "Index File has byte order swapped!" << endl;
00361       if (mIndexSizeOfLong != sizeof(long))
00362          kdDebug(5006) << "Index File sizeOfLong is " << mIndexSizeOfLong << " while sizeof(long) is " << sizeof(long) << " !" << endl;
00363 
00364   }
00365   return TRUE;
00366 }
00367 
00368 
00369 #ifdef HAVE_MMAP
00370 bool KMFolderIndex::updateIndexStreamPtr(bool just_close)
00371 #else
00372 bool KMFolderIndex::updateIndexStreamPtr(bool)
00373 #endif
00374 {
00375     // We touch the folder, otherwise the index is regenerated, if KMail is
00376     // running, while the clock switches from daylight savings time to normal time
00377     utime(QFile::encodeName(location()), 0);
00378     utime(QFile::encodeName(indexLocation()), 0);
00379     utime(QFile::encodeName(KMMsgDict::getFolderIdsLocation( this )), 0);
00380 
00381   mIndexSwapByteOrder = false;
00382 #ifdef HAVE_MMAP
00383     if(just_close) {
00384     if(mIndexStreamPtr)
00385         munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00386     mIndexStreamPtr = 0;
00387     mIndexStreamPtrLength = 0;
00388     return TRUE;
00389     }
00390 
00391     assert(mIndexStream);
00392     struct stat stat_buf;
00393     if(fstat(fileno(mIndexStream), &stat_buf) == -1) {
00394     if(mIndexStreamPtr)
00395         munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00396     mIndexStreamPtr = 0;
00397     mIndexStreamPtrLength = 0;
00398     return FALSE;
00399     }
00400     if(mIndexStreamPtr)
00401     munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00402     mIndexStreamPtrLength = stat_buf.st_size;
00403     mIndexStreamPtr = (uchar *)mmap(0, mIndexStreamPtrLength, PROT_READ, MAP_SHARED,
00404                     fileno(mIndexStream), 0);
00405     if(mIndexStreamPtr == MAP_FAILED) {
00406     mIndexStreamPtr = 0;
00407     mIndexStreamPtrLength = 0;
00408     return FALSE;
00409     }
00410 #endif
00411     return TRUE;
00412 }
00413 
00414 
00415 KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
00416 {
00417     QFileInfo contInfo(location());
00418     QFileInfo indInfo(indexLocation());
00419 
00420     if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00421     if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00422 
00423     return ( contInfo.lastModified() > indInfo.lastModified() )
00424         ? KMFolderIndex::IndexTooOld
00425         : KMFolderIndex::IndexOk;
00426 }
00427 
00428 void KMFolderIndex::clearIndex(bool autoDelete, bool syncDict)
00429 {
00430     mMsgList.clear(autoDelete, syncDict);
00431 }
00432 
00433 
00434 void KMFolderIndex::truncateIndex()
00435 {
00436   if ( mHeaderOffset )
00437     truncate(QFile::encodeName(indexLocation()), mHeaderOffset);
00438   else
00439     // The index file wasn't opened, so we don't know the header offset.
00440     // So let's just create a new empty index.
00441     writeIndex( true );
00442 }
00443 
00444 
00445 void KMFolderIndex::fillDictFromIndex(KMMsgDict *dict)
00446 {
00447   open();
00448   mMsgList.fillMsgDict(dict);
00449   close();
00450 }
00451 
00452 
00453 KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
00454 {
00455   KMMsgInfo *msgInfo = new KMMsgInfo( this );
00456   *msgInfo = *msg;
00457   mMsgList.set( idx, msgInfo );
00458   return msgInfo;
00459 }
00460 #include "kmfolderindex.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