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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:48 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003