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 }