00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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) {
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
00081
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
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
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, false , tmpIndexStream );
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
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) {
00151
00152
00153
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+");
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;
00251 setDirty( true );
00252 needsCompact = true;
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;
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
00328 quint32 byteOrder = 0;
00329 quint32 sizeOfLong = sizeof(long);
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
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
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
00379
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
00451
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;
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"