00001
00002
00003
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
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
00045
00046
00047
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) {
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
00120
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
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
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+");
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;
00252 setDirty(
true );
00253 needsCompact =
true;
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;
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
00329 Q_UINT32 byteOrder = 0;
00330 Q_UINT32 sizeOfLong =
sizeof(
long);
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
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
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
00380
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
00444
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"