32 #include "kmfoldermgr.h"
33 #include "kmmsgdict.h"
35 #include "kmsearchpattern.h"
36 #include "kmfoldersearch.h"
39 #include <tdeapplication.h>
42 #include <tqvaluestack.h>
43 #include <tqptrlist.h>
44 #include <tqfileinfo.h>
46 #include <indexlib/create.h>
49 #include <sys/types.h>
57 const unsigned int MaintenanceLimit = 1000;
58 const char*
const folderIndexDisabledKey =
"fulltextIndexDisabled";
63 TQValueList<int> vectorToTQValueList(
const std::vector<TQ_UINT32>& input ) {
65 std::copy( input.begin(), input.end(), std::back_inserter( res ) );
70 std::vector<TQ_UINT32> TQValueListToVector(
const TQValueList<int>& input ) {
71 std::vector<TQ_UINT32> res;
73 for ( TQValueList<int>::const_iterator first = input.begin(), past = input.end(); first != past; ++first ) {
74 res.push_back( *first );
80 KMMsgIndex::KMMsgIndex( TQObject* parent ):
81 TQObject( parent,
"index" ),
84 mLockFile( std::string( static_cast<const char*>( TQFile::encodeName( defaultPath() ) +
"/lock" ) ) ),
87 mIndexPath( TQFile::encodeName( defaultPath() ) ),
88 mTimer( new TQTimer( this,
"mTimer" ) ),
92 kdDebug( 5006 ) <<
"KMMsgIndex::KMMsgIndex()" << endl;
94 connect( kmkernel->folderMgr(), TQ_SIGNAL( msgRemoved(
KMFolder*, TQ_UINT32 ) ), TQ_SLOT( slotRemoveMessage(
KMFolder*, TQ_UINT32 ) ) );
95 connect( kmkernel->folderMgr(), TQ_SIGNAL( msgAdded(
KMFolder*, TQ_UINT32 ) ), TQ_SLOT( slotAddMessage(
KMFolder*, TQ_UINT32 ) ) );
96 connect( kmkernel->dimapFolderMgr(), TQ_SIGNAL( msgRemoved(
KMFolder*, TQ_UINT32 ) ), TQ_SLOT( slotRemoveMessage(
KMFolder*, TQ_UINT32 ) ) );
97 connect( kmkernel->dimapFolderMgr(), TQ_SIGNAL( msgAdded(
KMFolder*, TQ_UINT32 ) ), TQ_SLOT( slotAddMessage(
KMFolder*, TQ_UINT32 ) ) );
99 connect( mTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( act() ) );
103 TDEConfigGroup cfg( KMKernel::config(),
"text-index" );
104 if ( !cfg.readBoolEntry(
"enabled",
false ) ) {
105 indexlib::remove( mIndexPath );
106 mLockFile.force_unlock();
110 if ( !mLockFile.trylock() ) {
111 indexlib::remove( mIndexPath );
113 mLockFile.force_unlock();
116 mIndex = indexlib::open( mIndexPath, indexlib::open_flags::fail_if_nonexistant ).release();
119 TQTimer::singleShot( 8000,
this, TQ_SLOT( create() ) );
120 mState = s_willcreate;
122 if ( cfg.readBoolEntry(
"creating" ) ) {
123 TQTimer::singleShot( 8000,
this, TQ_SLOT( continueCreation() ) );
126 mPendingMsgs = TQValueListToVector( cfg.readIntListEntry(
"pending" ) );
127 mRemovedMsgs = TQValueListToVector( cfg.readIntListEntry(
"removed" ) );
138 KMMsgIndex::~KMMsgIndex() {
139 kdDebug( 5006 ) <<
"KMMsgIndex::~KMMsgIndex()" << endl;
141 TDEConfigGroup cfg( KMKernel::config(),
"text-index" );
142 cfg.writeEntry(
"creating", mState == s_creating );
143 TQValueList<int> pendingMsg;
144 if ( mState == s_processing ) {
145 Q_ASSERT( mAddedMsgs.empty() );
146 pendingMsg = vectorToTQValueList( mPendingMsgs );
148 cfg.writeEntry(
"pending", pendingMsg );
149 cfg.writeEntry(
"removed", vectorToTQValueList( mRemovedMsgs ) );
154 bool KMMsgIndex::isIndexable(
KMFolder* folder )
const {
155 if ( !folder || !folder->parent() )
return false;
156 const KMFolderMgr* manager = folder->parent()->manager();
157 return manager == kmkernel->folderMgr() || manager == kmkernel->dimapFolderMgr();
160 bool KMMsgIndex::isIndexed(
KMFolder* folder )
const {
161 if ( !isIndexable( folder ) )
return false;
162 TDEConfig* config = KMKernel::config();
163 TDEConfigGroupSaver saver( config,
"Folder-" + folder->
idString() );
164 return !config->readBoolEntry( folderIndexDisabledKey,
false );
167 void KMMsgIndex::setEnabled(
bool e ) {
168 kdDebug( 5006 ) <<
"KMMsgIndex::setEnabled( " << e <<
" )" << endl;
169 TDEConfig* config = KMKernel::config();
170 TDEConfigGroupSaver saver( config,
"text-index" );
171 if ( config->readBoolEntry(
"enabled", !e ) == e )
return;
172 config->writeEntry(
"enabled", e );
185 TQTimer::singleShot( 8000,
this, TQ_SLOT( create() ) );
186 mState = s_willcreate;
193 void KMMsgIndex::setIndexingEnabled(
KMFolder* folder,
bool e ) {
194 TDEConfig* config = KMKernel::config();
195 TDEConfigGroupSaver saver( config,
"Folder-" + folder->
idString() );
196 if ( config->readBoolEntry( folderIndexDisabledKey, e ) == e )
return;
197 config->writeEntry( folderIndexDisabledKey, e );
204 mPendingFolders.push_back( folder );
222 if ( std::find( mPendingFolders.begin(), mPendingFolders.end(), folder ) != mPendingFolders.end() ) {
224 mPendingFolders.erase( std::find( mPendingFolders.begin(), mPendingFolders.end(), folder ) );
239 void KMMsgIndex::clear() {
240 kdDebug( 5006 ) <<
"KMMsgIndex::clear()" << endl;
243 mLockFile.force_unlock();
245 indexlib::remove( mIndexPath );
246 mPendingMsgs.clear();
247 mPendingFolders.clear();
248 mMaintenanceCount = 0;
250 mRemovedMsgs.clear();
253 for ( std::set<KMFolder*>::const_iterator first = mOpenedFolders.begin(), past = mOpenedFolders.end(); first != past; ++first ) {
254 ( *first )->close(
"msgindex");
256 mOpenedFolders.clear();
257 for ( std::vector<Search*>::const_iterator first = mSearches.begin(), past = mSearches.end(); first != past; ++first ) {
265 void KMMsgIndex::maintenance() {
267 if ( mState != s_idle || kapp->hasPendingEvents() ) {
268 TQTimer::singleShot( 8000,
this, TQ_SLOT( maintenance() ) );
271 mIndex->maintenance();
275 int KMMsgIndex::addMessage( TQ_UINT32 serNum ) {
276 kdDebug( 5006 ) <<
"KMMsgIndex::addMessage( " << serNum <<
" )" << endl;
277 if ( mState == s_error )
return 0;
280 if ( !mExisting.empty() && std::binary_search( mExisting.begin(), mExisting.end(), serNum ) )
return 0;
285 if ( !folder || idx == -1 )
return -1;
286 if ( !mOpenedFolders.count( folder ) ) {
287 mOpenedFolders.insert( folder );
288 folder->
open(
"msgindex");
296 if ( !body.isEmpty() &&
static_cast<const char*
>( body.latin1() ) ) {
297 mIndex->add( body.latin1(), TQString::number( serNum ).latin1() );
299 kdDebug( 5006 ) <<
"Funny, no body" << endl;
306 void KMMsgIndex::act() {
307 kdDebug( 5006 ) <<
"KMMsgIndex::act()" << endl;
308 if ( kapp->hasPendingEvents() ) {
310 mTimer->start( 500 );
318 if ( !mPendingMsgs.empty() ) {
319 addMessage( mPendingMsgs.back() );
320 mPendingMsgs.pop_back();
323 if ( !mPendingFolders.empty() ) {
324 KMFolder *f = mPendingFolders.back();
325 mPendingFolders.pop_back();
326 if ( !mOpenedFolders.count( f ) ) {
327 mOpenedFolders.insert( f );
331 TDEConfig* config = KMKernel::config();
332 TDEConfigGroupSaver saver( config,
"Folder-" + f->
idString() );
333 if ( config->readBoolEntry( folderIndexDisabledKey,
true ) ) {
334 for (
int i = 0; i < f->
count(); ++i ) {
340 if ( !mAddedMsgs.empty() ) {
341 std::swap( mAddedMsgs, mPendingMsgs );
342 mState = s_processing;
345 for ( std::set<KMFolder*>::const_iterator first = mOpenedFolders.begin(), past = mOpenedFolders.end();
348 ( *first )->close(
"msgindex");
350 mOpenedFolders.clear();
355 void KMMsgIndex::continueCreation() {
356 kdDebug( 5006 ) <<
"KMMsgIndex::continueCreation()" << endl;
359 unsigned count = mIndex->ndocs();
361 mExisting.reserve( count );
362 for (
unsigned i = 0; i != count; ++i ) {
363 mExisting.push_back( std::atoi( mIndex->lookup_docname( i ).c_str() ) );
365 std::sort( mExisting.begin(), mExisting.end() );
369 void KMMsgIndex::create() {
370 kdDebug( 5006 ) <<
"KMMsgIndex::create()" << endl;
373 if ( !TQFileInfo( mIndexPath ).exists() ) {
374 ::mkdir( mIndexPath, S_IRWXU );
377 if ( !mIndex ) mIndex = indexlib::create( mIndexPath ).release();
379 kdDebug( 5006 ) <<
"Error creating index" << endl;
383 TQValueStack<KMFolderDir*> folders;
384 folders.push(&(kmkernel->folderMgr()->dir()));
385 folders.push(&(kmkernel->dimapFolderMgr()->dir()));
386 while ( !folders.empty() ) {
388 for(KMFolderNode *child = dir->first(); child; child = dir->next()) {
389 if ( child->isDir() )
392 mPendingFolders.push_back( (
KMFolder*)child );
395 mTimer->start( 4000 );
400 bool KMMsgIndex::startQuery( KMSearch* s ) {
401 kdDebug( 5006 ) <<
"KMMsgIndex::startQuery( . )" << endl;
402 if ( mState != s_idle )
return false;
403 if ( !isIndexed( s->root() ) || !canHandleQuery( s->searchPattern() ) )
return false;
405 kdDebug( 5006 ) <<
"KMMsgIndex::startQuery( . ) starting query" << endl;
406 Search* search =
new Search( s );
407 connect( search, TQ_SIGNAL( finished(
bool ) ), s, TQ_SIGNAL( finished(
bool ) ) );
408 connect( search, TQ_SIGNAL( finished(
bool ) ), s, TQ_SLOT( indexFinished() ) );
409 connect( search, TQ_SIGNAL( destroyed( TQObject* ) ), TQ_SLOT( removeSearch( TQObject* ) ) );
410 connect( search, TQ_SIGNAL( found( TQ_UINT32 ) ), s, TQ_SIGNAL( found( TQ_UINT32 ) ) );
411 mSearches.push_back( search );
435 void KMMsgIndex::removeSearch( TQObject* destroyed ) {
436 mSearches.erase( std::find( mSearches.begin(), mSearches.end(), destroyed ) );
440 bool KMMsgIndex::stopQuery( KMSearch* s ) {
441 kdDebug( 5006 ) <<
"KMMsgIndex::stopQuery( . )" << endl;
442 for ( std::vector<Search*>::iterator iter = mSearches.begin(), past = mSearches.end(); iter != past; ++iter ) {
443 if ( ( *iter )->search() == s ) {
445 mSearches.erase( iter );
452 std::vector<TQ_UINT32> KMMsgIndex::simpleSearch( TQString s,
bool* ok )
const {
453 kdDebug( 5006 ) <<
"KMMsgIndex::simpleSearch( -" << s.latin1() <<
"- )" << endl;
454 if ( mState == s_error || mState == s_disabled ) {
455 if ( ok ) *ok =
false;
456 return std::vector<TQ_UINT32>();
458 std::vector<TQ_UINT32> res;
461 std::vector<unsigned> residx = mIndex->search( s.latin1() )->list();
462 res.reserve( residx.size() );
463 for ( std::vector<unsigned>::const_iterator first = residx.begin(), past = residx.end();first != past; ++first ) {
464 res.push_back( std::atoi( mIndex->lookup_docname( *first ).c_str() ) );
466 if ( ok ) *ok =
true;
472 kdDebug( 5006 ) <<
"KMMsgIndex::canHandleQuery( . )" << endl;
473 if ( !pat )
return false;
474 TQPtrListIterator<KMSearchRule> it( *pat );
476 while ( (rule = it.current()) != 0 ) {
478 if ( !rule->
field().isEmpty() && !rule->
contents().isEmpty() &&
479 rule->
function() == KMSearchRule::FuncContains &&
480 rule->
field() ==
"<body>" )
return true;
485 void KMMsgIndex::slotAddMessage(
KMFolder*, TQ_UINT32 serNum ) {
486 kdDebug( 5006 ) <<
"KMMsgIndex::slotAddMessage( . , " << serNum <<
" )" << endl;
487 if ( mState == s_error || mState == s_disabled )
return;
489 if ( mState == s_creating ) mAddedMsgs.push_back( serNum );
490 else mPendingMsgs.push_back( serNum );
492 if ( mState == s_idle ) mState = s_processing;
496 void KMMsgIndex::slotRemoveMessage(
KMFolder*, TQ_UINT32 serNum ) {
497 kdDebug( 5006 ) <<
"KMMsgIndex::slotRemoveMessage( . , " << serNum <<
" )" << endl;
498 if ( mState == s_error || mState == s_disabled )
return;
500 if ( mState == s_idle ) mState = s_processing;
501 mRemovedMsgs.push_back( serNum );
505 void KMMsgIndex::scheduleAction() {
507 if ( mState == s_willcreate || !mIndex )
return;
508 if ( !mSlowDown ) mTimer->start( 0 );
512 void KMMsgIndex::removeMessage( TQ_UINT32 serNum ) {
513 kdDebug( 5006 ) <<
"KMMsgIndex::removeMessage( " << serNum <<
" )" << endl;
514 if ( mState == s_error || mState == s_disabled )
return;
517 mIndex->remove_doc( TQString::number( serNum ).latin1() );
519 if ( mMaintenanceCount > MaintenanceLimit && mRemovedMsgs.empty() ) {
520 TQTimer::singleShot( 100,
this, TQ_SLOT( maintenance() ) );
525 TQString KMMsgIndex::defaultPath() {
529 bool KMMsgIndex::creating()
const {
530 return !mPendingMsgs.empty() || !mPendingFolders.empty();
533 KMMsgIndex::Search::Search( KMSearch* s ):
535 mTimer( new TQTimer( this,
"mTimer" ) ),
537 mState( s_starting ) {
538 connect( mTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( act() ) );
542 KMMsgIndex::Search::~Search() {
546 void KMMsgIndex::Search::act() {
551 for (
KMSearchRule* rule = pat->first(); rule; rule = pat->next() ) {
552 Q_ASSERT( rule->
function() == KMSearchRule::FuncContains );
553 terms += TQString::fromLatin1(
" %1 " ).arg( rule->
contents() );
556 mValues = kmkernel->msgIndex()->simpleSearch( terms, 0 );
564 if ( kapp->hasPendingEvents() ) {
566 mTimer->start( 250 );
567 mState = s_emitstopped;
570 for (
int i = 0; i != 16 && !mValues.empty(); ++i ) {
575 mSearch->inScope( folder ) &&
576 ( !mResidual || mResidual->matches( mValues.back() ) ) ) {
578 emit found( mValues.back() );
582 if ( mValues.empty() ) {
583 emit finished(
true );
KMail list that manages the contents of one directory that may contain folders and/or other directori...
TQString idString() const
Returns a string that can be used to identify this folder.
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
KMMessage * getMsg(int idx)
Read message at given index.
int count(bool cache=false) const
Number of messages in this folder.
int open(const char *owner)
Open folder for access.
static TQString localDataPath()
Returns the full path of the user's local data directory for KMail.
TQString asPlainText(bool stripSignature, bool allowDecryption) const
Return the textual content of the message as plain text, converting HTML to plain text if necessary.
KMail message dictionary.
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
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...
static const KMMsgDict * instance()
Access the globally unique MessageDict.
This class is an abstraction of a search over messages.
Incoming mail is sent through the list of mail filter rules before it is placed in the associated mai...
TQCString field() const
Return message header field name (without the trailing ':').
Function function() const
Return filter function.
TQString contents() const
Return the value.