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::self()->msgDictSizeHint();
00138   lastSizeOfDict = ( lastSizeOfDict * 11 ) / 10;
00139   GlobalSettings::self()->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::self()->setMsgDictSizeHint( GlobalSettings::self()->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.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Dec 21 14:25:01 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003