8 #include "kmaddrbook.h"
9 #include "kmsearchpattern.h"
10 #include "kmmsgdict.h"
11 #include "filterlog.h"
13 #include "kmmsgdict.h"
17 #include <libemailfunctions/email.h>
19 #include <tdeglobal.h>
20 #include <tdelocale.h>
22 #include <tdeconfig.h>
24 #include <tdeabc/stdaddressbook.h>
28 #include <mimelib/string.h>
29 #include <mimelib/boyermor.h>
30 #include <mimelib/field.h>
31 #include <mimelib/headers.h>
35 static const char* funcConfigNames[] =
36 {
"contains",
"contains-not",
"equals",
"not-equal",
"regexp",
37 "not-regexp",
"greater",
"less-or-equal",
"less",
"greater-or-equal",
38 "is-in-addressbook",
"is-not-in-addressbook" ,
"is-in-category",
"is-not-in-category",
39 "has-attachment",
"has-no-attachment"};
40 static const int numFuncConfigNames =
sizeof funcConfigNames /
sizeof *funcConfigNames;
47 static struct _statusNames statusNames[] = {
48 {
"Important", KMMsgStatusFlag },
49 {
"New", KMMsgStatusNew },
50 {
"Unread", KMMsgStatusUnread | KMMsgStatusNew },
51 {
"Read", KMMsgStatusRead },
52 {
"Old", KMMsgStatusOld },
53 {
"Deleted", KMMsgStatusDeleted },
54 {
"Replied", KMMsgStatusReplied },
55 {
"Forwarded", KMMsgStatusForwarded },
56 {
"Queued", KMMsgStatusQueued },
57 {
"Sent", KMMsgStatusSent },
58 {
"Watched", KMMsgStatusWatched },
59 {
"Ignored", KMMsgStatusIgnored },
60 {
"To Do", KMMsgStatusTodo },
61 {
"Spam", KMMsgStatusSpam },
62 {
"Ham", KMMsgStatusHam },
63 {
"Has Attachment", KMMsgStatusHasAttach },
64 {
"Invitation", KMMsgStatusHasInvitation }
67 static const int numStatusNames =
sizeof statusNames /
sizeof (
struct _statusNames );
76 KMSearchRule::KMSearchRule(
const TQCString & field, Function func,
const TQString & contents )
84 : mField( other.mField ),
85 mFunction( other.mFunction ),
86 mContents( other.mContents )
94 mField = other.mField;
95 mFunction = other.mFunction;
96 mContents = other.mContents;
103 const TQString & contents )
106 if (field ==
"<status>")
108 else if ( field ==
"<age in days>" || field ==
"<size>" )
118 const TQString & contents )
120 return (
createInstance( field, configValueToFunc( func ), contents ) );
130 const char cIdx = char(
int(
'A') + aIdx );
132 static const TQString & field = TDEGlobal::staticQString(
"field" );
133 static const TQString & func = TDEGlobal::staticQString(
"func" );
134 static const TQString & contents = TDEGlobal::staticQString(
"contents" );
136 const TQCString &field2 = config->readEntry( field + cIdx ).latin1();
137 Function func2 = configValueToFunc( config->readEntry( func + cIdx ).latin1() );
138 const TQString & contents2 = config->readEntry( contents + cIdx );
140 if ( field2 ==
"<To or Cc>" )
150 for (
int i = 0 ; i < numFuncConfigNames ; ++i )
151 if ( tqstricmp( funcConfigNames[i], str ) == 0 )
return (
Function)i;
156 TQString KMSearchRule::functionToString( Function
function )
158 if (
function != FuncNone )
159 return funcConfigNames[int(
function )];
165 const char cIdx = char(
'A' + aIdx);
166 static const TQString & field = TDEGlobal::staticQString(
"field" );
167 static const TQString & func = TDEGlobal::staticQString(
"func" );
168 static const TQString & contents = TDEGlobal::staticQString(
"contents" );
170 config->writeEntry( field + cIdx, TQString(mField) );
171 config->writeEntry( func + cIdx, functionToString( mFunction ) );
172 config->writeEntry( contents + cIdx, mContents );
176 const DwBoyerMoore *,
int )
const
187 TQString result =
"\"" + mField +
"\" <";
188 result += functionToString( mFunction );
189 result +=
"> \"" + mContents +
"\"";
200 KMSearchRuleString::KMSearchRuleString(
const TQCString & field,
201 Function func,
const TQString & contents )
204 if ( field.isEmpty() || field[0] ==
'<' )
207 mBmHeaderField =
new DwBoyerMoore((
"\n" + field +
": ").data());
214 if ( other.mBmHeaderField )
215 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
220 if (
this == &other )
224 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
227 delete mBmHeaderField; mBmHeaderField = 0;
228 if ( other.mBmHeaderField )
229 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
234 KMSearchRuleString::~KMSearchRuleString()
236 delete mBmHeaderField;
242 return field().stripWhiteSpace().isEmpty() ||
contents().isEmpty();
247 if (mBmHeaderField || (
field() ==
"<recipients>" ))
253 const DwBoyerMoore * aHeaderField,
int aHeaderLen )
const
260 const DwBoyerMoore * headerField = aHeaderField ? aHeaderField : mBmHeaderField ;
262 const int headerLen = ( aHeaderLen > -1 ? aHeaderLen :
field().length() ) + 2 ;
265 static const DwBoyerMoore lflf(
"\n\n" );
266 static const DwBoyerMoore lfcrlf(
"\n\r\n" );
268 size_t endOfHeader = lflf.FindIn( aStr, 0 );
269 if ( endOfHeader == DwString::npos )
270 endOfHeader = lfcrlf.FindIn( aStr, 0 );
271 const DwString headers = ( endOfHeader == DwString::npos ) ? aStr : aStr.substr( 0, endOfHeader );
274 DwString fakedHeaders(
"\n" );
275 size_t start = headerField->FindIn( fakedHeaders.append( headers ), 0, false );
280 if ( start == DwString::npos )
281 rc = ( (
function() & 1 ) == 1 );
284 size_t stop = aStr.find(
'\n', start );
286 while ( stop != DwString::npos && ( ( ch = aStr.at( stop + 1 ) ) ==
' ' || ch ==
'\t' ) )
287 stop = aStr.find(
'\n', stop + 1 );
288 const int len = stop == DwString::npos ? aStr.length() - start : stop - start ;
289 const TQCString codedValue( aStr.data() + start, len + 1 );
290 const TQString msgContents = KMMsgBase::decodeRFC2047String( codedValue ).stripWhiteSpace();
293 }
else if (
field() ==
"<recipients>" ) {
294 static const DwBoyerMoore to(
"\nTo: ");
295 static const DwBoyerMoore cc(
"\nCc: ");
296 static const DwBoyerMoore bcc(
"\nBcc: ");
300 if ( (
function() & 1 ) == 0 ) {
302 rc = (
matches( aStr, msg, &to, 2 ) ||
303 matches( aStr, msg, &cc, 2 ) ||
304 matches( aStr, msg, &bcc, 3 ) );
308 rc = (
matches( aStr, msg, &to, 2 ) &&
309 matches( aStr, msg, &cc, 2 ) &&
310 matches( aStr, msg, &bcc, 3 ) );
313 if ( FilterLog::instance()->isLogging() ) {
314 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
315 :
"<font color=#FF0000>0 = </font>" );
316 msg += FilterLog::recode(
asString() );
321 FilterLog::instance()->add( msg, FilterLog::ruleResult );
333 TQString msgContents;
336 bool logContents =
true;
338 if(
field() ==
"<message>" ) {
344 const DwHeaders& headers = msg->
headers();
345 const DwField * dwField = headers.FirstField();
346 while( dwField != 0 ) {
347 const char *
const fieldName = dwField->FieldNameStr().c_str();
348 const TQString fieldValue = msg->
headerFields( fieldName ).join(
" " );
349 msgContents +=
" " + fieldValue;
350 dwField = dwField->Next();
353 }
else if (
field() ==
"<body>" ) {
356 }
else if (
field() ==
"<any header>" ) {
359 }
else if (
field() ==
"<recipients>" ) {
363 if (
function() == FuncEquals ||
function() == FuncNotEqual )
376 msgContents +=
", " + msg->
cc();
384 if (
function() == FuncIsInAddressbook ||
385 function() == FuncIsNotInAddressbook ) {
388 if ( msgContents.isEmpty() )
389 return (
function() == FuncIsInAddressbook ) ?
false :
true;
393 if (
function() == FuncHasAttachment )
394 return ( msg->
toMsgBase().attachmentState() == KMMsgHasAttachment );
395 if (
function() == FuncHasNoAttachment )
396 return ( ((KMMsgAttachmentState) msg->
toMsgBase().attachmentState()) == KMMsgHasNoAttachment );
399 if ( FilterLog::instance()->isLogging() ) {
400 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
401 :
"<font color=#FF0000>0 = </font>" );
402 msg += FilterLog::recode(
asString() );
405 msg +=
" (<i>" + FilterLog::recode( msgContents ) +
"</i>)";
406 FilterLog::instance()->add( msg, FilterLog::ruleResult );
414 switch (
function() ) {
415 case KMSearchRule::FuncEquals:
416 return ( TQString::compare( msgContents.lower(),
contents().lower() ) == 0 );
418 case KMSearchRule::FuncNotEqual:
419 return ( TQString::compare( msgContents.lower(),
contents().lower() ) != 0 );
421 case KMSearchRule::FuncContains:
422 return ( msgContents.find( contents(), 0,
false ) >= 0 );
424 case KMSearchRule::FuncContainsNot:
425 return ( msgContents.find( contents(), 0,
false ) < 0 );
427 case KMSearchRule::FuncRegExp:
429 TQRegExp regexp(
contents(),
false );
430 return ( regexp.search( msgContents ) >= 0 );
433 case KMSearchRule::FuncNotRegExp:
435 TQRegExp regexp(
contents(),
false );
436 return ( regexp.search( msgContents ) < 0 );
440 return ( TQString::compare( msgContents.lower(),
contents().lower() ) > 0 );
442 case FuncIsLessOrEqual:
443 return ( TQString::compare( msgContents.lower(),
contents().lower() ) <= 0 );
446 return ( TQString::compare( msgContents.lower(),
contents().lower() ) < 0 );
448 case FuncIsGreaterOrEqual:
449 return ( TQString::compare( msgContents.lower(),
contents().lower() ) >= 0 );
451 case FuncIsInAddressbook: {
452 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
453 TQStringList addressList =
454 KPIM::splitEmailAddrList( msgContents.lower() );
455 for( TQStringList::ConstIterator it = addressList.begin();
456 ( it != addressList.end() );
458 if ( !stdAb->findByEmail( KPIM::getEmailAddress( *it ) ).isEmpty() )
464 case FuncIsNotInAddressbook: {
465 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
466 TQStringList addressList =
467 KPIM::splitEmailAddrList( msgContents.lower() );
468 for( TQStringList::ConstIterator it = addressList.begin();
469 ( it != addressList.end() );
471 if ( stdAb->findByEmail( KPIM::getEmailAddress( *it ) ).isEmpty() )
477 case FuncIsInCategory: {
479 TQStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
480 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
482 for( TQStringList::ConstIterator it = addressList.begin();
483 it != addressList.end(); ++it ) {
484 TDEABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddress( *it ) );
486 for ( TDEABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
487 if ( (*itAd).hasCategory(category) )
494 case FuncIsNotInCategory: {
496 TQStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
497 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
499 for( TQStringList::ConstIterator it = addressList.begin();
500 it != addressList.end(); ++it ) {
501 TDEABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddress( *it ) );
503 for ( TDEABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
504 if ( (*itAd).hasCategory(category) )
524 KMSearchRuleNumerical::KMSearchRuleNumerical(
const TQCString & field,
525 Function func,
const TQString & contents )
542 TQString msgContents;
543 int numericalMsgContents = 0;
544 int numericalValue = 0;
546 if (
field() ==
"<size>" ) {
547 numericalMsgContents = int( msg->
msgLength() );
548 numericalValue =
contents().toInt();
549 msgContents.setNum( numericalMsgContents );
550 }
else if (
field() ==
"<age in days>" ) {
551 TQDateTime msgDateTime;
552 msgDateTime.setTime_t( msg->date() );
553 numericalMsgContents = msgDateTime.daysTo( TQDateTime::currentDateTime() );
554 numericalValue =
contents().toInt();
555 msgContents.setNum( numericalMsgContents );
557 bool rc =
matchesInternal( numericalValue, numericalMsgContents, msgContents );
558 if ( FilterLog::instance()->isLogging() ) {
559 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
560 :
"<font color=#FF0000>0 = </font>" );
561 msg += FilterLog::recode(
asString() );
562 msg +=
" ( <i>" + TQString::number( numericalMsgContents ) +
"</i> )";
563 FilterLog::instance()->add( msg, FilterLog::ruleResult );
569 long numericalMsgContents,
const TQString & msgContents )
const
571 switch (
function() ) {
572 case KMSearchRule::FuncEquals:
573 return ( numericalValue == numericalMsgContents );
575 case KMSearchRule::FuncNotEqual:
576 return ( numericalValue != numericalMsgContents );
578 case KMSearchRule::FuncContains:
579 return ( msgContents.find( contents(), 0,
false ) >= 0 );
581 case KMSearchRule::FuncContainsNot:
582 return ( msgContents.find( contents(), 0,
false ) < 0 );
584 case KMSearchRule::FuncRegExp:
586 TQRegExp regexp(
contents(),
false );
587 return ( regexp.search( msgContents ) >= 0 );
590 case KMSearchRule::FuncNotRegExp:
592 TQRegExp regexp(
contents(),
false );
593 return ( regexp.search( msgContents ) < 0 );
597 return ( numericalMsgContents > numericalValue );
599 case FuncIsLessOrEqual:
600 return ( numericalMsgContents <= numericalValue );
603 return ( numericalMsgContents < numericalValue );
605 case FuncIsGreaterOrEqual:
606 return ( numericalMsgContents >= numericalValue );
608 case FuncIsInAddressbook:
611 case FuncIsNotInAddressbook:
628 TQString englishNameForStatus(
const KMMsgStatus& status )
630 for (
int i=0; i< numStatusNames; i++ ) {
631 if ( statusNames[i].status == status ) {
632 return statusNames[i].name;
638 KMSearchRuleStatus::KMSearchRuleStatus(
const TQCString & field,
639 Function func,
const TQString & aContents )
644 mStatus = statusFromEnglishName( aContents );
647 KMSearchRuleStatus::KMSearchRuleStatus(
int status, Function func )
648 :
KMSearchRule(
"<status>", func, englishNameForStatus( status ) )
653 KMMsgStatus KMSearchRuleStatus::statusFromEnglishName(
const TQString & aStatusString )
655 for (
int i=0; i< numStatusNames; i++ ) {
656 if ( !aStatusString.compare( statusNames[i].name ) ) {
657 return statusNames[i].status;
660 return KMMsgStatusUnknown;
665 return field().stripWhiteSpace().isEmpty() ||
contents().isEmpty();
669 const DwBoyerMoore *,
int )
const
678 KMMsgStatus msgStatus = msg->
status();
681 switch (
function() ) {
684 if (msgStatus & mStatus)
688 case FuncContainsNot:
689 if (! (msgStatus & mStatus) )
698 if ( FilterLog::instance()->isLogging() ) {
699 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
700 :
"<font color=#FF0000>0 = </font>" );
701 msg += FilterLog::recode(
asString() );
702 FilterLog::instance()->add( msg, FilterLog::ruleResult );
718 setAutoDelete(
true );
734 TQPtrListIterator<KMSearchRule> it( *
this );
735 switch ( mOperator ) {
737 for ( it.toFirst() ; it.current() ; ++it )
738 if ( !((*it)->requiresBody() && ignoreBody) )
739 if ( !(*it)->matches( msg ) )
743 for ( it.toFirst() ; it.current() ; ++it )
744 if ( !((*it)->requiresBody() && ignoreBody) )
745 if ( (*it)->matches( msg ) )
759 TQPtrListIterator<KMSearchRule> it( *
this );
760 switch ( mOperator ) {
762 for ( it.toFirst() ; it.current() ; ++it )
763 if ( !((*it)->requiresBody() && ignoreBody) )
764 if ( !(*it)->matches( aStr, msg ) )
768 for ( it.toFirst() ; it.current() ; ++it )
769 if ( !((*it)->requiresBody() && ignoreBody) )
770 if ( (*it)->matches( aStr, msg ) )
787 if (!folder || (idx == -1) || (idx >= folder->
count())) {
794 bool unGet = !msgBase->isMessage();
798 res =
matches( msg, ignoreBody );
809 TQPtrListIterator<KMSearchRule> it( *
this );
810 for ( it.toFirst() ; it.current() ; ++it )
811 if ( (*it)->requiresBody() )
817 TQPtrListIterator<KMSearchRule> it( *
this );
819 while ( it.current() )
820 if ( (*it)->isEmpty() ) {
822 kdDebug(5006) <<
"KMSearchPattern::purify(): removing " << (*it)->asString() << endl;
833 mName = config->readEntry(
"name");
834 if ( !config->hasKey(
"rules") ) {
835 kdDebug(5006) <<
"KMSearchPattern::readConfig: found legacy config! Converting." << endl;
836 importLegacyConfig( config );
840 mOperator = config->readEntry(
"operator") ==
"or" ? OpOr : OpAnd;
842 const int nRules = config->readNumEntry(
"rules", 0 );
844 for (
int i = 0 ; i < nRules ; i++ ) {
853 void KMSearchPattern::importLegacyConfig(
const TDEConfig * config ) {
855 config->readEntry(
"funcA").latin1(),
856 config->readEntry(
"contentsA") );
865 const TQString sOperator = config->readEntry(
"operator");
866 if ( sOperator ==
"ignore" )
return;
869 config->readEntry(
"funcB").latin1(),
870 config->readEntry(
"contentsB") );
877 if ( sOperator ==
"or" ) {
882 if ( sOperator ==
"unless" ) {
887 unsigned int intFunc = (
unsigned int)func;
890 last()->setFunction( func );
897 config->writeEntry(
"name", mName);
898 config->writeEntry(
"operator", (mOperator == KMSearchPattern::OpOr) ?
"or" :
"and" );
901 for ( TQPtrListIterator<KMSearchRule> it( *
this ) ; it.current() && i < FILTER_MAX_RULES ; ++i , ++it )
904 (*it)->writeConfig( config, i );
907 config->writeEntry(
"rules", i );
910 void KMSearchPattern::init() {
913 mName =
'<' + i18n(
"name used for a virgin filter",
"unknown") +
'>';
918 if ( mOperator == OpOr )
919 result = i18n(
"(match any of the following)");
921 result = i18n(
"(match all of the following)");
923 for ( TQPtrListIterator<KMSearchRule> it( *
this ) ; it.current() ; ++it )
924 result +=
"\n\t" + FilterLog::recode( (*it)->asString() );
930 if (
this == &other )
938 for ( TQPtrListIterator<KMSearchRule> it( other ) ; it.current() ; ++it ) {