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