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