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::msgDictSizeHint();
00138 lastSizeOfDict = ( lastSizeOfDict * 11 ) / 10;
00139 GlobalSettings::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::setMsgDictSizeHint( GlobalSettings::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 }