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