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