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 182 return matches( &msg );
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 )
223 setField( other.
field() );
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();
291 rc = matchesInternal( msgContents );
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 )
370 || matchesInternal( msg->
cc() );
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 );
398 bool rc = matchesInternal( msgContents );
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: {
478 TQString category = contents();
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: {
495 TQString category = contents();
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 )
533 contents().toInt( &ok );
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 ) {
This class is an abstraction of a search over messages.
Function
Operators for comparison of field and contents.
static KMSearchRule * createInstance(const TQCString &field=0, Function function=FuncContains, const TQString &contents=TQString())
Create a search rule of a certain type by instantiating the appro- priate subclass depending on the f...
TQString headerAsString() const
Return header as string.
This class represents a search to be performed against a string.
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
virtual bool isEmpty() const
Determine whether the rule is worth considering.
const TQString asString() const
Returns the rule as string.
This class represents a search to be performed against the status of a messsage.
virtual bool isEmpty() const
Determine whether the rule is worth considering.
RAII for KMFolder::open() / close().
bool matchesInternal(const TQString &msgContents) const
Helper for the main matches() method.
size_t msgLength() const
Unlike the above function this works also, if the message is not in a folder.
virtual bool matches(const KMMessage *msg) const
Tries to match the rule against the given KMMessage.
KMMsgBase & toMsgBase()
Get KMMsgBase for this object.
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
static const KMMsgDict * instance()
Access the globally unique MessageDict.
KMSearchPattern::Operator op() const
Get the filter operator.
TQString asString() const
Returns the pattern as string.
virtual bool isEmpty() const
Determine whether the rule is worth considering.
void setOp(KMSearchPattern::Operator aOp)
Set the filter operator.
void readConfig(const TDEConfig *config)
Reads a search pattern from a TDEConfig.
~KMSearchPattern()
Destructor.
void purify()
Removes all empty rules from the list.
Incoming mail is sent through the list of mail filter rules before it is placed in the associated mai...
void setComplete(bool v)
Set if the message is a complete message.
KMSearchPattern(const TDEConfig *config=0)
Constructor that initializes from a given TDEConfig group, if given.
TQCString field() const
Return message header field name (without the trailing ':').
virtual bool matches(const KMMessage *msg) const
Tries to match the rule against the given KMMessage.
virtual bool requiresBody() const
Returns true if the rule depends on a complete message, otherwise returns false.
void setName(const TQString &newName)
Set the name of the search pattern.
virtual bool matches(const KMMessage *msg) const
Tries to match the rule against the given KMMessage.
bool matches(const KMMessage *msg, bool ignoreBody=false) const
The central function of this class.
virtual bool matches(const KMMessage *msg) const =0
Tries to match the rule against the given KMMessage.
TQString name() const
Get the name of the search pattern.
bool isComplete() const
Return true if the complete message is available without referring to the backing store...
void fromDwString(const DwString &str, bool setStatus=false)
Parse the string and create this message from it.
TQString contents() const
Return the value.
virtual bool isEmpty() const =0
Determine whether the rule is worth considering.
This class represents a search to be performed against a numerical value, such as the age of the mess...
int count(bool cache=false) const
Number of messages in this folder.
void writeConfig(TDEConfig *config, int aIdx) const
Save the object into a given config file.
KMMessage * getMsg(int idx)
Read message at given index.
TQStringList headerFields(const TQCString &name) const
Returns a list of the values of all header fields with the given name.
KMMsgStatus status() const
Status of the message.
TQString bodyToUnicode(const TQTextCodec *codec=0) const
Returns the body part decoded to unicode.
void writeConfig(TDEConfig *config) const
Writes itself into config.
TQString cc() const
Get or set the 'Cc' header field.
const KMSearchPattern & operator=(const KMSearchPattern &aPattern)
Overloaded assignment operator.
Function function() const
Return filter function.
DwString getDwString(int idx)
Read a message and returns a DwString.
bool matchesInternal(long numericalValue, long numericalMsgContents, const TQString &msgContents) const
Helper for the main matches() method.
static KMSearchRule * createInstanceFromConfig(const TDEConfig *config, int aIdx)
Initialize the object from a given config file.
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage.
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
KMail Filter Log Collector.
bool requiresBody() const
Returns true if the pattern only depends the DwString that backs a message.
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.