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 dirty = !mMsgList.at(i)->syncIndexString();
00116 if (!dirty) {
00117 touchFolderIdsFile();
00118 return 0;
00119 }
00120 return writeIndex();
00121 }
00122
00123 int KMFolderIndex::writeIndex( bool createEmptyIndex )
00124 {
00125 QString tempName;
00126 QString indexName;
00127 mode_t old_umask;
00128 int len;
00129 const uchar *buffer = 0;
00130
00131 indexName = indexLocation();
00132 tempName = indexName + ".temp";
00133 unlink(QFile::encodeName(tempName));
00134
00135
00136
00137 utime(QFile::encodeName(location()), 0);
00138
00139 old_umask = umask(077);
00140 FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w");
00141 umask(old_umask);
00142 if (!tmpIndexStream)
00143 return errno;
00144
00145 fprintf(tmpIndexStream, "# KMail-Index V%d\n", INDEX_VERSION);
00146
00147
00148 Q_UINT32 byteOrder = 0x12345678;
00149 Q_UINT32 sizeOfLong = sizeof(long);
00150
00151 Q_UINT32 header_length = sizeof(byteOrder)+sizeof(sizeOfLong);
00152 char pad_char = '\0';
00153 fwrite(&pad_char, sizeof(pad_char), 1, tmpIndexStream);
00154 fwrite(&header_length, sizeof(header_length), 1, tmpIndexStream);
00155
00156
00157 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
00158 fwrite(&sizeOfLong, sizeof(sizeOfLong), 1, tmpIndexStream);
00159
00160 off_t nho = ftell(tmpIndexStream);
00161
00162 if ( !createEmptyIndex ) {
00163 KMMsgBase* msgBase;
00164 for (unsigned int i=0; i<mMsgList.high(); i++)
00165 {
00166 if (!(msgBase = mMsgList.at(i))) continue;
00167 buffer = msgBase->asIndexString(len);
00168 fwrite(&len,sizeof(len), 1, tmpIndexStream);
00169
00170 off_t tmp = ftell(tmpIndexStream);
00171 msgBase->setIndexOffset(tmp);
00172 msgBase->setIndexLength(len);
00173 if(fwrite(buffer, len, 1, tmpIndexStream) != 1)
00174 kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
00175 }
00176 }
00177
00178 int fError = ferror( tmpIndexStream );
00179 if( fError != 0 ) {
00180 fclose( tmpIndexStream );
00181 return fError;
00182 }
00183 if( ( fflush( tmpIndexStream ) != 0 )
00184 || ( fsync( fileno( tmpIndexStream ) ) != 0 ) ) {
00185 int errNo = errno;
00186 fclose( tmpIndexStream );
00187 return errNo;
00188 }
00189 if( fclose( tmpIndexStream ) != 0 )
00190 return errno;
00191
00192 ::rename(QFile::encodeName(tempName), QFile::encodeName(indexName));
00193 mHeaderOffset = nho;
00194 if (mIndexStream)
00195 fclose(mIndexStream);
00196
00197 if ( createEmptyIndex )
00198 return 0;
00199
00200 mIndexStream = fopen(QFile::encodeName(indexName), "r+");
00201 assert( mIndexStream );
00202 fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00203
00204 updateIndexStreamPtr();
00205
00206 writeFolderIdsFile();
00207
00208 setDirty( false );
00209 return 0;
00210 }
00211
00212
00213 bool KMFolderIndex::readIndex()
00214 {
00215 Q_INT32 len;
00216 KMMsgInfo* mi;
00217
00218 assert(mIndexStream != 0);
00219 rewind(mIndexStream);
00220
00221 clearIndex();
00222 int version;
00223
00224 setDirty( false );
00225
00226 if (!readIndexHeader(&version)) return false;
00227
00228 mUnreadMsgs = 0;
00229 mTotalMsgs = 0;
00230 mHeaderOffset = ftell(mIndexStream);
00231
00232 clearIndex();
00233 while (!feof(mIndexStream))
00234 {
00235 mi = 0;
00236 if(version >= 1505) {
00237 if(!fread(&len, sizeof(len), 1, mIndexStream))
00238 break;
00239
00240 if (mIndexSwapByteOrder)
00241 len = kmail_swap_32(len);
00242
00243 off_t offs = ftell(mIndexStream);
00244 if(fseek(mIndexStream, len, SEEK_CUR))
00245 break;
00246 mi = new KMMsgInfo(folder(), offs, len);
00247 }
00248 else
00249 {
00250 QCString line(MAX_LINE);
00251 fgets(line.data(), MAX_LINE, mIndexStream);
00252 if (feof(mIndexStream)) break;
00253 if (*line.data() == '\0') {
00254 fclose(mIndexStream);
00255 mIndexStream = 0;
00256 clearIndex();
00257 return false;
00258 }
00259 mi = new KMMsgInfo(folder());
00260 mi->compat_fromOldIndexString(line, mConvertToUtf8);
00261 }
00262 if(!mi)
00263 break;
00264
00265 if (mi->isDeleted())
00266 {
00267 delete mi;
00268 setDirty( true );
00269 needsCompact = true;
00270 continue;
00271 }
00272 #ifdef OBSOLETE
00273 else if (mi->isNew())
00274 {
00275 mi->setStatus(KMMsgStatusUnread);
00276 mi->setDirty(false);
00277 }
00278 #endif
00279 if ((mi->isNew()) || (mi->isUnread()) ||
00280 (folder() == kmkernel->outboxFolder()))
00281 {
00282 ++mUnreadMsgs;
00283 if (mUnreadMsgs == 0) ++mUnreadMsgs;
00284 }
00285 mMsgList.append(mi, false);
00286 }
00287 if( version < 1505)
00288 {
00289 mConvertToUtf8 = false;
00290 setDirty( true );
00291 writeIndex();
00292 }
00293 mTotalMsgs = mMsgList.count();
00294 return true;
00295 }
00296
00297
00298 int KMFolderIndex::count(bool cache) const
00299 {
00300 int res = FolderStorage::count(cache);
00301 if (res == -1)
00302 res = mMsgList.count();
00303 return res;
00304 }
00305
00306
00307 bool KMFolderIndex::readIndexHeader(int *gv)
00308 {
00309 int indexVersion;
00310 assert(mIndexStream != 0);
00311 mIndexSwapByteOrder = false;
00312 mIndexSizeOfLong = sizeof(long);
00313
00314 int ret = fscanf(mIndexStream, "# KMail-Index V%d\n", &indexVersion);
00315 if ( ret == EOF || ret == 0 )
00316 return false;
00317 if(gv)
00318 *gv = indexVersion;
00319 if (indexVersion < 1505 ) {
00320 if(indexVersion == 1503) {
00321 kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl;
00322 mConvertToUtf8 = true;
00323 }
00324 return true;
00325 } else if (indexVersion == 1505) {
00326 } else if (indexVersion < INDEX_VERSION) {
00327 kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl;
00328 createIndexFromContents();
00329 return false;
00330 } else if(indexVersion > INDEX_VERSION) {
00331 kapp->setOverrideCursor(KCursor::arrowCursor());
00332 int r = KMessageBox::questionYesNo(0,
00333 i18n(
00334 "The mail index for '%1' is from an unknown version of KMail (%2).\n"
00335 "This index can be regenerated from your mail folder, but some "
00336 "information, including status flags, may be lost. Do you wish "
00337 "to downgrade your index file?") .arg(name()) .arg(indexVersion), QString::null, i18n("Downgrade"), i18n("Do Not Downgrade") );
00338 kapp->restoreOverrideCursor();
00339 if (r == KMessageBox::Yes)
00340 createIndexFromContents();
00341 return false;
00342 }
00343 else {
00344
00345 Q_UINT32 byteOrder = 0;
00346 Q_UINT32 sizeOfLong = sizeof(long);
00347
00348 Q_UINT32 header_length = 0;
00349 fseek(mIndexStream, sizeof(char), SEEK_CUR );
00350 fread(&header_length, sizeof(header_length), 1, mIndexStream);
00351 if (header_length > 0xFFFF)
00352 header_length = kmail_swap_32(header_length);
00353
00354 off_t endOfHeader = ftell(mIndexStream) + header_length;
00355
00356 bool needs_update = true;
00357
00358 if (header_length >= sizeof(byteOrder))
00359 {
00360 fread(&byteOrder, sizeof(byteOrder), 1, mIndexStream);
00361 mIndexSwapByteOrder = (byteOrder == 0x78563412);
00362 header_length -= sizeof(byteOrder);
00363
00364 if (header_length >= sizeof(sizeOfLong))
00365 {
00366 fread(&sizeOfLong, sizeof(sizeOfLong), 1, mIndexStream);
00367 if (mIndexSwapByteOrder)
00368 sizeOfLong = kmail_swap_32(sizeOfLong);
00369 mIndexSizeOfLong = sizeOfLong;
00370 header_length -= sizeof(sizeOfLong);
00371 needs_update = false;
00372 }
00373 }
00374 if (needs_update || mIndexSwapByteOrder || (mIndexSizeOfLong != sizeof(long)))
00375 setDirty( true );
00376
00377 fseek(mIndexStream, endOfHeader, SEEK_SET );
00378
00379 if (mIndexSwapByteOrder)
00380 kdDebug(5006) << "Index File has byte order swapped!" << endl;
00381 if (mIndexSizeOfLong != sizeof(long))
00382 kdDebug(5006) << "Index File sizeOfLong is " << mIndexSizeOfLong << " while sizeof(long) is " << sizeof(long) << " !" << endl;
00383
00384 }
00385 return true;
00386 }
00387
00388
00389 #ifdef HAVE_MMAP
00390 bool KMFolderIndex::updateIndexStreamPtr(bool just_close)
00391 #else
00392 bool KMFolderIndex::updateIndexStreamPtr(bool)
00393 #endif
00394 {
00395
00396
00397 utime(QFile::encodeName(location()), 0);
00398 utime(QFile::encodeName(indexLocation()), 0);
00399 utime(QFile::encodeName( KMMsgDict::getFolderIdsLocation( *this ) ), 0);
00400
00401 mIndexSwapByteOrder = false;
00402 #ifdef HAVE_MMAP
00403 if(just_close) {
00404 if(mIndexStreamPtr)
00405 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00406 mIndexStreamPtr = 0;
00407 mIndexStreamPtrLength = 0;
00408 return true;
00409 }
00410
00411 assert(mIndexStream);
00412 struct stat stat_buf;
00413 if(fstat(fileno(mIndexStream), &stat_buf) == -1) {
00414 if(mIndexStreamPtr)
00415 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00416 mIndexStreamPtr = 0;
00417 mIndexStreamPtrLength = 0;
00418 return false;
00419 }
00420 if(mIndexStreamPtr)
00421 munmap((char *)mIndexStreamPtr, mIndexStreamPtrLength);
00422 mIndexStreamPtrLength = stat_buf.st_size;
00423 mIndexStreamPtr = (uchar *)mmap(0, mIndexStreamPtrLength, PROT_READ, MAP_SHARED,
00424 fileno(mIndexStream), 0);
00425 if(mIndexStreamPtr == MAP_FAILED) {
00426 mIndexStreamPtr = 0;
00427 mIndexStreamPtrLength = 0;
00428 return false;
00429 }
00430 #endif
00431 return true;
00432 }
00433
00434
00435 KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
00436 {
00437 QFileInfo contInfo(location());
00438 QFileInfo indInfo(indexLocation());
00439
00440 if (!contInfo.exists()) return KMFolderIndex::IndexOk;
00441 if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
00442
00443 return ( contInfo.lastModified() > indInfo.lastModified() )
00444 ? KMFolderIndex::IndexTooOld
00445 : KMFolderIndex::IndexOk;
00446 }
00447
00448 void KMFolderIndex::clearIndex(bool autoDelete, bool syncDict)
00449 {
00450 mMsgList.clear(autoDelete, syncDict);
00451 }
00452
00453
00454 void KMFolderIndex::truncateIndex()
00455 {
00456 if ( mHeaderOffset )
00457 truncate(QFile::encodeName(indexLocation()), mHeaderOffset);
00458 else
00459
00460
00461 writeIndex( true );
00462 }
00463
00464 void KMFolderIndex::fillMessageDict()
00465 {
00466 open("fillDict");
00467 for (unsigned int idx = 0; idx < mMsgList.high(); idx++)
00468 if ( mMsgList.at( idx ) )
00469 KMMsgDict::mutableInstance()->insert(0, mMsgList.at( idx ), idx);
00470 close("fillDict");
00471 }
00472
00473
00474 KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
00475 {
00476 KMMsgInfo *msgInfo = msg->msgInfo();
00477 if ( !msgInfo )
00478 msgInfo = new KMMsgInfo( folder() );
00479
00480 *msgInfo = *msg;
00481 mMsgList.set( idx, msgInfo );
00482 msg->setMsgInfo( 0 );
00483 delete msg;
00484 return msgInfo;
00485 }
00486
00487 void KMFolderIndex::recreateIndex()
00488 {
00489 kapp->setOverrideCursor(KCursor::arrowCursor());
00490 KMessageBox::error(0,
00491 i18n("The mail index for '%1' is corrupted and will be regenerated now, "
00492 "but some information, including status flags, will be lost.").arg(name()));
00493 kapp->restoreOverrideCursor();
00494 createIndexFromContents();
00495 readIndex();
00496 }
00497
00498
00499 #include "kmfolderindex.moc"