• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kmail

kmfolderindex.cpp

Go to the documentation of this file.
00001 /* -*- mode: C++; c-file-style: "gnu" -*-
00002     This file is part of KMail, the KDE mail client.
00003     Copyright (c) 2000 Don Sanders <sanders@kde.org>
00004 
00005     KMail is free software; you can redistribute it and/or modify it
00006     under the terms of the GNU General Public License, version 2, as
00007     published by the Free Software Foundation.
00008 
00009     KMail is distributed in the hope that it will be useful, but
00010     WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License along
00015     with this program; if not, write to the Free Software Foundation, Inc.,
00016     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00017 */
00018 
00019 #include "kmfolderindex_common.cpp"
00020 
00021 #ifdef HAVE_MMAP
00022 #include <sys/mman.h>
00023 #endif
00024 
00025 #ifdef KMAIL_SQLITE_INDEX
00026 # include "kmfolderindex_sqlite.cpp"
00027 #else
00028 
00029 KMFolderIndex::KMFolderIndex(KMFolder* folder, const char* name)
00030   : FolderStorage(folder, name), mMsgList(INIT_MSGS)
00031 {
00032     mIndexStream = 0;
00033     mIndexStreamPtr = 0;
00034     mIndexStreamPtrLength = 0;
00035     mIndexSwapByteOrder = false;
00036     mIndexSizeOfLong = sizeof(long);
00037     mIndexId = 0;
00038     mHeaderOffset   = 0;
00039 }
00040 
00041 
00042 KMFolderIndex::~KMFolderIndex()
00043 {
00044 }
00045 
00046 int KMFolderIndex::updateIndex( bool aboutToClose )
00047 {
00048   Q_UNUSED( aboutToClose );
00049   if (!mAutoCreateIndex)
00050     return 0;
00051   bool dirty = mDirty;
00052   mDirtyTimer->stop();
00053   const uint high = mMsgList.high();
00054   for (uint i=0; !dirty && i<high; i++) {
00055     KMMsgBase *msg = mMsgList.at(i);
00056     if (msg)
00057       dirty = !msg->syncIndexString();
00058   }
00059   if (!dirty) { // Update successful
00060       touchFolderIdsFile();
00061       return 0;
00062   }
00063   return writeIndex();
00064 }
00065 
00066 int KMFolderIndex::writeIndex( bool createEmptyIndex )
00067 {
00068   QString tempName;
00069   QString indexName;
00070   mode_t old_umask;
00071 
00072   indexName = indexLocation();
00073   tempName = indexName + ".temp";
00074   {
00075     int result = unlink( QFile::encodeName( tempName ) );
00076     if ( ! ( result == 0 || (result == -1 && errno == ENOENT ) ) )
00077       return errno;
00078   }
00079 
00080   // We touch the folder, otherwise the index is regenerated, if KMail is
00081   // running, while the clock switches from daylight savings time to normal time
00082   utime( QFile::encodeName( QDir::toNativeSeparators(location()) ), 0 );
00083 
00084   old_umask = umask( 077 );
00085   FILE *tmpIndexStream = KDE_fopen( QFile::encodeName( tempName ), "w" );
00086   kDebug( StorageDebug ) << "KDE_fopen(tempName=" << tempName << ", \"w\") == tmpIndexStream == " << tmpIndexStream;
00087   umask( old_umask );
00088   if ( !tmpIndexStream ) {
00089     return errno;
00090   }
00091 
00092   fprintf(tmpIndexStream, "# KMail-Index V%d\n", INDEX_VERSION);
00093 
00094   // Header
00095   quint32 byteOrder = 0x12345678;
00096   quint32 sizeOfLong = sizeof(long);
00097 
00098   quint32 header_length = sizeof(byteOrder)+sizeof(sizeOfLong);
00099   char pad_char = '\0';
00100   fwrite(&pad_char, sizeof(pad_char), 1, tmpIndexStream);
00101   fwrite(&header_length, sizeof(header_length), 1, tmpIndexStream);
00102 
00103   // Write header
00104   fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00105   fwrite(&sizeOfLong, sizeof(sizeOfLong), 1, tmpIndexStream);
00106 
00107   off_t nho = KDE_ftell(tmpIndexStream);
00108 
00109   int fError = 0;
00110   if ( !createEmptyIndex ) {
00111     fError = writeMessages( 0/*all*/, false /* !flush */, tmpIndexStream );
00112 /* moved to writeMessages()
00113     KMMsgBase* msgBase;
00114     int len;
00115     const uchar *buffer = 0;
00116     for (unsigned int i=0; i<mMsgList.high(); i++)
00117     {
00118       if (!(msgBase = mMsgList.at(i))) continue;
00119       buffer = msgBase->asIndexString(len);
00120       fwrite(&len,sizeof(len), 1, tmpIndexStream);
00121 
00122       off_t tmp = KDE_ftell(tmpIndexStream);
00123       msgBase->setIndexOffset(tmp);
00124       msgBase->setIndexLength(len);
00125       if(fwrite(buffer, len, 1, tmpIndexStream) != 1)
00126         kDebug(5006) <<"Whoa!";
00127     }*/
00128   }
00129 
00130   fError |= ferror( tmpIndexStream );
00131   if( fError != 0 ) {
00132     fclose( tmpIndexStream );
00133     kDebug( StorageDebug ) << "fclose(tmpIndexStream = " << tmpIndexStream << ")";
00134     return fError;
00135   }
00136   if(    ( fflush( tmpIndexStream ) != 0 )
00137       || ( fsync( fileno( tmpIndexStream ) ) != 0 ) ) {
00138     int errNo = errno;
00139     fclose( tmpIndexStream );
00140     kWarning() << "fflush() or fsync() failed; fclose(tmpIndexStream = " << tmpIndexStream << ")";
00141     return errNo;
00142   }
00143   kDebug( StorageDebug ) << "fclose(tmpIndexStream = " << tmpIndexStream << ")";
00144   if( fclose( tmpIndexStream ) != 0 ) {
00145     kWarning() << "fclose() failed";
00146     return errno;
00147   }
00148 
00149 #ifdef Q_WS_WIN
00150   if (mIndexStream) { // close before renaming
00151     // neither this fixes windows port:
00152     // if ( !updateIndexStreamPtr() )
00153     //  return 1;
00154     bool ok = fclose( mIndexStream ) == 0;
00155     kDebug( StorageDebug ) << "fclose(mIndexStream = " << mIndexStream << ")";
00156     mIndexStream = 0;
00157     if ( !ok ) {
00158       kWarning() << "fclose() failed";
00159       return errno;
00160     }
00161   }
00162 #endif
00163 
00164   if ( KDE_rename(QFile::encodeName(tempName), QFile::encodeName(indexName)) != 0 )
00165     return errno;
00166   mHeaderOffset = nho;
00167 
00168 #ifndef Q_WS_WIN
00169   if (mIndexStream) {
00170       fclose(mIndexStream);
00171       kDebug( StorageDebug ) << "fclose(mIndexStream = " << mIndexStream << ")";
00172   }
00173 #endif
00174 
00175   if ( createEmptyIndex )
00176     return 0;
00177 
00178   mIndexStream = KDE_fopen(QFile::encodeName(indexName), "r+"); // index file
00179   kDebug( StorageDebug ) << "KDE_fopen(indexName=" << indexName << ", \"r+\") == mIndexStream == " << mIndexStream;
00180   assert( mIndexStream );
00181 #ifndef Q_WS_WIN
00182   fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00183 #endif
00184 
00185   if ( !updateIndexStreamPtr() )
00186     return 1;
00187   if ( 0 != writeFolderIdsFile() )
00188     return 1;
00189 
00190   setDirty( false );
00191   return 0;
00192 }
00193 
00194 
00195 bool KMFolderIndex::readIndex()
00196 {
00197   qint32 len;
00198   KMMsgInfo* mi;
00199 
00200   assert(mIndexStream != 0);
00201   rewind(mIndexStream);
00202 
00203   clearIndex();
00204   int version;
00205 
00206   setDirty( false );
00207 
00208   if (!readIndexHeader(&version)) return false;
00209 
00210   mUnreadMsgs = 0;
00211   mTotalMsgs = 0;
00212   mHeaderOffset = KDE_ftell(mIndexStream);
00213 
00214   clearIndex();
00215   while (!feof(mIndexStream))
00216   {
00217     mi = 0;
00218     if(version >= 1505) {
00219       if(!fread(&len, sizeof(len), 1, mIndexStream))
00220         break;
00221 
00222       if (mIndexSwapByteOrder)
00223         len = kmail_swap_32(len);
00224 
00225       off_t offs = KDE_ftell(mIndexStream);
00226       if(KDE_fseek(mIndexStream, len, SEEK_CUR))
00227         break;
00228       mi = new KMMsgInfo(folder(), offs, len);
00229     }
00230     else
00231     {
00232       QByteArray line( MAX_LINE, '\0' );
00233       fgets(line.data(), MAX_LINE, mIndexStream);
00234       if (feof(mIndexStream)) break;
00235       if (*line.data() == '\0') {
00236         fclose(mIndexStream);
00237         kDebug( StorageDebug ) << "fclose(mIndexStream = " << mIndexStream << ")";
00238         mIndexStream = 0;
00239         clearIndex();
00240         return false;
00241       }
00242       mi = new KMMsgInfo(folder());
00243       mi->compat_fromOldIndexString(line, mConvertToUtf8);
00244     }
00245     if(!mi)
00246       break;
00247 
00248     if (mi->status().isDeleted())
00249     {
00250       delete mi;  // skip messages that are marked as deleted
00251       setDirty( true );
00252       needsCompact = true;  //We have deleted messages - needs to be compacted
00253       continue;
00254     }
00255 #ifdef OBSOLETE
00256     else if (mi->isNew())
00257     {
00258       mi->setStatus(KMMsgStatusUnread);
00259       mi->setDirty(false);
00260     }
00261 #endif
00262     if ((mi->status().isNew()) || (mi->status().isUnread()) ||
00263         (folder() == kmkernel->outboxFolder()))
00264     {
00265       ++mUnreadMsgs;
00266       if (mUnreadMsgs == 0) ++mUnreadMsgs;
00267     }
00268     mMsgList.append(mi, false);
00269   }
00270   if( version < 1505)
00271   {
00272     mConvertToUtf8 = false;
00273     setDirty( true );
00274     writeIndex();
00275   }
00276   mTotalMsgs = mMsgList.count();
00277   return true;
00278 }
00279 
00280 
00281 int KMFolderIndex::count(bool cache) const
00282 {
00283   int res = FolderStorage::count(cache);
00284   if (res == -1)
00285     res = mMsgList.count();
00286   return res;
00287 }
00288 
00289 
00290 bool KMFolderIndex::readIndexHeader(int *gv)
00291 {
00292   int indexVersion;
00293   assert(mIndexStream != 0);
00294   mIndexSwapByteOrder = false;
00295   mIndexSizeOfLong = sizeof(long);
00296 
00297   int ret = fscanf(mIndexStream, "# KMail-Index V%d\n", &indexVersion);
00298   if ( ret == EOF || ret == 0 )
00299       return false; // index file has invalid header
00300   if(gv)
00301       *gv = indexVersion;
00302   if (indexVersion < 1505 ) {
00303       if(indexVersion == 1503) {
00304         kDebug(5006) <<"Converting old index file" << indexLocation() <<" to utf-8";
00305         mConvertToUtf8 = true;
00306       }
00307       return true;
00308   } else if (indexVersion == 1505) {
00309   } else if (indexVersion < INDEX_VERSION) {
00310       kDebug(5006) <<"Index file" << indexLocation() <<" is out of date. Re-creating it.";
00311       createIndexFromContents();
00312       return false;
00313   } else if(indexVersion > INDEX_VERSION) {
00314       QApplication::setOverrideCursor( QCursor( Qt::ArrowCursor ) );
00315       int r = KMessageBox::questionYesNo(0,
00316         i18n(
00317           "The mail index for '%1' is from an unknown version of KMail (%2).\n"
00318           "This index can be regenerated from your mail folder, but some "
00319           "information, including status flags, may be lost. Do you wish "
00320           "to downgrade your index file?" , objectName() , indexVersion), QString(), KGuiItem(i18n("Downgrade")), KGuiItem(i18n("Do Not Downgrade")) );
00321       QApplication::restoreOverrideCursor();
00322       if (r == KMessageBox::Yes)
00323         createIndexFromContents();
00324       return false;
00325   }
00326   else {
00327       // Header
00328       quint32 byteOrder = 0;
00329       quint32 sizeOfLong = sizeof(long); // default
00330 
00331       quint32 header_length = 0;
00332       KDE_fseek(mIndexStream, sizeof(char), SEEK_CUR );
00333       fread(&header_length, sizeof(header_length), 1, mIndexStream);
00334       if (header_length > 0xFFFF)
00335          header_length = kmail_swap_32(header_length);
00336 
00337       off_t endOfHeader = KDE_ftell(mIndexStream) + header_length;
00338 
00339       bool needs_update = true;
00340       // Process available header parts
00341       if (header_length >= sizeof(byteOrder))
00342       {
00343          fread(&byteOrder, sizeof(byteOrder), 1, mIndexStream);
00344          mIndexSwapByteOrder = (byteOrder == 0x78563412);
00345          header_length -= sizeof(byteOrder);
00346 
00347          if (header_length >= sizeof(sizeOfLong))
00348          {
00349             fread(&sizeOfLong, sizeof(sizeOfLong), 1, mIndexStream);
00350             if (mIndexSwapByteOrder)
00351                sizeOfLong = kmail_swap_32(sizeOfLong);
00352             mIndexSizeOfLong = sizeOfLong;
00353             header_length -= sizeof(sizeOfLong);
00354             needs_update = false;
00355          }
00356       }
00357       if (needs_update || mIndexSwapByteOrder || (mIndexSizeOfLong != sizeof(long)))
00358         setDirty( true );
00359       // Seek to end of header
00360       KDE_fseek(mIndexStream, endOfHeader, SEEK_SET );
00361 
00362       if (mIndexSwapByteOrder)
00363          kDebug(5006) <<"Index File has byte order swapped!";
00364       if (mIndexSizeOfLong != sizeof(long))
00365          kDebug(5006) <<"Index File sizeOfLong is" << mIndexSizeOfLong <<" while sizeof(long) is" << sizeof(long) <<" !";
00366 
00367   }
00368   return true;
00369 }
00370 
00371 
00372 bool KMFolderIndex::updateIndexStreamPtr(bool just_close)
00373 {
00374 #ifndef HAVE_MMAP
00375   Q_UNUSED( just_close );
00376 #endif
00377 
00378   // We touch the folder, otherwise the index is regenerated, if KMail is
00379   // running, while the clock switches from daylight savings time to normal time
00380   if ( 0 != utime( QFile::encodeName( QDir::toNativeSeparators( location() ) ), 0 ) )
00381     kWarning() << "utime(" << QDir::toNativeSeparators( location() ) << ", 0) failed (location())";
00382   if ( 0 != utime( QFile::encodeName( QDir::toNativeSeparators( indexLocation() ) ), 0 ) )
00383     kWarning() << "utime(" << QDir::toNativeSeparators( indexLocation() ) << ", 0) failed (indexLocation())";
00384   if ( 0 != utime( QFile::encodeName( QDir::toNativeSeparators( KMMsgDict::getFolderIdsLocation( *this ) ) ), 0 ) )
00385     kWarning() << "utime(" << QDir::toNativeSeparators( KMMsgDict::getFolderIdsLocation( *this ) ) << ", 0) failed (KMMsgDict::getFolderIdsLocation( *this ))";
00386 
00387   mIndexSwapByteOrder = false;
00388 #ifdef HAVE_MMAP
00389   if ( just_close ) {
00390     bool munmapResult = true;
00391     if( mIndexStreamPtr )
00392       munmapResult = 0 == munmap( reinterpret_cast<char *>( mIndexStreamPtr ), mIndexStreamPtrLength );
00393     mIndexStreamPtr = 0;
00394     mIndexStreamPtrLength = 0;
00395     return munmapResult;
00396   }
00397 
00398   assert( mIndexStream );
00399   KDE_struct_stat stat_buf;
00400   if ( KDE_fstat( fileno( mIndexStream ), &stat_buf ) == -1 ) {
00401     if( mIndexStreamPtr )
00402       munmap( reinterpret_cast<char *>( mIndexStreamPtr ), mIndexStreamPtrLength );
00403     mIndexStreamPtr = 0;
00404     mIndexStreamPtrLength = 0;
00405     return false;
00406   }
00407 
00408   if ( mIndexStreamPtr )
00409     if ( 0 != munmap( reinterpret_cast<char *>( mIndexStreamPtr ), mIndexStreamPtrLength ) )
00410       return false;
00411 
00412   mIndexStreamPtrLength = stat_buf.st_size;
00413   mIndexStreamPtr = static_cast<uchar *>( mmap( 0, mIndexStreamPtrLength,
00414                                                 PROT_READ, MAP_SHARED,
00415                                                 fileno( mIndexStream ), 0 ) );
00416   if ( mIndexStreamPtr == MAP_FAILED ) {
00417     mIndexStreamPtr = 0;
00418     mIndexStreamPtrLength = 0;
00419     return false;
00420   }
00421 #endif
00422     return true;
00423 }
00424 
00425 
00426 KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
00427 {
00428     QFileInfo contInfo(location());
00429     QFileInfo indInfo(indexLocation());
00430 
00431     if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00432     if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00433 
00434     return ( contInfo.lastModified() > indInfo.lastModified() )
00435         ? KMFolderIndex::IndexTooOld
00436         : KMFolderIndex::IndexOk;
00437 }
00438 
00439 void KMFolderIndex::clearIndex(bool autoDelete, bool syncDict)
00440 {
00441     mMsgList.clear(autoDelete, syncDict);
00442 }
00443 
00444 
00445 void KMFolderIndex::truncateIndex()
00446 {
00447   if ( mHeaderOffset )
00448     truncate(QFile::encodeName(indexLocation()), mHeaderOffset);
00449   else
00450     // The index file wasn't opened, so we don't know the header offset.
00451     // So let's just create a new empty index.
00452     writeIndex( true );
00453 }
00454 
00455 void KMFolderIndex::fillMessageDict()
00456 {
00457   open( "fillDict" );
00458   for ( unsigned int idx = 0; idx < mMsgList.high(); idx++ ) {
00459     KMMsgBase* msg = mMsgList.at( idx );
00460     if ( msg ) {
00461       KMMsgDict::mutableInstance()->insert( 0, msg, idx );
00462     }
00463   }
00464   close( "fillDict" );
00465 }
00466 
00467 
00468 KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
00469 {
00470   KMMsgInfo *msgInfo = new KMMsgInfo( folder() );
00471   *msgInfo = *msg;
00472   mMsgList.set( idx, msgInfo );
00473   return msgInfo;
00474 }
00475 
00476 bool KMFolderIndex::recreateIndex()
00477 {
00478   QApplication::setOverrideCursor( QCursor( Qt::ArrowCursor ) );
00479   KMessageBox::error(0,
00480        i18n("The mail index for '%1' is corrupted and will be regenerated now, "
00481             "but some information, including status flags, will be lost.", label()));
00482   QApplication::restoreOverrideCursor();
00483   if ( createIndexFromContents() != 0 )
00484     return false;
00485   return readIndex();
00486 }
00487 
00488 int KMFolderIndex::writeMessages( KMMsgBase* msg, bool flush, FILE* indexStream )
00489 {
00490   const uint high = mMsgList.high();
00491   for ( uint i = 0; i < high || msg; i++ )
00492   {
00493     KMMsgBase* msgBase = msg ? msg : mMsgList.at(i);
00494     if ( !msgBase )
00495       continue;
00496     int len;
00497     const uchar *buffer = msgBase->asIndexString( len );
00498     if ( fwrite( &len, sizeof( len ), 1, indexStream ) != 1 )
00499       return 1;
00500     off_t offset = KDE_ftell( indexStream );
00501     msgBase->setIndexOffset( offset );
00502     msgBase->setIndexLength( len );
00503     if ( fwrite( buffer, len, 1, indexStream ) != 1 ) {
00504       kDebug() << "Whoa!";
00505       return 1;
00506     }
00507     if ( msg )
00508       break; // only one
00509   }
00510   if ( flush )
00511     fflush( indexStream );
00512   int error = ferror( indexStream );
00513   if ( error != 0 )
00514     return error;
00515   return 0;
00516 }
00517 
00518 int KMFolderIndex::writeMessages( KMMsgBase* msg, bool flush )
00519 {
00520   return writeMessages( msg, flush, mIndexStream );
00521 }
00522 
00523 KMMsgBase * KMFolderIndex::takeIndexEntry(int idx)
00524 {
00525   return mMsgList.take( idx );
00526 }
00527 
00528 #endif // !KMAIL_SQLITE_INDEX
00529 
00530 #include "kmfolderindex.moc"

kmail

Skip menu "kmail"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdepim

Skip menu "kdepim"
  • akonadi
  •   clients
  •   kabc
  •   kcal
  •   kcm
  • akregator
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt
  • kdgantt1
  • kjots
  • kleopatra
  • kmail
  • kmobiletools
  • knode
  • knotes
  • kontact
  • kontactinterfaces
  • korganizer
  •   korgac
  • kpilot
  • ktimetracker
  • libkdepim
  • libkholidays
  • libkleo
  • libkpgp
  • maildir
Generated for kdepim by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal