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 );