kmail Library API Documentation

kmmsgdict.cpp

00001 /* kmail message dictionary */
00002 /* Author: Ronen Tzur <rtzur@shani.net> */
00003 
00004 #include "kmfolderindex.h"
00005 #include "kmmsgdict.h"
00006 #include "kmdict.h"
00007 
00008 #include <qfileinfo.h>
00009 
00010 #include <kdebug.h>
00011 
00012 #include <stdio.h>
00013 #include <unistd.h>
00014 
00015 #include <errno.h>
00016 
00017 #include <config.h>
00018 
00019 #ifdef HAVE_BYTESWAP_H
00020 #include <byteswap.h>
00021 #endif
00022 
00023 // We define functions as kmail_swap_NN so that we don't get compile errors
00024 // on platforms where bswap_NN happens to be a function instead of a define.
00025 
00026 /* Swap bytes in 32 bit value.  */
00027 #ifdef bswap_32
00028 #define kmail_swap_32(x) bswap_32(x)
00029 #else
00030 #define kmail_swap_32(x) \
00031      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |           \
00032       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
00033 #endif
00034 
00035 
00036 //-----------------------------------------------------------------------------
00037 
00038 // Current version of the .index.ids files
00039 #define IDS_VERSION 1002
00040 
00041 // The asterisk at the end is important
00042 #define IDS_HEADER "# KMail-Index-IDs V%d\n*"
00043 
00044 //-----------------------------------------------------------------------------
00045 
00046 class KMMsgDictEntry : public KMDictItem
00047 {
00048 public:
00049   KMMsgDictEntry(const KMFolder *aFolder, int aIndex)
00050     { folder = aFolder; index = aIndex; }
00051 
00052   const KMFolder *folder;
00053   int index;
00054 };
00055 
00056 //-----------------------------------------------------------------------------
00057 
00058 class KMMsgDictREntry
00059 {
00060 public:
00061   KMMsgDictREntry(int size = 0)
00062   {
00063     array.resize(size);
00064     for (int i = 0; i < size; i++)
00065       array.at(i) = 0;
00066     fp = 0;
00067     swapByteOrder = false;
00068     baseOffset = 0;
00069   }
00070 
00071   ~KMMsgDictREntry()
00072   {
00073     array.resize(0);
00074     if (fp)
00075       fclose(fp);
00076   }
00077 
00078   void set(int index, KMMsgDictEntry *entry)
00079   {
00080     if (index >= 0) {
00081       int size = array.size();
00082       if (index >= size) {
00083         int newsize = QMAX(size + 25, index + 1);
00084         array.resize(newsize);
00085         for (int j = size; j < newsize; j++)
00086           array.at(j) = 0;
00087       }
00088       array.at(index) = entry;
00089     }
00090   }
00091 
00092   KMMsgDictEntry *get(int index)
00093   {
00094     if (index >= 0 && (unsigned)index < array.size())
00095       return array.at(index);
00096     return 0;
00097   }
00098 
00099   ulong getMsn(int index)
00100   {
00101     KMMsgDictEntry *entry = get(index);
00102     if (entry)
00103       return entry->key;
00104     return 0;
00105   }
00106 
00107   int getRealSize()
00108   {
00109     int count = array.size() - 1;
00110     while (count >= 0) {
00111       if (array.at(count))
00112         break;
00113       count--;
00114     }
00115     return count + 1;
00116   }
00117 
00118   void sync()
00119   {
00120     fflush(fp);
00121   }
00122 
00123 public:
00124   QMemArray<KMMsgDictEntry *> array;
00125   FILE *fp;
00126   bool swapByteOrder;
00127   off_t baseOffset;
00128 };
00129 
00130 //-----------------------------------------------------------------------------
00131 
00132 KMMsgDict::KMMsgDict()
00133 {
00134   dict = new KMDict(9973);
00135   nextMsgSerNum = 1;
00136 }
00137 
00138 //-----------------------------------------------------------------------------
00139 
00140 KMMsgDict::~KMMsgDict()
00141 {
00142   delete dict;
00143 }
00144 
00145 //-----------------------------------------------------------------------------
00146 
00147 unsigned long KMMsgDict::getNextMsgSerNum() {
00148   unsigned long msn = nextMsgSerNum;
00149   nextMsgSerNum++;
00150   return msn;
00151 }
00152 
00153 void KMMsgDict::deleteRentry(KMMsgDictREntry *entry)
00154 {
00155   delete entry;
00156 }
00157 
00158 //-----------------------------------------------------------------------------
00159 unsigned long KMMsgDict::insert(unsigned long msn, const KMMessage * msg, int idx ) {
00160   return insert( msn, &msg->toMsgBase(), idx );
00161 }
00162 
00163 
00164 unsigned long KMMsgDict::insert(unsigned long msgSerNum,
00165                                 const KMMsgBase *msg, int index)
00166 {
00167   unsigned long msn = msgSerNum;
00168   if (!msn) {
00169     msn = getNextMsgSerNum();
00170   } else {
00171     if (msn >= nextMsgSerNum)
00172       nextMsgSerNum = msn + 1;
00173   }
00174 
00175   KMFolderIndex *folder = msg->parent();
00176   if (folder && index == -1)
00177     index = folder->find(msg);
00178 
00179   // Should not happen, indicates id file corruption
00180   while (dict->find((long)msn)) {
00181     msn = getNextMsgSerNum();
00182     folder->setDirty( true ); // rewrite id file
00183   }
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   KMMsgDictEntry *entry = new KMMsgDictEntry(folder, index);
00192   dict->replace((long)msn, entry);
00193 
00194   KMMsgDictREntry *rentry = folder->rDict();
00195   if (!rentry) {
00196     rentry = new KMMsgDictREntry();
00197     folder->setRDict(rentry);
00198   }
00199   rentry->set(index, entry);
00200 
00201   return msn;
00202 }
00203 
00204 unsigned long KMMsgDict::insert(const KMMsgBase *msg, int index)
00205 {
00206   unsigned long msn = msg->getMsgSerNum();
00207   return insert(msn, msg, index);
00208 }
00209 
00210 //-----------------------------------------------------------------------------
00211 
00212 void KMMsgDict::remove(unsigned long msgSerNum)
00213 {
00214   long key = (long)msgSerNum;
00215   KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find(key);
00216   if (!entry)
00217     return;
00218 
00219   if (entry->folder) {
00220     KMMsgDictREntry *rentry = entry->folder->rDict();
00221     if (rentry)
00222       rentry->set(entry->index, 0);
00223   }
00224 
00225   dict->remove((long)key);
00226 }
00227 
00228 unsigned long KMMsgDict::remove(const KMMsgBase *msg)
00229 {
00230   unsigned long msn = msg->getMsgSerNum();
00231   remove(msn);
00232   return msn;
00233 }
00234 
00235 //-----------------------------------------------------------------------------
00236 
00237 void KMMsgDict::update(const KMMsgBase *msg, int index, int newIndex)
00238 {
00239   KMMsgDictREntry *rentry = msg->parent()->rDict();
00240   if (rentry) {
00241     KMMsgDictEntry *entry = rentry->get(index);
00242     if (entry) {
00243       entry->index = newIndex;
00244       rentry->set(index, 0);
00245       rentry->set(newIndex, entry);
00246     }
00247   }
00248 }
00249 
00250 //-----------------------------------------------------------------------------
00251 
00252 void KMMsgDict::getLocation(unsigned long key,
00253                             KMFolder **retFolder, int *retIndex)
00254 {
00255   KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find((long)key);
00256   if (entry) {
00257     *retFolder = (KMFolder *)entry->folder;
00258     *retIndex = entry->index;
00259   } else {
00260     *retFolder = 0;
00261     *retIndex = -1;
00262   }
00263 }
00264 
00265 void KMMsgDict::getLocation(const KMMsgBase *msg,
00266                             KMFolder **retFolder, int *retIndex)
00267 {
00268   getLocation(msg->getMsgSerNum(), retFolder, retIndex);
00269 }
00270 
00271 void KMMsgDict::getLocation( const KMMessage * msg, KMFolder * *retFolder, int * retIndex ) {
00272   getLocation( msg->toMsgBase().getMsgSerNum(), retFolder, retIndex );
00273 }
00274 
00275 //-----------------------------------------------------------------------------
00276 
00277 unsigned long KMMsgDict::getMsgSerNum(KMFolder *folder, int index)
00278 {
00279   unsigned long msn = 0;
00280   KMMsgDictREntry *rentry = folder->rDict();
00281   if (rentry)
00282     msn = rentry->getMsn(index);
00283   return msn;
00284 }
00285 
00286 //-----------------------------------------------------------------------------
00287 
00288 QString KMMsgDict::getFolderIdsLocation(const KMFolder *folder)
00289 {
00290   return folder->indexLocation() + ".ids";
00291 }
00292 
00293 //-----------------------------------------------------------------------------
00294 
00295 bool KMMsgDict::isFolderIdsOutdated(const KMFolder *folder)
00296 {
00297   bool outdated = false;
00298 
00299   QFileInfo indexInfo(folder->indexLocation());
00300   QFileInfo idsInfo(getFolderIdsLocation(folder));
00301 
00302   if (!indexInfo.exists() || !idsInfo.exists())
00303     outdated = true;
00304   if (indexInfo.lastModified() > idsInfo.lastModified())
00305     outdated = true;
00306   
00307   return outdated;
00308 }
00309 
00310 //-----------------------------------------------------------------------------
00311 
00312 int KMMsgDict::readFolderIds(KMFolder *folder)
00313 {
00314   if (isFolderIdsOutdated(folder))
00315     return -1;
00316 
00317   QString filename = getFolderIdsLocation(folder);
00318   FILE *fp = fopen(QFile::encodeName(filename), "r+");
00319   if (!fp)
00320     return -1;
00321 
00322   int version = 0;
00323   fscanf(fp, IDS_HEADER, &version);
00324   if (version != IDS_VERSION) {
00325     fclose(fp);
00326     return -1;
00327   }
00328 
00329   bool swapByteOrder;
00330   Q_UINT32 byte_order;
00331   if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
00332     fclose(fp);
00333     return -1;
00334   }
00335   swapByteOrder = (byte_order == 0x78563412);
00336 
00337   Q_UINT32 count;
00338   if (!fread(&count, sizeof(count), 1, fp)) {
00339     fclose(fp);
00340     return -1;
00341   }
00342   if (swapByteOrder)
00343      count = kmail_swap_32(count);
00344 
00345   KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00346 
00347   for (unsigned int index = 0; index < count; index++) {
00348     Q_UINT32 msn;
00349 
00350     bool readOk = fread(&msn, sizeof(msn), 1, fp);
00351     if (swapByteOrder)
00352        msn = kmail_swap_32(msn);
00353 
00354     if (!readOk || dict->find(msn)) {
00355       for (unsigned int i = 0; i < index; i++) {
00356         msn = rentry->getMsn(i);
00357         dict->remove((long)msn);
00358       }
00359       delete rentry;
00360       fclose(fp);
00361       return -1;
00362     }
00363 
00364     //if (!msn)
00365       //kdDebug(5006) << "Dict found zero serial number in folder " << folder->label() << endl;
00366 
00367     KMMsgDictEntry *entry = new KMMsgDictEntry(folder, index);
00368     dict->replace((long)msn, entry);
00369     if (msn >= nextMsgSerNum)
00370       nextMsgSerNum = msn + 1;
00371 
00372     rentry->set(index, entry);
00373   }
00374 
00375   fclose(fp);
00376   folder->setRDict(rentry);
00377 
00378   return 0;
00379 }
00380 
00381 //-----------------------------------------------------------------------------
00382 
00383 KMMsgDictREntry *KMMsgDict::openFolderIds(KMFolder *folder, bool truncate)
00384 {
00385   KMMsgDictREntry *rentry = folder->rDict();
00386   if (!rentry) {
00387     rentry = new KMMsgDictREntry();
00388     folder->setRDict(rentry);
00389   }
00390 
00391   if (!rentry->fp) {
00392     QString filename = getFolderIdsLocation(folder);
00393     FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+");
00394     if (fp)
00395     {
00396       int version = 0;
00397       fscanf(fp, IDS_HEADER, &version);
00398       if (version == IDS_VERSION)
00399       {
00400          Q_UINT32 byte_order = 0;
00401          fread(&byte_order, sizeof(byte_order), 1, fp);
00402          rentry->swapByteOrder = (byte_order == 0x78563412);
00403       }
00404       else
00405       {
00406          fclose(fp);
00407          fp = 0;
00408       }
00409     }
00410 
00411     if (!fp)
00412     {
00413       fp = fopen(QFile::encodeName(filename), "w+");
00414       if (!fp)
00415       {
00416         kdDebug(5006) << "Dict '" << filename
00417                       << "' cannot open with folder " << folder->label() << ": "
00418                       << strerror(errno) << " (" << errno << ")" << endl;
00419          delete rentry;
00420          rentry = 0;
00421          return 0;
00422       }
00423       fprintf(fp, IDS_HEADER, IDS_VERSION);
00424       Q_UINT32 byteOrder = 0x12345678;
00425       fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00426       rentry->swapByteOrder = false;
00427     }
00428     rentry->baseOffset = ftell(fp);
00429     rentry->fp = fp;
00430   }
00431 
00432   return rentry;
00433 }
00434 
00435 //-----------------------------------------------------------------------------
00436 
00437 int KMMsgDict::writeFolderIds(KMFolder *folder)
00438 {
00439   KMMsgDictREntry *rentry = openFolderIds(folder, true);
00440   if (!rentry)
00441     return 0;
00442   FILE *fp = rentry->fp;
00443 
00444   fseek(fp, rentry->baseOffset, SEEK_SET);
00445   // kdDebug(5006) << "Dict writing for folder " << folder->label() << endl;
00446   Q_UINT32 count = rentry->getRealSize();
00447   if (!fwrite(&count, sizeof(count), 1, fp)) {
00448     kdDebug(5006) << "Dict cannot write count with folder " << folder->label() << ": "
00449                   << strerror(errno) << " (" << errno << ")" << endl;
00450     return -1;
00451   }
00452 
00453   for (unsigned int index = 0; index < count; index++) {
00454     Q_UINT32 msn = rentry->getMsn(index);
00455     if (!fwrite(&msn, sizeof(msn), 1, fp))
00456       return -1;
00457   }
00458 
00459   rentry->sync();
00460 
00461   off_t eof = ftell(fp);
00462   QString filename = getFolderIdsLocation(folder);
00463   truncate(QFile::encodeName(filename), eof);
00464   fclose(rentry->fp);
00465   rentry->fp = 0;
00466 
00467   return 0;
00468 }
00469 
00470 //-----------------------------------------------------------------------------
00471 
00472 int KMMsgDict::touchFolderIds(KMFolder *folder)
00473 {
00474   KMMsgDictREntry *rentry = openFolderIds(folder, false);
00475   if (rentry) {
00476     rentry->sync();
00477     fclose(rentry->fp);
00478     rentry->fp = 0;
00479   }
00480   return 0;
00481 }
00482 
00483 //-----------------------------------------------------------------------------
00484 
00485 int KMMsgDict::appendtoFolderIds(KMFolder *folder, int index)
00486 {
00487   KMMsgDictREntry *rentry = openFolderIds(folder, false);
00488   if (!rentry)
00489     return 0;
00490   FILE *fp = rentry->fp;
00491 
00492 //  kdDebug(5006) << "Dict appending for folder " << folder->label() << endl;
00493 
00494   fseek(fp, rentry->baseOffset, SEEK_SET);
00495   Q_UINT32 count;
00496   if (!fread(&count, sizeof(count), 1, fp)) {
00497     kdDebug(5006) << "Dict cannot read count for folder " << folder->label() << ": "
00498                   << strerror(errno) << " (" << errno << ")" << endl;
00499     return 0;
00500   }
00501   if (rentry->swapByteOrder)
00502      count = kmail_swap_32(count);
00503 
00504   count++;
00505 
00506   if (rentry->swapByteOrder)
00507      count = kmail_swap_32(count);
00508   fseek(fp, rentry->baseOffset, SEEK_SET);
00509   if (!fwrite(&count, sizeof(count), 1, fp)) {
00510     kdDebug(5006) << "Dict cannot write count for folder " << folder->label() << ": "
00511                   << strerror(errno) << " (" << errno << ")" << endl;
00512     return 0;
00513   }
00514 
00515   long ofs = (count - 1) * sizeof(ulong);
00516   if (ofs > 0)
00517     fseek(fp, ofs, SEEK_CUR);
00518 
00519   Q_UINT32 msn = rentry->getMsn(index);
00520   if (rentry->swapByteOrder)
00521      msn = kmail_swap_32(msn);
00522   if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00523     kdDebug(5006) << "Dict cannot write count for folder " << folder->label() << ": "
00524                   << strerror(errno) << " (" << errno << ")" << endl;
00525     return 0;
00526   }
00527 
00528   rentry->sync();
00529   fclose(rentry->fp);
00530   rentry->fp = 0;
00531 
00532   return 0;
00533 }
00534 
00535 //-----------------------------------------------------------------------------
00536 
00537 bool KMMsgDict::hasFolderIds(const KMFolder *folder)
00538 {
00539   return folder->rDict() != 0;
00540 }
00541 
00542 //-----------------------------------------------------------------------------
00543 
00544 bool KMMsgDict::removeFolderIds(KMFolder *folder)
00545 {
00546   folder->setRDict(0);
00547   QString filename = getFolderIdsLocation(folder);
00548   return unlink(QFile::encodeName(filename));
00549 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:33 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003