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