25 #include <ontologies/nie.h>
26 #include <ontologies/nmo.h>
27 #include <ontologies/nco.h>
29 #include <Nepomuk2/Tag>
30 #include <Nepomuk2/Query/Query>
31 #include <Nepomuk2/Query/AndTerm>
32 #include <Nepomuk2/Query/OrTerm>
33 #include <Nepomuk2/Query/LiteralTerm>
34 #include <Nepomuk2/Query/ResourceTerm>
35 #include <Nepomuk2/Query/NegationTerm>
36 #include <Nepomuk2/Query/ResourceTypeTerm>
37 #include <Nepomuk2/Vocabulary/PIMO>
38 #include <Soprano/Vocabulary/NAO>
39 #include <Soprano/Vocabulary/RDF>
40 #include <Nepomuk2/Vocabulary/NIE>
42 #include <Akonadi/Contact/ContactSearchJob>
44 #include <KMime/KMimeMessage>
46 #include <KPIMUtils/Email>
51 #include <KConfigGroup>
55 #include <QDataStream>
57 #include <QXmlStreamWriter>
60 #include <boost/bind.hpp>
63 using namespace MailCommon;
67 "contains",
"contains-not",
68 "equals",
"not-equal",
69 "regexp",
"not-regexp",
70 "greater",
"less-or-equal",
"less",
"greater-or-equal",
71 "is-in-addressbook",
"is-not-in-addressbook",
72 "is-in-category",
"is-not-in-category",
73 "has-attachment",
"has-no-attachment",
74 "start-with",
"not-start-with",
75 "end-with",
"not-end-with"
83 Akonadi::MessageStatus status;
88 {
"Important", Akonadi::MessageStatus::statusImportant() },
89 {
"Unread", Akonadi::MessageStatus::statusUnread() },
90 {
"Read", Akonadi::MessageStatus::statusRead() },
91 {
"Deleted", Akonadi::MessageStatus::statusDeleted() },
92 {
"Replied", Akonadi::MessageStatus::statusReplied() },
93 {
"Forwarded", Akonadi::MessageStatus::statusForwarded() },
94 {
"Queued", Akonadi::MessageStatus::statusQueued() },
95 {
"Sent", Akonadi::MessageStatus::statusSent() },
96 {
"Watched", Akonadi::MessageStatus::statusWatched() },
97 {
"Ignored", Akonadi::MessageStatus::statusIgnored() },
98 {
"Action Item", Akonadi::MessageStatus::statusToAct() },
99 {
"Spam", Akonadi::MessageStatus::statusSpam() },
100 {
"Ham", Akonadi::MessageStatus::statusHam() },
101 {
"Has Attachment", Akonadi::MessageStatus::statusHasAttachment() }
105 sizeof statusNames /
sizeof (
struct _statusNames );
116 mContents( contents )
121 : mField( other.mField ),
122 mFunction( other.mFunction ),
123 mContents( other.mContents )
129 if (
this == &other ) {
133 mField = other.mField;
134 mFunction = other.mFunction;
135 mContents = other.mContents;
142 const QString &contents )
145 if ( field ==
"<status>" ) {
147 }
else if ( field ==
"<age in days>" || field ==
"<size>" ) {
149 }
else if ( field ==
"<date>" ) {
160 const QString &contents )
162 return (
createInstance( field, configValueToFunc( func ), contents ) );
172 const char cIdx = char(
int(
'A' ) + aIdx );
174 static const QString &
field = KGlobal::staticQString(
"field" );
175 static const QString & func = KGlobal::staticQString(
"func" );
176 static const QString &
contents = KGlobal::staticQString(
"contents" );
178 const QByteArray &field2 = config.readEntry( field + cIdx, QString() ).toLatin1();
179 Function func2 = configValueToFunc( config.readEntry( func + cIdx, QString() ).toLatin1() );
180 const QString & contents2 = config.readEntry( contents + cIdx, QString() );
182 if ( field2 ==
"<To or Cc>" ) {
195 Function func = configValueToFunc(
function.toUtf8() );
220 QString SearchRule::functionToString( Function
function )
231 const char cIdx = char(
'A' + aIdx );
232 static const QString &
field = KGlobal::staticQString(
"field" );
233 static const QString & func = KGlobal::staticQString(
"func" );
234 static const QString &
contents = KGlobal::staticQString(
"contents" );
236 config.writeEntry( field + cIdx, QString(mField) );
237 config.writeEntry( func + cIdx, functionToString( mFunction ) );
238 config.writeEntry( contents + cIdx, mContents );
241 QString SearchRule::conditionToString(Function
function)
249 str = i18n(
"not equal");
252 str = i18n(
"is greater");
255 str = i18n(
"is less or equal");
258 str = i18n(
"is less");
261 str = i18n(
"is greater or equal");
264 str = i18n(
"is in addressbook");
267 str = i18n(
"is not in addressbook");
270 str = i18n(
"is in category");
273 str = i18n(
"is in category");
276 str = i18n(
"has an attachment");
279 str = i18n(
"has not an attachment");
282 str = i18n(
"start with");
285 str = i18n(
"not start with");
288 str = i18n(
"end with");
291 str = i18n(
"not end with");
297 str = i18n(
"contains");
300 str = i18n(
"not contains");
303 str = i18n(
"has regexp");
306 str = i18n(
"not regexp");
314 QString contentStr = mContents;
315 if (mField ==
"<size>") {
320 comparaison = QLatin1Char(
'"') + i18n(
"size equals not supported") + QLatin1Char(
'"');
323 comparaison = QLatin1Char(
'"') + i18n(
"size not equals not supported") + QLatin1Char(
'"');
326 comparaison = QLatin1String(
":over");
329 comparaison = QLatin1String(
":under");
333 comparaison = QLatin1String(
":under");
336 comparaison = QLatin1String(
":over");
354 code += QLatin1Char(
'"') + i18n(
"\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char(
'"');
357 code += QString::fromLatin1(
"size %1 %2K").arg(comparaison).arg(QString::number(mContents.toInt() + offset));
358 }
else if (mField ==
"<status>") {
360 code += QLatin1Char(
'"') + i18n(
"<status> not implemented/supported") + QLatin1Char(
'"');
361 }
else if (mField ==
"<any header>") {
363 code += QLatin1Char(
'"') + i18n(
"<any header> not implemented/supported") + QLatin1Char(
'"');
364 }
else if (mField ==
"contents") {
366 code += QLatin1Char(
'"') + i18n(
"<contents> not implemented/supported") + QLatin1Char(
'"');
367 }
else if (mField ==
"<age in days>") {
369 code += QLatin1Char(
'"') + i18n(
"<age in days> not implemented/supported") + QLatin1Char(
'"');
370 }
else if (mField ==
"<date>") {
372 code += QLatin1Char(
'"') + i18n(
"<date> not implemented/supported") + QLatin1Char(
'"');
373 }
else if (mField ==
"<recipients>") {
375 code += QLatin1Char(
'"') + i18n(
"<recipients> not implemented/supported") + QLatin1Char(
'"');
376 }
else if (mField ==
"<tag>") {
377 code += QLatin1Char(
'"') + i18n(
"<Tag> is not supported") + QLatin1Char(
'"');
378 }
else if (mField ==
"<message>") {
380 code += i18n(
"<message> not implemented/supported");
381 }
else if (mField ==
"<body>") {
382 if (!requires.contains(QLatin1String(
"body")))
383 requires << QLatin1String(
"body");
385 bool negative =
false;
390 comparaison = QLatin1String(
":contains");
394 comparaison = QLatin1String(
":contains");
397 comparaison = QLatin1String(
":is");
400 comparaison = QLatin1String(
":is");
404 comparaison = QLatin1String(
":regex");
405 if (!requires.contains(QLatin1String(
"regex")))
406 requires << QLatin1String(
"regex");
409 if (!requires.contains(QLatin1String(
"regex")))
410 requires << QLatin1String(
"regex");
411 comparaison = QLatin1String(
":regex");
415 comparaison = QLatin1String(
":regex");
416 if (!requires.contains(QLatin1String(
"regex")))
417 requires << QLatin1String(
"regex");
418 contentStr = QLatin1Char(
'^') + contentStr;
421 comparaison = QLatin1String(
":regex");
422 if (!requires.contains(QLatin1String(
"regex")))
423 requires << QLatin1String(
"regex");
424 comparaison = QLatin1String(
":regex");
425 contentStr = QLatin1Char(
'^') + contentStr;
429 comparaison = QLatin1String(
":regex");
430 if (!requires.contains(QLatin1String(
"regex")))
431 requires << QLatin1String(
"regex");
432 comparaison = QLatin1String(
":regex");
433 contentStr = contentStr + QLatin1Char(
'$');
436 comparaison = QLatin1String(
":regex");
437 if (!requires.contains(QLatin1String(
"regex")))
438 requires << QLatin1String(
"regex");
439 comparaison = QLatin1String(
":regex");
440 contentStr = contentStr + QLatin1Char(
'$');
453 code += QLatin1Char(
'"') + i18n(
"\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char(
'"');
456 code += (negative ? QLatin1String(
"not ") : QString()) + QString::fromLatin1(
"body :text %1 \"%2\"").arg(comparaison).arg(contentStr);
459 bool negative =
false;
464 comparaison = QLatin1String(
":contains");
468 comparaison = QLatin1String(
":contains");
471 comparaison = QLatin1String(
":is");
474 comparaison = QLatin1String(
":is");
478 comparaison = QLatin1String(
":regex");
479 if (!requires.contains(QLatin1String(
"regex")))
480 requires << QLatin1String(
"regex");
483 if (!requires.contains(QLatin1String(
"regex")))
484 requires << QLatin1String(
"regex");
485 comparaison = QLatin1String(
":regex");
489 comparaison = QLatin1String(
":regex");
490 if (!requires.contains(QLatin1String(
"regex")))
491 requires << QLatin1String(
"regex");
492 contentStr = QLatin1Char(
'^') + contentStr;
495 comparaison = QLatin1String(
":regex");
496 if (!requires.contains(QLatin1String(
"regex")))
497 requires << QLatin1String(
"regex");
498 comparaison = QLatin1String(
":regex");
499 contentStr = QLatin1Char(
'^') + contentStr;
503 comparaison = QLatin1String(
":regex");
504 if (!requires.contains(QLatin1String(
"regex")))
505 requires << QLatin1String(
"regex");
506 comparaison = QLatin1String(
":regex");
507 contentStr = contentStr + QLatin1Char(
'$');
510 comparaison = QLatin1String(
":regex");
511 if (!requires.contains(QLatin1String(
"regex")))
512 requires << QLatin1String(
"regex");
513 comparaison = QLatin1String(
":regex");
514 contentStr = contentStr + QLatin1Char(
'$');
528 code += QLatin1Char(
'"') + i18n(
"\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char(
'"');
531 code += (negative ? QLatin1String(
"not ") : QString()) + QString::fromLatin1(
"header %1 \"%2\" \"%3\"").arg(comparaison).arg(QLatin1String(mField)).arg(contentStr);
537 mFunction =
function;
567 QString result =
"\"" + mField +
"\" <";
568 result += functionToString( mFunction );
569 result +=
"> \"" + mContents +
"\"";
577 switch (
function() ) {
580 return Nepomuk2::Query::ComparisonTerm::Contains;
584 return Nepomuk2::Query::ComparisonTerm::Equal;
587 return Nepomuk2::Query::ComparisonTerm::Greater;
590 return Nepomuk2::Query::ComparisonTerm::GreaterOrEqual;
593 return Nepomuk2::Query::ComparisonTerm::Smaller;
596 return Nepomuk2::Query::ComparisonTerm::SmallerOrEqual;
600 return Nepomuk2::Query::ComparisonTerm::Regexp;
606 return Nepomuk2::Query::ComparisonTerm::Regexp;
608 kDebug() <<
"Unhandled function type: " <<
function();
611 return Nepomuk2::Query::ComparisonTerm::Equal;
617 switch (
function() ) {
634 Nepomuk2::Query::GroupTerm &termGroup )
const
637 Nepomuk2::Query::NegationTerm neg;
638 neg.setSubTerm( term );
639 termGroup.addSubTerm( neg );
641 termGroup.addSubTerm( term );
647 s << mField << functionToString( mFunction ) << mContents;
659 const QString &contents )
671 if (
this == &other ) {
693 const QByteArray f =
field();
695 if ( kasciistricmp( f,
"<recipients>" ) == 0 ||
696 kasciistricmp( f,
"<status>" ) == 0 ||
697 kasciistricmp( f,
"<tag>" ) == 0 ||
698 kasciistricmp( f,
"Subject" ) == 0 ||
699 kasciistricmp( f,
"From" ) == 0 ) {
701 }
else if ( kasciistricmp( f,
"<message>" ) == 0 ||
702 kasciistricmp( f,
"<body>" ) == 0 ) {
712 const KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>();
713 Q_ASSERT( msg.get() );
719 if ( !msg->hasHeader(
"From" ) ) {
726 bool logContents =
true;
728 if ( kasciistricmp(
field(),
"<message>" ) == 0 ) {
729 msgContents = msg->encodedContent();
731 }
else if ( kasciistricmp(
field(),
"<body>" ) == 0 ) {
732 msgContents = msg->body();
734 }
else if ( kasciistricmp(
field(),
"<any header>" ) == 0 ) {
735 msgContents = msg->head();
737 }
else if ( kasciistricmp(
field(),
"<recipients>" ) == 0 ) {
749 msgContents = msg->to()->asUnicodeString();
750 msgContents +=
", " + msg->cc()->asUnicodeString();
751 msgContents +=
", " + msg->bcc()->asUnicodeString();
752 }
else if ( kasciistricmp(
field(),
"<tag>" ) == 0 ) {
753 const Nepomuk2::Resource res( item.url() );
754 foreach (
const Nepomuk2::Tag &tag, res.tags() ) {
755 msgContents += tag.label();
761 msgContents = msg->headerByType(
field() ) ?
762 msg->headerByType(
field() )->asUnicodeString() :
769 msgContents = msg->headerByType(
field() ) ?
770 msg->headerByType(
field() )->asUnicodeString() :
773 if ( msgContents.isEmpty() ) {
780 return ( msg->attachments().size() > 0 );
783 return ( msg->attachments().size() == 0 );
788 QString msg = ( rc ?
"<font color=#00FF00>1 = </font>" :
"<font color=#FF0000>0 = </font>" );
804 switch(
function() ) {
807 newContent = content;
811 newContent = QString::fromLatin1(
"^%1" ).arg( content );
815 newContent = QString::fromLatin1(
"%1$" ).arg( content );;
819 newContent = QString::fromLatin1(
"\'%1*\'" ).arg( content );
822 newContent = QString::fromLatin1(
"\'%1\'" ).arg( content );
828 void SearchRuleString::addPersonTerm( Nepomuk2::Query::GroupTerm &groupTerm,
829 const QUrl &field )
const
832 const Nepomuk2::Query::ComparisonTerm valueTerm(
833 Vocabulary::NCO::emailAddress(),
834 Nepomuk2::Query::LiteralTerm(
contents().toLower() ),
837 const Nepomuk2::Query::ComparisonTerm addressTerm(
838 Vocabulary::NCO::hasEmailAddress(),
840 Nepomuk2::Query::ComparisonTerm::Equal );
842 const Nepomuk2::Query::ComparisonTerm personTerm(
845 Nepomuk2::Query::ComparisonTerm::Equal );
847 groupTerm.addSubTerm( personTerm );
850 void SearchRuleString::addHeaderTerm( Nepomuk2::Query::GroupTerm &groupTerm,
851 const Nepomuk2::Query::Term &field )
const
853 const Nepomuk2::Query::ComparisonTerm headerName(
854 Vocabulary::NMO::headerName(),
856 Nepomuk2::Query::ComparisonTerm::Equal );
858 const Nepomuk2::Query::ComparisonTerm headerTerm(
859 Vocabulary::NMO::headerValue(),
863 groupTerm.addSubTerm( headerName );
864 groupTerm.addSubTerm( headerTerm );
870 emptyIsNotAnError =
false;
871 Nepomuk2::Query::OrTerm termGroup;
872 if ( kasciistricmp(
field(),
"<message>" ) == 0 ||
873 kasciistricmp(
field(),
"<recipients>" ) == 0 ||
874 kasciistricmp(
field(),
"<any header>" ) == 0 ) {
876 const Nepomuk2::Query::ComparisonTerm valueTerm(
877 Vocabulary::NCO::emailAddress(),
881 const Nepomuk2::Query::ComparisonTerm addressTerm(
882 Vocabulary::NCO::hasEmailAddress(),
884 Nepomuk2::Query::ComparisonTerm::Equal );
886 const Nepomuk2::Query::ComparisonTerm personTerm(
887 Vocabulary::NMO::to(),
889 Nepomuk2::Query::ComparisonTerm::Equal );
891 const Nepomuk2::Query::ComparisonTerm personTermTo(
892 Vocabulary::NMO::cc(),
894 Nepomuk2::Query::ComparisonTerm::Equal );
896 const Nepomuk2::Query::ComparisonTerm personTermCC(
897 Vocabulary::NMO::bcc(),
899 Nepomuk2::Query::ComparisonTerm::Equal );
901 if ( kasciistricmp(
field(),
"<any header>" ) == 0 ) {
902 const Nepomuk2::Query::ComparisonTerm personTermBCC(
903 Vocabulary::NMO::from(),
905 Nepomuk2::Query::ComparisonTerm::Equal );
906 termGroup.addSubTerm( personTermBCC );
908 termGroup.addSubTerm( personTermCC );
912 if ( kasciistricmp(
field(),
"to" ) == 0 ) {
913 addPersonTerm( termGroup, Vocabulary::NMO::to() );
914 }
else if ( kasciistricmp(
field(),
"cc" ) == 0 ) {
915 addPersonTerm( termGroup, Vocabulary::NMO::cc() );
916 }
else if ( kasciistricmp(
field(),
"bcc" ) == 0 ) {
917 addPersonTerm( termGroup, Vocabulary::NMO::bcc() );
918 }
else if ( kasciistricmp(
field(),
"from" ) == 0 ) {
919 addPersonTerm( termGroup, Vocabulary::NMO::from() );
922 if ( kasciistricmp(
field(),
"subject" ) == 0 ||
923 kasciistricmp(
field(),
"<any header>" ) == 0 ||
924 kasciistricmp(
field(),
"<message>" ) == 0 ) {
925 const Nepomuk2::Query::ComparisonTerm subjectTerm(
926 Vocabulary::NMO::messageSubject(),
929 termGroup.addSubTerm( subjectTerm );
931 if ( kasciistricmp(
field(),
"reply-to" ) == 0 ) {
932 const Nepomuk2::Query::ComparisonTerm replyToTerm(
933 Vocabulary::NMO::messageReplyTo(),
936 termGroup.addSubTerm( replyToTerm );
939 if ( kasciistricmp(
field(),
"list-id" ) == 0 ) {
940 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"List-Id" ) );
941 }
else if ( kasciistricmp(
field(),
"resent-from" ) == 0 ) {
942 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"Resent-From" ) );
943 }
else if ( kasciistricmp(
field(),
"x-loop" ) == 0 ) {
944 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"X-Loop" ) );
945 }
else if ( kasciistricmp(
field(),
"x-mailing-list" ) == 0 ) {
946 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"X-Mailing-List" ) );
947 }
else if ( kasciistricmp(
field(),
"x-spam-flag" ) == 0 ) {
948 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"X-Spam-Flag" ) );
953 if ( kasciistricmp(
field(),
"organization" ) == 0 ) {
954 addHeaderTerm( termGroup, Nepomuk2::Query::LiteralTerm(
"Organization" ) );
957 if ( kasciistricmp(
field(),
"<tag>" ) == 0 ) {
958 const Nepomuk2::Tag tag(
contents() );
959 if ( tag.exists() ) {
960 addAndNegateTerm(Nepomuk2::Query::ComparisonTerm(Soprano::Vocabulary::NAO::hasTag(),Nepomuk2::Query::ResourceTerm( tag ), Nepomuk2::Query::ComparisonTerm::Equal ),groupTerm );
962 foreach (
const Nepomuk2::Tag &tag, Nepomuk2::Tag::allTags() ) {
964 addAndNegateTerm(Nepomuk2::Query::ComparisonTerm(Soprano::Vocabulary::NAO::hasTag(),Nepomuk2::Query::ResourceTerm( tag ), Nepomuk2::Query::ComparisonTerm::Equal ),groupTerm );
971 if (
field() ==
"<body>" ||
field() ==
"<message>" ) {
972 const Nepomuk2::Query::ComparisonTerm bodyTerm(
973 Vocabulary::NMO::plainTextMessageContent(),
977 termGroup.addSubTerm( bodyTerm );
979 const Nepomuk2::Query::ComparisonTerm attachmentBodyTerm(
980 Vocabulary::NMO::plainTextMessageContent(),
984 const Nepomuk2::Query::ComparisonTerm attachmentTerm(
985 Vocabulary::NIE::isPartOf(),
987 Nepomuk2::Query::ComparisonTerm::Equal );
989 termGroup.addSubTerm( attachmentTerm );
992 if ( !termGroup.subTerms().isEmpty() ) {
1000 if( msgContents.isEmpty()) {
1004 switch (
function() ) {
1006 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) == 0 );
1009 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) != 0 );
1012 return ( msgContents.contains(
contents(), Qt::CaseInsensitive ) );
1015 return ( !msgContents.contains(
contents(), Qt::CaseInsensitive ) );
1019 QRegExp regexp(
contents(), Qt::CaseInsensitive );
1020 return ( regexp.indexIn( msgContents ) >= 0 );
1025 QRegExp regexp(
contents(), Qt::CaseInsensitive );
1026 return ( regexp.indexIn( msgContents ) < 0 );
1031 return msgContents.startsWith(
contents() );
1036 return !msgContents.startsWith(
contents() );
1041 return msgContents.endsWith(
contents() );
1046 return !msgContents.endsWith(
contents() );
1050 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) > 0 );
1053 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) <= 0 );
1056 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) < 0 );
1059 return ( QString::compare( msgContents.toLower(),
contents().toLower() ) >= 0 );
1063 const QStringList addressList = KPIMUtils::splitAddressList( msgContents.toLower() );
1064 QStringList::ConstIterator end( addressList.constEnd() );
1065 for ( QStringList::ConstIterator it = addressList.constBegin(); ( it != end ); ++it ) {
1066 Akonadi::ContactSearchJob *job =
new Akonadi::ContactSearchJob();
1068 job->setQuery( Akonadi::ContactSearchJob::Email, KPIMUtils::extractEmailAddress( *it ).toLower() );
1071 if ( !job->contacts().isEmpty() ) {
1080 const QStringList addressList = KPIMUtils::splitAddressList( msgContents.toLower() );
1081 QStringList::ConstIterator end( addressList.constEnd() );
1083 for ( QStringList::ConstIterator it = addressList.constBegin(); ( it != end ); ++it ) {
1084 Akonadi::ContactSearchJob *job =
new Akonadi::ContactSearchJob();
1086 job->setQuery( Akonadi::ContactSearchJob::Email, KPIMUtils::extractEmailAddress( *it ).toLower() );
1089 if ( job->contacts().isEmpty() ) {
1099 const QStringList addressList = KPIMUtils::splitAddressList( msgContents.toLower() );
1101 QStringList::ConstIterator end( addressList.constEnd() );
1102 for ( QStringList::ConstIterator it = addressList.constBegin(); it != end; ++it ) {
1103 Akonadi::ContactSearchJob *job =
new Akonadi::ContactSearchJob();
1104 job->setQuery( Akonadi::ContactSearchJob::Email, KPIMUtils::extractEmailAddress( *it ).toLower() );
1107 const KABC::Addressee::List contacts = job->contacts();
1109 foreach (
const KABC::Addressee &contact, contacts ) {
1110 if ( contact.hasCategory( category ) ) {
1121 const QStringList addressList = KPIMUtils::splitAddressList( msgContents.toLower() );
1123 QStringList::ConstIterator end( addressList.constEnd() );
1124 for ( QStringList::ConstIterator it = addressList.constBegin(); it != end; ++it ) {
1125 Akonadi::ContactSearchJob *job =
new Akonadi::ContactSearchJob();
1126 job->setQuery( Akonadi::ContactSearchJob::Email, KPIMUtils::extractEmailAddress( *it ).toLower() );
1129 const KABC::Addressee::List contacts = job->contacts();
1131 foreach (
const KABC::Addressee &contact, contacts ) {
1132 if ( contact.hasCategory( category ) ) {
1155 const QString &contents )
1170 const KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>();
1172 QString msgContents;
1173 qint64 numericalMsgContents = 0;
1174 qint64 numericalValue = 0;
1176 if ( kasciistricmp(
field(),
"<size>" ) == 0 ) {
1177 numericalMsgContents = item.size();
1178 numericalValue =
contents().toLongLong();
1179 msgContents.setNum( numericalMsgContents );
1180 }
else if ( kasciistricmp(
field(),
"<age in days>" ) == 0 ) {
1181 QDateTime msgDateTime = msg->date()->dateTime().dateTime();
1182 numericalMsgContents = msgDateTime.daysTo( QDateTime::currentDateTime() );
1183 numericalValue =
contents().toInt();
1184 msgContents.setNum( numericalMsgContents );
1186 bool rc =
matchesInternal( numericalValue, numericalMsgContents, msgContents );
1188 QString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
1189 :
"<font color=#FF0000>0 = </font>" );
1191 msg +=
" ( <i>" + QString::number( numericalMsgContents ) +
"</i> )";
1204 long numericalMsgContents,
const QString & msgContents )
const
1206 switch (
function() ) {
1208 return ( numericalValue == numericalMsgContents );
1211 return ( numericalValue != numericalMsgContents );
1214 return ( msgContents.contains(
contents(), Qt::CaseInsensitive ) );
1217 return ( !msgContents.contains(
contents(), Qt::CaseInsensitive ) );
1221 QRegExp regexp(
contents(), Qt::CaseInsensitive );
1222 return ( regexp.indexIn( msgContents ) >= 0 );
1227 QRegExp regexp(
contents(), Qt::CaseInsensitive );
1228 return ( regexp.indexIn( msgContents ) < 0 );
1232 return ( numericalMsgContents > numericalValue );
1235 return ( numericalMsgContents <= numericalValue );
1238 return ( numericalMsgContents < numericalValue );
1241 return ( numericalMsgContents >= numericalValue );
1259 emptyIsNotAnError =
false;
1260 if ( kasciistricmp(
field(),
"<size>" ) == 0 ) {
1261 const Nepomuk2::Query::ComparisonTerm sizeTerm(
1262 Vocabulary::NIE::byteSize(),
1263 Nepomuk2::Query::LiteralTerm(
contents().toInt() ),
1266 }
else if ( kasciistricmp(
field(),
"<age in days>" ) == 0 ) {
1267 QDate date = QDate::currentDate();
1268 date = date.addDays(
contents().toInt() );
1269 const Nepomuk2::Query::ComparisonTerm dateTerm(
1270 Vocabulary::NMO::sentDate(),
1271 Nepomuk2::Query::LiteralTerm( date ),
1285 const QString &contents )
1292 return !QDate::fromString(
contents(), Qt::ISODate ).isValid();
1297 const KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>();
1300 QDate msgDate = msg->date()->dateTime().date();
1301 QDate dateValue = QDate::fromString(
contents(), Qt::ISODate );
1304 QString msg = ( rc ?
"<font color=#00FF00>1 = </font>"
1305 :
"<font color=#FF0000>0 = </font>" );
1307 msg +=
" ( <i>" +
contents() +
"</i> )";
1314 const QDate& msgDate )
const
1316 switch (
function() ) {
1318 return ( dateValue == msgDate );
1321 return ( dateValue != msgDate );
1324 return ( msgDate > dateValue );
1327 return ( msgDate <= dateValue );
1330 return ( msgDate < dateValue );
1333 return ( msgDate >= dateValue );
1350 emptyIsNotAnError =
false;
1351 const QDate date = QDate::fromString(
contents(), Qt::ISODate );
1352 const Nepomuk2::Query::ComparisonTerm dateTerm(
1353 Vocabulary::NMO::sentDate(),
1354 Nepomuk2::Query::LiteralTerm( date ),
1376 Function func,
const QString &aContents )
1393 if ( !aStatusString.compare(
statusNames[i].name ) ) {
1397 Akonadi::MessageStatus unknown;
1408 Akonadi::MessageStatus status;
1409 status.setStatusFromFlags( item.flags() );
1411 switch (
function() ) {
1414 if ( status & mStatus ) {
1420 if ( ! ( status & mStatus ) ) {
1430 QString msg = ( rc ?
"<font color=#00FF00>1 = </font>" :
"<font color=#FF0000>0 = </font>" );
1442 void SearchRuleStatus::addTagTerm( Nepomuk2::Query::GroupTerm &groupTerm,
1443 const QString &tagId )
const
1446 qDebug()<<
" tagId"<<tagId;
1447 const Nepomuk2::Tag tag( tagId );
1449 qDebug()<<
" tag exist !";
1451 Nepomuk2::Query::ComparisonTerm(
1452 Soprano::Vocabulary::NAO::hasTag(),
1453 Nepomuk2::Query::ResourceTerm( tag.uri() ),
1454 Nepomuk2::Query::ComparisonTerm::Equal ),
1461 emptyIsNotAnError =
true;
1462 if ( mStatus.isImportant() ) {
1463 addTagTerm( groupTerm,
"important" );
1464 }
else if ( mStatus.isToAct() ) {
1465 addTagTerm( groupTerm,
"todo" );
1466 }
else if ( mStatus.isWatched() ) {
1467 addTagTerm( groupTerm,
"watched" );
1468 }
else if ( mStatus.isDeleted() ) {
1469 addTagTerm( groupTerm,
"deleted" );
1470 }
else if ( mStatus.isSpam() ) {
1471 addTagTerm( groupTerm,
"spam" );
1472 }
else if ( mStatus.isReplied() ) {
1473 addTagTerm( groupTerm,
"replied" );
1474 }
else if ( mStatus.isIgnored() ) {
1475 addTagTerm( groupTerm,
"ignored" );
1476 }
else if ( mStatus.isForwarded() ) {
1477 addTagTerm( groupTerm,
"forwarded" );
1478 }
else if ( mStatus.isSent() ) {
1479 addTagTerm( groupTerm,
"sent" );
1480 }
else if ( mStatus.isQueued() ) {
1481 addTagTerm( groupTerm,
"queued" );
1482 }
else if ( mStatus.isHam() ) {
1483 addTagTerm( groupTerm,
"ham" );
1490 if ( !mStatus.isRead() ) {
1493 groupTerm.addSubTerm(
1494 Nepomuk2::Query::ComparisonTerm(
1495 Vocabulary::NMO::isRead(),
1496 Nepomuk2::Query::LiteralTerm( read ),
1497 Nepomuk2::Query::ComparisonTerm::Equal ) );
1531 if ( !item.hasPayload<KMime::Message::Ptr>() ) {
1537 switch ( mOperator ) {
1539 for ( it = constBegin(); it != end; ++it ) {
1541 if ( !(*it)->matches( item ) ) {
1549 for ( it = constBegin(); it != end; ++it ) {
1551 if ( (*it)->matches( item ) ) {
1571 reqPart = (*std::max_element(constBegin(), constEnd(),
1582 while ( it != begin() ) {
1584 if ( (*it)->isEmpty() ) {
1586 kDebug() <<
"Removing" << (*it)->asString();
1598 mName = config.readEntry(
"name" );
1599 if ( !config.hasKey(
"rules" ) ) {
1600 kDebug() <<
"Found legacy config! Converting.";
1601 importLegacyConfig( config );
1605 const QString
op = config.readEntry(
"operator" );
1606 if ( op == QLatin1String(
"or" ) ) {
1608 }
else if ( op == QLatin1String(
"and" ) ) {
1610 }
else if ( op == QLatin1String(
"all" ) ) {
1614 const int nRules = config.readEntry(
"rules", 0 );
1616 for (
int i = 0; i < nRules; ++i ) {
1618 if ( !r->isEmpty() ) {
1624 void SearchPattern::importLegacyConfig(
const KConfigGroup & config )
1628 config.readEntry(
"fieldA" ).toLatin1(),
1629 config.readEntry(
"funcA" ).toLatin1(),
1630 config.readEntry(
"contentsA" ) );
1632 if ( rule->isEmpty() ) {
1639 const QString sOperator = config.readEntry(
"operator" );
1640 if ( sOperator ==
"ignore" ) {
1646 config.readEntry(
"fieldB" ).toLatin1(),
1647 config.readEntry(
"funcB" ).toLatin1(),
1648 config.readEntry(
"contentsB" ) );
1650 if ( rule->isEmpty() ) {
1655 if ( sOperator == QLatin1String(
"or" ) ) {
1660 if ( sOperator == QLatin1String(
"unless" ) ) {
1665 unsigned int intFunc = (
unsigned int)func;
1668 last()->setFunction( func );
1676 config.writeEntry(
"name", mName );
1677 switch( mOperator ) {
1679 config.writeEntry(
"operator",
"or" );
1682 config.writeEntry(
"operator",
"and" );
1685 config.writeEntry(
"operator",
"all" );
1693 for ( it = constBegin(); it != endIt && i <
FILTER_MAX_RULES; ++i, ++it ) {
1696 (*it)->writeConfig( config, i );
1700 config.writeEntry(
"rules", i );
1703 void SearchPattern::init()
1707 mName =
'<' + i18nc(
"name used for a virgin filter",
"unknown" ) +
'>';
1713 switch( mOperator ) {
1715 result = i18n(
"(match any of the following)" );
1718 result = i18n(
"(match all of the following)" );
1721 result = i18n(
"(match all messages)" );
1727 for ( it = constBegin(); it != endIt; ++it ) {
1738 return Nepomuk2::Query::OrTerm();
1740 return Nepomuk2::Query::AndTerm();
1743 Nepomuk2::Query::ComparisonTerm SearchPattern::createChildTerm(
const KUrl& url,
bool& empty )
const
1745 const Nepomuk2::Resource parentResource( url );
1746 if ( !parentResource.exists() ) {
1748 return Nepomuk2::Query::ComparisonTerm();
1751 const Nepomuk2::Query::ComparisonTerm isChildTerm( Vocabulary::NIE::isPartOf(), Nepomuk2::Query::ResourceTerm( parentResource ) );
1757 Nepomuk2::Query::Query query;
1759 Nepomuk2::Query::AndTerm outerGroup;
1760 const Nepomuk2::Types::Class cl( Vocabulary::NMO::Email() );
1761 const Nepomuk2::Query::ResourceTypeTerm typeTerm( cl );
1762 const Nepomuk2::Query::Query::RequestProperty itemIdProperty(
1763 Akonadi::ItemSearchJob::akonadiItemIdUri(),
false );
1765 Nepomuk2::Query::GroupTerm innerGroup =
makeGroupTerm( mOperator );
1766 const_iterator end( constEnd() );
1767 bool emptyIsNotAnError =
false;
1768 bool resultAddQuery = emptyIsNotAnError;
1769 for ( const_iterator it = constBegin(); it != end; ++it ) {
1770 (*it)->addQueryTerms( innerGroup, emptyIsNotAnError );
1771 resultAddQuery &= emptyIsNotAnError;
1774 if ( innerGroup.subTerms().isEmpty() ) {
1775 if (resultAddQuery) {
1776 qDebug()<<
" innergroup is Empty. Need to report bug";
1782 if ( !urlList.isEmpty() ) {
1783 const int numberOfUrl = urlList.count();
1784 if ( numberOfUrl == 1 ) {
1786 const Nepomuk2::Query::ComparisonTerm isChildTerm = createChildTerm( urlList.at( 0 ), empty );
1790 const Nepomuk2::Query::AndTerm andTerm( isChildTerm, innerGroup );
1791 outerGroup.addSubTerm( andTerm );
1794 bool allFolderIsEmpty =
true;
1795 for (
int i = 0; i < numberOfUrl; ++i ) {
1797 const Nepomuk2::Query::ComparisonTerm childTerm = createChildTerm( urlList.at( i ), empty );
1800 allFolderIsEmpty =
false;
1803 if (allFolderIsEmpty) {
1806 const Nepomuk2::Query::OrTerm orTerm( term );
1807 const Nepomuk2::Query::AndTerm andTerm( orTerm, innerGroup );
1808 outerGroup.addSubTerm( andTerm );
1812 outerGroup.addSubTerm( innerGroup );
1814 outerGroup.addSubTerm( typeTerm );
1815 query.setTerm( outerGroup );
1816 query.addRequestProperty( itemIdProperty );
1817 queryStr = query.toSparqlQuery();
1824 if (
this == &other ) {
1834 for ( it = other.constBegin(); it != end; ++it ) {
1844 QDataStream stream( &out, QIODevice::WriteOnly );
1851 QDataStream stream( str );
1859 s << QString::fromLatin1(
"and" );
1862 s << QString::fromLatin1(
"or" );
1865 s << QString::fromLatin1(
"all" );
1879 if ( op == QLatin1String(
"and" ) ) {
1881 }
else if ( op == QLatin1String(
"or" ) ) {
1883 }
else if ( op == QLatin1String(
"all" ) ) {
1887 while ( !s.atEnd() ) {
1896 code += QLatin1String(
"\n#") + mName + QLatin1Char(
'\n');
1897 switch( mOperator ) {
1899 code += QLatin1String(
"if anyof (");
1902 code += QLatin1String(
"if allof (");
1905 code += QLatin1String(
"if (true) {");
1912 for ( it = constBegin(); it != endIt && i <
FILTER_MAX_RULES; ++i, ++it ) {
1914 code += QLatin1String(
"\n, ");
1916 (*it)->generateSieveScript(requires, code);
1922 namespace MailCommon {
1925 return ::qHash( sr.get() );
bool matches(const Akonadi::Item &item, bool ignoreBody=false) const
The central function of this class.
virtual bool matches(const Akonadi::Item &item) const
Tries to match the rule against the KMime::Message in the given item.
SparqlQueryError asSparqlQuery(QString &queryStr, const KUrl::List &url=KUrl::List()) const
Returns the pattern as a SPARQL query.
QDataStream & operator>>(QDataStream &) const
virtual void addQueryTerms(Nepomuk2::Query::GroupTerm &groupTerm, bool &emptyIsNotAnError) const
Adds query terms to the given term group.
QString asString() const
Returns the pattern as string.
SearchPattern()
Constructor which provides a pattern with minimal, but sufficient initialization. ...
bool matchesInternal(const QString &contents) const
A helper method for the main matches() method.
void generateSieveScript(QStringList &requires, QString &code)
static struct _statusNames statusNames[]
SearchPattern::Operator op() const
Returns the filter operator.
SearchRuleStatus(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
static QString recode(const QString &plain)
Returns an escaped version of the log which can be used in a HTML document.
static Akonadi::MessageStatus statusFromEnglishName(const QString &)
SearchRule(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
Creates new new search rule.
QByteArray serialize() const
Writes the pattern into a byte array for persistance purposes.
virtual RequiredPart requiredPart() const
Returns the required part from the item that is needed for the search to operate. ...
SearchRuleNumerical(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
Creates new numerical search rule.
void setField(const QByteArray &name)
Sets the message header field name.
virtual bool isEmpty() const
Determines whether the rule is worth considering.
static const int numFuncConfigNames
This class represents a search pattern rule operating on numerical values.
const SearchPattern & operator=(const SearchPattern &aPattern)
Overloaded assignment operator.
void readConfig(const KConfigGroup &config)
Reads a search pattern from a KConfigGroup.
void writeConfig(KConfigGroup &group, int index) const
Saves the object into a given config group.
virtual bool isEmpty() const
Determines whether the rule is worth considering.
QString quote(const QString &content) const
boost::shared_ptr< SearchRule > Ptr
Defines a pointer to a search rule.
~SearchPattern()
Destructor.
bool matchesInternal(const QDate &dateValue, const QDate &msgDate) const
A helper method for the main matches() method.
void setContents(const QString &contents)
Set the contents of the rule.
void setFunction(Function function)
Sets the filter function of the rule.
static FilterLog * instance()
Returns the single global instance of the filter log.
KMail Filter Log Collector.
virtual RequiredPart requiredPart() const
Returns the required part from the item that is needed for the search to operate. ...
const SearchRule & operator=(const SearchRule &other)
Initializes this rule with an other rule.
QDataStream & operator<<(QDataStream &s)
Function
Describes operators for comparison of field and contents.
virtual SearchRule::RequiredPart requiredPart() const =0
Returns the required part from the item that is needed for the search to operate. ...
static SearchRule::Ptr createInstanceFromConfig(const KConfigGroup &group, int index)
Creates a new search rule from a given config group.
void deserialize(const QByteArray &)
Constructs the pattern from a byte array serialization.
bool isLogging() const
Returns whether the filter log is currently active.
Operator
Boolean operators that connect the return values of the individual rules.
virtual bool isEmpty() const
Determines whether the rule is worth considering.
const QString asString() const
Returns the rule as string for debugging purpose.
static const char * funcConfigNames[]
QString contents() const
Returns the contents of the rule.
virtual ~SearchRule()
Destroys the search rule.
SearchRuleString(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
Creates new new string search rule.
virtual bool matches(const Akonadi::Item &item) const
Tries to match the rule against the KMime::Message in the given item.
void writeConfig(KConfigGroup &config) const
Writes itself into config.
virtual RequiredPart requiredPart() const
Returns the required part from the item that is needed for the search to operate. ...
This class is an abstraction of a search over messages.
QByteArray field() const
Returns the message header field name (without the trailing ':').
bool isNegated() const
Helper that returns whether the rule has a negated function.
This class represents a search to be performed against the status of a messsage.
virtual RequiredPart requiredPart() const
Returns the required part from the item that is needed for the search to operate. ...
This class represents one search pattern rule.
Function function() const
Returns the filter function of the rule.
QDataStream & operator>>(QDataStream &s) const
static const int numStatusNames
virtual void addQueryTerms(Nepomuk2::Query::GroupTerm &groupTerm, bool &emptyIsNotAnError) const
Adds query terms to the given term group.
void generateSieveScript(QStringList &requires, QString &code)
This class represents a search pattern rule operating on a string.
const SearchRuleString & operator=(const SearchRuleString &other)
Initializes this rule with an other rule.
virtual bool matches(const Akonadi::Item &item) const
Tries to match the rule against the KMime::Message in the given item.
virtual bool matches(const Akonadi::Item &item) const
Tries to match the rule against the KMime::Message in the given item.
void setOp(SearchPattern::Operator aOp)
Sets the filter operator.
virtual bool isEmpty() const
Determines whether the rule is worth considering.
virtual void addQueryTerms(Nepomuk2::Query::GroupTerm &groupTerm, bool &emptyIsNotAnError) const
Adds query terms to the given term group.
SearchRule::RequiredPart requiredPart() const
Returns the required part from the item that is needed for the search to operate. ...
void add(const QString &entry, ContentType type)
Adds the given log entry under the given content type to the log.
virtual ~SearchRuleString()
Destroys the string search rule.
static SearchRule::Ptr createInstance(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
Creates a new search rule of a certain type by instantiating the appropriate subclass depending on th...
QString englishNameForStatus(const Akonadi::MessageStatus &status)
void purify()
Removes all empty rules from the list.
const int FILTER_MAX_RULES
bool matchesInternal(long numericalValue, long numericalContents, const QString &contents) const
A helper method for the main matches() method.
virtual void addQueryTerms(Nepomuk2::Query::GroupTerm &groupTerm, bool &emptyIsNotAnError) const
Adds query terms to the given term group.
void addAndNegateTerm(const Nepomuk2::Query::Term &term, Nepomuk2::Query::GroupTerm &termGroup) const
Adds term to termGroup and adds a negation term inbetween if needed.
void setName(const QString &newName)
Sets the name of the search pattern.
SearchRuleDate(const QByteArray &field=0, Function function=FuncContains, const QString &contents=QString())
Creates new date search rule.
static Nepomuk2::Query::GroupTerm makeGroupTerm(SearchPattern::Operator op)
QString name() const
Returns the name of the search pattern.
Nepomuk2::Query::ComparisonTerm::Comparator nepomukComparator() const
Converts the rule function into the corresponding Nepomuk query operator.
Log all rule matching results.