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 && index == -1)
00209 index = folder->find(msg);
00210
00211
00212 while (dict->find((long)msn)) {
00213 msn = getNextMsgSerNum();
00214 folder->setDirty( true );
00215 }
00216
00217
00218
00219 KMMsgDictEntry *entry = new KMMsgDictEntry(folder->folder(), index);
00220 dict->insert((long)msn, entry);
00221
00222 KMMsgDictREntry *rentry = folder->rDict();
00223 if (!rentry) {
00224 rentry = new KMMsgDictREntry();
00225 folder->setRDict(rentry);
00226 }
00227 rentry->set(index, entry);
00228
00229 return msn;
00230 }
00231
00232 unsigned long KMMsgDict::insert(const KMMsgBase *msg, int index)
00233 {
00234 unsigned long msn = msg->getMsgSerNum();
00235 return insert(msn, msg, index);
00236 }
00237
00238
00239
00240 void KMMsgDict::replace(unsigned long msgSerNum,
00241 const KMMsgBase *msg, int index)
00242 {
00243 KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() );
00244 if ( folder && index == -1 )
00245 index = folder->find( msg );
00246
00247 remove( msgSerNum );
00248 KMMsgDictEntry *entry = new KMMsgDictEntry( folder->folder(), index );
00249 dict->insert( (long)msgSerNum, entry );
00250
00251 KMMsgDictREntry *rentry = folder->rDict();
00252 if (!rentry) {
00253 rentry = new KMMsgDictREntry();
00254 folder->setRDict(rentry);
00255 }
00256 rentry->set(index, entry);
00257 }
00258
00259
00260
00261 void KMMsgDict::remove(unsigned long msgSerNum)
00262 {
00263 long key = (long)msgSerNum;
00264 KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find(key);
00265 if (!entry)
00266 return;
00267
00268 if (entry->folder) {
00269 KMMsgDictREntry *rentry = entry->folder->storage()->rDict();
00270 if (rentry)
00271 rentry->set(entry->index, 0);
00272 }
00273
00274 dict->remove((long)key);
00275 }
00276
00277 unsigned long KMMsgDict::remove(const KMMsgBase *msg)
00278 {
00279 unsigned long msn = msg->getMsgSerNum();
00280 remove(msn);
00281 return msn;
00282 }
00283
00284
00285
00286 void KMMsgDict::update(const KMMsgBase *msg, int index, int newIndex)
00287 {
00288 KMMsgDictREntry *rentry = msg->parent()->storage()->rDict();
00289 if (rentry) {
00290 KMMsgDictEntry *entry = rentry->get(index);
00291 if (entry) {
00292 entry->index = newIndex;
00293 rentry->set(index, 0);
00294 rentry->set(newIndex, entry);
00295 }
00296 }
00297 }
00298
00299
00300
00301 void KMMsgDict::getLocation(unsigned long key,
00302 KMFolder **retFolder, int *retIndex) const
00303 {
00304 KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find((long)key);
00305 if (entry) {
00306 *retFolder = (KMFolder *)entry->folder;
00307 *retIndex = entry->index;
00308 } else {
00309 *retFolder = 0;
00310 *retIndex = -1;
00311 }
00312 }
00313
00314 void KMMsgDict::getLocation(const KMMsgBase *msg,
00315 KMFolder **retFolder, int *retIndex) const
00316 {
00317 getLocation(msg->getMsgSerNum(), retFolder, retIndex);
00318 }
00319
00320 void KMMsgDict::getLocation( const KMMessage * msg, KMFolder * *retFolder, int * retIndex ) const
00321 {
00322 getLocation( msg->toMsgBase().getMsgSerNum(), retFolder, retIndex );
00323 }
00324
00325
00326
00327 unsigned long KMMsgDict::getMsgSerNum(KMFolder *folder, int index) const
00328 {
00329 unsigned long msn = 0;
00330 KMMsgDictREntry *rentry = folder->storage()->rDict();
00331 if (rentry)
00332 msn = rentry->getMsn(index);
00333 return msn;
00334 }
00335
00336
00337
00338 QString KMMsgDict::getFolderIdsLocation( const FolderStorage &storage )
00339 {
00340 return storage.indexLocation() + ".ids";
00341 }
00342
00343
00344
00345 bool KMMsgDict::isFolderIdsOutdated( const FolderStorage &storage )
00346 {
00347 bool outdated = false;
00348
00349 QFileInfo indexInfo( storage.indexLocation() );
00350 QFileInfo idsInfo( getFolderIdsLocation( storage ) );
00351
00352 if (!indexInfo.exists() || !idsInfo.exists())
00353 outdated = true;
00354 if (indexInfo.lastModified() > idsInfo.lastModified())
00355 outdated = true;
00356
00357 return outdated;
00358 }
00359
00360
00361
00362 int KMMsgDict::readFolderIds( FolderStorage& storage )
00363 {
00364 if ( isFolderIdsOutdated( storage ) )
00365 return -1;
00366
00367 QString filename = getFolderIdsLocation( storage );
00368 FILE *fp = fopen(QFile::encodeName(filename), "r+");
00369 if (!fp)
00370 return -1;
00371
00372 int version = 0;
00373 fscanf(fp, IDS_HEADER, &version);
00374 if (version != IDS_VERSION) {
00375 fclose(fp);
00376 return -1;
00377 }
00378
00379 bool swapByteOrder;
00380 Q_UINT32 byte_order;
00381 if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
00382 fclose(fp);
00383 return -1;
00384 }
00385 swapByteOrder = (byte_order == 0x78563412);
00386
00387 Q_UINT32 count;
00388 if (!fread(&count, sizeof(count), 1, fp)) {
00389 fclose(fp);
00390 return -1;
00391 }
00392 if (swapByteOrder)
00393 count = kmail_swap_32(count);
00394
00395 KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00396
00397 for (unsigned int index = 0; index < count; index++) {
00398 Q_UINT32 msn;
00399
00400 bool readOk = fread(&msn, sizeof(msn), 1, fp);
00401 if (swapByteOrder)
00402 msn = kmail_swap_32(msn);
00403
00404 if (!readOk || dict->find(msn)) {
00405 for (unsigned int i = 0; i < index; i++) {
00406 msn = rentry->getMsn(i);
00407 dict->remove((long)msn);
00408 }
00409 delete rentry;
00410 fclose(fp);
00411 return -1;
00412 }
00413
00414
00415
00416
00417
00418
00419 KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index);
00420 dict->insert((long)msn, entry);
00421 if (msn >= nextMsgSerNum)
00422 nextMsgSerNum = msn + 1;
00423
00424 rentry->set(index, entry);
00425 }
00426
00427
00428 GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count );
00429
00430 fclose(fp);
00431 storage.setRDict(rentry);
00432
00433 return 0;
00434 }
00435
00436
00437
00438 KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate)
00439 {
00440 KMMsgDictREntry *rentry = storage.rDict();
00441 if (!rentry) {
00442 rentry = new KMMsgDictREntry();
00443 storage.setRDict(rentry);
00444 }
00445
00446 if (!rentry->fp) {
00447 QString filename = getFolderIdsLocation( storage );
00448 FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+");
00449 if (fp)
00450 {
00451 int version = 0;
00452 fscanf(fp, IDS_HEADER, &version);
00453 if (version == IDS_VERSION)
00454 {
00455 Q_UINT32 byte_order = 0;
00456 fread(&byte_order, sizeof(byte_order), 1, fp);
00457 rentry->swapByteOrder = (byte_order == 0x78563412);
00458 }
00459 else
00460 {
00461 fclose(fp);
00462 fp = 0;
00463 }
00464 }
00465
00466 if (!fp)
00467 {
00468 fp = fopen(QFile::encodeName(filename), "w+");
00469 if (!fp)
00470 {
00471 kdDebug(5006) << "Dict '" << filename
00472 << "' cannot open with folder " << storage.label() << ": "
00473 << strerror(errno) << " (" << errno << ")" << endl;
00474 delete rentry;
00475 rentry = 0;
00476 return 0;
00477 }
00478 fprintf(fp, IDS_HEADER, IDS_VERSION);
00479 Q_UINT32 byteOrder = 0x12345678;
00480 fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00481 rentry->swapByteOrder = false;
00482 }
00483 rentry->baseOffset = ftell(fp);
00484 rentry->fp = fp;
00485 }
00486
00487 return rentry;
00488 }
00489
00490
00491
00492 int KMMsgDict::writeFolderIds( const FolderStorage &storage )
00493 {
00494 KMMsgDictREntry *rentry = openFolderIds( storage, true );
00495 if (!rentry)
00496 return 0;
00497 FILE *fp = rentry->fp;
00498
00499 fseek(fp, rentry->baseOffset, SEEK_SET);
00500
00501 Q_UINT32 count = rentry->getRealSize();
00502 if (!fwrite(&count, sizeof(count), 1, fp)) {
00503 kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": "
00504 << strerror(errno) << " (" << errno << ")" << endl;
00505 return -1;
00506 }
00507
00508 for (unsigned int index = 0; index < count; index++) {
00509 Q_UINT32 msn = rentry->getMsn(index);
00510 if (!fwrite(&msn, sizeof(msn), 1, fp))
00511 return -1;
00512 }
00513
00514 rentry->sync();
00515
00516 off_t eof = ftell(fp);
00517 QString filename = getFolderIdsLocation( storage );
00518 truncate(QFile::encodeName(filename), eof);
00519 fclose(rentry->fp);
00520 rentry->fp = 0;
00521
00522 return 0;
00523 }
00524
00525
00526
00527 int KMMsgDict::touchFolderIds( const FolderStorage &storage )
00528 {
00529 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00530 if (rentry) {
00531 rentry->sync();
00532 fclose(rentry->fp);
00533 rentry->fp = 0;
00534 }
00535 return 0;
00536 }
00537
00538
00539
00540 int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index)
00541 {
00542 KMMsgDictREntry *rentry = openFolderIds( storage, false);
00543 if (!rentry)
00544 return 0;
00545 FILE *fp = rentry->fp;
00546
00547
00548
00549 fseek(fp, rentry->baseOffset, SEEK_SET);
00550 Q_UINT32 count;
00551 if (!fread(&count, sizeof(count), 1, fp)) {
00552 kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": "
00553 << strerror(errno) << " (" << errno << ")" << endl;
00554 return 0;
00555 }
00556 if (rentry->swapByteOrder)
00557 count = kmail_swap_32(count);
00558
00559 count++;
00560
00561 if (rentry->swapByteOrder)
00562 count = kmail_swap_32(count);
00563 fseek(fp, rentry->baseOffset, SEEK_SET);
00564 if (!fwrite(&count, sizeof(count), 1, fp)) {
00565 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00566 << strerror(errno) << " (" << errno << ")" << endl;
00567 return 0;
00568 }
00569
00570 long ofs = (count - 1) * sizeof(ulong);
00571 if (ofs > 0)
00572 fseek(fp, ofs, SEEK_CUR);
00573
00574 Q_UINT32 msn = rentry->getMsn(index);
00575 if (rentry->swapByteOrder)
00576 msn = kmail_swap_32(msn);
00577 if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00578 kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00579 << strerror(errno) << " (" << errno << ")" << endl;
00580 return 0;
00581 }
00582
00583 rentry->sync();
00584 fclose(rentry->fp);
00585 rentry->fp = 0;
00586
00587 return 0;
00588 }
00589
00590
00591
00592 bool KMMsgDict::hasFolderIds( const FolderStorage& storage )
00593 {
00594 return storage.rDict() != 0;
00595 }
00596
00597
00598
00599 bool KMMsgDict::removeFolderIds( FolderStorage& storage )
00600 {
00601 storage.setRDict( 0 );
00602 QString filename = getFolderIdsLocation( storage );
00603 return unlink( QFile::encodeName( filename) );
00604 }