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