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"