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