00001
00002
00003
00004 #include "kmfolderindex.h"
00005 #include "kmfolder.h"
00006 #include "kmmsgdict.h"
00007 #include "kmdict.h"
00008 #include "globalsettings.h"
00009
00010 #include <qfileinfo.h>
00011
00012 #include <kdebug.h>
00013
00014 #include <stdio.h>
00015 #include <unistd.h>
00016
00017 #include <errno.h>
00018
00019 #include <config.h>
00020
00021 #ifdef HAVE_BYTESWAP_H
00022 #include <byteswap.h>
00023 #endif
00024
00025
00026
00027
00028
00029 #ifdef bswap_32
00030 #define kmail_swap_32(x) bswap_32(x)
00031 #else
00032 #define kmail_swap_32(x) \
00033 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00034 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00035 #endif
00036
00037
00038
00039
00040
00041 #define IDS_VERSION 1002
00042
00043
00044 #define IDS_HEADER "# KMail-Index-IDs V%d\n*"
00045
00046
00047
00048 class KMMsgDictEntry : public KMDictItem
00049 {
00050 public:
00051 KMMsgDictEntry(const KMFolder *aFolder, int aIndex)
00052 { folder = aFolder; index = aIndex; }
00053
00054 const KMFolder *folder;
00055 int index;
00056 };
00057
00058
00059
00060 class KMMsgDictREntry
00061 {
00062 public:
00063 KMMsgDictREntry(int size = 0)
00064 {
00065 array.resize(size);
00066 for (int i = 0; i < size; i++)
00067 array.at(i) = 0;
00068 fp = 0;
00069 swapByteOrder = false;
00070 baseOffset = 0;
00071 }
00072
00073 ~KMMsgDictREntry()
00074 {
00075 array.resize(0);
00076 if (fp)
00077 fclose(fp);
00078 }
00079
00080 void set(int index, KMMsgDictEntry *entry)
00081 {
00082 if (index >= 0) {
00083 int size = array.size();
00084 if (index >= size) {
00085 int newsize = QMAX(size + 25, index + 1);
00086 array.resize(newsize);
00087 for (int j = size; j < newsize; j++)
00088 array.at(j) = 0;
00089 }
00090 array.at(index) = entry;
00091 }
00092 }
00093
00094 KMMsgDictEntry *get(int index)
00095 {
00096 if (index >= 0 && (unsigned)index < array.size())
00097 return array.at(index);
00098 return 0;
00099 }
00100
00101 ulong getMsn(int index)
00102 {
00103 KMMsgDictEntry *entry = get(index);
00104 if (entry)
00105 return entry->key;
00106 return 0;
00107 }
00108
00109 int getRealSize()
00110 {
00111 int count = array.size() - 1;
00112 while (count >= 0) {
00113 if (array.at(count))
00114 break;
00115 count--;
00116 }
00117 return count + 1;
00118 }
00119
00120 void sync()
00121 {
00122 fflush(fp);
00123 }
00124
00125 public:
00126 QMemArray<KMMsgDictEntry *> array;
00127 FILE *fp;
00128 bool swapByteOrder;
00129 off_t baseOffset;
00130 };
00131
00132
00133
00134
00135 KMMsgDict::KMMsgDict()
00136 {
00137 int lastSizeOfDict = GlobalSettings::self()->msgDictSizeHint();
00138 lastSizeOfDict = ( lastSizeOfDict * 11 ) / 10;
00139 GlobalSettings::self()->setMsgDictSizeHint( 0 );
00140 dict = new KMDict( lastSizeOfDict );
00141 nextMsgSerNum = 1;
00142 }
00143
00144
00145
00146 KMMsgDict::~KMMsgDict()
00147 {
00148 delete dict;
00149 }
00150
00151
00152
00153 unsigned long KMMsgDict::getNextMsgSerNum() {
00154 unsigned long msn = nextMsgSerNum;
00155 nextMsgSerNum++;
00156 return msn;
00157 }
00158
00159 void KMMsgDict::deleteRentry(KMMsgDictREntry *entry)
00160 {
00161 delete entry;
00162 }
00163
00164
00165 unsigned long KMMsgDict::insert(unsigned long msn, const KMMessage * msg, int idx ) {
00166 return insert( msn, &msg->toMsgBase(), idx );
00167 }
00168
00169
00170 unsigned long KMMsgDict::insert(unsigned long msgSerNum,
00171 const KMMsgBase *msg, int index)
00172 {
00173 unsigned long msn = msgSerNum;
00174 if (!msn) {
00175 msn = getNextMsgSerNum();
00176 } else {
00177 if (msn >= nextMsgSerNum)
00178 nextMsgSerNum = msn + 1;
00179 }
00180
00181 KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() );
00182 if (folder && index == -1)
00183 index = folder->find(msg);
00184
00185
00186 while (dict->find((long)msn)) {
00187 msn = getNextMsgSerNum();
00188 folder->setDirty( true );
00189 }
00190
00191
00192
00193 KMMsgDictEntry *entry = new KMMsgDictEntry(folder->folder(), index);
00194 dict->insert((long)msn, entry);
00195
00196 KMMsgDictREntry *rentry = folder->rDict();
00197 if (!rentry) {
00198 rentry = new KMMsgDictREntry();
00199 folder->setRDict(rentry);
00200 }
00201 rentry->set(index, entry);
00202
00203 return msn;
00204 }
00205
00206 unsigned long KMMsgDict::insert(const KMMsgBase *msg, int index)
00207 {
00208 unsigned long msn = msg->getMsgSerNum();
00209 return insert(msn, msg, index);
00210 }
00211
00212
00213
00214 void KMMsgDict::remove(unsigned long msgSerNum)
00215 {
00216 long key = (long)msgSerNum;
00217 KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find(key);
00218 if (!entry)
00219 return;
00220
00221 if (entry->folder) {
00222 KMMsgDictREntry *rentry = entry->folder->rDict();
00223 if (rentry)
00224 rentry->set(entry->index, 0);
00225 }
00226
00227 dict->remove((long)key);
00228 }
00229
00230 unsigned long KMMsgDict::remove(const KMMsgBase *msg)
00231 {
00232 unsigned long msn = msg->getMsgSerNum();
00233 remove(msn);
00234 return msn;
00235 }
00236
00237
00238
00239 void KMMsgDict::update(const KMMsgBase *msg, int index, int newIndex)
00240 {
00241 KMMsgDictREntry *rentry = msg->parent()->rDict();
00242 if (rentry) {
00243 KMMsgDictEntry *entry = rentry->get(index);
00244 if (entry) {
00245 entry->index = newIndex;
00246 rentry->set(index, 0);
00247 rentry->set(newIndex, entry);
00248 }
00249 }
00250 }
00251
00252
00253
00254 void KMMsgDict::getLocation(unsigned long key,
00255 KMFolder **retFolder, int *retIndex)
00256 {
00257 KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find((long)key);
00258 if (entry) {
00259 *retFolder = (KMFolder *)entry->folder;
00260 *retIndex = entry->index;
00261 } else {
00262 *retFolder = 0;
00263 *retIndex = -1;
00264 }
00265 }
00266
00267 void KMMsgDict::getLocation(const KMMsgBase *msg,
00268 KMFolder **retFolder, int *retIndex)
00269 {
00270 getLocation(msg->getMsgSerNum(), retFolder, retIndex);
00271 }
00272
00273 void KMMsgDict::getLocation( const KMMessage * msg, KMFolder * *retFolder, int * retIndex ) {
00274 getLocation( msg->toMsgBase().getMsgSerNum(), retFolder, retIndex );
00275 }
00276
00277
00278
00279 unsigned long KMMsgDict::getMsgSerNum(KMFolder *folder, int index)
00280 {
00281 unsigned long msn = 0;
00282 KMMsgDictREntry *rentry = folder->rDict();
00283 if (rentry)
00284 msn = rentry->getMsn(index);
00285 return msn;
00286 }
00287
00288
00289
00290 QString KMMsgDict::getFolderIdsLocation(const KMFolder *folder)
00291 {
00292 return folder->indexLocation() + ".ids";
00293 }
00294
00295
00296
00297 bool KMMsgDict::isFolderIdsOutdated(const KMFolder *folder)
00298 {
00299 bool outdated = false;
00300
00301 QFileInfo indexInfo(folder->indexLocation());
00302 QFileInfo idsInfo(getFolderIdsLocation(folder));
00303
00304 if (!indexInfo.exists() || !idsInfo.exists())
00305 outdated = true;
00306 if (indexInfo.lastModified() > idsInfo.lastModified())
00307 outdated = true;
00308
00309 return outdated;
00310 }
00311
00312
00313
00314 int KMMsgDict::readFolderIds(KMFolder *folder)
00315 {
00316 if (isFolderIdsOutdated(folder))
00317 return -1;
00318
00319 QString filename = getFolderIdsLocation(folder);
00320 FILE *fp = fopen(QFile::encodeName(filename), "r+");
00321 if (!fp)
00322 return -1;
00323
00324 int version = 0;
00325 fscanf(fp, IDS_HEADER, &version);
00326 if (version != IDS_VERSION) {
00327 fclose(fp);
00328 return -1;
00329 }
00330
00331 bool swapByteOrder;
00332 Q_UINT32 byte_order;
00333 if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
00334 fclose(fp);
00335 return -1;
00336 }
00337 swapByteOrder = (byte_order == 0x78563412);
00338
00339 Q_UINT32 count;
00340 if (!fread(&count, sizeof(count), 1, fp)) {
00341 fclose(fp);
00342 return -1;
00343 }
00344 if (swapByteOrder)
00345 count = kmail_swap_32(count);
00346
00347 KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00348
00349 for (unsigned int index = 0; index < count; index++) {
00350 Q_UINT32 msn;
00351
00352 bool readOk = fread(&msn, sizeof(msn), 1, fp);
00353 if (swapByteOrder)
00354 msn = kmail_swap_32(msn);
00355
00356 if (!readOk || dict->find(msn)) {
00357 for (unsigned int i = 0; i < index; i++) {
00358 msn = rentry->getMsn(i);
00359 dict->remove((long)msn);
00360 }
00361 delete rentry;
00362 fclose(fp);
00363 return -1;
00364 }
00365
00366
00367
00368
00369
00370
00371 KMMsgDictEntry *entry = new KMMsgDictEntry(folder, index);
00372 dict->insert((long)msn, entry);
00373 if (msn >= nextMsgSerNum)
00374 nextMsgSerNum = msn + 1;
00375
00376 rentry->set(index, entry);
00377 }
00378
00379
00380 GlobalSettings::self()->setMsgDictSizeHint( GlobalSettings::self()->msgDictSizeHint() + count );
00381
00382 fclose(fp);
00383 folder->setRDict(rentry);
00384
00385 return 0;
00386 }
00387
00388
00389
00390 KMMsgDictREntry *KMMsgDict::openFolderIds(KMFolder *folder, bool truncate)
00391 {
00392 KMMsgDictREntry *rentry = folder->rDict();
00393 if (!rentry) {
00394 rentry = new KMMsgDictREntry();
00395 folder->setRDict(rentry);
00396 }
00397
00398 if (!rentry->fp) {
00399 QString filename = getFolderIdsLocation(folder);
00400 FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+");
00401 if (fp)
00402 {
00403 int version = 0;
00404 fscanf(fp, IDS_HEADER, &version);
00405 if (version == IDS_VERSION)
00406 {
00407 Q_UINT32 byte_order = 0;
00408 fread(&byte_order, sizeof(byte_order), 1, fp);
00409 rentry->swapByteOrder = (byte_order == 0x78563412);
00410 }
00411 else
00412 {
00413 fclose(fp);
00414 fp = 0;
00415 }
00416 }
00417
00418 if (!fp)
00419 {
00420 fp = fopen(QFile::encodeName(filename), "w+");
00421 if (!fp)
00422 {
00423 kdDebug(5006) << "Dict '" << filename
00424 << "' cannot open with folder " << folder->label() << ": "
00425 << strerror(errno) << " (" << errno << ")" << endl;
00426 delete rentry;
00427 rentry = 0;
00428 return 0;
00429 }
00430 fprintf(fp, IDS_HEADER, IDS_VERSION);
00431 Q_UINT32 byteOrder = 0x12345678;
00432 fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00433 rentry->swapByteOrder = false;
00434 }
00435 rentry->baseOffset = ftell(fp);
00436 rentry->fp = fp;
00437 }
00438
00439 return rentry;
00440 }
00441
00442
00443
00444 int KMMsgDict::writeFolderIds(KMFolder *folder)
00445 {
00446 KMMsgDictREntry *rentry = openFolderIds(folder, true);
00447 if (!rentry)
00448 return 0;
00449 FILE *fp = rentry->fp;
00450
00451 fseek(fp, rentry->baseOffset, SEEK_SET);
00452
00453 Q_UINT32 count = rentry->getRealSize();
00454 if (!fwrite(&count, sizeof(count), 1, fp)) {
00455 kdDebug(5006) << "Dict cannot write count with folder " << folder->label() << ": "
00456 << strerror(errno) << " (" << errno << ")" << endl;
00457 return -1;
00458 }
00459
00460 for (unsigned int index = 0; index < count; index++) {
00461 Q_UINT32 msn = rentry->getMsn(index);
00462 if (!fwrite(&msn, sizeof(msn), 1, fp))
00463 return -1;
00464 }
00465
00466 rentry->sync();
00467
00468 off_t eof = ftell(fp);
00469 QString filename = getFolderIdsLocation(folder);
00470 truncate(QFile::encodeName(filename), eof);
00471 fclose(rentry->fp);
00472 rentry->fp = 0;
00473
00474 return 0;
00475 }
00476
00477
00478
00479 int KMMsgDict::touchFolderIds(KMFolder *folder)
00480 {
00481 KMMsgDictREntry *rentry = openFolderIds(folder, false);
00482 if (rentry) {
00483 rentry->sync();
00484 fclose(rentry->fp);
00485 rentry->fp = 0;
00486 }
00487 return 0;
00488 }
00489
00490
00491
00492 int KMMsgDict::appendtoFolderIds(KMFolder *folder, int index)
00493 {
00494 KMMsgDictREntry *rentry = openFolderIds(folder, false);
00495 if (!rentry)
00496 return 0;
00497 FILE *fp = rentry->fp;
00498
00499
00500
00501 fseek(fp, rentry->baseOffset, SEEK_SET);
00502 Q_UINT32 count;
00503 if (!fread(&count, sizeof(count), 1, fp)) {
00504 kdDebug(5006) << "Dict cannot read count for folder " << folder->label() << ": "
00505 << strerror(errno) << " (" << errno << ")" << endl;
00506 return 0;
00507 }
00508 if (rentry->swapByteOrder)
00509 count = kmail_swap_32(count);
00510
00511 count++;
00512
00513 if (rentry->swapByteOrder)
00514 count = kmail_swap_32(count);
00515 fseek(fp, rentry->baseOffset, SEEK_SET);
00516 if (!fwrite(&count, sizeof(count), 1, fp)) {
00517 kdDebug(5006) << "Dict cannot write count for folder " << folder->label() << ": "
00518 << strerror(errno) << " (" << errno << ")" << endl;
00519 return 0;
00520 }
00521
00522 long ofs = (count - 1) * sizeof(ulong);
00523 if (ofs > 0)
00524 fseek(fp, ofs, SEEK_CUR);
00525
00526 Q_UINT32 msn = rentry->getMsn(index);
00527 if (rentry->swapByteOrder)
00528 msn = kmail_swap_32(msn);
00529 if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00530 kdDebug(5006) << "Dict cannot write count for folder " << folder->label() << ": "
00531 << strerror(errno) << " (" << errno << ")" << endl;
00532 return 0;
00533 }
00534
00535 rentry->sync();
00536 fclose(rentry->fp);
00537 rentry->fp = 0;
00538
00539 return 0;
00540 }
00541
00542
00543
00544 bool KMMsgDict::hasFolderIds(const KMFolder *folder)
00545 {
00546 return folder->rDict() != 0;
00547 }
00548
00549
00550
00551 bool KMMsgDict::removeFolderIds(KMFolder *folder)
00552 {
00553 folder->setRDict(0);
00554 QString filename = getFolderIdsLocation(folder);
00555 return unlink(QFile::encodeName(filename));
00556 }