00001
00002
00003
00004
00005
00006 #include <config.h>
00007
00008 #include "kmaddrbook.h"
00009 #include "kmsearchpattern.h"
00010 #include "kmmessage.h"
00011 #include "kmmsgindex.h"
00012 #include "kmmsgdict.h"
00013 #include "filterlog.h"
00014 using KMail::FilterLog;
00015
00016 #include <libkdepim/email.h>
00017
00018 #include <kglobal.h>
00019 #include <klocale.h>
00020 #include <kdebug.h>
00021 #include <kconfig.h>
00022
00023 #include <kabc/stdaddressbook.h>
00024
00025 #include <qregexp.h>
00026
00027 #include <mimelib/string.h>
00028 #include <mimelib/boyermor.h>
00029
00030 #include <assert.h>
00031
00032 static const char* funcConfigNames[] =
00033 { "contains", "contains-not", "equals", "not-equal", "regexp",
00034 "not-regexp", "greater", "less-or-equal", "less", "greater-or-equal",
00035 "is-in-addressbook", "is-not-in-addressbook" , "is-in-category", "is-not-in-category",
00036 "has-attachment", "has-no-attachment"};
00037 static const int numFuncConfigNames = sizeof funcConfigNames / sizeof *funcConfigNames;
00038
00039
00040
00041
00042
00043
00044
00045
00046 KMSearchRule::KMSearchRule( const QCString & field, Function func, const QString & contents )
00047 : mField( field ),
00048 mFunction( func ),
00049 mContents( contents )
00050 {
00051 }
00052
00053 KMSearchRule::KMSearchRule( const KMSearchRule & other )
00054 : mField( other.mField ),
00055 mFunction( other.mFunction ),
00056 mContents( other.mContents )
00057 {
00058 }
00059
00060 const KMSearchRule & KMSearchRule::operator=( const KMSearchRule & other ) {
00061 if ( this == &other )
00062 return *this;
00063
00064 mField = other.mField;
00065 mFunction = other.mFunction;
00066 mContents = other.mContents;
00067
00068 return *this;
00069 }
00070
00071 KMSearchRule * KMSearchRule::createInstance( const QCString & field,
00072 Function func,
00073 const QString & contents )
00074 {
00075 KMSearchRule *ret;
00076 if (field == "<status>")
00077 ret = new KMSearchRuleStatus( field, func, contents );
00078 else if ( field == "<age in days>" || field == "<size>" )
00079 ret = new KMSearchRuleNumerical( field, func, contents );
00080 else
00081 ret = new KMSearchRuleString( field, func, contents );
00082
00083 return ret;
00084 }
00085
00086 KMSearchRule * KMSearchRule::createInstance( const QCString & field,
00087 const char *func,
00088 const QString & contents )
00089 {
00090 return ( createInstance( field, configValueToFunc( func ), contents ) );
00091 }
00092
00093 KMSearchRule * KMSearchRule::createInstance( const KMSearchRule & other )
00094 {
00095 return ( createInstance( other.field(), other.function(), other.contents() ) );
00096 }
00097
00098 KMSearchRule * KMSearchRule::createInstanceFromConfig( const KConfig * config, int aIdx )
00099 {
00100 const char cIdx = char( int('A') + aIdx );
00101
00102 static const QString & field = KGlobal::staticQString( "field" );
00103 static const QString & func = KGlobal::staticQString( "func" );
00104 static const QString & contents = KGlobal::staticQString( "contents" );
00105
00106 const QCString &field2 = config->readEntry( field + cIdx ).latin1();
00107 Function func2 = configValueToFunc( config->readEntry( func + cIdx ).latin1() );
00108 const QString & contents2 = config->readEntry( contents + cIdx );
00109
00110 if ( field2 == "<To or Cc>" )
00111 return KMSearchRule::createInstance( "<recipients>", func2, contents2 );
00112 else
00113 return KMSearchRule::createInstance( field2, func2, contents2 );
00114 }
00115
00116 KMSearchRule::Function KMSearchRule::configValueToFunc( const char * str ) {
00117 if ( !str )
00118 return FuncNone;
00119
00120 for ( int i = 0 ; i < numFuncConfigNames ; ++i )
00121 if ( qstricmp( funcConfigNames[i], str ) == 0 ) return (Function)i;
00122
00123 return FuncNone;
00124 }
00125
00126 QString KMSearchRule::functionToString( Function function )
00127 {
00128 if ( function != FuncNone )
00129 return funcConfigNames[int( function )];
00130 else
00131 return "invalid";
00132 }
00133
00134 void KMSearchRule::writeConfig( KConfig * config, int aIdx ) const {
00135 const char cIdx = char('A' + aIdx);
00136 static const QString & field = KGlobal::staticQString( "field" );
00137 static const QString & func = KGlobal::staticQString( "func" );
00138 static const QString & contents = KGlobal::staticQString( "contents" );
00139
00140 config->writeEntry( field + cIdx, QString(mField) );
00141 config->writeEntry( func + cIdx, functionToString( mFunction ) );
00142 config->writeEntry( contents + cIdx, mContents );
00143 }
00144
00145 bool KMSearchRule::matches( const DwString & aStr, KMMessage & msg,
00146 const DwBoyerMoore *, int ) const
00147 {
00148 if ( !msg.isComplete() ) {
00149 msg.fromDwString( aStr );
00150 msg.setComplete( true );
00151 }
00152 return matches( &msg );
00153 }
00154
00155 const QString KMSearchRule::asString() const
00156 {
00157 QString result = "\"" + mField + "\" <";
00158 result += functionToString( mFunction );
00159 result += "> \"" + mContents + "\"";
00160
00161 return result;
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172 KMSearchRuleString::KMSearchRuleString( const QCString & field,
00173 Function func, const QString & contents )
00174 : KMSearchRule(field, func, contents)
00175 {
00176 if ( field.isEmpty() || field[0] == '<' )
00177 mBmHeaderField = 0;
00178 else
00179 mBmHeaderField = new DwBoyerMoore(("\n" + field + ": ").data());
00180 }
00181
00182 KMSearchRuleString::KMSearchRuleString( const KMSearchRuleString & other )
00183 : KMSearchRule( other ),
00184 mBmHeaderField( 0 )
00185 {
00186 if ( other.mBmHeaderField )
00187 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField );
00188 }
00189
00190 const KMSearchRuleString & KMSearchRuleString::operator=( const KMSearchRuleString & other )
00191 {
00192 if ( this == &other )
00193 return *this;
00194
00195 setField( other.field() );
00196 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField );
00197 setFunction( other.function() );
00198 setContents( other.contents() );
00199 delete mBmHeaderField; mBmHeaderField = 0;
00200 if ( other.mBmHeaderField )
00201 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField );
00202
00203 return *this;
00204 }
00205
00206 KMSearchRuleString::~KMSearchRuleString()
00207 {
00208 delete mBmHeaderField;
00209 mBmHeaderField = 0;
00210 }
00211
00212 bool KMSearchRuleString::isEmpty() const
00213 {
00214 return field().stripWhiteSpace().isEmpty() || contents().isEmpty();
00215 }
00216
00217 bool KMSearchRuleString::requiresBody() const
00218 {
00219 if (mBmHeaderField || (field() == "<recipients>" ))
00220 return false;
00221 return true;
00222 }
00223
00224 bool KMSearchRuleString::matches( const DwString & aStr, KMMessage & msg,
00225 const DwBoyerMoore * aHeaderField, int aHeaderLen ) const
00226 {
00227 if ( isEmpty() )
00228 return false;
00229
00230 const DwBoyerMoore * headerField = aHeaderField ? aHeaderField : mBmHeaderField ;
00231
00232 const int headerLen = ( aHeaderLen > -1 ? aHeaderLen : field().length() ) + 2 ;
00233
00234 if ( headerField ) {
00235 static const DwBoyerMoore lflf( "\n\n" );
00236 static const DwBoyerMoore lfcrlf( "\n\r\n" );
00237
00238 size_t endOfHeader = lflf.FindIn( aStr, 0 );
00239 if ( endOfHeader == DwString::npos )
00240 endOfHeader = lfcrlf.FindIn( aStr, 0 );
00241 const DwString headers = ( endOfHeader == DwString::npos ) ? aStr : aStr.substr( 0, endOfHeader );
00242
00243
00244 DwString fakedHeaders( "\n" );
00245 size_t start = headerField->FindIn( fakedHeaders.append( headers ), 0, false );
00246
00247
00248
00249
00250 if ( start == DwString::npos )
00251 return ( ( function() & 1 ) == 1 );
00252 start += headerLen;
00253 size_t stop = aStr.find( '\n', start );
00254 char ch = '\0';
00255 while ( stop != DwString::npos && ( ch = aStr.at( stop + 1 ) ) == ' ' || ch == '\t' )
00256 stop = aStr.find( '\n', stop + 1 );
00257 const int len = stop == DwString::npos ? aStr.length() - start : stop - start ;
00258 const QCString codedValue( aStr.data() + start, len + 1 );
00259 const QString msgContents = KMMsgBase::decodeRFC2047String( codedValue ).stripWhiteSpace();
00260 return matchesInternal( msgContents );
00261 } else if ( field() == "<recipients>" ) {
00262 static const DwBoyerMoore to("\nTo: ");
00263 static const DwBoyerMoore cc("\nCc: ");
00264 static const DwBoyerMoore bcc("\nBcc: ");
00265
00266
00267
00268 if ( ( function() & 1 ) == 0 ) {
00269
00270 return ( matches( aStr, msg, &to, 2 ) ||
00271 matches( aStr, msg, &cc, 2 ) ||
00272 matches( aStr, msg, &bcc, 3 ) );
00273 }
00274 else {
00275
00276 return ( matches( aStr, msg, &to, 2 ) &&
00277 matches( aStr, msg, &cc, 2 ) &&
00278 matches( aStr, msg, &bcc, 3 ) );
00279 }
00280 }
00281 return false;
00282 }
00283
00284 bool KMSearchRuleString::matches( const KMMessage * msg ) const
00285 {
00286 assert( msg );
00287
00288 if ( isEmpty() )
00289 return false;
00290
00291 QString msgContents;
00292
00293
00294 bool logContents = true;
00295
00296 if( field() == "<message>" ) {
00297 msgContents = msg->asString();
00298 logContents = false;
00299 } else if ( field() == "<body>" ) {
00300 msgContents = msg->bodyToUnicode();
00301 logContents = false;
00302 } else if ( field() == "<any header>" ) {
00303 msgContents = msg->headerAsString();
00304 logContents = false;
00305 } else if ( field() == "<recipients>" ) {
00306
00307
00308
00309 if ( function() == FuncEquals || function() == FuncNotEqual )
00310
00311
00312 return matchesInternal( msg->headerField("To") )
00313 || matchesInternal( msg->headerField("Cc") )
00314 || matchesInternal( msg->headerField("Bcc") )
00315
00316 || matchesInternal( msg->cc() );
00317
00318 msgContents = msg->headerField("To");
00319 if ( !msg->headerField("Cc").compare( msg->cc() ) )
00320 msgContents += ", " + msg->headerField("Cc");
00321 else
00322 msgContents += ", " + msg->cc();
00323 msgContents += ", " + msg->headerField("Bcc");
00324 } else {
00325
00326
00327 msgContents = msg->headerFields( field() ).join( " " );
00328 }
00329
00330 if ( function() == FuncIsInAddressbook ||
00331 function() == FuncIsNotInAddressbook ) {
00332
00333 msgContents = msg->headerField( field() );
00334 if ( msgContents.isEmpty() )
00335 return false;
00336 }
00337
00338
00339 if ( function() == FuncHasAttachment )
00340 return ( msg->toMsgBase().attachmentState() == KMMsgHasAttachment );
00341 if ( function() == FuncHasNoAttachment )
00342 return ( ((KMMsgAttachmentState) msg->toMsgBase().attachmentState()) == KMMsgHasNoAttachment );
00343
00344 bool rc = matchesInternal( msgContents );
00345 if ( FilterLog::instance()->isLogging() ) {
00346 QString msg = ( rc ? "<font color=#00FF00>1 = </font>"
00347 : "<font color=#FF0000>0 = </font>" );
00348 msg += FilterLog::recode( asString() );
00349
00350 if ( logContents )
00351 msg += " (<i>" + FilterLog::recode( msgContents ) + "</i>)";
00352 FilterLog::instance()->add( msg, FilterLog::ruleResult );
00353 }
00354 return rc;
00355 }
00356
00357
00358 bool KMSearchRuleString::matchesInternal( const QString & msgContents ) const
00359 {
00360 switch ( function() ) {
00361 case KMSearchRule::FuncEquals:
00362 return ( QString::compare( msgContents.lower(), contents().lower() ) == 0 );
00363
00364 case KMSearchRule::FuncNotEqual:
00365 return ( QString::compare( msgContents.lower(), contents().lower() ) != 0 );
00366
00367 case KMSearchRule::FuncContains:
00368 return ( msgContents.find( contents(), 0, false ) >= 0 );
00369
00370 case KMSearchRule::FuncContainsNot:
00371 return ( msgContents.find( contents(), 0, false ) < 0 );
00372
00373 case KMSearchRule::FuncRegExp:
00374 {
00375 QRegExp regexp( contents(), false );
00376 return ( regexp.search( msgContents ) >= 0 );
00377 }
00378
00379 case KMSearchRule::FuncNotRegExp:
00380 {
00381 QRegExp regexp( contents(), false );
00382 return ( regexp.search( msgContents ) < 0 );
00383 }
00384
00385 case FuncIsGreater:
00386 return ( QString::compare( msgContents.lower(), contents().lower() ) > 0 );
00387
00388 case FuncIsLessOrEqual:
00389 return ( QString::compare( msgContents.lower(), contents().lower() ) <= 0 );
00390
00391 case FuncIsLess:
00392 return ( QString::compare( msgContents.lower(), contents().lower() ) < 0 );
00393
00394 case FuncIsGreaterOrEqual:
00395 return ( QString::compare( msgContents.lower(), contents().lower() ) >= 0 );
00396
00397 case FuncIsInAddressbook: {
00398 KABC::AddressBook *stdAb = KABC::StdAddressBook::self();
00399 QStringList addressList =
00400 KPIM::splitEmailAddrList( msgContents.lower() );
00401 for( QStringList::ConstIterator it = addressList.begin();
00402 ( it != addressList.end() );
00403 ++it ) {
00404 if ( !stdAb->findByEmail( KPIM::getEmailAddr( *it ) ).isEmpty() )
00405 return true;
00406 }
00407 return false;
00408 }
00409
00410 case FuncIsNotInAddressbook: {
00411 KABC::AddressBook *stdAb = KABC::StdAddressBook::self();
00412 QStringList addressList =
00413 KPIM::splitEmailAddrList( msgContents.lower() );
00414 for( QStringList::ConstIterator it = addressList.begin();
00415 ( it != addressList.end() );
00416 ++it ) {
00417 if ( stdAb->findByEmail( KPIM::getEmailAddr( *it ) ).isEmpty() )
00418 return true;
00419 }
00420 return false;
00421 }
00422
00423 case FuncIsInCategory: {
00424 QString category = contents();
00425 QStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
00426 KABC::AddressBook *stdAb = KABC::StdAddressBook::self();
00427
00428 for( QStringList::ConstIterator it = addressList.begin();
00429 it != addressList.end(); ++it ) {
00430 KABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddr( *it ) );
00431
00432 for ( KABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
00433 if ( (*itAd).hasCategory(category) )
00434 return true;
00435
00436 }
00437 return false;
00438 }
00439
00440 case FuncIsNotInCategory: {
00441 QString category = contents();
00442 QStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
00443 KABC::AddressBook *stdAb = KABC::StdAddressBook::self();
00444
00445 for( QStringList::ConstIterator it = addressList.begin();
00446 it != addressList.end(); ++it ) {
00447 KABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddr( *it ) );
00448
00449 for ( KABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
00450 if ( (*itAd).hasCategory(category) )
00451 return false;
00452
00453 }
00454 return true;
00455 }
00456 default:
00457 ;
00458 }
00459
00460 return false;
00461 }
00462
00463
00464
00465
00466
00467
00468
00469
00470 KMSearchRuleNumerical::KMSearchRuleNumerical( const QCString & field,
00471 Function func, const QString & contents )
00472 : KMSearchRule(field, func, contents)
00473 {
00474 }
00475
00476 bool KMSearchRuleNumerical::isEmpty() const
00477 {
00478 bool ok = false;
00479 contents().toInt( &ok );
00480
00481 return !ok;
00482 }
00483
00484
00485 bool KMSearchRuleNumerical::matches( const KMMessage * msg ) const
00486 {
00487
00488 QString msgContents;
00489 int numericalMsgContents = 0;
00490 int numericalValue = 0;
00491
00492 if ( field() == "<size>" ) {
00493 numericalMsgContents = int( msg->msgLength() );
00494 numericalValue = contents().toInt();
00495 msgContents.setNum( numericalMsgContents );
00496 } else if ( field() == "<age in days>" ) {
00497 QDateTime msgDateTime;
00498 msgDateTime.setTime_t( msg->date() );
00499 numericalMsgContents = msgDateTime.daysTo( QDateTime::currentDateTime() );
00500 numericalValue = contents().toInt();
00501 msgContents.setNum( numericalMsgContents );
00502 }
00503 bool rc = matchesInternal( numericalValue, numericalMsgContents, msgContents );
00504 if ( FilterLog::instance()->isLogging() ) {
00505 QString msg = ( rc ? "<font color=#00FF00>1 = </font>"
00506 : "<font color=#FF0000>0 = </font>" );
00507 msg += FilterLog::recode( asString() );
00508 msg += " ( <i>" + QString::number( numericalMsgContents ) + "</i> )";
00509 FilterLog::instance()->add( msg, FilterLog::ruleResult );
00510 }
00511 return rc;
00512 }
00513
00514 bool KMSearchRuleNumerical::matchesInternal( long numericalValue,
00515 long numericalMsgContents, const QString & msgContents ) const
00516 {
00517 switch ( function() ) {
00518 case KMSearchRule::FuncEquals:
00519 return ( numericalValue == numericalMsgContents );
00520
00521 case KMSearchRule::FuncNotEqual:
00522 return ( numericalValue != numericalMsgContents );
00523
00524 case KMSearchRule::FuncContains:
00525 return ( msgContents.find( contents(), 0, false ) >= 0 );
00526
00527 case KMSearchRule::FuncContainsNot:
00528 return ( msgContents.find( contents(), 0, false ) < 0 );
00529
00530 case KMSearchRule::FuncRegExp:
00531 {
00532 QRegExp regexp( contents(), false );
00533 return ( regexp.search( msgContents ) >= 0 );
00534 }
00535
00536 case KMSearchRule::FuncNotRegExp:
00537 {
00538 QRegExp regexp( contents(), false );
00539 return ( regexp.search( msgContents ) < 0 );
00540 }
00541
00542 case FuncIsGreater:
00543 return ( numericalMsgContents > numericalValue );
00544
00545 case FuncIsLessOrEqual:
00546 return ( numericalMsgContents <= numericalValue );
00547
00548 case FuncIsLess:
00549 return ( numericalMsgContents < numericalValue );
00550
00551 case FuncIsGreaterOrEqual:
00552 return ( numericalMsgContents >= numericalValue );
00553
00554 case FuncIsInAddressbook:
00555 return false;
00556
00557 case FuncIsNotInAddressbook:
00558 return false;
00559
00560 default:
00561 ;
00562 }
00563
00564 return false;
00565 }
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 KMSearchRuleStatus::KMSearchRuleStatus( const QCString & field,
00577 Function func, const QString & aContents )
00578 : KMSearchRule(field, func, aContents)
00579 {
00580
00581
00582 mStatus = statusFromEnglishName( aContents );
00583 }
00584
00585 KMMsgStatus KMSearchRuleStatus::statusFromEnglishName(
00586 const QString & aStatusString )
00587 {
00588 KMMsgStatus status = 0;
00589 if ( ! aStatusString.compare("important") )
00590 status = KMMsgStatusFlag;
00591 if ( ! aStatusString.compare("new") )
00592 status = KMMsgStatusNew;
00593 if ( ! aStatusString.compare("unread") )
00594 status = KMMsgStatusUnread | KMMsgStatusNew;
00595 if ( ! aStatusString.compare("read") )
00596 status = KMMsgStatusRead;
00597 if ( ! aStatusString.compare("old") )
00598 status = KMMsgStatusOld;
00599 if ( ! aStatusString.compare("deleted") )
00600 status = KMMsgStatusDeleted;
00601 if ( ! aStatusString.compare("replied") )
00602 status = KMMsgStatusReplied;
00603 if ( ! aStatusString.compare("forwarded") )
00604 status = KMMsgStatusForwarded;
00605 if ( ! aStatusString.compare("queued") )
00606 status = KMMsgStatusQueued;
00607 if ( ! aStatusString.compare("sent") )
00608 status = KMMsgStatusSent;
00609 if ( ! aStatusString.compare("watched") )
00610 status = KMMsgStatusWatched;
00611 if ( ! aStatusString.compare("ignored") )
00612 status = KMMsgStatusIgnored;
00613 if ( ! aStatusString.compare("todo") )
00614 status = KMMsgStatusTodo;
00615 if ( ! aStatusString.compare("spam") )
00616 status = KMMsgStatusSpam;
00617 if ( ! aStatusString.compare("ham") )
00618 status = KMMsgStatusHam;
00619 if ( ! aStatusString.compare("has an attachment") )
00620 status = KMMsgStatusHasAttach;
00621
00622 return status;
00623 }
00624
00625 bool KMSearchRuleStatus::isEmpty() const
00626 {
00627 return field().stripWhiteSpace().isEmpty() || contents().isEmpty();
00628 }
00629
00630 bool KMSearchRuleStatus::matches( const DwString &, KMMessage &,
00631 const DwBoyerMoore *, int ) const
00632 {
00633 assert( 0 );
00634 return false;
00635 }
00636
00637 bool KMSearchRuleStatus::matches( const KMMessage * msg ) const
00638 {
00639
00640 KMMsgStatus msgStatus = msg->status();
00641 bool rc = false;
00642
00643 switch ( function() ) {
00644 case FuncEquals:
00645 case FuncContains:
00646 if (msgStatus & mStatus)
00647 rc = true;
00648 break;
00649 case FuncNotEqual:
00650 case FuncContainsNot:
00651 if (! (msgStatus & mStatus) )
00652 rc = true;
00653 break;
00654
00655
00656 default:
00657 break;
00658 }
00659
00660 if ( FilterLog::instance()->isLogging() ) {
00661 QString msg = ( rc ? "<font color=#00FF00>1 = </font>"
00662 : "<font color=#FF0000>0 = </font>" );
00663 msg += FilterLog::recode( asString() );
00664 FilterLog::instance()->add( msg, FilterLog::ruleResult );
00665 }
00666 return rc;
00667 }
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677 KMSearchPattern::KMSearchPattern( const KConfig * config )
00678 : QPtrList<KMSearchRule>()
00679 {
00680 setAutoDelete( true );
00681 if ( config )
00682 readConfig( config );
00683 else
00684 init();
00685 }
00686
00687 KMSearchPattern::~KMSearchPattern()
00688 {
00689 }
00690
00691 bool KMSearchPattern::matches( const KMMessage * msg ) const
00692 {
00693 if ( isEmpty() )
00694 return true;
00695
00696 QPtrListIterator<KMSearchRule> it( *this );
00697 switch ( mOperator ) {
00698 case OpAnd:
00699 for ( it.toFirst() ; it.current() ; ++it )
00700 if ( !(*it)->matches( msg ) )
00701 return false;
00702 return true;
00703 case OpOr:
00704 for ( it.toFirst() ; it.current() ; ++it )
00705 if ( (*it)->matches( msg ) )
00706 return true;
00707
00708 default:
00709 return false;
00710 }
00711 }
00712
00713 bool KMSearchPattern::matches( const DwString & aStr ) const
00714 {
00715 if ( isEmpty() )
00716 return true;
00717
00718 KMMessage msg;
00719 QPtrListIterator<KMSearchRule> it( *this );
00720 switch ( mOperator ) {
00721 case OpAnd:
00722 for ( it.toFirst() ; it.current() ; ++it )
00723 if ( !(*it)->matches( aStr, msg ) )
00724 return false;
00725 return true;
00726 case OpOr:
00727 for ( it.toFirst() ; it.current() ; ++it )
00728 if ( (*it)->matches( aStr, msg ) )
00729 return true;
00730
00731 default:
00732 return false;
00733 }
00734 }
00735
00736 bool KMSearchPattern::matches( Q_UINT32 serNum ) const
00737 {
00738 if ( isEmpty() )
00739 return true;
00740
00741 bool res;
00742 int idx = -1;
00743 KMFolder *folder = 0;
00744 kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
00745 if (!folder || (idx == -1) || (idx >= folder->count())) {
00746 return false;
00747 }
00748
00749 bool opened = folder->isOpened();
00750 if ( !opened )
00751 folder->open();
00752 KMMsgBase *msgBase = folder->getMsgBase(idx);
00753 if (requiresBody()) {
00754 bool unGet = !msgBase->isMessage();
00755 KMMessage *msg = folder->getMsg(idx);
00756 res = matches( msg );
00757 if (unGet)
00758 folder->unGetMsg(idx);
00759 } else {
00760 res = matches( folder->getDwString(idx) );
00761 }
00762 if ( !opened )
00763 folder->close();
00764 return res;
00765 }
00766
00767 bool KMSearchPattern::requiresBody() const {
00768 QPtrListIterator<KMSearchRule> it( *this );
00769 for ( it.toFirst() ; it.current() ; ++it )
00770 if ( (*it)->requiresBody() )
00771 return true;
00772 return false;
00773 }
00774
00775 void KMSearchPattern::purify() {
00776 QPtrListIterator<KMSearchRule> it( *this );
00777 it.toLast();
00778 while ( it.current() )
00779 if ( (*it)->isEmpty() ) {
00780 #ifndef NDEBUG
00781 kdDebug(5006) << "KMSearchPattern::purify(): removing " << (*it)->asString() << endl;
00782 #endif
00783 remove( *it );
00784 } else {
00785 --it;
00786 }
00787 }
00788
00789 void KMSearchPattern::readConfig( const KConfig * config ) {
00790 init();
00791
00792 mName = config->readEntry("name");
00793 if ( !config->hasKey("rules") ) {
00794 kdDebug(5006) << "KMSearchPattern::readConfig: found legacy config! Converting." << endl;
00795 importLegacyConfig( config );
00796 return;
00797 }
00798
00799 mOperator = config->readEntry("operator") == "or" ? OpOr : OpAnd;
00800
00801 const int nRules = config->readNumEntry( "rules", 0 );
00802
00803 for ( int i = 0 ; i < nRules ; i++ ) {
00804 KMSearchRule * r = KMSearchRule::createInstanceFromConfig( config, i );
00805 if ( r->isEmpty() )
00806 delete r;
00807 else
00808 append( r );
00809 }
00810 }
00811
00812 void KMSearchPattern::importLegacyConfig( const KConfig * config ) {
00813 KMSearchRule * rule = KMSearchRule::createInstance( config->readEntry("fieldA").latin1(),
00814 config->readEntry("funcA").latin1(),
00815 config->readEntry("contentsA") );
00816 if ( rule->isEmpty() ) {
00817
00818
00819 delete rule;
00820 return;
00821 }
00822 append( rule );
00823
00824 const QString sOperator = config->readEntry("operator");
00825 if ( sOperator == "ignore" ) return;
00826
00827 rule = KMSearchRule::createInstance( config->readEntry("fieldB").latin1(),
00828 config->readEntry("funcB").latin1(),
00829 config->readEntry("contentsB") );
00830 if ( rule->isEmpty() ) {
00831 delete rule;
00832 return;
00833 }
00834 append( rule );
00835
00836 if ( sOperator == "or" ) {
00837 mOperator = OpOr;
00838 return;
00839 }
00840
00841 if ( sOperator == "unless" ) {
00842
00843
00844
00845 KMSearchRule::Function func = last()->function();
00846 unsigned int intFunc = (unsigned int)func;
00847 func = KMSearchRule::Function( intFunc ^ 0x1 );
00848
00849 last()->setFunction( func );
00850 }
00851
00852
00853 }
00854
00855 void KMSearchPattern::writeConfig( KConfig * config ) const {
00856 config->writeEntry("name", mName);
00857 config->writeEntry("operator", (mOperator == KMSearchPattern::OpOr) ? "or" : "and" );
00858
00859 int i = 0;
00860 for ( QPtrListIterator<KMSearchRule> it( *this ) ; it.current() && i < FILTER_MAX_RULES ; ++i , ++it )
00861
00862
00863 (*it)->writeConfig( config, i );
00864
00865
00866 config->writeEntry( "rules", i );
00867 }
00868
00869 void KMSearchPattern::init() {
00870 clear();
00871 mOperator = OpAnd;
00872 mName = '<' + i18n("name used for a virgin filter","unknown") + '>';
00873 }
00874
00875 QString KMSearchPattern::asString() const {
00876 QString result;
00877 if ( mOperator == OpOr )
00878 result = i18n("(match any of the following)");
00879 else
00880 result = i18n("(match all of the following)");
00881
00882 for ( QPtrListIterator<KMSearchRule> it( *this ) ; it.current() ; ++it )
00883 result += "\n\t" + FilterLog::recode( (*it)->asString() );
00884
00885 return result;
00886 }
00887
00888 const KMSearchPattern & KMSearchPattern::operator=( const KMSearchPattern & other ) {
00889 if ( this == &other )
00890 return *this;
00891
00892 setOp( other.op() );
00893 setName( other.name() );
00894
00895 clear();
00896 for ( QPtrListIterator<KMSearchRule> it( other ) ; it.current() ; ++it )
00897 append( KMSearchRule::createInstance( **it ) );
00898
00899 return *this;
00900 }