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
72KMSearch::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
87KMSearch::~KMSearch()
88{
89 delete mProcessNextBatchTimer;
90 delete mSearchPattern;
91}
92
93bool 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
107bool 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
120void KMSearch::setSearchPattern(KMSearchPattern *searchPattern)
121{
122 if ( running() )
123 stop();
124 if ( mSearchPattern != searchPattern ) {
125 delete mSearchPattern;
126 mSearchPattern = searchPattern;
127 }
128}
129
130bool 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
147void 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
211void 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
245void KMSearch::indexFinished() {
246 mRunning = false;
247 mRunByIndex = false;
248}
249
250void 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
276void 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//-----------------------------------------------------------------------------
316KMFolderSearch::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
378KMFolderSearch::~KMFolderSearch()
379{
380 delete mExecuteSearchTimer;
381 delete mSearch;
382 mSearch = 0;
383 if (mOpenCount > 0)
384 close("~foldersearch", TRUE);
385}
386
387void 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
421void KMFolderSearch::executeSearch()
422{
423 if (mSearch)
424 mSearch->stop();
425 setSearch(mSearch);
426 invalidateFolder();
427}
428
429const KMSearch* KMFolderSearch::search() const
430{
431 return mSearch;
432}
433
434void KMFolderSearch::searchFinished(bool success)
435{
436 if (!success)
437 mSerNums.clear();
438 close("foldersearch");
439}
440
441void 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
475void 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
495int 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
502bool 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
510int 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
530int 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
539void KMFolderSearch::sync()
540{
541 if (mDirty) {
542 if (mSearch)
543 mSearch->write(location());
544 updateIndex();
545 }
546}
547
548void 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
579int 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
617int KMFolderSearch::compact( bool )
618{
619 needsCompact = false;
620 return 0;
621}
622
623bool KMFolderSearch::isReadOnly() const
624{
625 return false; //TODO: Make it true and get that working ok
626}
627
628FolderJob* 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
636FolderJob* 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
644const 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
655KMMsgBase* 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//-----------------------------------------------------------------------------
668KMMessage* 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//-------------------------------------------------------------
681void
682KMFolderSearch::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
701int 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
714TQString 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
727int 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
737int 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
788DwString KMFolderSearch::getDwString(int idx)
789{
790 return getMsgBase(idx)->parent()->getDwString( idx );
791}
792
793KMMessage* 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
802bool 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
880int KMFolderSearch::removeContents()
881{
882 unlink(TQFile::encodeName(location()));
883 unlink(TQFile::encodeName(indexLocation()));
884 mUnlinked = true;
885 return 0;
886}
887
888int KMFolderSearch::expungeContents()
889{
890 setSearch(new KMSearch());
891 return 0;
892}
893
894int KMFolderSearch::count(bool cache) const
895{
896 Q_UNUSED(cache);
897 return mSerNums.count();
898}
899
900KMMsgBase* 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
909KMMsgInfo* 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
916void 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
930void KMFolderSearch::truncateIndex()
931{
932 truncate(TQFile::encodeName(indexLocation()), IDS_SEARCH_HEADER_LEN);
933}
934
935void 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
971void 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
1017void 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
1035void 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
1054void 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
1084void KMFolderSearch::examineRemovedFolder(KMFolder *folder)
1085{
1086 examineInvalidatedFolder(folder);
1087 if (mSearch->root() == folder) {
1088 delete mSearch;
1089 mSearch = 0;
1090 }
1091}
1092
1093void 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
1132void 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
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
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition: kmfolder.h:157
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.