kmail

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 #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 // We define functions as kmail_swap_NN so that we don't get compile errors
00028 // on platforms where bswap_NN happens to be a function instead of a define.
00029 
00030 /* Swap bytes in 32 bit value.  */
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 // Current version of the .index.ids files
00043 #define IDS_VERSION 1002
00044 
00045 // The asterisk at the end is important
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 ) {
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   // Should not happen, indicates id file corruption
00221   while (dict->find((long)msn)) {
00222     msn = getNextMsgSerNum();
00223     folder->setDirty( true ); // rewrite id file
00224   }
00225 
00226   // Insert into the dict. Don't use dict->replace() as we _know_
00227   // there is no entry with the same msn, we just made sure.
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   KMMsgDictREntry *rentry = folder->storage()->rDict();
00349   if (rentry)
00350     msn = rentry->getMsn(index);
00351   return msn;
00352 }
00353 
00354 //-----------------------------------------------------------------------------
00355 
00356 QString KMMsgDict::getFolderIdsLocation( const FolderStorage &storage )
00357 {
00358   return storage.indexLocation() + ".ids";
00359 }
00360 
00361 //-----------------------------------------------------------------------------
00362 
00363 bool KMMsgDict::isFolderIdsOutdated( const FolderStorage &storage )
00364 {
00365   bool outdated = false;
00366 
00367   QFileInfo indexInfo( storage.indexLocation() );
00368   QFileInfo idsInfo( getFolderIdsLocation( storage ) );
00369 
00370   if (!indexInfo.exists() || !idsInfo.exists())
00371     outdated = true;
00372   if (indexInfo.lastModified() > idsInfo.lastModified())
00373     outdated = true;
00374 
00375   return outdated;
00376 }
00377 
00378 //-----------------------------------------------------------------------------
00379 
00380 int KMMsgDict::readFolderIds( FolderStorage& storage )
00381 {
00382   if ( isFolderIdsOutdated( storage ) )
00383     return -1;
00384 
00385   QString filename = getFolderIdsLocation( storage );
00386   FILE *fp = fopen(QFile::encodeName(filename), "r+");
00387   if (!fp)
00388     return -1;
00389 
00390   int version = 0;
00391   fscanf(fp, IDS_HEADER, &version);
00392   if (version != IDS_VERSION) {
00393     fclose(fp);
00394     return -1;
00395   }
00396 
00397   bool swapByteOrder;
00398   Q_UINT32 byte_order;
00399   if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
00400     fclose(fp);
00401     return -1;
00402   }
00403   swapByteOrder = (byte_order == 0x78563412);
00404 
00405   Q_UINT32 count;
00406   if (!fread(&count, sizeof(count), 1, fp)) {
00407     fclose(fp);
00408     return -1;
00409   }
00410   if (swapByteOrder)
00411      count = kmail_swap_32(count);
00412 
00413   KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
00414 
00415   for (unsigned int index = 0; index < count; index++) {
00416     Q_UINT32 msn;
00417 
00418     bool readOk = fread(&msn, sizeof(msn), 1, fp);
00419     if (swapByteOrder)
00420        msn = kmail_swap_32(msn);
00421 
00422     if (!readOk || dict->find(msn)) {
00423       for (unsigned int i = 0; i < index; i++) {
00424         msn = rentry->getMsn(i);
00425         dict->remove((long)msn);
00426       }
00427       delete rentry;
00428       fclose(fp);
00429       return -1;
00430     }
00431 
00432     //if (!msn)
00433       //kdDebug(5006) << "Dict found zero serial number in folder " << folder->label() << endl;
00434 
00435     // Insert into the dict. Don't use dict->replace() as we _know_
00436     // there is no entry with the same msn, we just made sure.
00437     KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index);
00438     dict->insert((long)msn, entry);
00439     if (msn >= nextMsgSerNum)
00440       nextMsgSerNum = msn + 1;
00441 
00442     rentry->set(index, entry);
00443   }
00444   // Remember how many items we put into the dict this time so we can create
00445   // it with an appropriate size next time.
00446   GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count );
00447 
00448   fclose(fp);
00449   storage.setRDict(rentry);
00450 
00451   return 0;
00452 }
00453 
00454 //-----------------------------------------------------------------------------
00455 
00456 KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate)
00457 {
00458   KMMsgDictREntry *rentry = storage.rDict();
00459   if (!rentry) {
00460     rentry = new KMMsgDictREntry();
00461     storage.setRDict(rentry);
00462   }
00463 
00464   if (!rentry->fp) {
00465     QString filename = getFolderIdsLocation( storage );
00466     FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+");
00467     if (fp)
00468     {
00469       int version = 0;
00470       fscanf(fp, IDS_HEADER, &version);
00471       if (version == IDS_VERSION)
00472       {
00473          Q_UINT32 byte_order = 0;
00474          fread(&byte_order, sizeof(byte_order), 1, fp);
00475          rentry->swapByteOrder = (byte_order == 0x78563412);
00476       }
00477       else
00478       {
00479          fclose(fp);
00480          fp = 0;
00481       }
00482     }
00483 
00484     if (!fp)
00485     {
00486       fp = fopen(QFile::encodeName(filename), "w+");
00487       if (!fp)
00488       {
00489         kdDebug(5006) << "Dict '" << filename
00490                       << "' cannot open with folder " << storage.label() << ": "
00491                       << strerror(errno) << " (" << errno << ")" << endl;
00492          delete rentry;
00493          rentry = 0;
00494          return 0;
00495       }
00496       fprintf(fp, IDS_HEADER, IDS_VERSION);
00497       Q_UINT32 byteOrder = 0x12345678;
00498       fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
00499       rentry->swapByteOrder = false;
00500     }
00501     rentry->baseOffset = ftell(fp);
00502     rentry->fp = fp;
00503   }
00504 
00505   return rentry;
00506 }
00507 
00508 //-----------------------------------------------------------------------------
00509 
00510 int KMMsgDict::writeFolderIds( const FolderStorage &storage )
00511 {
00512   KMMsgDictREntry *rentry = openFolderIds( storage, true );
00513   if (!rentry)
00514     return 0;
00515   FILE *fp = rentry->fp;
00516 
00517   fseek(fp, rentry->baseOffset, SEEK_SET);
00518   // kdDebug(5006) << "Dict writing for folder " << storage.label() << endl;
00519   Q_UINT32 count = rentry->getRealSize();
00520   if (!fwrite(&count, sizeof(count), 1, fp)) {
00521     kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": "
00522                   << strerror(errno) << " (" << errno << ")" << endl;
00523     return -1;
00524   }
00525 
00526   for (unsigned int index = 0; index < count; index++) {
00527     Q_UINT32 msn = rentry->getMsn(index);
00528     if (!fwrite(&msn, sizeof(msn), 1, fp))
00529       return -1;
00530   }
00531 
00532   rentry->sync();
00533 
00534   off_t eof = ftell(fp);
00535   QString filename = getFolderIdsLocation( storage );
00536   truncate(QFile::encodeName(filename), eof);
00537   fclose(rentry->fp);
00538   rentry->fp = 0;
00539 
00540   return 0;
00541 }
00542 
00543 //-----------------------------------------------------------------------------
00544 
00545 int KMMsgDict::touchFolderIds( const FolderStorage &storage )
00546 {
00547   KMMsgDictREntry *rentry = openFolderIds( storage, false);
00548   if (rentry) {
00549     rentry->sync();
00550     fclose(rentry->fp);
00551     rentry->fp = 0;
00552   }
00553   return 0;
00554 }
00555 
00556 //-----------------------------------------------------------------------------
00557 
00558 int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index)
00559 {
00560   KMMsgDictREntry *rentry = openFolderIds( storage, false);
00561   if (!rentry)
00562     return 0;
00563   FILE *fp = rentry->fp;
00564 
00565 //  kdDebug(5006) << "Dict appending for folder " << storage.label() << endl;
00566 
00567   fseek(fp, rentry->baseOffset, SEEK_SET);
00568   Q_UINT32 count;
00569   if (!fread(&count, sizeof(count), 1, fp)) {
00570     kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": "
00571                   << strerror(errno) << " (" << errno << ")" << endl;
00572     return 0;
00573   }
00574   if (rentry->swapByteOrder)
00575      count = kmail_swap_32(count);
00576 
00577   count++;
00578 
00579   if (rentry->swapByteOrder)
00580      count = kmail_swap_32(count);
00581   fseek(fp, rentry->baseOffset, SEEK_SET);
00582   if (!fwrite(&count, sizeof(count), 1, fp)) {
00583     kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00584                   << strerror(errno) << " (" << errno << ")" << endl;
00585     return 0;
00586   }
00587 
00588   long ofs = (count - 1) * sizeof(ulong);
00589   if (ofs > 0)
00590     fseek(fp, ofs, SEEK_CUR);
00591 
00592   Q_UINT32 msn = rentry->getMsn(index);
00593   if (rentry->swapByteOrder)
00594      msn = kmail_swap_32(msn);
00595   if (!fwrite(&msn, sizeof(msn), 1, fp)) {
00596     kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
00597                   << strerror(errno) << " (" << errno << ")" << endl;
00598     return 0;
00599   }
00600 
00601   rentry->sync();
00602   fclose(rentry->fp);
00603   rentry->fp = 0;
00604 
00605   return 0;
00606 }
00607 
00608 //-----------------------------------------------------------------------------
00609 
00610 bool KMMsgDict::hasFolderIds( const FolderStorage& storage )
00611 {
00612   return storage.rDict() != 0;
00613 }
00614 
00615 //-----------------------------------------------------------------------------
00616 
00617 bool KMMsgDict::removeFolderIds( FolderStorage& storage )
00618 {
00619   storage.setRDict( 0 );
00620   QString filename = getFolderIdsLocation( storage );
00621   return unlink( QFile::encodeName( filename) );
00622 }
KDE Home | KDE Accessibility Home | Description of Access Keys