kmail

kmfoldersearch.cpp
1 /*
2  This file is part of KMail, the KDE mail client.
3  Copyright (c) 2000 Don Sanders <sanders@kde.org>
4 
5  KMail is free software; you can redistribute it and/or modify it
6  under the terms of the GNU General Public License, version 2, as
7  published by the Free Software Foundation.
8 
9  KMail is distributed in the hope that it will be useful, but
10  WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 //Factor byteswap stuff into one header file
20 
21 #include <config.h>
22 
23 #include "kmfoldersearch.h"
24 #include "kmfolderimap.h"
25 #include "kmfoldermgr.h"
26 #include "kmsearchpattern.h"
27 #include "kmmsgdict.h"
28 #include "index.h"
29 #include "jobscheduler.h"
30 
31 #include <kdebug.h>
32 #include <tdelocale.h>
33 #include <tdeconfig.h>
34 
35 #include <assert.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/file.h>
43 #include <utime.h>
44 
45 #include <tqfile.h>
46 
47 #ifdef HAVE_BYTESWAP_H
48 #include <byteswap.h>
49 #endif
50 
51 // We define functions as kmail_swap_NN so that we don't get compile errors
52 // on platforms where bswap_NN happens to be a function instead of a define.
53 
54 /* Swap bytes in 32 bit value. */
55 #ifndef kmail_swap_32
56 #ifdef bswap_32
57 #define kmail_swap_32(x) bswap_32(x)
58 #else
59 #define kmail_swap_32(x) \
60  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
61  (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
62 #endif
63 #endif // kmail_swap_32
64 
65 // Current version of the .index.search files
66 #define IDS_SEARCH_VERSION 1000
67 // The asterisk at the end is important
68 #define IDS_SEARCH_HEADER "# KMail-Search-IDs V%d\n*"
69 #define IDS_SEARCH_HEADER_LEN 30
70 
71 
72 KMSearch::KMSearch(TQObject * parent, const char * name)
73  :TQObject(parent, name)
74 {
75  mRemainingFolders = -1;
76  mRecursive = true;
77  mRunByIndex = mRunning = false;
78  mRoot = 0;
79  mSearchPattern = 0;
80  mFoundCount = 0;
81  mSearchCount = 0;
82 
83  mProcessNextBatchTimer = new TQTimer(0, "mProcessNextBatchTimer");
84  connect(mProcessNextBatchTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotProcessNextBatch()));
85 }
86 
87 KMSearch::~KMSearch()
88 {
89  delete mProcessNextBatchTimer;
90  delete mSearchPattern;
91 }
92 
93 bool KMSearch::write(TQString location) const
94 {
95  TDEConfig config(location);
96  config.setGroup("Search Folder");
97  if (mSearchPattern)
98  mSearchPattern->writeConfig(&config);
99  if (mRoot.isNull())
100  config.writeEntry("Base", "");
101  else
102  config.writeEntry("Base", mRoot->idString());
103  config.writeEntry("Recursive", recursive());
104  return true;
105 }
106 
107 bool KMSearch::read(TQString location)
108 {
109  TDEConfig config( location );
110  config.setGroup( "Search Folder" );
111  if ( !mSearchPattern )
112  mSearchPattern = new KMSearchPattern();
113  mSearchPattern->readConfig( &config );
114  TQString rootString = config.readEntry( "Base" );
115  mRoot = kmkernel->findFolderById( rootString );
116  mRecursive = config.readBoolEntry( "Recursive" );
117  return true;
118 }
119 
120 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern)
121 {
122  if ( running() )
123  stop();
124  if ( mSearchPattern != searchPattern ) {
125  delete mSearchPattern;
126  mSearchPattern = searchPattern;
127  }
128 }
129 
130 bool KMSearch::inScope(KMFolder* folder) const
131 {
132  if ( mRoot.isNull() || folder == mRoot )
133  return true;
134  if ( !recursive() )
135  return false;
136 
137  KMFolderDir *rootDir = mRoot->child();
138  KMFolderDir *ancestorDir = folder->parent();
139  while ( ancestorDir ) {
140  if ( ancestorDir == rootDir )
141  return true;
142  ancestorDir = ancestorDir->parent();
143  }
144  return false;
145 }
146 
147 void KMSearch::start()
148 {
149  //close all referenced folders
150  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
151  for (fit = mOpenedFolders.begin(); fit != mOpenedFolders.end(); ++fit) {
152  if (!(*fit))
153  continue;
154  (*fit)->close( "kmsearch" );
155  }
156  mOpenedFolders.clear();
157  mFolders.clear();
158 
159  if ( running() )
160  return;
161 
162  if ( !mSearchPattern ) {
163  emit finished(true);
164  return;
165  }
166 
167  mFoundCount = 0;
168  mSearchCount = 0;
169  mRunning = true;
170  mRunByIndex = false;
171  // check if this query can be done with the index
172  if ( kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery( this ) ) {
173  mRunByIndex = true;
174  return;
175  }
176 
177  mFolders.append( mRoot );
178  if ( recursive() )
179  {
180  //Append all descendants to folders
181  KMFolderNode* node;
182  KMFolder* folder;
183  TQValueListConstIterator<TQGuardedPtr<KMFolder> > it;
184  for ( it = mFolders.begin(); it != mFolders.end(); ++it )
185  {
186  folder = *it;
187  KMFolderDir *dir = 0;
188  if ( folder )
189  dir = folder->child();
190  else
191  dir = &kmkernel->folderMgr()->dir();
192  if ( !dir )
193  continue;
194  TQPtrListIterator<KMFolderNode> it(*dir);
195  while ( (node = it.current()) ) {
196  ++it;
197  if ( !node->isDir() ) {
198  KMFolder* kmf = dynamic_cast<KMFolder*>( node );
199  if ( kmf )
200  mFolders.append( kmf );
201  }
202  }
203  }
204  }
205 
206  mRemainingFolders = mFolders.count();
207  mLastFolder = TQString();
208  mProcessNextBatchTimer->start( 0, true );
209 }
210 
211 void KMSearch::stop()
212 {
213  if ( !running() )
214  return;
215  if ( mRunByIndex ) {
216  if ( kmkernel->msgIndex() )
217  kmkernel->msgIndex()->stopQuery( this );
218  } else {
219  mIncompleteFolders.clear();
220  TQValueListConstIterator<TQGuardedPtr<KMFolder> > jt;
221  for ( jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt ) {
222  KMFolder *folder = *jt;
223  if ( !folder )
224  continue;
225  // explicitely stop jobs for this folder as it will not be closed below
226  // when the folder is currently selected
227  if ( folder->folderType() == KMFolderTypeImap ) {
228  KMAcctImap *account =
229  static_cast<KMFolderImap*>( folder->storage() )->account();
230  account->ignoreJobsForFolder( folder );
231  }
232  folder->storage()->search( 0 );
233  mSearchCount += folder->count();
234  folder->close("kmsearch");
235  }
236  }
237  mRemainingFolders = -1;
238  mOpenedFolders.clear();
239  mFolders.clear();
240  mLastFolder = TQString();
241  mRunByIndex = mRunning = false;
242  emit finished(false);
243 }
244 
245 void KMSearch::indexFinished() {
246  mRunning = false;
247  mRunByIndex = false;
248 }
249 
250 void KMSearch::slotProcessNextBatch()
251 {
252  if ( !running() )
253  return;
254 
255  if ( mFolders.count() != 0 )
256  {
257  KMFolder *folder = *( mFolders.begin() );
258  mFolders.erase( mFolders.begin() );
259  if ( folder )
260  {
261  mLastFolder = folder->label();
262  folder->open("kmsearch");
263  mOpenedFolders.append( folder );
264  connect( folder->storage(),
265  TQ_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ),
266  this,
267  TQ_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ) );
268  folder->storage()->search( mSearchPattern );
269  } else
270  --mRemainingFolders;
271  mProcessNextBatchTimer->start( 0, true );
272  return;
273  }
274 }
275 
276 void KMSearch::slotSearchFolderResult( KMFolder* folder,
277  TQValueList<TQ_UINT32> serNums,
278  const KMSearchPattern* pattern,
279  bool complete )
280 {
281  if ( pattern != mSearchPattern )
282  return;
283  kdDebug(5006) << k_funcinfo << folder->label() << " found " << serNums.count() << endl;
284  mLastFolder = folder->label();
285  TQValueListIterator<TQ_UINT32> it;
286  for ( it = serNums.begin(); it != serNums.end(); ++it )
287  {
288  emit found( *it );
289  ++mFoundCount;
290  }
291  if ( complete )
292  {
293  disconnect( folder->storage(),
294  TQ_SIGNAL( searchResult( KMFolder*, TQValueList<TQ_UINT32>,
295  const KMSearchPattern*, bool ) ),
296  this,
297  TQ_SLOT( slotSearchFolderResult( KMFolder*, TQValueList<TQ_UINT32>,
298  const KMSearchPattern*, bool ) ) );
299  --mRemainingFolders;
300  mSearchCount += folder->count();
301  folder->close("kmsearch");
302  mOpenedFolders.remove( folder );
303  if ( mRemainingFolders <= 0 )
304  {
305  mRemainingFolders = 0;
306  mRunning = false;
307  mLastFolder = TQString();
308  mRemainingFolders = -1;
309  mFolders.clear();
310  emit finished( true );
311  }
312  }
313 }
314 
315 //-----------------------------------------------------------------------------
316 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name)
317  : FolderStorage(folder, name)
318 {
319  mIdsStream = 0;
320  mSearch = 0;
321  mInvalid = false;
322  mUnlinked = true;
323  mTempOpened = false;
324  setNoChildren(true);
325 
326  //Hook up some slots for live updating of search folders
327  //TODO: Optimize folderInvalidated, folderAdded, folderRemoved
328  connect(kmkernel->folderMgr(), TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
329  this, TQ_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
330  connect(kmkernel->folderMgr(), TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
331  this, TQ_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
332  connect(kmkernel->folderMgr(), TQ_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
333  this, TQ_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
334  connect(kmkernel->folderMgr(), TQ_SIGNAL(folderInvalidated(KMFolder*)),
335  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
336  connect(kmkernel->folderMgr(), TQ_SIGNAL(folderAdded(KMFolder*)),
337  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
338  connect(kmkernel->folderMgr(), TQ_SIGNAL(folderRemoved(KMFolder*)),
339  this, TQ_SLOT(examineRemovedFolder(KMFolder*)));
340  connect(kmkernel->folderMgr(), TQ_SIGNAL(msgHeaderChanged(KMFolder*,int)),
341  this, TQ_SLOT(propagateHeaderChanged(KMFolder*,int)));
342 
343  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
344  this, TQ_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
345  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
346  this, TQ_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
347  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
348  this, TQ_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
349  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(folderInvalidated(KMFolder*)),
350  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
351  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(folderAdded(KMFolder*)),
352  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
353  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(folderRemoved(KMFolder*)),
354  this, TQ_SLOT(examineRemovedFolder(KMFolder*)));
355  connect(kmkernel->imapFolderMgr(), TQ_SIGNAL(msgHeaderChanged(KMFolder*,int)),
356  this, TQ_SLOT(propagateHeaderChanged(KMFolder*,int)));
357 
358  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
359  this, TQ_SLOT(examineAddedMessage(KMFolder*, TQ_UINT32)));
360  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
361  this, TQ_SLOT(examineRemovedMessage(KMFolder*, TQ_UINT32)));
362  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(msgChanged(KMFolder*, TQ_UINT32, int)),
363  this, TQ_SLOT(examineChangedMessage(KMFolder*, TQ_UINT32, int)));
364  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(folderInvalidated(KMFolder*)),
365  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
366  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(folderAdded(KMFolder*)),
367  this, TQ_SLOT(examineInvalidatedFolder(KMFolder*)));
368  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(folderRemoved(KMFolder*)),
369  this, TQ_SLOT(examineRemovedFolder(KMFolder*)));
370  connect(kmkernel->dimapFolderMgr(), TQ_SIGNAL(msgHeaderChanged(KMFolder*,int)),
371  this, TQ_SLOT(propagateHeaderChanged(KMFolder*,int)));
372 
373  mExecuteSearchTimer = new TQTimer(0, "mExecuteSearchTimer");
374  connect(mExecuteSearchTimer, TQ_SIGNAL(timeout()),
375  this, TQ_SLOT(executeSearch()));
376 }
377 
378 KMFolderSearch::~KMFolderSearch()
379 {
380  delete mExecuteSearchTimer;
381  delete mSearch;
382  mSearch = 0;
383  if (mOpenCount > 0)
384  close("~foldersearch", TRUE);
385 }
386 
387 void KMFolderSearch::setSearch(KMSearch *search)
388 {
389  truncateIndex(); //new search old index is obsolete
390  emit cleared();
391  mInvalid = false;
392  setDirty( true ); //have to write the index
393  if (!mUnlinked) {
394  unlink(TQFile::encodeName(indexLocation()));
395  mUnlinked = true;
396  }
397  if (mSearch != search) {
398  mSearch->stop();
399  delete mSearch;
400  mSearch = search; // take ownership
401  if (mSearch) {
402  TQObject::connect(search, TQ_SIGNAL(found(TQ_UINT32)),
403  TQ_SLOT(addSerNum(TQ_UINT32)));
404  TQObject::connect(search, TQ_SIGNAL(finished(bool)),
405  TQ_SLOT(searchFinished(bool)));
406  }
407  }
408  if (mSearch)
409  mSearch->write(location());
410  clearIndex();
411  mTotalMsgs = 0;
412  mUnreadMsgs = 0;
413  emit numUnreadMsgsChanged( folder() );
414  emit changed(); // really want a kmfolder cleared signal
415  /* TODO There is KMFolder::cleared signal now. Adjust. */
416  if (mSearch)
417  mSearch->start();
418  open("foldersearch"); // will be closed in searchFinished
419 }
420 
421 void KMFolderSearch::executeSearch()
422 {
423  if (mSearch)
424  mSearch->stop();
425  setSearch(mSearch);
426  invalidateFolder();
427 }
428 
429 const KMSearch* KMFolderSearch::search() const
430 {
431  return mSearch;
432 }
433 
434 void KMFolderSearch::searchFinished(bool success)
435 {
436  if (!success)
437  mSerNums.clear();
438  close("foldersearch");
439 }
440 
441 void KMFolderSearch::addSerNum(TQ_UINT32 serNum)
442 {
443  if (mInvalid) // A new search is scheduled don't bother doing anything
444  return;
445  int idx = -1;
446  KMFolder *aFolder = 0;
447  KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
448  // warn instead of assert() because of
449  // https://intevation.de/roundup/kolab/issue2216
450  if (!aFolder || (idx == -1)) {
451  kdDebug(5006) << "Not adding message with serNum " << serNum
452  << ": folder is " << aFolder << ", index is " << idx << endl;
453  return;
454  }
455  if(mFolders.findIndex(aFolder) == -1) {
456  aFolder->open("foldersearch");
457  mFolders.append(aFolder);
458  }
459  setDirty( true ); //TODO append a single entry to .ids file and sync.
460  if (!mUnlinked) {
461  unlink(TQFile::encodeName(indexLocation()));
462  mUnlinked = true;
463  }
464  mSerNums.append(serNum);
465  KMMsgBase *mb = aFolder->getMsgBase(idx);
466  if (mb && (mb->isUnread() || mb->isNew())) {
467  if (mUnreadMsgs == -1)
468  mUnreadMsgs = 0;
469  ++mUnreadMsgs;
470  emit numUnreadMsgsChanged( folder() );
471  }
472  emitMsgAddedSignals(mSerNums.count()-1);
473 }
474 
475 void KMFolderSearch::removeSerNum(TQ_UINT32 serNum)
476 {
477  TQValueVector<TQ_UINT32>::const_iterator it;
478  int i = 0;
479  for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i)
480  if ((*it) == serNum) {
481  int idx = -1;
482  KMFolder *aFolder = 0;
483  KMMsgDict::instance()->getLocation(serNum, &aFolder, &idx);
484  assert(aFolder && (idx != -1));
485  emit msgRemoved(folder(), serNum);
486  removeMsg(i);
487  return;
488  }
489  if (!mUnlinked) {
490  unlink(TQFile::encodeName(indexLocation()));
491  mUnlinked = true;
492  }
493 }
494 
495 int KMFolderSearch::addMsg(KMMessage*, int* index_return)
496 {
497  //Not supported search folder can't own messages
498  *index_return = -1;
499  return 0;
500 }
501 
502 bool KMFolderSearch::readSearch()
503 {
504  mSearch = new KMSearch;
505  TQObject::connect(mSearch, TQ_SIGNAL(found(TQ_UINT32)), TQ_SLOT(addSerNum(TQ_UINT32)));
506  TQObject::connect(mSearch, TQ_SIGNAL(finished(bool)), TQ_SLOT(searchFinished(bool)));
507  return mSearch->read(location());
508 }
509 
510 int KMFolderSearch::open(const char *)
511 {
512  mOpenCount++;
513  kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
514  if (mOpenCount > 1)
515  return 0; // already open
516 
517  readConfig();
518  if (!mSearch && !readSearch())
519  return -1;
520 
521  emit cleared();
522  if (!mSearch || !search()->running())
523  if (!readIndex()) {
524  executeSearch();
525  }
526 
527  return 0;
528 }
529 
530 int KMFolderSearch::canAccess()
531 {
532  assert(!folder()->name().isEmpty());
533 
534  if (access(TQFile::encodeName(location()), R_OK | W_OK | X_OK) != 0)
535  return 1;
536  return 0;
537 }
538 
539 void KMFolderSearch::sync()
540 {
541  if (mDirty) {
542  if (mSearch)
543  mSearch->write(location());
544  updateIndex();
545  }
546 }
547 
548 void KMFolderSearch::reallyDoClose(const char* owner)
549 {
550  Q_UNUSED( owner );
551  if (mAutoCreateIndex) {
552  if (mSearch)
553  mSearch->write(location());
554  updateIndex();
555  if (mSearch && search()->running())
556  mSearch->stop();
557  writeConfig();
558  }
559 
560  //close all referenced folders
561  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
562  for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
563  if (!(*fit))
564  continue;
565  (*fit)->close("foldersearch");
566  }
567  mFolders.clear();
568 
569  clearIndex(TRUE);
570 
571  if (mIdsStream)
572  fclose(mIdsStream);
573 
574  mOpenCount = 0;
575  mIdsStream = 0;
576  mUnreadMsgs = -1;
577 }
578 
579 int KMFolderSearch::create()
580 {
581  int old_umask;
582  int rc = unlink(TQFile::encodeName(location()));
583  if (!rc)
584  return rc;
585  rc = 0;
586 
587  assert(!folder()->name().isEmpty());
588  assert(mOpenCount == 0);
589 
590  kdDebug(5006) << "Creating folder " << location() << endl;
591  if (access(TQFile::encodeName(location()), F_OK) == 0) {
592  kdDebug(5006) << "KMFolderSearch::create call to access function failed."
593  << endl;
594  return EEXIST;
595  }
596 
597  old_umask = umask(077);
598  FILE *mStream = fopen(TQFile::encodeName(location()), "w+");
599  umask(old_umask);
600  if (!mStream) return errno;
601  fclose(mStream);
602 
603  clearIndex();
604  if (!mSearch) {
605  mSearch = new KMSearch();
606  TQObject::connect(mSearch, TQ_SIGNAL(found(TQ_UINT32)), TQ_SLOT(addSerNum(TQ_UINT32)));
607  TQObject::connect(mSearch, TQ_SIGNAL(finished(bool)), TQ_SLOT(searchFinished(bool)));
608  }
609  mSearch->write(location());
610  mOpenCount++;
611  mChanged = false;
612  mUnreadMsgs = 0;
613  mTotalMsgs = 0;
614  return rc;
615 }
616 
617 int KMFolderSearch::compact( bool )
618 {
619  needsCompact = false;
620  return 0;
621 }
622 
623 bool KMFolderSearch::isReadOnly() const
624 {
625  return false; //TODO: Make it true and get that working ok
626 }
627 
628 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType,
629  KMFolder*, TQString, const AttachmentStrategy* ) const
630 {
631  // Should never be called
632  assert(0);
633  return 0;
634 }
635 
636 FolderJob* KMFolderSearch::doCreateJob(TQPtrList<KMMessage>&, const TQString&,
637  FolderJob::JobType, KMFolder*) const
638 {
639  // Should never be called
640  assert(0);
641  return 0;
642 }
643 
644 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const
645 {
646  int folderIdx = -1;
647  KMFolder *folder = 0;
648  if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
649  return 0;
650  KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
651  assert(folder && (folderIdx != -1));
652  return folder->getMsgBase(folderIdx);
653 }
654 
655 KMMsgBase* KMFolderSearch::getMsgBase(int idx)
656 {
657  int folderIdx = -1;
658  KMFolder *folder = 0;
659  if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
660  return 0;
661  KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
662  if (!folder || folderIdx == -1)
663  return 0; //exceptional case
664  return folder->getMsgBase(folderIdx);
665 }
666 
667 //-----------------------------------------------------------------------------
668 KMMessage* KMFolderSearch::getMsg(int idx)
669 {
670  int folderIdx = -1;
671  KMFolder *folder = 0;
672  if (idx < 0 || (TQ_UINT32)idx >= mSerNums.count())
673  return 0;
674  KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
675  assert(folder && (folderIdx != -1));
676  KMMessage* msg = folder->getMsg( folderIdx );
677  return msg;
678 }
679 
680 //-------------------------------------------------------------
681 void
682 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg )
683 {
684  if ( !msg || msg->transferInProgress() )
685  return;
686  /* While non-imap folders manage their jobs themselves, imap ones let
687  their account manage them. Therefor first clear the jobs managed by
688  this folder via the inherited method, then clear the imap ones. */
690 
691  if (msg->parent()->folderType() == KMFolderTypeImap) {
692  KMAcctImap *account =
693  static_cast<KMFolderImap*>( msg->storage() )->account();
694  if( !account )
695  return;
696  account->ignoreJobsForMessage( msg );
697  }
698 }
699 
700 
701 int KMFolderSearch::find(const KMMsgBase* msg) const
702 {
703  int pos = 0;
704  TQ_UINT32 serNum = msg->getMsgSerNum();
705  TQValueVector<TQ_UINT32>::const_iterator it;
706  for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
707  if ((*it) == serNum)
708  return pos;
709  ++pos;
710  }
711  return -1;
712 }
713 
714 TQString KMFolderSearch::indexLocation() const
715 {
716  TQString sLocation(folder()->path());
717 
718  if (!sLocation.isEmpty()) sLocation += '/';
719  sLocation += '.';
720  sLocation += dotEscape(fileName());
721  sLocation += ".index";
722  sLocation += ".search";
723 
724  return sLocation;
725 }
726 
727 int KMFolderSearch::updateIndex()
728 {
729  if (mSearch && search()->running())
730  unlink(TQFile::encodeName(indexLocation()));
731  else
732  if (dirty())
733  return writeIndex();
734  return 0;
735 }
736 
737 int KMFolderSearch::writeIndex( bool )
738 {
739  // TODO:If we fail to write the index we should panic the kernel
740  // TODO:and the same for other folder types too, and the msgDict.
741  TQString filename = indexLocation();
742  int old_umask = umask(077);
743  TQString tempName = filename + ".temp";
744  unlink(TQFile::encodeName(tempName));
745 
746  // We touch the folder, otherwise the index is regenerated, if KMail is
747  // running, while the clock switches from daylight savings time to normal time
748  utime(TQFile::encodeName(location()), 0);
749 
750  FILE *tmpIndexStream = fopen(TQFile::encodeName(tempName), "w");
751  umask(old_umask);
752 
753  if (!tmpIndexStream) {
754  kdDebug(5006) << "Cannot write '" << filename
755  << strerror(errno) << " (" << errno << ")" << endl;
756  truncate(TQFile::encodeName(filename), 0);
757  return -1;
758  }
759  fprintf(tmpIndexStream, IDS_SEARCH_HEADER, IDS_SEARCH_VERSION);
760  TQ_UINT32 byteOrder = 0x12345678;
761  fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream);
762 
763  TQ_UINT32 count = mSerNums.count();
764  if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) {
765  fclose(tmpIndexStream);
766  truncate(TQFile::encodeName(filename), 0);
767  return -1;
768  }
769 
770  TQValueVector<TQ_UINT32>::iterator it;
771  for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
772  TQ_UINT32 serNum = *it;
773  if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream))
774  return -1;
775  }
776  if (ferror(tmpIndexStream)) return ferror(tmpIndexStream);
777  if (fflush(tmpIndexStream) != 0) return errno;
778  if (fsync(fileno(tmpIndexStream)) != 0) return errno;
779  if (fclose(tmpIndexStream) != 0) return errno;
780 
781  ::rename(TQFile::encodeName(tempName), TQFile::encodeName(indexLocation()));
782  mDirty = FALSE;
783  mUnlinked = FALSE;
784 
785  return 0;
786 }
787 
788 DwString KMFolderSearch::getDwString(int idx)
789 {
790  return getMsgBase(idx)->parent()->getDwString( idx );
791 }
792 
793 KMMessage* KMFolderSearch::readMsg(int idx)
794 {
795  int folderIdx = -1;
796  KMFolder *folder = 0;
797  KMMsgDict::instance()->getLocation(mSerNums[idx], &folder, &folderIdx);
798  assert(folder && (folderIdx != -1));
799  return folder->getMsg( folderIdx );
800 }
801 
802 bool KMFolderSearch::readIndex()
803 {
804  clearIndex();
805  TQString filename = indexLocation();
806  mIdsStream = fopen(TQFile::encodeName(filename), "r+");
807  if (!mIdsStream)
808  return false;
809 
810  int version = 0;
811  fscanf(mIdsStream, IDS_SEARCH_HEADER, &version);
812  if (version != IDS_SEARCH_VERSION) {
813  fclose(mIdsStream);
814  mIdsStream = 0;
815  return false;
816  }
817  bool swapByteOrder;
818  TQ_UINT32 byte_order;
819  if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) {
820  fclose(mIdsStream);
821  mIdsStream = 0;
822  return false;
823  }
824  swapByteOrder = (byte_order == 0x78563412);
825 
826  TQ_UINT32 count;
827  if (!fread(&count, sizeof(count), 1, mIdsStream)) {
828  fclose(mIdsStream);
829  mIdsStream = 0;
830  return false;
831  }
832  if (swapByteOrder)
833  count = kmail_swap_32(count);
834 
835  mUnreadMsgs = 0;
836  mSerNums.reserve(count);
837  for (unsigned int index = 0; index < count; index++) {
838  TQ_UINT32 serNum;
839  int folderIdx = -1;
840  KMFolder *folder = 0;
841  bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream);
842  if (!readOk) {
843  clearIndex();
844  fclose(mIdsStream);
845  mIdsStream = 0;
846  return false;
847  }
848  if (swapByteOrder)
849  serNum = kmail_swap_32(serNum);
850 
851  KMMsgDict::instance()->getLocation( serNum, &folder, &folderIdx );
852  if (!folder || (folderIdx == -1)) {
853  clearIndex();
854  fclose(mIdsStream);
855  mIdsStream = 0;
856  return false;
857  }
858  mSerNums.push_back(serNum);
859  if(mFolders.findIndex(folder) == -1) {
860  if (mInvalid) //exceptional case for when folder has invalid ids
861  return false;
862  folder->open("foldersearch");
863  mFolders.append(folder);
864  }
865  KMMsgBase *mb = folder->getMsgBase(folderIdx);
866  if (!mb) //Exceptional case our .ids file is messed up
867  return false;
868  if (mb->isNew() || mb->isUnread()) {
869  if (mUnreadMsgs == -1) ++mUnreadMsgs;
870  ++mUnreadMsgs;
871  }
872  }
873  mTotalMsgs = mSerNums.count();
874  fclose(mIdsStream);
875  mIdsStream = 0;
876  mUnlinked = true;
877  return true;
878 }
879 
880 int KMFolderSearch::removeContents()
881 {
882  unlink(TQFile::encodeName(location()));
883  unlink(TQFile::encodeName(indexLocation()));
884  mUnlinked = true;
885  return 0;
886 }
887 
888 int KMFolderSearch::expungeContents()
889 {
890  setSearch(new KMSearch());
891  return 0;
892 }
893 
894 int KMFolderSearch::count(bool cache) const
895 {
896  Q_UNUSED(cache);
897  return mSerNums.count();
898 }
899 
900 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx)
901 {
902  assert(idx >= 0 && idx < (int)mSerNums.count());
903  KMMsgBase *msgBase = getMsgBase(idx);
904  TQValueVector<TQ_UINT32>::iterator it = mSerNums.begin();
905  mSerNums.erase(&it[idx]);
906  return msgBase;
907 }
908 
909 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg)
910 {
911  assert(idx >= 0 && idx < (int)mSerNums.count());
912  Q_UNUSED( idx );
913  return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg);
914 }
915 
916 void KMFolderSearch::clearIndex(bool, bool)
917 {
918  //close all referenced folders
919  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
920  for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) {
921  if (!(*fit))
922  continue;
923  (*fit)->close("foldersearch");
924  }
925  mFolders.clear();
926 
927  mSerNums.clear();
928 }
929 
930 void KMFolderSearch::truncateIndex()
931 {
932  truncate(TQFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
933 }
934 
935 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, TQ_UINT32 serNum)
936 {
937  if (!search() && !readSearch())
938  return;
939  if (!search()->inScope(aFolder))
940  return;
941  if (!mTempOpened) {
942  open("foldersearch");
943  mTempOpened = true;
944  }
945 
946  if (!search()->searchPattern())
947  return;
948 
949  int idx = -1;
950  KMFolder *folder = 0;
951  KMMsgDict::instance()->getLocation(serNum, &folder, &idx);
952  assert(folder && (idx != -1));
953  assert(folder == aFolder);
954  KMFolderOpener openFolder(folder, "foldersearch");
955 
956  // if we are already checking this folder, refcount
957  if ( mFoldersCurrentlyBeingSearched.contains( folder ) ) {
958  unsigned int count = mFoldersCurrentlyBeingSearched[folder];
959  mFoldersCurrentlyBeingSearched.replace( folder, count+1 );
960  } else {
961  connect( folder->storage(),
962  TQ_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ),
963  this,
964  TQ_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
965  const KMSearchPattern*, bool ) ) );
966  mFoldersCurrentlyBeingSearched.insert( folder, 1 );
967  }
968  folder->storage()->search( search()->searchPattern(), serNum );
969 }
970 
971 void KMFolderSearch::slotSearchExamineMsgDone( KMFolder* folder,
972  TQ_UINT32 serNum,
973  const KMSearchPattern* pattern,
974  bool matches )
975 {
976  if ( search()->searchPattern() != pattern ) return;
977  kdDebug(5006) << folder->label() << ": serNum " << serNum
978  << " matches?" << matches << endl;
979  KMFolderOpener openFolder(folder, "foldersearch");
980 
981  Q_ASSERT( mFoldersCurrentlyBeingSearched.contains( folder ) );
982 
983  unsigned int count = mFoldersCurrentlyBeingSearched[folder];
984  if ( count == 1 ) {
985  disconnect( folder->storage(),
986  TQ_SIGNAL( searchDone( KMFolder*, TQ_UINT32,
987  const KMSearchPattern*, bool ) ),
988  this,
989  TQ_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
990  const KMSearchPattern*, bool ) ) );
991  mFoldersCurrentlyBeingSearched.remove( folder );
992  } else {
993  mFoldersCurrentlyBeingSearched.replace( folder, count-1 );
994  }
995 
996  if ( !matches ) {
997  TQValueVector<TQ_UINT32>::const_iterator it;
998  it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
999  if (it != mSerNums.end()) {
1000  removeSerNum( serNum );
1001  }
1002  return;
1003  }
1004 
1005 // if (mSearch->running()) {
1006 // mSearch->stop();
1007 // mExecuteSearchTimer->start( 0, true );
1008 // } else {
1009  TQValueVector<TQ_UINT32>::const_iterator it;
1010  it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
1011  if (it == mSerNums.end()) {
1012  addSerNum( serNum );
1013  }
1014 // }
1015 }
1016 
1017 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, TQ_UINT32 serNum)
1018 {
1019  if (!search() && !readSearch())
1020  return;
1021  if (!search()->inScope(folder))
1022  return;
1023  if (!mTempOpened) {
1024  open("foldersearch");
1025  mTempOpened = true;
1026  }
1027 
1028  if (mSearch->running()) {
1029  mExecuteSearchTimer->start(0, true);
1030  } else {
1031  removeSerNum(serNum);
1032  }
1033 }
1034 
1035 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, TQ_UINT32 serNum, int delta)
1036 {
1037  if (!search() && !readSearch())
1038  return;
1039  if (!search()->inScope(aFolder))
1040  return;
1041  if (!mTempOpened) {
1042  open("foldersearch");
1043  mTempOpened = true;
1044  }
1045  TQValueVector<TQ_UINT32>::const_iterator it;
1046  it = tqFind( mSerNums.begin(), mSerNums.end(), serNum );
1047  if (it != mSerNums.end()) {
1048  mUnreadMsgs += delta;
1049  emit numUnreadMsgsChanged( folder() );
1050  emit msgChanged( folder(), serNum, delta );
1051  }
1052 }
1053 
1054 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder)
1055 {
1056  if (!search() && !readSearch())
1057  return;
1058  if (!search()->inScope(folder))
1059  return;
1060  if (mTempOpened) {
1061  close("foldersearch");
1062  mTempOpened = false;
1063  }
1064 
1065  mInvalid = true;
1066  if (mSearch)
1067  mSearch->stop();
1068 
1069  if (!mUnlinked) {
1070  unlink(TQFile::encodeName(indexLocation()));
1071  mUnlinked = true;
1072  }
1073 
1074  if (!isOpened()) //give up, until the user manually opens the folder
1075  return;
1076 
1077  if (!mTempOpened) {
1078  open("foldersearch");
1079  mTempOpened = true;
1080  }
1081  mExecuteSearchTimer->start(0, true);
1082 }
1083 
1084 void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
1085 {
1086  examineInvalidatedFolder(folder);
1087  if (mSearch->root() == folder) {
1088  delete mSearch;
1089  mSearch = 0;
1090  }
1091 }
1092 
1093 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx)
1094 {
1095  int pos = 0;
1096  if (!search() && !readSearch())
1097  return;
1098  if (!search()->inScope(aFolder))
1099  return;
1100  if (!mTempOpened) {
1101  open("foldersearch");
1102  mTempOpened = true;
1103  }
1104 
1105  TQ_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(aFolder, idx);
1106  TQValueVector<TQ_UINT32>::const_iterator it;
1107  for(it = mSerNums.begin(); it != mSerNums.end(); ++it) {
1108  if ((*it) == serNum) {
1109  emit msgHeaderChanged(folder(), pos);
1110  break;
1111  }
1112  ++pos;
1113  }
1114  // let's try if the message matches our search
1115  KMFolderOpener openAFolder(aFolder, "foldersearch");
1116 
1117  // if we are already checking this folder, refcount
1118  if ( mFoldersCurrentlyBeingSearched.contains( aFolder ) ) {
1119  unsigned int count = mFoldersCurrentlyBeingSearched[aFolder];
1120  mFoldersCurrentlyBeingSearched.replace( aFolder, count+1 );
1121  } else {
1122  connect( aFolder->storage(),
1123  TQ_SIGNAL( searchDone( KMFolder*, TQ_UINT32, const KMSearchPattern*, bool ) ),
1124  this,
1125  TQ_SLOT( slotSearchExamineMsgDone( KMFolder*, TQ_UINT32,
1126  const KMSearchPattern*, bool ) ) );
1127  mFoldersCurrentlyBeingSearched.insert( aFolder, 1 );
1128  }
1129  aFolder->storage()->search( search()->searchPattern(), serNum );
1130 }
1131 
1132 void KMFolderSearch::tryReleasingFolder(KMFolder* folder)
1133 {
1134  // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1.
1135  // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing
1136  if ( mTempOpened && mOpenCount == 1 )
1137  {
1138  examineInvalidatedFolder( folder );
1139  }
1140 }
1141 
1142 #include "kmfoldersearch.moc"
The FolderStorage class is the bass class for the storage related aspects of a collection of mail (a ...
Definition: folderstorage.h:80
virtual void ignoreJobsForMessage(KMMessage *)
Removes and deletes all jobs associated with the particular message.
virtual void search(const KMSearchPattern *)
Search for messages The end is signaled with searchDone()
virtual int addMsg(TQPtrList< KMMessage > &, TQValueList< int > &index_return)
Adds the given messages to the folder.
KMail list that manages the contents of one directory that may contain folders and/or other directori...
Definition: kmfolderdir.h:16
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
Mail folder.
Definition: kmfolder.h:69
virtual TQString label() const
Returns the label of the folder for visualization.
Definition: kmfolder.cpp:581
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition: kmfolder.h:157
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition: kmfolder.cpp:360
KMFolderType folderType() const
Returns the type of this folder.
Definition: kmfolder.cpp:233
int count(bool cache=false) const
Number of messages in this folder.
Definition: kmfolder.cpp:445
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
This is a Mime Message.
Definition: kmmessage.h:68
bool transferInProgress() const
Return, if the message should not be deleted.
Definition: kmmessage.cpp:236
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
Definition: kmmsgdict.cpp:345
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
This class is an abstraction of a search over messages.