TDE personal information management applications
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

663 linhas
17KB

  1. /* kmail message dictionary */
  2. /* Author: Ronen Tzur <rtzur@shani.net> */
  3. #include "kmfolderindex.h"
  4. #include "kmfolder.h"
  5. #include "kmmsgdict.h"
  6. #include "kmdict.h"
  7. #include "globalsettings.h"
  8. #include "folderstorage.h"
  9. #include <tqfileinfo.h>
  10. #include <kdebug.h>
  11. #include <kstaticdeleter.h>
  12. #include <stdio.h>
  13. #include <unistd.h>
  14. #include <string.h>
  15. #include <errno.h>
  16. #include <config.h>
  17. #ifdef HAVE_BYTESWAP_H
  18. #include <byteswap.h>
  19. #endif
  20. // We define functions as kmail_swap_NN so that we don't get compile errors
  21. // on platforms where bswap_NN happens to be a function instead of a define.
  22. /* Swap bytes in 32 bit value. */
  23. #ifdef bswap_32
  24. #define kmail_swap_32(x) bswap_32(x)
  25. #else
  26. #define kmail_swap_32(x) \
  27. ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
  28. (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
  29. #endif
  30. //-----------------------------------------------------------------------------
  31. // Current version of the .index.ids files
  32. #define IDS_VERSION 1002
  33. // The asterisk at the end is important
  34. #define IDS_HEADER "# KMail-Index-IDs V%d\n*"
  35. /**
  36. * @short an entry in the global message dictionary consisting of a pointer
  37. * to a folder and the index of a message in the folder.
  38. */
  39. class KMMsgDictEntry : public KMDictItem
  40. {
  41. public:
  42. KMMsgDictEntry(const KMFolder *aFolder, int aIndex)
  43. : folder( aFolder ), index( aIndex )
  44. {}
  45. const KMFolder *folder;
  46. int index;
  47. };
  48. /**
  49. * @short A "reverse entry", consisting of an array of DictEntry pointers.
  50. *
  51. * Each folder (storage) holds such an entry. That's useful for looking up the
  52. * serial number of a message at a certain index in the folder, since that is the
  53. * key of these entries.
  54. */
  55. class KMMsgDictREntry
  56. {
  57. public:
  58. KMMsgDictREntry(int size = 0)
  59. {
  60. array.resize(size);
  61. memset(array.data(), 0, array.size() * sizeof(KMMsgDictEntry *)); // faster than a loop
  62. fp = 0;
  63. swapByteOrder = false;
  64. baseOffset = 0;
  65. }
  66. ~KMMsgDictREntry()
  67. {
  68. array.resize(0);
  69. if (fp)
  70. fclose(fp);
  71. }
  72. void set(int index, KMMsgDictEntry *entry)
  73. {
  74. if (index >= 0) {
  75. int size = array.size();
  76. if (index >= size) {
  77. int newsize = TQMAX(size + 25, index + 1);
  78. array.resize(newsize);
  79. for (int j = size; j < newsize; j++)
  80. array.at(j) = 0;
  81. }
  82. array.at(index) = entry;
  83. }
  84. }
  85. KMMsgDictEntry *get(int index)
  86. {
  87. if (index >= 0 && (unsigned)index < array.size())
  88. return array.at(index);
  89. return 0;
  90. }
  91. ulong getMsn(int index)
  92. {
  93. KMMsgDictEntry *entry = get(index);
  94. if (entry)
  95. return entry->key;
  96. return 0;
  97. }
  98. int getRealSize()
  99. {
  100. int count = array.size() - 1;
  101. while (count >= 0) {
  102. if (array.at(count))
  103. break;
  104. count--;
  105. }
  106. return count + 1;
  107. }
  108. void sync()
  109. {
  110. fflush(fp);
  111. }
  112. public:
  113. TQMemArray<KMMsgDictEntry *> array;
  114. FILE *fp;
  115. bool swapByteOrder;
  116. off_t baseOffset;
  117. };
  118. static KStaticDeleter<KMMsgDict> msgDict_sd;
  119. KMMsgDict * KMMsgDict::m_self = 0;
  120. //-----------------------------------------------------------------------------
  121. KMMsgDict::KMMsgDict()
  122. {
  123. int lastSizeOfDict = GlobalSettings::self()->msgDictSizeHint();
  124. lastSizeOfDict = ( lastSizeOfDict * 11 ) / 10;
  125. GlobalSettings::self()->setMsgDictSizeHint( 0 );
  126. dict = new KMDict( lastSizeOfDict );
  127. nextMsgSerNum = 1;
  128. m_self = this;
  129. }
  130. //-----------------------------------------------------------------------------
  131. KMMsgDict::~KMMsgDict()
  132. {
  133. delete dict;
  134. }
  135. //-----------------------------------------------------------------------------
  136. const KMMsgDict* KMMsgDict::instance()
  137. {
  138. if ( !m_self ) {
  139. msgDict_sd.setObject( m_self, new KMMsgDict() );
  140. }
  141. return m_self;
  142. }
  143. KMMsgDict* KMMsgDict::mutableInstance()
  144. {
  145. if ( !m_self ) {
  146. msgDict_sd.setObject( m_self, new KMMsgDict() );
  147. }
  148. return m_self;
  149. }
  150. //-----------------------------------------------------------------------------
  151. unsigned long KMMsgDict::getNextMsgSerNum() {
  152. unsigned long msn = nextMsgSerNum;
  153. nextMsgSerNum++;
  154. return msn;
  155. }
  156. void KMMsgDict::deleteRentry(KMMsgDictREntry *entry)
  157. {
  158. delete entry;
  159. }
  160. unsigned long KMMsgDict::insert(unsigned long msgSerNum,
  161. const KMMsgBase *msg, int index)
  162. {
  163. unsigned long msn = msgSerNum;
  164. if (!msn) {
  165. msn = getNextMsgSerNum();
  166. } else {
  167. if (msn >= nextMsgSerNum)
  168. nextMsgSerNum = msn + 1;
  169. }
  170. KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() );
  171. if ( !folder ) {
  172. kdDebug(5006) << "KMMsgDict::insert: Cannot insert the message, "
  173. << "null pointer to storage. Requested serial: " << msgSerNum
  174. << endl;
  175. kdDebug(5006) << " Message info: Subject: " << msg->subject() << ", To: "
  176. << msg->toStrip() << ", Date: " << msg->dateStr() << endl;
  177. return 0;
  178. }
  179. if (index == -1)
  180. index = folder->find(msg);
  181. // Should not happen, indicates id file corruption
  182. while (dict->find((long)msn)) {
  183. msn = getNextMsgSerNum();
  184. folder->setDirty( true ); // rewrite id file
  185. }
  186. // Insert into the dict. Don't use dict->replace() as we _know_
  187. // there is no entry with the same msn, we just made sure.
  188. KMMsgDictEntry *entry = new KMMsgDictEntry(folder->folder(), index);
  189. dict->insert((long)msn, entry);
  190. KMMsgDictREntry *rentry = folder->rDict();
  191. if (!rentry) {
  192. rentry = new KMMsgDictREntry();
  193. folder->setRDict(rentry);
  194. }
  195. rentry->set(index, entry);
  196. return msn;
  197. }
  198. unsigned long KMMsgDict::insert(const KMMsgBase *msg, int index)
  199. {
  200. unsigned long msn = msg->getMsgSerNum();
  201. return insert(msn, msg, index);
  202. }
  203. //-----------------------------------------------------------------------------
  204. void KMMsgDict::replace(unsigned long msgSerNum,
  205. const KMMsgBase *msg, int index)
  206. {
  207. KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() );
  208. if ( !folder ) {
  209. kdDebug(5006) << "KMMsgDict::replace: Cannot replace the message serial "
  210. << "number, null pointer to storage. Requested serial: " << msgSerNum
  211. << endl;
  212. kdDebug(5006) << " Message info: Subject: " << msg->subject() << ", To: "
  213. << msg->toStrip() << ", Date: " << msg->dateStr() << endl;
  214. return;
  215. }
  216. if ( index == -1 )
  217. index = folder->find( msg );
  218. remove( msgSerNum );
  219. KMMsgDictEntry *entry = new KMMsgDictEntry( folder->folder(), index );
  220. dict->insert( (long)msgSerNum, entry );
  221. KMMsgDictREntry *rentry = folder->rDict();
  222. if (!rentry) {
  223. rentry = new KMMsgDictREntry();
  224. folder->setRDict(rentry);
  225. }
  226. rentry->set(index, entry);
  227. }
  228. //-----------------------------------------------------------------------------
  229. void KMMsgDict::remove(unsigned long msgSerNum)
  230. {
  231. long key = (long)msgSerNum;
  232. KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find(key);
  233. if (!entry)
  234. return;
  235. if (entry->folder) {
  236. KMMsgDictREntry *rentry = entry->folder->storage()->rDict();
  237. if (rentry)
  238. rentry->set(entry->index, 0);
  239. }
  240. dict->remove((long)key);
  241. }
  242. unsigned long KMMsgDict::remove(const KMMsgBase *msg)
  243. {
  244. unsigned long msn = msg->getMsgSerNum();
  245. remove(msn);
  246. return msn;
  247. }
  248. //-----------------------------------------------------------------------------
  249. void KMMsgDict::update(const KMMsgBase *msg, int index, int newIndex)
  250. {
  251. KMMsgDictREntry *rentry = msg->parent()->storage()->rDict();
  252. if (rentry) {
  253. KMMsgDictEntry *entry = rentry->get(index);
  254. if (entry) {
  255. entry->index = newIndex;
  256. rentry->set(index, 0);
  257. rentry->set(newIndex, entry);
  258. }
  259. }
  260. }
  261. //-----------------------------------------------------------------------------
  262. void KMMsgDict::getLocation(unsigned long key,
  263. KMFolder **retFolder, int *retIndex) const
  264. {
  265. KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find((long)key);
  266. if (entry) {
  267. *retFolder = (KMFolder *)entry->folder;
  268. *retIndex = entry->index;
  269. } else {
  270. *retFolder = 0;
  271. *retIndex = -1;
  272. }
  273. }
  274. void KMMsgDict::getLocation(const KMMsgBase *msg,
  275. KMFolder **retFolder, int *retIndex) const
  276. {
  277. getLocation(msg->getMsgSerNum(), retFolder, retIndex);
  278. }
  279. void KMMsgDict::getLocation( const KMMessage * msg, KMFolder * *retFolder, int * retIndex ) const
  280. {
  281. getLocation( msg->toMsgBase().getMsgSerNum(), retFolder, retIndex );
  282. }
  283. //-----------------------------------------------------------------------------
  284. unsigned long KMMsgDict::getMsgSerNum(KMFolder *folder, int index) const
  285. {
  286. unsigned long msn = 0;
  287. if ( folder ) {
  288. KMMsgDictREntry *rentry = folder->storage()->rDict();
  289. if (rentry)
  290. msn = rentry->getMsn(index);
  291. }
  292. return msn;
  293. }
  294. //-----------------------------------------------------------------------------
  295. //static
  296. TQValueList<unsigned long> KMMsgDict::serNumList(TQPtrList<KMMsgBase> msgList)
  297. {
  298. TQValueList<unsigned long> ret;
  299. for ( unsigned int i = 0; i < msgList.count(); i++ ) {
  300. unsigned long serNum = msgList.at(i)->getMsgSerNum();
  301. assert( serNum );
  302. ret.append( serNum );
  303. }
  304. return ret;
  305. }
  306. //-----------------------------------------------------------------------------
  307. TQString KMMsgDict::getFolderIdsLocation( const FolderStorage &storage )
  308. {
  309. return storage.indexLocation() + ".ids";
  310. }
  311. //-----------------------------------------------------------------------------
  312. bool KMMsgDict::isFolderIdsOutdated( const FolderStorage &storage )
  313. {
  314. bool outdated = false;
  315. TQFileInfo indexInfo( storage.indexLocation() );
  316. TQFileInfo idsInfo( getFolderIdsLocation( storage ) );
  317. if (!indexInfo.exists() || !idsInfo.exists())
  318. outdated = true;
  319. if (indexInfo.lastModified() > idsInfo.lastModified())
  320. outdated = true;
  321. return outdated;
  322. }
  323. //-----------------------------------------------------------------------------
  324. int KMMsgDict::readFolderIds( FolderStorage& storage )
  325. {
  326. if ( isFolderIdsOutdated( storage ) )
  327. return -1;
  328. TQString filename = getFolderIdsLocation( storage );
  329. FILE *fp = fopen(TQFile::encodeName(filename), "r+");
  330. if (!fp)
  331. return -1;
  332. int version = 0;
  333. fscanf(fp, IDS_HEADER, &version);
  334. if (version != IDS_VERSION) {
  335. fclose(fp);
  336. return -1;
  337. }
  338. bool swapByteOrder;
  339. TQ_UINT32 byte_order;
  340. if (!fread(&byte_order, sizeof(byte_order), 1, fp)) {
  341. fclose(fp);
  342. return -1;
  343. }
  344. swapByteOrder = (byte_order == 0x78563412);
  345. TQ_UINT32 count;
  346. if (!fread(&count, sizeof(count), 1, fp)) {
  347. fclose(fp);
  348. return -1;
  349. }
  350. if (swapByteOrder)
  351. count = kmail_swap_32(count);
  352. // quick consistency check to avoid allocating huge amount of memory
  353. // due to reading corrupt file (#71549)
  354. long pos = ftell(fp); // store current position
  355. fseek(fp, 0, SEEK_END);
  356. long fileSize = ftell(fp); // how large is the file ?
  357. fseek(fp, pos, SEEK_SET); // back to previous position
  358. // the file must at least contain what we try to read below
  359. if ( (fileSize - pos) < (long)(count * sizeof(TQ_UINT32)) ) {
  360. fclose(fp);
  361. return -1;
  362. }
  363. KMMsgDictREntry *rentry = new KMMsgDictREntry(count);
  364. for (unsigned int index = 0; index < count; index++) {
  365. TQ_UINT32 msn;
  366. bool readOk = fread(&msn, sizeof(msn), 1, fp);
  367. if (swapByteOrder)
  368. msn = kmail_swap_32(msn);
  369. if (!readOk || dict->find(msn)) {
  370. for (unsigned int i = 0; i < index; i++) {
  371. msn = rentry->getMsn(i);
  372. dict->remove((long)msn);
  373. }
  374. delete rentry;
  375. fclose(fp);
  376. return -1;
  377. }
  378. // We found a serial number that is zero. This is not allowed, and would
  379. // later cause problems like in bug 149715.
  380. // Therefore, use a fresh serial number instead
  381. if ( msn == 0 ) {
  382. kdWarning(5006) << "readFolderIds(): Found serial number zero at index " << index
  383. << " in folder " << filename << endl;
  384. msn = getNextMsgSerNum();
  385. Q_ASSERT( msn != 0 );
  386. }
  387. // Insert into the dict. Don't use dict->replace() as we _know_
  388. // there is no entry with the same msn, we just made sure.
  389. KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index);
  390. dict->insert((long)msn, entry);
  391. if (msn >= nextMsgSerNum)
  392. nextMsgSerNum = msn + 1;
  393. rentry->set(index, entry);
  394. }
  395. // Remember how many items we put into the dict this time so we can create
  396. // it with an appropriate size next time.
  397. GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count );
  398. fclose(fp);
  399. storage.setRDict(rentry);
  400. return 0;
  401. }
  402. //-----------------------------------------------------------------------------
  403. KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate)
  404. {
  405. KMMsgDictREntry *rentry = storage.rDict();
  406. if (!rentry) {
  407. rentry = new KMMsgDictREntry();
  408. storage.setRDict(rentry);
  409. }
  410. if (!rentry->fp) {
  411. TQString filename = getFolderIdsLocation( storage );
  412. FILE *fp = truncate ? 0 : fopen(TQFile::encodeName(filename), "r+");
  413. if (fp)
  414. {
  415. int version = 0;
  416. fscanf(fp, IDS_HEADER, &version);
  417. if (version == IDS_VERSION)
  418. {
  419. TQ_UINT32 byte_order = 0;
  420. fread(&byte_order, sizeof(byte_order), 1, fp);
  421. rentry->swapByteOrder = (byte_order == 0x78563412);
  422. }
  423. else
  424. {
  425. fclose(fp);
  426. fp = 0;
  427. }
  428. }
  429. if (!fp)
  430. {
  431. fp = fopen(TQFile::encodeName(filename), "w+");
  432. if (!fp)
  433. {
  434. kdDebug(5006) << "Dict '" << filename
  435. << "' cannot open with folder " << storage.label() << ": "
  436. << strerror(errno) << " (" << errno << ")" << endl;
  437. delete rentry;
  438. rentry = 0;
  439. return 0;
  440. }
  441. fprintf(fp, IDS_HEADER, IDS_VERSION);
  442. TQ_UINT32 byteOrder = 0x12345678;
  443. fwrite(&byteOrder, sizeof(byteOrder), 1, fp);
  444. rentry->swapByteOrder = false;
  445. }
  446. rentry->baseOffset = ftell(fp);
  447. rentry->fp = fp;
  448. }
  449. return rentry;
  450. }
  451. //-----------------------------------------------------------------------------
  452. int KMMsgDict::writeFolderIds( const FolderStorage &storage )
  453. {
  454. KMMsgDictREntry *rentry = openFolderIds( storage, true );
  455. if (!rentry)
  456. return 0;
  457. FILE *fp = rentry->fp;
  458. fseek(fp, rentry->baseOffset, SEEK_SET);
  459. // kdDebug(5006) << "Dict writing for folder " << storage.label() << endl;
  460. TQ_UINT32 count = rentry->getRealSize();
  461. if (!fwrite(&count, sizeof(count), 1, fp)) {
  462. kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": "
  463. << strerror(errno) << " (" << errno << ")" << endl;
  464. return -1;
  465. }
  466. for (unsigned int index = 0; index < count; index++) {
  467. TQ_UINT32 msn = rentry->getMsn(index);
  468. if (!fwrite(&msn, sizeof(msn), 1, fp))
  469. return -1;
  470. if ( msn == 0 ) {
  471. kdWarning(5006) << "writeFolderIds(): Serial number of message at index "
  472. << index << " is zero in folder " << storage.label() << endl;
  473. }
  474. }
  475. rentry->sync();
  476. off_t eof = ftell(fp);
  477. TQString filename = getFolderIdsLocation( storage );
  478. truncate(TQFile::encodeName(filename), eof);
  479. fclose(rentry->fp);
  480. rentry->fp = 0;
  481. return 0;
  482. }
  483. //-----------------------------------------------------------------------------
  484. int KMMsgDict::touchFolderIds( const FolderStorage &storage )
  485. {
  486. KMMsgDictREntry *rentry = openFolderIds( storage, false);
  487. if (rentry) {
  488. rentry->sync();
  489. fclose(rentry->fp);
  490. rentry->fp = 0;
  491. }
  492. return 0;
  493. }
  494. //-----------------------------------------------------------------------------
  495. int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index)
  496. {
  497. KMMsgDictREntry *rentry = openFolderIds( storage, false);
  498. if (!rentry)
  499. return 0;
  500. FILE *fp = rentry->fp;
  501. // kdDebug(5006) << "Dict appending for folder " << storage.label() << endl;
  502. fseek(fp, rentry->baseOffset, SEEK_SET);
  503. TQ_UINT32 count;
  504. if (!fread(&count, sizeof(count), 1, fp)) {
  505. kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": "
  506. << strerror(errno) << " (" << errno << ")" << endl;
  507. return 0;
  508. }
  509. if (rentry->swapByteOrder)
  510. count = kmail_swap_32(count);
  511. count++;
  512. if (rentry->swapByteOrder)
  513. count = kmail_swap_32(count);
  514. fseek(fp, rentry->baseOffset, SEEK_SET);
  515. if (!fwrite(&count, sizeof(count), 1, fp)) {
  516. kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
  517. << strerror(errno) << " (" << errno << ")" << endl;
  518. return 0;
  519. }
  520. long ofs = (count - 1) * sizeof(ulong);
  521. if (ofs > 0)
  522. fseek(fp, ofs, SEEK_CUR);
  523. TQ_UINT32 msn = rentry->getMsn(index);
  524. if (rentry->swapByteOrder)
  525. msn = kmail_swap_32(msn);
  526. if (!fwrite(&msn, sizeof(msn), 1, fp)) {
  527. kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": "
  528. << strerror(errno) << " (" << errno << ")" << endl;
  529. return 0;
  530. }
  531. rentry->sync();
  532. fclose(rentry->fp);
  533. rentry->fp = 0;
  534. return 0;
  535. }
  536. //-----------------------------------------------------------------------------
  537. bool KMMsgDict::hasFolderIds( const FolderStorage& storage )
  538. {
  539. return storage.rDict() != 0;
  540. }
  541. //-----------------------------------------------------------------------------
  542. bool KMMsgDict::removeFolderIds( FolderStorage& storage )
  543. {
  544. storage.setRDict( 0 );
  545. TQString filename = getFolderIdsLocation( storage );
  546. return unlink( TQFile::encodeName( filename) );
  547. }