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 1506
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 mUnreadMsgs = 0;
00233 mTotalMsgs = 0;
00234 mHeaderOffset = ftell(mIndexStream);
00235
00236 clearIndex();
00237 while (!feof(mIndexStream))
00238 {
00239 mi = 0;
00240 if(version >= 1505) {
00241 if(!fread(&len, sizeof(len), 1, mIndexStream))
00242 break;
00243
00244 if (mIndexSwapByteOrder)
00245 len = kmail_swap_32(len);
00246
00247 off_t offs = ftell(mIndexStream);
00248 if(fseek(mIndexStream, len, SEEK_CUR))
00249 break;
00250 mi = new KMMsgInfo(folder(), offs, len);
00251 }
00252 else
00253 {
00254 QCString line(MAX_LINE);
00255 fgets(line.data(), MAX_LINE, mIndexStream);
00256 if (feof(mIndexStream)) break;
00257 if (*line.data() == '\0') {
00258 fclose(mIndexStream);
00259 mIndexStream = 0;
00260 clearIndex();
00261 return false;
00262 }
00263 mi = new KMMsgInfo(folder());
00264 mi->compat_fromOldIndexString(line, mConvertToUtf8);
00265 }
00266 if(!mi)
00267 break;
00268
00269 if (mi->isDeleted())
00270 {
00271 delete mi;
00272 setDirty( true );
00273 needsCompact = true;
00274 continue;
00275 }
00276 #ifdef OBSOLETE
00277 else if (mi->isNew())
00278 {
00279 mi->setStatus(KMMsgStatusUnread);
00280 mi->setDirty(false);
00281 }
00282 #endif
00283 if ((mi->isNew()) || (mi->isUnread()) ||
00284 (folder() == kmkernel->outboxFolder()))
00285 {
00286 ++mUnreadMsgs;
00287 if (mUnreadMsgs == 0) ++mUnreadMsgs;
00288 }
00289 mMsgList.append(mi, false);
00290 }
00291 if( version < 1505)
00292 {
00293 mConvertToUtf8 = false;
00294 setDirty( true );
00295 writeIndex();
00296 }
00297 mTotalMsgs = mMsgList.count();
00298 return true;
00299 }
00300
00301
00302 int KMFolderIndex::count(bool cache) const
00303 {
00304 int res = FolderStorage::count(cache);
00305 if (res == -1)
00306 res = mMsgList.count();
00307 return res;
00308 }
00309
00310
00311 bool KMFolderIndex::readIndexHeader(int *gv)
00312 {
00313 int indexVersion;
00314 assert(mIndexStream != 0);
00315 mIndexSwapByteOrder = false;
00316 mIndexSizeOfLong = sizeof(long);
00317
00318 int ret = fscanf(mIndexStream, "# KMail-Index V%d\n", &indexVersion);
00319 if ( ret == EOF || ret == 0 )
00320 return false;
00321 if(gv)
00322 *gv = indexVersion;
00323 if (indexVersion < 1505 ) {
00324 if(indexVersion == 1503) {
00325 kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl;
00326 mConvertToUtf8 = true;
00327 }
00328 return true;
00329 } else if (indexVersion == 1505) {
00330 } else if (indexVersion < INDEX_VERSION) {
00331 kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl;
00332 createIndexFromContents();
00333 return false;
00334 } else if(indexVersion > INDEX_VERSION) {
00335 kapp->setOverrideCursor(KCursor::arrowCursor());
00336 int r = KMessageBox::questionYesNo(0,
00337 i18n(
00338 "The mail index for '%1' is from an unknown version of KMail (%2).\n"
00339 "This index can be regenerated from your mail folder, but some "
00340 "information, including status flags, may be lost. Do you wish "
00341 "to downgrade your index file?") .arg(name()) .arg(indexVersion), QString::null, i18n("Downgrade"), i18n("Do Not Downgrade") );
00342 kapp->restoreOverrideCursor();
00343 if (r == KMessageBox::Yes)
00344 createIndexFromContents();
00345 return false;
00346 }
00347 else {
00348
00349 Q_UINT32 byteOrder = 0;
00350 Q_UINT32 sizeOfLong = sizeof(long);
00351
00352 Q_UINT32 header_length = 0;
00353 fseek(mIndexStream, sizeof(char), SEEK_CUR );
00354 fread(&header_length, sizeof(header_length), 1, mIndexStream);
00355 if (header_length > 0xFFFF)
00356 header_length = kmail_swap_32(header_length);
00357
00358 off_t endOfHeader = ftell(mIndexStream) + header_length;
00359
00360 bool needs_update = true;
00361
00362 if (header_length >= sizeof(byteOrder))
00363 {
00364 fread(&byteOrder, sizeof(byteOrder), 1, mIndexStream);
00365 mIndexSwapByteOrder = (byteOrder == 0x78563412);
00366 header_length -= sizeof(byteOrder);
00367
00368 if (header_length >= sizeof(sizeOfLong))
00369 {
00370 fread(&sizeOfLong, sizeof(sizeOfLong), 1, mIndexStream);
00371 if (mIndexSwapByteOrder)
00372 sizeOfLong = kmail_swap_32(sizeOfLong);
00373 mIndexSizeOfLong = sizeOfLong;
00374 header_length -= sizeof(sizeOfLong);
00375 needs_update = false;
00376 }
00377 }
00378 if (needs_update || mIndexSwapByteOrder || (mIndexSizeOfLong != sizeof(long)))
00379 setDirty( true );
00380
00381 fseek(mIndexStream, endOfHeader, SEEK_SET );
00382
00383 if (mIndexSwapByteOrder)
00384 kdDebug(5006) << "Index File has byte order swapped!" << endl;
00385 if (mIndexSizeOfLong != sizeof(long))
00386 kdDebug(5006) << "Index File sizeOfLong is " << mIndexSizeOfLong << " while sizeof(long) is " << sizeof(long) << " !" << endl;
00387
00388 }
00389 return true;
00390 }
00391
00392
00393 #ifdef HAVE_MMAP
00394 bool KMFolderIndex::updateIndexStreamPtr(bool just_close)
00395 #else
00396 bool KMFolderIndex::updateIndexStreamPtr(bool)
00397 #endif
00398 {
00399
00400
00401 utime(QFile::encodeName(location()), 0);
00402 utime(QFile::encodeName(indexLocation()), 0);
00403 utime(QFile::encodeName( KMMsgDict::getFolderIdsLocation( *this ) ), 0);
00404
00405 mIndexSwapByteOrder = false;
00406 #ifdef HAVE_MMAP
00407 if(just_close) {
00408 if(mIndexStreamPtr)
00409 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00410 mIndexStreamPtr = 0;
00411 mIndexStreamPtrLength = 0;
00412 return true;
00413 }
00414
00415 assert(mIndexStream);
00416 struct stat stat_buf;
00417 if(fstat(fileno(mIndexStream), &stat_buf) == -1) {
00418 if(mIndexStreamPtr)
00419 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00420 mIndexStreamPtr = 0;
00421 mIndexStreamPtrLength = 0;
00422 return false;
00423 }
00424 if(mIndexStreamPtr)
00425 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00426 mIndexStreamPtrLength = stat_buf.st_size;
00427 mIndexStreamPtr = (uchar *)mmap(0, mIndexStreamPtrLength, PROT_READ, MAP_SHARED,
00428 fileno(mIndexStream), 0);
00429 if(mIndexStreamPtr == MAP_FAILED) {
00430 mIndexStreamPtr = 0;
00431 mIndexStreamPtrLength = 0;
00432 return false;
00433 }
00434 #endif
00435 return true;
00436 }
00437
00438
00439 KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
00440 {
00441 QFileInfo contInfo(location());
00442 QFileInfo indInfo(indexLocation());
00443
00444 if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00445 if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00446
00447 return ( contInfo.lastModified() > indInfo.lastModified() )
00448 ? KMFolderIndex::IndexTooOld
00449 : KMFolderIndex::IndexOk;
00450 }
00451
00452 void KMFolderIndex::clearIndex(bool autoDelete, bool syncDict)
00453 {
00454 mMsgList.clear(autoDelete, syncDict);
00455 }
00456
00457
00458 void KMFolderIndex::truncateIndex()
00459 {
00460 if ( mHeaderOffset )
00461 truncate(QFile::encodeName(indexLocation()), mHeaderOffset);
00462 else
00463
00464
00465 writeIndex( true );
00466 }
00467
00468 void KMFolderIndex::fillMessageDict()
00469 {
00470 open("fillDict");
00471 for (unsigned int idx = 0; idx < mMsgList.high(); idx++)
00472 if ( mMsgList.at( idx ) )
00473 KMMsgDict::mutableInstance()->insert(0, mMsgList.at( idx ), idx);
00474 close("fillDict");
00475 }
00476
00477
00478 KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
00479 {
00480 KMMsgInfo *msgInfo = msg->msgInfo();
00481 if ( !msgInfo )
00482 msgInfo = new KMMsgInfo( folder() );
00483
00484 *msgInfo = *msg;
00485 mMsgList.set( idx, msgInfo );
00486 msg->setMsgInfo( 0 );
00487 delete msg;
00488 return msgInfo;
00489 }
00490
00491 void KMFolderIndex::recreateIndex()
00492 {
00493 kapp->setOverrideCursor(KCursor::arrowCursor());
00494 KMessageBox::error(0,
00495 i18n("The mail index for '%1' is corrupted and will be regenerated now, "
00496 "but some information, including status flags, will be lost.").arg(name()));
00497 kapp->restoreOverrideCursor();
00498 createIndexFromContents();
00499 readIndex();
00500 }
00501
00502
00503 #include "kmfolderindex.moc"