kmail Library API Documentation

kmmsgdict.cpp

00001 /* kmail message dictionary */ 00002 /* Author: Ronen Tzur <rtzur@shani.net> */ 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 // We define functions as kmail_swap_NN so that we don't get compile errors 00026 // on platforms where bswap_NN happens to be a function instead of a define. 00027 00028 /* Swap bytes in 32 bit value. */ 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 // Current version of the .index.ids files 00041 #define IDS_VERSION 1002 00042 00043 // The asterisk at the end is important 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 // Should not happen, indicates id file corruption 00186 while (dict->find((long)msn)) { 00187 msn = getNextMsgSerNum(); 00188 folder->setDirty( true ); // rewrite id file 00189 } 00190 00191 // Insert into the dict. Don't use dict->replace() as we _know_ 00192 // there is no entry with the same msn, we just made sure. 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 //if (!msn) 00367 //kdDebug(5006) << "Dict found zero serial number in folder " << folder->label() << endl; 00368 00369 // Insert into the dict. Don't use dict->replace() as we _know_ 00370 // there is no entry with the same msn, we just made sure. 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 // Remember how many items we put into the dict this time so we can create 00379 // it with an appropriate size next time. 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 // kdDebug(5006) << "Dict writing for folder " << folder->label() << endl; 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 // kdDebug(5006) << "Dict appending for folder " << folder->label() << endl; 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 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:50 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003