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 <tqfileinfo.h>
00012
00013 #include <kdebug.h>
00014 #include <kstaticdeleter.h>
00015
00016 #include <stdio.h>
00017 #include <unistd.h>
00018
00019 #include <string.h>
00020 #include <errno.h>
00021
00022 #include <config.h>
00023
00024 #ifdef HAVE_BYTESWAP_H
00025 #include <byteswap.h>
00026 #endif
00027
00028
00029
00030
00031
00032 #ifdef bswap_32
00033 #define kmail_swap_32(x) bswap_32(x)
00034 #else
00035 #define kmail_swap_32(x) \
00036 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00037 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00038 #endif
00039
00040
00041
00042
00043
00044 #define IDS_VERSION 1002
00045
00046
00047 #define IDS_HEADER "# KMail-Index-IDs V%d\n*"
00048
00053 class KMMsgDictEntry : public KMDictItem
00054 {
00055 public:
00056 KMMsgDictEntry(const KMFolder *aFolder, int aIndex)
00057 : folder( aFolder ), index( aIndex )
00058 {}
00059
00060 const KMFolder *folder;
00061 int index;
00062 };
00063
00071 class KMMsgDictREntry
00072 {
00073 public:
00074 KMMsgDictREntry(int size = 0)
00075 {
00076 array.resize(size);
00077 memset(array.data(), 0, array.size() * sizeof(KMMsgDictEntry *));
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 = TQMAX(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 TQMemArray<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
00359 TQValueList<unsigned long> KMMsgDict::serNumList(TQPtrList<KMMsgBase> msgList)
00360 {
00361 TQValueList<unsigned long> ret;
00362 for ( unsigned int i = 0; i < msgList.count(); i++ ) {
00363 unsigned long serNum = msgList.at(i)->getMsgSerNum();
00364 assert( serNum );
00365 ret.append( serNum );
00366 }
00367 return ret;
00368 }
00369
00370
00371
00372 TQString KMMsgDict::getFolderIdsLocation( const FolderStorage &storage )
00373 {
00374 return storage.indexLocation() + ".ids";
00375 }
00376
00377
00378
00379 bool KMMsgDict::isFolderIdsOutdated( const FolderStorage &storage )
00380 {
00381 bool outdated = false;
00382
00383 TQFileInfo indexInfo( storage.indexLocation() );
00384 TQFileInfo idsInfo( getFolderIdsLocation( storage ) );
00385
00386 if (!indexInfo.exists() || !idsInfo.exists())
00387 outdated = true;
00388 if (indexInfo.lastModified() > idsInfo.lastModified())
00389 outdated = true;
00390
00391 return outdated;
00392 }
00393
00394
00395
00396 int KMMsgDict::readFolderIds( FolderStorage& storage )
00397 {
00398 if ( isFolderIdsOutdated( storage ) )
00399 return -1;
00400
00401 TQString filename = getFolderIdsLocation( storage );
00402 FILE *fp = fopen(TQFile::encodeName(filename), "r+");
00403 if (!fp)
00404 return -1;
00405
00406 int version = 0;
00407 fscanf(fp, IDS_HEADER, &version);
00408 if (version != IDS_VERSION) {
00409 fclose(fp);
00410 return -1;
00411 }
00412
00413 bool swapByteOrder;
00414 TQ_UINT32 byte_order;
00415 if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
00416 fclose(fp);
00417 return -1;
00418 }
00419 swapByteOrder = (byte_order == 0x78563412);
00420
00421 TQ_UINT32 count;
00422 if (!fread(&count, sizeof(count), 1, fp)) {
00423 fclose(fp);
00424 return -1;
00425 }
00426 if (swapByteOrder)
00427 count = kmail_swap_32(count);
00428
00429
00430
00431 long pos = ftell(fp);
00432 fseek(fp, 0, SEEK_END);
00433 long fileSize = ftell(fp);
00434 fseek(fp, pos, SEEK_SET);
00435
00436
00437 if ( (fileSize - pos) < (long)(count * sizeof(TQ_UINT32)) ) {
00438 fclose(fp);
00439 return -1;
00440 }
00441
00442 KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00443
00444 for (unsigned int index = 0; index < count; index++) {
00445 TQ_UINT32 msn;
00446
00447 bool readOk = fread(&msn, sizeof(msn), 1, fp);
00448 if (swapByteOrder)
00449 msn = kmail_swap_32(msn);
00450
00451 if (!readOk || dict->find(msn)) {
00452 for (unsigned int i = 0; i < index; i++) {
00453 msn = rentry->getMsn(i);
00454 dict->remove((long)msn);
00455 }
00456 delete rentry;
00457 fclose(fp);
00458 return -1;
00459 }
00460
00461
00462
00463
00464 if ( msn == 0 ) {
00465 kdWarning(5006) << "readFolderIds(): Found serial number zero at index " << index
00466 << " in folder " << filename << endl;
00467 msn = getNextMsgSerNum();
00468 Q_ASSERT( msn != 0 );
00469 }
00470
00471
00472
00473 KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index);
00474 dict->insert((long)msn, entry);
00475 if (msn >= nextMsgSerNum)
00476 nextMsgSerNum = msn + 1;
00477
00478 rentry->set(index, entry);
00479 }
00480
00481
00482 GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count );
00483
00484 fclose(fp);
00485 storage.setRDict(rentry);
00486
00487 return 0;
00488 }
00489
00490
00491
00492 KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate)
00493 {
00494 KMMsgDictREntry *rentry = storage.rDict();
00495 if (!rentry) {
00496 rentry = new KMMsgDictREntry();
00497 storage.setRDict(rentry);
00498 }
00499
00500 if (!rentry->fp) {
00501 TQString filename = getFolderIdsLocation( storage );
00502 FILE *fp = truncate ? 0 : fopen(TQFile::encodeName(filename), "r+");
00503 if (fp)
00504 {
00505 int version = 0;
00506 fscanf(fp, IDS_HEADER, &version);
00507 if (version == IDS_VERSION)
00508 {
00509 TQ_UINT32 byte_order = 0;
00510 fread(&byte_order, sizeof(byte_order), 1, fp);
00511 rentry->swapByteOrder = (byte_order == 0x78563412);
00512 }
00513 else
00514 {
00515 fclose(fp);
00516 fp = 0;
00517 }
00518 }
00519
00520 if (!fp)
00521 {
00522 fp = fopen(TQFile::encodeName(filename), "w+");
00523 if (!fp)
00524 {
00525 kdDebug(5006) << "Dict '" << filename
00526 << "' cannot open with folder " << storage.label() << ": "
00527 << strerror(errno) << " (" << errno << ")" << endl;
00528 delete rentry;
00529 rentry = 0;
00530 return 0;
00531 }
00532 fprintf(fp, IDS_HEADER, IDS_VERSION);
00533 TQ_UINT32 byteOrder = 0x12345678;
00534 fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00535 rentry->swapByteOrder = false;
00536 }
00537 rentry->baseOffset = ftell(fp);
00538 rentry->fp = fp;
00539 }
00540
00541 return rentry;
00542 }
00543
00544
00545
00546 int KMMsgDict::writeFolderIds( const FolderStorage &storage )
00547 {
00548 KMMsgDictREntry *rentry = openFolderIds( storage, true );
00549 if (!rentry)
00550 return 0;
00551 FILE *fp = rentry->fp;
00552
00553 fseek(fp, rentry->baseOffset, SEEK_SET);
00554
00555 TQ_UINT32 count = rentry->getRealSize();
00556 if (!fwrite(&count, sizeof(count), 1, fp)) {
00557 kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": "
00558 << strerror(errno) << " (" << errno << ")" << endl;
00559 return -1;
00560 }
00561
00562 for (unsigned int index = 0; index < count; index++) {
00563 TQ_UINT32 msn = rentry->getMsn(index);
00564 if (!fwrite(&msn, sizeof(msn), 1, fp))
00565 return -1;
00566 if ( msn == 0 ) {
00567 kdWarning(5006) << "writeFolderIds(): Serial number of message at index "
00568 << index << " is zero in folder " << storage.label() << endl;
00569 }
00570 }
00571
00572 rentry->sync();
00573
00574 off_t eof = ftell(fp);
00575 TQString filename = getFolderIdsLocation( storage );
00576 truncate(TQFile::encodeName(filename), eof);
00577 fclose(rentry->fp);
00578 rentry->fp = 0;
00579
00580 return 0;
00581 }
00582
00583
00584
00585 int KMMsgDict::touchFolderIds( const FolderStorage &storage )
00586 {
00587 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00588 if (rentry) {
00589 rentry->sync();
00590 fclose(rentry->fp);
00591 rentry->fp = 0;
00592 }
00593 return 0;
00594 }
00595
00596
00597
00598 int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index)
00599 {
00600 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00601 if (!rentry)
00602 return 0;
00603 FILE *fp = rentry->fp;
00604
00605
00606
00607 fseek(fp, rentry->baseOffset, SEEK_SET);
00608 TQ_UINT32 count;
00609 if (!fread(&count, sizeof(count), 1, fp)) {
00610 kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": "
00611 << strerror(errno) << " (" << errno << ")" << endl;
00612 return 0;
00613 }
00614 if (rentry->swapByteOrder)
00615 count = kmail_swap_32(count);
00616
00617 count++;
00618
00619 if (rentry->swapByteOrder)
00620 count = kmail_swap_32(count);
00621 fseek(fp, rentry->baseOffset, SEEK_SET);
00622 if (!fwrite(&count, sizeof(count), 1, fp)) {
00623 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00624 << strerror(errno) << " (" << errno << ")" << endl;
00625 return 0;
00626 }
00627
00628 long ofs = (count - 1) * sizeof(ulong);
00629 if (ofs > 0)
00630 fseek(fp, ofs, SEEK_CUR);
00631
00632 TQ_UINT32 msn = rentry->getMsn(index);
00633 if (rentry->swapByteOrder)
00634 msn = kmail_swap_32(msn);
00635 if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00636 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00637 << strerror(errno) << " (" << errno << ")" << endl;
00638 return 0;
00639 }
00640
00641 rentry->sync();
00642 fclose(rentry->fp);
00643 rentry->fp = 0;
00644
00645 return 0;
00646 }
00647
00648
00649
00650 bool KMMsgDict::hasFolderIds( const FolderStorage& storage )
00651 {
00652 return storage.rDict() != 0;
00653 }
00654
00655
00656
00657 bool KMMsgDict::removeFolderIds( FolderStorage& storage )
00658 {
00659 storage.setRDict( 0 );
00660 TQString filename = getFolderIdsLocation( storage );
00661 return unlink( TQFile::encodeName( filename) );
00662 }