00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "kmfolderindex.h"
00020 #include "kmfolder.h"
00021 #include <config.h>
00022 #include <qfileinfo.h>
00023 #include <qtimer.h>
00024 #include <kdebug.h>
00025
00026
00027 #define HAVE_MMAP //need to get this into autoconf FIXME --Sam
00028 #include <unistd.h>
00029 #ifdef HAVE_MMAP
00030 #include <sys/mman.h>
00031 #endif
00032
00033
00034 #define INDEX_VERSION 1507
00035
00036 #ifndef MAX_LINE
00037 #define MAX_LINE 4096
00038 #endif
00039
00040 #ifndef INIT_MSGS
00041 #define INIT_MSGS 8
00042 #endif
00043
00044 #include <errno.h>
00045 #include <assert.h>
00046 #include <utime.h>
00047 #include <fcntl.h>
00048
00049 #ifdef HAVE_BYTESWAP_H
00050 #include <byteswap.h>
00051 #endif
00052 #include <kapplication.h>
00053 #include <kcursor.h>
00054 #include <kmessagebox.h>
00055 #include <klocale.h>
00056 #include "kmmsgdict.h"
00057
00058
00059
00060
00061
00062 #ifdef bswap_32
00063 #define kmail_swap_32(x) bswap_32(x)
00064 #else
00065 #define kmail_swap_32(x) \
00066 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00067 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00068 #endif
00069
00070 #include <stdlib.h>
00071 #include <sys/types.h>
00072 #include <sys/stat.h>
00073 #include <sys/file.h>
00074
00075 KMFolderIndex::KMFolderIndex(KMFolder* folder, const char* name)
00076 : FolderStorage(folder, name), mMsgList(INIT_MSGS)
00077 {
00078 mIndexStream = 0;
00079 mIndexStreamPtr = 0;
00080 mIndexStreamPtrLength = 0;
00081 mIndexSwapByteOrder = false;
00082 mIndexSizeOfLong = sizeof(long);
00083 mIndexId = 0;
00084 mHeaderOffset = 0;
00085 }
00086
00087
00088 KMFolderIndex::~KMFolderIndex()
00089 {
00090 }
00091
00092
00093 QString KMFolderIndex::indexLocation() const
00094 {
00095 QString sLocation(folder()->path());
00096
00097 if ( !sLocation.isEmpty() ) {
00098 sLocation += '/';
00099 sLocation += '.';
00100 }
00101 sLocation += dotEscape(fileName());
00102 sLocation += ".index";
00103
00104 return sLocation;
00105 }
00106
00107 int KMFolderIndex::updateIndex()
00108 {
00109 if (!mAutoCreateIndex)
00110 return 0;
00111 bool dirty = mDirty;
00112 mDirtyTimer->stop();
00113 for ( unsigned int i = 0; !dirty && i < mMsgList.high(); i++ ) {
00114 if ( mMsgList.at(i) ) {
00115 if ( !mMsgList.at(i)->syncIndexString() ) {
00116 dirty = true;
00117 }
00118 }
00119 }
00120 if (!dirty) {
00121 touchFolderIdsFile();
00122 return 0;
00123 }
00124 return writeIndex();
00125 }
00126
00127 int KMFolderIndex::writeIndex( bool createEmptyIndex )
00128 {
00129 QString tempName;
00130 QString indexName;
00131 mode_t old_umask;
00132 int len;
00133 const uchar *buffer = 0;
00134
00135 indexName = indexLocation();
00136 tempName = indexName + ".temp";
00137 unlink(QFile::encodeName(tempName));
00138
00139
00140
00141 utime(QFile::encodeName(location()), 0);
00142
00143 old_umask = umask(077);
00144 FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00145 umask(old_umask);
00146 if (!tmpIndexStream)
00147 return errno;
00148
00149 fprintf(tmpIndexStream, "# KMail-Index V%d\n", INDEX_VERSION);
00150
00151
00152 Q_UINT32 byteOrder = 0x12345678;
00153 Q_UINT32 sizeOfLong = sizeof(long);
00154
00155 Q_UINT32 header_length = sizeof(byteOrder)+sizeof(sizeOfLong);
00156 char pad_char = '\0';
00157 fwrite(&pad_char, sizeof(pad_char), 1, tmpIndexStream);
00158 fwrite(&header_length, sizeof(header_length), 1, tmpIndexStream);
00159
00160
00161 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00162 fwrite(&sizeOfLong, sizeof(sizeOfLong), 1, tmpIndexStream);
00163
00164 off_t nho = ftell(tmpIndexStream);
00165
00166 if ( !createEmptyIndex ) {
00167 KMMsgBase* msgBase;
00168 for (unsigned int i=0; i<mMsgList.high(); i++)
00169 {
00170 if (!(msgBase = mMsgList.at(i))) continue;
00171 buffer = msgBase->asIndexString(len);
00172 fwrite(&len,sizeof(len), 1, tmpIndexStream);
00173
00174 off_t tmp = ftell(tmpIndexStream);
00175 msgBase->setIndexOffset(tmp);
00176 msgBase->setIndexLength(len);
00177 if(fwrite(buffer, len, 1, tmpIndexStream) != 1)
00178 kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
00179 }
00180 }
00181
00182 int fError = ferror( tmpIndexStream );
00183 if( fError != 0 ) {
00184 fclose( tmpIndexStream );
00185 return fError;
00186 }
00187 if( ( fflush( tmpIndexStream ) != 0 )
00188 || ( fsync( fileno( tmpIndexStream ) ) != 0 ) ) {
00189 int errNo = errno;
00190 fclose( tmpIndexStream );
00191 return errNo;
00192 }
00193 if( fclose( tmpIndexStream ) != 0 )
00194 return errno;
00195
00196 ::rename(QFile::encodeName(tempName), QFile::encodeName(indexName));
00197 mHeaderOffset = nho;
00198 if (mIndexStream)
00199 fclose(mIndexStream);
00200
00201 if ( createEmptyIndex )
00202 return 0;
00203
00204 mIndexStream = fopen(QFile::encodeName(indexName), "r+");
00205 assert( mIndexStream );
00206 fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00207
00208 updateIndexStreamPtr();
00209
00210 writeFolderIdsFile();
00211
00212 setDirty( false );
00213 return 0;
00214 }
00215
00216
00217 bool KMFolderIndex::readIndex()
00218 {
00219 Q_INT32 len;
00220 KMMsgInfo* mi;
00221
00222 assert(mIndexStream != 0);
00223 rewind(mIndexStream);
00224
00225 clearIndex();
00226 int version;
00227
00228 setDirty( false );
00229
00230 if (!readIndexHeader(&version)) return false;
00231
00232
00233 mUnreadMsgs = 0;
00234 mTotalMsgs = 0;
00235 mHeaderOffset = ftell(mIndexStream);
00236
00237 clearIndex();
00238 while (!feof(mIndexStream))
00239 {
00240 mi = 0;
00241 if(version >= 1505) {
00242 if(!fread(&len, sizeof(len), 1, mIndexStream))
00243 break;
00244
00245 if (mIndexSwapByteOrder)
00246 len = kmail_swap_32(len);
00247
00248 off_t offs = ftell(mIndexStream);
00249 if(fseek(mIndexStream, len, SEEK_CUR))
00250 break;
00251 mi = new KMMsgInfo(folder(), offs, len);
00252 }
00253 else
00254 {
00255 QCString line(MAX_LINE);
00256 fgets(line.data(), MAX_LINE, mIndexStream);
00257 if (feof(mIndexStream)) break;
00258 if (*line.data() == '\0') {
00259 fclose(mIndexStream);
00260 mIndexStream = 0;
00261 clearIndex();
00262 return false;
00263 }
00264 mi = new KMMsgInfo(folder());
00265 mi->compat_fromOldIndexString(line, mConvertToUtf8);
00266 }
00267 if(!mi)
00268 break;
00269
00270 if (mi->isDeleted())
00271 {
00272 delete mi;
00273 setDirty( true );
00274 needsCompact = true;
00275 continue;
00276 }
00277 #ifdef OBSOLETE
00278 else if (mi->isNew())
00279 {
00280 mi->setStatus(KMMsgStatusUnread);
00281 mi->setDirty(false);
00282 }
00283 #endif
00284 if ((mi->isNew()) || (mi->isUnread()) ||
00285 (folder() == kmkernel->outboxFolder()))
00286 {
00287 ++mUnreadMsgs;
00288 if (mUnreadMsgs == 0) ++mUnreadMsgs;
00289 }
00290 mMsgList.append(mi, false);
00291 }
00292 if( version < 1505)
00293 {
00294 mConvertToUtf8 = false;
00295 setDirty( true );
00296 writeIndex();
00297 }
00298
00299 if ( version < 1507 ) {
00300 updateInvitationAndAddressFieldsFromContents();
00301 setDirty( true );
00302 writeIndex();
00303 }
00304
00305 mTotalMsgs = mMsgList.count();
00306 return true;
00307 }
00308
00309
00310 int KMFolderIndex::count(bool cache) const
00311 {
00312 int res = FolderStorage::count(cache);
00313 if (res == -1)
00314 res = mMsgList.count();
00315 return res;
00316 }
00317
00318
00319 bool KMFolderIndex::readIndexHeader(int *gv)
00320 {
00321 int indexVersion;
00322 assert(mIndexStream != 0);
00323 mIndexSwapByteOrder = false;
00324 mIndexSizeOfLong = sizeof(long);
00325
00326 int ret = fscanf(mIndexStream, "# KMail-Index V%d\n", &indexVersion);
00327 if ( ret == EOF || ret == 0 )
00328 return false;
00329 if(gv)
00330 *gv = indexVersion;
00331 if (indexVersion < 1505 ) {
00332 if(indexVersion == 1503) {
00333 kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl;
00334 mConvertToUtf8 = true;
00335 }
00336 return true;
00337 } else if (indexVersion == 1505) {
00338 } else if (indexVersion < INDEX_VERSION && indexVersion != 1506) {
00339 kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl;
00340 createIndexFromContents();
00341 return false;
00342 } else if(indexVersion > INDEX_VERSION) {
00343 kapp->setOverrideCursor(KCursor::arrowCursor());
00344 int r = KMessageBox::questionYesNo(0,
00345 i18n(
00346 "The mail index for '%1' is from an unknown version of KMail (%2).\n"
00347 "This index can be regenerated from your mail folder, but some "
00348 "information, including status flags, may be lost. Do you wish "
00349 "to downgrade your index file?") .arg(name()) .arg(indexVersion), QString::null, i18n("Downgrade"), i18n("Do Not Downgrade") );
00350 kapp->restoreOverrideCursor();
00351 if (r == KMessageBox::Yes)
00352 createIndexFromContents();
00353 return false;
00354 }
00355 else {
00356
00357 Q_UINT32 byteOrder = 0;
00358 Q_UINT32 sizeOfLong = sizeof(long);
00359
00360 Q_UINT32 header_length = 0;
00361 fseek(mIndexStream, sizeof(char), SEEK_CUR );
00362 fread(&header_length, sizeof(header_length), 1, mIndexStream);
00363 if (header_length > 0xFFFF)
00364 header_length = kmail_swap_32(header_length);
00365
00366 off_t endOfHeader = ftell(mIndexStream) + header_length;
00367
00368 bool needs_update = true;
00369
00370 if (header_length >= sizeof(byteOrder))
00371 {
00372 fread(&byteOrder, sizeof(byteOrder), 1, mIndexStream);
00373 mIndexSwapByteOrder = (byteOrder == 0x78563412);
00374 header_length -= sizeof(byteOrder);
00375
00376 if (header_length >= sizeof(sizeOfLong))
00377 {
00378 fread(&sizeOfLong, sizeof(sizeOfLong), 1, mIndexStream);
00379 if (mIndexSwapByteOrder)
00380 sizeOfLong = kmail_swap_32(sizeOfLong);
00381 mIndexSizeOfLong = sizeOfLong;
00382 header_length -= sizeof(sizeOfLong);
00383 needs_update = false;
00384 }
00385 }
00386 if (needs_update || mIndexSwapByteOrder || (mIndexSizeOfLong != sizeof(long)))
00387 setDirty( true );
00388
00389 fseek(mIndexStream, endOfHeader, SEEK_SET );
00390
00391 if (mIndexSwapByteOrder)
00392 kdDebug(5006) << "Index File has byte order swapped!" << endl;
00393 if (mIndexSizeOfLong != sizeof(long))
00394 kdDebug(5006) << "Index File sizeOfLong is " << mIndexSizeOfLong << " while sizeof(long) is " << sizeof(long) << " !" << endl;
00395
00396 }
00397 return true;
00398 }
00399
00400
00401 #ifdef HAVE_MMAP
00402 bool KMFolderIndex::updateIndexStreamPtr(bool just_close)
00403 #else
00404 bool KMFolderIndex::updateIndexStreamPtr(bool)
00405 #endif
00406 {
00407
00408
00409 utime(QFile::encodeName(location()), 0);
00410 utime(QFile::encodeName(indexLocation()), 0);
00411 utime(QFile::encodeName( KMMsgDict::getFolderIdsLocation( *this ) ), 0);
00412
00413 mIndexSwapByteOrder = false;
00414 #ifdef HAVE_MMAP
00415 if(just_close) {
00416 if(mIndexStreamPtr)
00417 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00418 mIndexStreamPtr = 0;
00419 mIndexStreamPtrLength = 0;
00420 return true;
00421 }
00422
00423 assert(mIndexStream);
00424 struct stat stat_buf;
00425 if(fstat(fileno(mIndexStream), &stat_buf) == -1) {
00426 if(mIndexStreamPtr)
00427 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00428 mIndexStreamPtr = 0;
00429 mIndexStreamPtrLength = 0;
00430 return false;
00431 }
00432 if(mIndexStreamPtr)
00433 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00434 mIndexStreamPtrLength = stat_buf.st_size;
00435 mIndexStreamPtr = (uchar *)mmap(0, mIndexStreamPtrLength, PROT_READ, MAP_SHARED,
00436 fileno(mIndexStream), 0);
00437 if(mIndexStreamPtr == MAP_FAILED) {
00438 mIndexStreamPtr = 0;
00439 mIndexStreamPtrLength = 0;
00440 return false;
00441 }
00442 #endif
00443 return true;
00444 }
00445
00446
00447 KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
00448 {
00449 QFileInfo contInfo(location());
00450 QFileInfo indInfo(indexLocation());
00451
00452 if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00453 if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00454
00455 return ( contInfo.lastModified() > indInfo.lastModified() )
00456 ? KMFolderIndex::IndexTooOld
00457 : KMFolderIndex::IndexOk;
00458 }
00459
00460 void KMFolderIndex::clearIndex(bool autoDelete, bool syncDict)
00461 {
00462 mMsgList.clear(autoDelete, syncDict);
00463 }
00464
00465
00466 void KMFolderIndex::truncateIndex()
00467 {
00468 if ( mHeaderOffset )
00469 truncate(QFile::encodeName(indexLocation()), mHeaderOffset);
00470 else
00471
00472
00473 writeIndex( true );
00474 }
00475
00476 void KMFolderIndex::fillMessageDict()
00477 {
00478 open("fillDict");
00479 for (unsigned int idx = 0; idx < mMsgList.high(); idx++)
00480 if ( mMsgList.at( idx ) )
00481 KMMsgDict::mutableInstance()->insert(0, mMsgList.at( idx ), idx);
00482 close("fillDict");
00483 }
00484
00485
00486 KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
00487 {
00488 KMMsgInfo *msgInfo = msg->msgInfo();
00489 if ( !msgInfo )
00490 msgInfo = new KMMsgInfo( folder() );
00491
00492 *msgInfo = *msg;
00493 mMsgList.set( idx, msgInfo );
00494 msg->setMsgInfo( 0 );
00495 delete msg;
00496 return msgInfo;
00497 }
00498
00499 void KMFolderIndex::recreateIndex()
00500 {
00501 kapp->setOverrideCursor(KCursor::arrowCursor());
00502 KMessageBox::error(0,
00503 i18n("The mail index for '%1' is corrupted and will be regenerated now, "
00504 "but some information, including status flags, will be lost.").arg(name()));
00505 kapp->restoreOverrideCursor();
00506 createIndexFromContents();
00507 readIndex();
00508 }
00509
00510 void KMFolderIndex::updateInvitationAndAddressFieldsFromContents()
00511 {
00512 kdDebug(5006) << "Updating index for " << label() << ", this might take a while." << endl;
00513 for ( uint i = 0; i < mMsgList.size(); i++ ) {
00514 KMMsgInfo * const msgInfo = dynamic_cast<KMMsgInfo*>( mMsgList[i] );
00515 if ( msgInfo ) {
00516 DwString msgString( getDwString( i ) );
00517 if ( msgString.size() > 0 ) {
00518 KMMessage msg;
00519 msg.fromDwString( msgString, false );
00520 msg.updateInvitationState();
00521 if ( msg.status() & KMMsgStatusHasInvitation ) {
00522 msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasInvitation );
00523 }
00524 if ( msg.status() & KMMsgStatusHasNoInvitation ) {
00525 msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasNoInvitation );
00526 }
00527 msgInfo->setFrom( msg.from() );
00528 msgInfo->setTo( msg.to() );
00529 }
00530 }
00531 }
00532 }
00533
00534 #include "kmfolderindex.moc"