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 <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 = 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
00416
00417 long pos = ftell(fp);
00418 fseek(fp, 0, SEEK_END);
00419 long fileSize = ftell(fp);
00420 fseek(fp, pos, SEEK_SET);
00421
00422
00423 if ( (fileSize - pos) < (long)(count * sizeof(Q_UINT32)) ) {
00424 fclose(fp);
00425 return -1;
00426 }
00427
00428 KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00429
00430 for (unsigned int index = 0; index < count; index++) {
00431 Q_UINT32 msn;
00432
00433 bool readOk = fread(&msn, sizeof(msn), 1, fp);
00434 if (swapByteOrder)
00435 msn = kmail_swap_32(msn);
00436
00437 if (!readOk || dict->find(msn)) {
00438 for (unsigned int i = 0; i < index; i++) {
00439 msn = rentry->getMsn(i);
00440 dict->remove((long)msn);
00441 }
00442 delete rentry;
00443 fclose(fp);
00444 return -1;
00445 }
00446
00447
00448
00449
00450
00451
00452 KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index);
00453 dict->insert((long)msn, entry);
00454 if (msn >= nextMsgSerNum)
00455 nextMsgSerNum = msn + 1;
00456
00457 rentry->set(index, entry);
00458 }
00459
00460
00461 GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count );
00462
00463 fclose(fp);
00464 storage.setRDict(rentry);
00465
00466 return 0;
00467 }
00468
00469
00470
00471 KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate)
00472 {
00473 KMMsgDictREntry *rentry = storage.rDict();
00474 if (!rentry) {
00475 rentry = new KMMsgDictREntry();
00476 storage.setRDict(rentry);
00477 }
00478
00479 if (!rentry->fp) {
00480 QString filename = getFolderIdsLocation( storage );
00481 FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+");
00482 if (fp)
00483 {
00484 int version = 0;
00485 fscanf(fp, IDS_HEADER, &version);
00486 if (version == IDS_VERSION)
00487 {
00488 Q_UINT32 byte_order = 0;
00489 fread(&byte_order, sizeof(byte_order), 1, fp);
00490 rentry->swapByteOrder = (byte_order == 0x78563412);
00491 }
00492 else
00493 {
00494 fclose(fp);
00495 fp = 0;
00496 }
00497 }
00498
00499 if (!fp)
00500 {
00501 fp = fopen(QFile::encodeName(filename), "w+");
00502 if (!fp)
00503 {
00504 kdDebug(5006) << "Dict '" << filename
00505 << "' cannot open with folder " << storage.label() << ": "
00506 << strerror(errno) << " (" << errno << ")" << endl;
00507 delete rentry;
00508 rentry = 0;
00509 return 0;
00510 }
00511 fprintf(fp, IDS_HEADER, IDS_VERSION);
00512 Q_UINT32 byteOrder = 0x12345678;
00513 fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00514 rentry->swapByteOrder = false;
00515 }
00516 rentry->baseOffset = ftell(fp);
00517 rentry->fp = fp;
00518 }
00519
00520 return rentry;
00521 }
00522
00523
00524
00525 int KMMsgDict::writeFolderIds( const FolderStorage &storage )
00526 {
00527 KMMsgDictREntry *rentry = openFolderIds( storage, true );
00528 if (!rentry)
00529 return 0;
00530 FILE *fp = rentry->fp;
00531
00532 fseek(fp, rentry->baseOffset, SEEK_SET);
00533
00534 Q_UINT32 count = rentry->getRealSize();
00535 if (!fwrite(&count, sizeof(count), 1, fp)) {
00536 kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": "
00537 << strerror(errno) << " (" << errno << ")" << endl;
00538 return -1;
00539 }
00540
00541 for (unsigned int index = 0; index < count; index++) {
00542 Q_UINT32 msn = rentry->getMsn(index);
00543 if (!fwrite(&msn, sizeof(msn), 1, fp))
00544 return -1;
00545 }
00546
00547 rentry->sync();
00548
00549 off_t eof = ftell(fp);
00550 QString filename = getFolderIdsLocation( storage );
00551 truncate(QFile::encodeName(filename), eof);
00552 fclose(rentry->fp);
00553 rentry->fp = 0;
00554
00555 return 0;
00556 }
00557
00558
00559
00560 int KMMsgDict::touchFolderIds( const FolderStorage &storage )
00561 {
00562 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00563 if (rentry) {
00564 rentry->sync();
00565 fclose(rentry->fp);
00566 rentry->fp = 0;
00567 }
00568 return 0;
00569 }
00570
00571
00572
00573 int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index)
00574 {
00575 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00576 if (!rentry)
00577 return 0;
00578 FILE *fp = rentry->fp;
00579
00580
00581
00582 fseek(fp, rentry->baseOffset, SEEK_SET);
00583 Q_UINT32 count;
00584 if (!fread(&count, sizeof(count), 1, fp)) {
00585 kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": "
00586 << strerror(errno) << " (" << errno << ")" << endl;
00587 return 0;
00588 }
00589 if (rentry->swapByteOrder)
00590 count = kmail_swap_32(count);
00591
00592 count++;
00593
00594 if (rentry->swapByteOrder)
00595 count = kmail_swap_32(count);
00596 fseek(fp, rentry->baseOffset, SEEK_SET);
00597 if (!fwrite(&count, sizeof(count), 1, fp)) {
00598 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00599 << strerror(errno) << " (" << errno << ")" << endl;
00600 return 0;
00601 }
00602
00603 long ofs = (count - 1) * sizeof(ulong);
00604 if (ofs > 0)
00605 fseek(fp, ofs, SEEK_CUR);
00606
00607 Q_UINT32 msn = rentry->getMsn(index);
00608 if (rentry->swapByteOrder)
00609 msn = kmail_swap_32(msn);
00610 if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00611 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00612 << strerror(errno) << " (" << errno << ")" << endl;
00613 return 0;
00614 }
00615
00616 rentry->sync();
00617 fclose(rentry->fp);
00618 rentry->fp = 0;
00619
00620 return 0;
00621 }
00622
00623
00624
00625 bool KMMsgDict::hasFolderIds( const FolderStorage& storage )
00626 {
00627 return storage.rDict() != 0;
00628 }
00629
00630
00631
00632 bool KMMsgDict::removeFolderIds( FolderStorage& storage )
00633 {
00634 storage.setRDict( 0 );
00635 QString filename = getFolderIdsLocation( storage );
00636 return unlink( QFile::encodeName( filename) );
00637 }