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