7 #include "kmaddrbook.h"
8 #include "kmsearchpattern.h"
10 #include "filterlog.h"
12 #include "kmmsgdict.h"
16 #include <libemailfunctions/email.h>
18 #include <tdeglobal.h>
19 #include <tdelocale.h>
21 #include <tdeconfig.h>
23 #include <tdeabc/stdaddressbook.h>
27 #include <mimelib/string.h>
28 #include <mimelib/boyermor.h>
29 #include <mimelib/field.h>
30 #include <mimelib/headers.h>
34 static const char* funcConfigNames[] =
35 {
"contains",
"contains-not",
"equals",
"not-equal",
"regexp",
36 "not-regexp",
"greater",
"less-or-equal",
"less",
"greater-or-equal",
37 "is-in-addressbook",
"is-not-in-addressbook" ,
"is-in-category",
"is-not-in-category",
38 "has-attachment",
"has-no-attachment"};
39 static const int numFuncConfigNames =
sizeof funcConfigNames /
sizeof *funcConfigNames;
46 static struct _statusNames statusNames[] = {
47 {
"Important", KMMsgStatusFlag },
48 {
"New", KMMsgStatusNew },
49 {
"Unread", KMMsgStatusUnread | KMMsgStatusNew },
50 {
"Read", KMMsgStatusRead },
51 {
"Old", KMMsgStatusOld },
52 {
"Deleted", KMMsgStatusDeleted },
53 {
"Replied", KMMsgStatusReplied },
54 {
"Forwarded", KMMsgStatusForwarded },
55 {
"Queued", KMMsgStatusQueued },
56 {
"Sent", KMMsgStatusSent },
57 {
"Watched", KMMsgStatusWatched },
58 {
"Ignored", KMMsgStatusIgnored },
59 {
"To Do", KMMsgStatusTodo },
60 {
"Spam", KMMsgStatusSpam },
61 {
"Ham", KMMsgStatusHam },
62 {
"Has Attachment", KMMsgStatusHasAttach },
63 {
"Invitation", KMMsgStatusHasInvitation }
66 static const int numStatusNames =
sizeof statusNames /
sizeof (
struct _statusNames );
75 KMSearchRule::KMSearchRule(
const TQCString & field, Function func,
const TQString & contents )
83 : mField( other.mField ),
84 mFunction( other.mFunction ),
85 mContents( other.mContents )
93 mField = other.mField;
94 mFunction = other.mFunction;
95 mContents = other.mContents;
102 const TQString & contents )
105 if (
field ==
"<status>")
107 else if (
field ==
"<age in days>" ||
field ==
"<size>" )
117 const TQString & contents )
129 const char cIdx = char(
int(
'A') + aIdx );
131 static const TQString &
field = TDEGlobal::staticQString(
"field" );
132 static const TQString & func = TDEGlobal::staticQString(
"func" );
133 static const TQString &
contents = TDEGlobal::staticQString(
"contents" );
135 const TQCString &field2 = config->readEntry(
field + cIdx ).latin1();
136 Function func2 = configValueToFunc( config->readEntry( func + cIdx ).latin1() );
137 const TQString & contents2 = config->readEntry(
contents + cIdx );
139 if ( field2 ==
"<To or Cc>" )
149 for (
int i = 0 ; i < numFuncConfigNames ; ++i )
150 if ( tqstricmp( funcConfigNames[i], str ) == 0 )
return (
Function)i;
155 TQString KMSearchRule::functionToString( Function
function )
157 if (
function != FuncNone )
158 return funcConfigNames[int(
function )];
164 const char cIdx = char(
'A' + aIdx);
165 static const TQString &
field = TDEGlobal::staticQString(
"field" );
166 static const TQString & func = TDEGlobal::staticQString(
"func" );
167 static const TQString &
contents = TDEGlobal::staticQString(
"contents" );
169 config->writeEntry(
field + cIdx, TQString(mField) );
170 config->writeEntry( func + cIdx, functionToString( mFunction ) );
171 config->writeEntry(
contents + cIdx, mContents );
175 const DwBoyerMoore *,
int )
const
186 TQString result =
"\"" + mField +
"\" <";
187 result += functionToString( mFunction );
188 result +=
"> \"" + mContents +
"\"";
199 KMSearchRuleString::KMSearchRuleString(
const TQCString & field,
200 Function func,
const TQString & contents )
206 mBmHeaderField =
new DwBoyerMoore((
"\n" +
field +
": ").data());
213 if ( other.mBmHeaderField )
214 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
219 if (
this == &other )
223 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
226 delete mBmHeaderField; mBmHeaderField = 0;
227 if ( other.mBmHeaderField )
228 mBmHeaderField =
new DwBoyerMoore( *other.mBmHeaderField );
233 KMSearchRuleString::~KMSearchRuleString()
235 delete mBmHeaderField;
241 return field().stripWhiteSpace().isEmpty() ||
contents().isEmpty();
246 if (mBmHeaderField || (
field() ==
"<recipients>" ))
252 const DwBoyerMoore * aHeaderField,
int aHeaderLen )
const
259 const DwBoyerMoore * headerField = aHeaderField ? aHeaderField : mBmHeaderField ;
261 const int headerLen = ( aHeaderLen > -1 ? aHeaderLen :
field().length() ) + 2 ;
264 static const DwBoyerMoore lflf(
"\n\n" );
265 static const DwBoyerMoore lfcrlf(
"\n\r\n" );
267 size_t endOfHeader = lflf.FindIn( aStr, 0 );
268 if ( endOfHeader == DwString::npos )
269 endOfHeader = lfcrlf.FindIn( aStr, 0 );
270 const DwString headers = ( endOfHeader == DwString::npos ) ? aStr : aStr.substr( 0, endOfHeader );
273 DwString fakedHeaders(
"\n" );
274 size_t start = headerField->FindIn( fakedHeaders.append( headers ), 0,
false );
279 if ( start == DwString::npos )
280 rc = ( (
function() & 1 ) == 1 );
283 size_t stop = aStr.find(
'\n', start );
285 while ( stop != DwString::npos && ( ( ch = aStr.at( stop + 1 ) ) ==
' ' || ch ==
'\t' ) )
286 stop = aStr.find(
'\n', stop + 1 );
287 const int len = stop == DwString::npos ? aStr.length() - start : stop - start ;
288 const TQCString codedValue( aStr.data() + start, len + 1 );
289 const TQString msgContents = KMMsgBase::decodeRFC2047String( codedValue ).stripWhiteSpace();
292 }
else if (
field() ==
"<recipients>" ) {
293 static const DwBoyerMoore to(
"\nTo: ");
294 static const DwBoyerMoore cc(
"\nCc: ");
295 static const DwBoyerMoore bcc(
"\nBcc: ");
299 if ( (
function() & 1 ) == 0 ) {
301 rc = (
matches( aStr, msg, &to, 2 ) ||
302 matches( aStr, msg, &cc, 2 ) ||
303 matches( aStr, msg, &bcc, 3 ) );
307 rc = (
matches( aStr, msg, &to, 2 ) &&
308 matches( aStr, msg, &cc, 2 ) &&
309 matches( aStr, msg, &bcc, 3 ) );
312 if ( FilterLog::instance()->isLogging() ) {
313 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
314 :
"<font color=#FF0000>0 = </font>" );
315 msg += FilterLog::recode(
asString() );
320 FilterLog::instance()->add( msg, FilterLog::ruleResult );
332 TQString msgContents;
335 bool logContents =
true;
337 if(
field() ==
"<message>" ) {
343 const DwHeaders& headers = msg->
headers();
344 const DwField * dwField = headers.FirstField();
345 while( dwField != 0 ) {
346 const char *
const fieldName = dwField->FieldNameStr().c_str();
347 const TQString fieldValue = msg->
headerFields( fieldName ).join(
" " );
348 msgContents +=
" " + fieldValue;
349 dwField = dwField->Next();
352 }
else if (
field() ==
"<body>" ) {
355 }
else if (
field() ==
"<any header>" ) {
358 }
else if (
field() ==
"<recipients>" ) {
362 if (
function() == FuncEquals ||
function() == FuncNotEqual )
375 msgContents +=
", " + msg->
cc();
383 if (
function() == FuncIsInAddressbook ||
384 function() == FuncIsNotInAddressbook ) {
387 if ( msgContents.isEmpty() )
388 return (
function() == FuncIsInAddressbook ) ? false :
true;
392 if (
function() == FuncHasAttachment )
393 return ( msg->
toMsgBase().attachmentState() == KMMsgHasAttachment );
394 if (
function() == FuncHasNoAttachment )
395 return ( ((KMMsgAttachmentState) msg->
toMsgBase().attachmentState()) == KMMsgHasNoAttachment );
398 if ( FilterLog::instance()->isLogging() ) {
399 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
400 :
"<font color=#FF0000>0 = </font>" );
401 msg += FilterLog::recode(
asString() );
404 msg +=
" (<i>" + FilterLog::recode( msgContents ) +
"</i>)";
405 FilterLog::instance()->add( msg, FilterLog::ruleResult );
413 switch (
function() ) {
414 case KMSearchRule::FuncEquals:
415 return ( TQString::compare( msgContents.lower(),
contents().lower() ) == 0 );
417 case KMSearchRule::FuncNotEqual:
418 return ( TQString::compare( msgContents.lower(),
contents().lower() ) != 0 );
420 case KMSearchRule::FuncContains:
421 return ( msgContents.find(
contents(), 0,
false ) >= 0 );
423 case KMSearchRule::FuncContainsNot:
424 return ( msgContents.find(
contents(), 0,
false ) < 0 );
426 case KMSearchRule::FuncRegExp:
428 TQRegExp regexp(
contents(),
false );
429 return ( regexp.search( msgContents ) >= 0 );
432 case KMSearchRule::FuncNotRegExp:
434 TQRegExp regexp(
contents(),
false );
435 return ( regexp.search( msgContents ) < 0 );
439 return ( TQString::compare( msgContents.lower(),
contents().lower() ) > 0 );
441 case FuncIsLessOrEqual:
442 return ( TQString::compare( msgContents.lower(),
contents().lower() ) <= 0 );
445 return ( TQString::compare( msgContents.lower(),
contents().lower() ) < 0 );
447 case FuncIsGreaterOrEqual:
448 return ( TQString::compare( msgContents.lower(),
contents().lower() ) >= 0 );
450 case FuncIsInAddressbook: {
451 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
452 TQStringList addressList =
453 KPIM::splitEmailAddrList( msgContents.lower() );
454 for( TQStringList::ConstIterator it = addressList.begin();
455 ( it != addressList.end() );
457 if ( !stdAb->findByEmail( KPIM::getEmailAddress( *it ) ).isEmpty() )
463 case FuncIsNotInAddressbook: {
464 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
465 TQStringList addressList =
466 KPIM::splitEmailAddrList( msgContents.lower() );
467 for( TQStringList::ConstIterator it = addressList.begin();
468 ( it != addressList.end() );
470 if ( stdAb->findByEmail( KPIM::getEmailAddress( *it ) ).isEmpty() )
476 case FuncIsInCategory: {
478 TQStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
479 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
481 for( TQStringList::ConstIterator it = addressList.begin();
482 it != addressList.end(); ++it ) {
483 TDEABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddress( *it ) );
485 for ( TDEABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
486 if ( (*itAd).hasCategory(category) )
493 case FuncIsNotInCategory: {
495 TQStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() );
496 TDEABC::AddressBook *stdAb = TDEABC::StdAddressBook::self(
true );
498 for( TQStringList::ConstIterator it = addressList.begin();
499 it != addressList.end(); ++it ) {
500 TDEABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddress( *it ) );
502 for ( TDEABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd )
503 if ( (*itAd).hasCategory(category) )
523 KMSearchRuleNumerical::KMSearchRuleNumerical(
const TQCString & field,
524 Function func,
const TQString & contents )
541 TQString msgContents;
542 int numericalMsgContents = 0;
543 int numericalValue = 0;
545 if (
field() ==
"<size>" ) {
546 numericalMsgContents = int( msg->
msgLength() );
547 numericalValue =
contents().toInt();
548 msgContents.setNum( numericalMsgContents );
549 }
else if (
field() ==
"<age in days>" ) {
550 TQDateTime msgDateTime;
551 msgDateTime.setTime_t( msg->date() );
552 numericalMsgContents = msgDateTime.daysTo( TQDateTime::currentDateTime() );
553 numericalValue =
contents().toInt();
554 msgContents.setNum( numericalMsgContents );
556 bool rc =
matchesInternal( numericalValue, numericalMsgContents, msgContents );
557 if ( FilterLog::instance()->isLogging() ) {
558 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
559 :
"<font color=#FF0000>0 = </font>" );
560 msg += FilterLog::recode(
asString() );
561 msg +=
" ( <i>" + TQString::number( numericalMsgContents ) +
"</i> )";
562 FilterLog::instance()->add( msg, FilterLog::ruleResult );
568 long numericalMsgContents,
const TQString & msgContents )
const
570 switch (
function() ) {
571 case KMSearchRule::FuncEquals:
572 return ( numericalValue == numericalMsgContents );
574 case KMSearchRule::FuncNotEqual:
575 return ( numericalValue != numericalMsgContents );
577 case KMSearchRule::FuncContains:
578 return ( msgContents.find(
contents(), 0,
false ) >= 0 );
580 case KMSearchRule::FuncContainsNot:
581 return ( msgContents.find(
contents(), 0,
false ) < 0 );
583 case KMSearchRule::FuncRegExp:
585 TQRegExp regexp(
contents(),
false );
586 return ( regexp.search( msgContents ) >= 0 );
589 case KMSearchRule::FuncNotRegExp:
591 TQRegExp regexp(
contents(),
false );
592 return ( regexp.search( msgContents ) < 0 );
596 return ( numericalMsgContents > numericalValue );
598 case FuncIsLessOrEqual:
599 return ( numericalMsgContents <= numericalValue );
602 return ( numericalMsgContents < numericalValue );
604 case FuncIsGreaterOrEqual:
605 return ( numericalMsgContents >= numericalValue );
607 case FuncIsInAddressbook:
610 case FuncIsNotInAddressbook:
627 TQString englishNameForStatus(
const KMMsgStatus& status )
629 for (
int i=0; i< numStatusNames; i++ ) {
630 if ( statusNames[i].status == status ) {
631 return statusNames[i].name;
637 KMSearchRuleStatus::KMSearchRuleStatus(
const TQCString & field,
638 Function func,
const TQString & aContents )
643 mStatus = statusFromEnglishName( aContents );
646 KMSearchRuleStatus::KMSearchRuleStatus(
int status, Function func )
647 :
KMSearchRule(
"<status>", func, englishNameForStatus( status ) )
652 KMMsgStatus KMSearchRuleStatus::statusFromEnglishName(
const TQString & aStatusString )
654 for (
int i=0; i< numStatusNames; i++ ) {
655 if ( !aStatusString.compare( statusNames[i].name ) ) {
656 return statusNames[i].status;
659 return KMMsgStatusUnknown;
664 return field().stripWhiteSpace().isEmpty() ||
contents().isEmpty();
668 const DwBoyerMoore *,
int )
const
677 KMMsgStatus msgStatus = msg->
status();
680 switch (
function() ) {
683 if (msgStatus & mStatus)
687 case FuncContainsNot:
688 if (! (msgStatus & mStatus) )
697 if ( FilterLog::instance()->isLogging() ) {
698 TQString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
699 :
"<font color=#FF0000>0 = </font>" );
700 msg += FilterLog::recode(
asString() );
701 FilterLog::instance()->add( msg, FilterLog::ruleResult );
717 setAutoDelete(
true );
733 TQPtrListIterator<KMSearchRule> it( *
this );
734 switch ( mOperator ) {
736 for ( it.toFirst() ; it.current() ; ++it )
737 if ( !((*it)->requiresBody() && ignoreBody) )
738 if ( !(*it)->matches( msg ) )
742 for ( it.toFirst() ; it.current() ; ++it )
743 if ( !((*it)->requiresBody() && ignoreBody) )
744 if ( (*it)->matches( msg ) )
758 TQPtrListIterator<KMSearchRule> it( *
this );
759 switch ( mOperator ) {
761 for ( it.toFirst() ; it.current() ; ++it )
762 if ( !((*it)->requiresBody() && ignoreBody) )
763 if ( !(*it)->matches( aStr, msg ) )
767 for ( it.toFirst() ; it.current() ; ++it )
768 if ( !((*it)->requiresBody() && ignoreBody) )
769 if ( (*it)->matches( aStr, msg ) )
786 if (!folder || (idx == -1) || (idx >= folder->
count())) {
793 bool unGet = !msgBase->isMessage();
797 res =
matches( msg, ignoreBody );
808 TQPtrListIterator<KMSearchRule> it( *
this );
809 for ( it.toFirst() ; it.current() ; ++it )
810 if ( (*it)->requiresBody() )
816 TQPtrListIterator<KMSearchRule> it( *
this );
818 while ( it.current() )
819 if ( (*it)->isEmpty() ) {
821 kdDebug(5006) <<
"KMSearchPattern::purify(): removing " << (*it)->asString() << endl;
832 mName = config->readEntry(
"name");
833 if ( !config->hasKey(
"rules") ) {
834 kdDebug(5006) <<
"KMSearchPattern::readConfig: found legacy config! Converting." << endl;
835 importLegacyConfig( config );
839 mOperator = config->readEntry(
"operator") ==
"or" ? OpOr : OpAnd;
841 const int nRules = config->readNumEntry(
"rules", 0 );
843 for (
int i = 0 ; i < nRules ; i++ ) {
852 void KMSearchPattern::importLegacyConfig(
const TDEConfig * config ) {
854 config->readEntry(
"funcA").latin1(),
855 config->readEntry(
"contentsA") );
864 const TQString sOperator = config->readEntry(
"operator");
865 if ( sOperator ==
"ignore" )
return;
868 config->readEntry(
"funcB").latin1(),
869 config->readEntry(
"contentsB") );
876 if ( sOperator ==
"or" ) {
881 if ( sOperator ==
"unless" ) {
886 unsigned int intFunc = (
unsigned int)func;
889 last()->setFunction( func );
896 config->writeEntry(
"name", mName);
897 config->writeEntry(
"operator", (mOperator == KMSearchPattern::OpOr) ?
"or" :
"and" );
900 for ( TQPtrListIterator<KMSearchRule> it( *
this ) ; it.current() && i < FILTER_MAX_RULES ; ++i , ++it )
903 (*it)->writeConfig( config, i );
906 config->writeEntry(
"rules", i );
909 void KMSearchPattern::init() {
912 mName =
'<' + i18n(
"name used for a virgin filter",
"unknown") +
'>';
917 if ( mOperator == OpOr )
918 result = i18n(
"(match any of the following)");
920 result = i18n(
"(match all of the following)");
922 for ( TQPtrListIterator<KMSearchRule> it( *
this ) ; it.current() ; ++it )
923 result +=
"\n\t" + FilterLog::recode( (*it)->asString() );
929 if (
this == &other )
937 for ( TQPtrListIterator<KMSearchRule> it( other ) ; it.current() ; ++it ) {