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