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 Wed Jul 28 23:58:01 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003