File indexing completed on 2025-02-16 04:50:20
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Daniel Vrátil <dvratil@redhat.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 * 0006 */ 0007 0008 #include "searchtask.h" 0009 #include "imapresource_debug.h" 0010 #include <Akonadi/MessageFlags> 0011 #include <Akonadi/SearchQuery> 0012 #include <KIMAP/SearchJob> 0013 #include <KIMAP/SelectJob> 0014 #include <KIMAP/Session> 0015 #include <KLocalizedString> 0016 Q_DECLARE_METATYPE(KIMAP::Session *) 0017 0018 SearchTask::SearchTask(const ResourceStateInterface::Ptr &state, const QString &query, QObject *parent) 0019 : ResourceTask(ResourceTask::DeferIfNoSession, state, parent) 0020 , m_query(query) 0021 { 0022 } 0023 0024 SearchTask::~SearchTask() = default; 0025 0026 void SearchTask::doStart(KIMAP::Session *session) 0027 { 0028 qCDebug(IMAPRESOURCE_LOG) << collection().remoteId(); 0029 0030 const QString mailbox = mailBoxForCollection(collection()); 0031 if (session->selectedMailBox() == mailbox) { 0032 doSearch(session); 0033 return; 0034 } 0035 0036 auto select = new KIMAP::SelectJob(session); 0037 select->setMailBox(mailbox); 0038 connect(select, &KJob::finished, this, &SearchTask::onSelectDone); 0039 select->start(); 0040 } 0041 0042 void SearchTask::onSelectDone(KJob *job) 0043 { 0044 if (job->error()) { 0045 searchFinished(QList<qint64>()); 0046 cancelTask(job->errorText()); 0047 return; 0048 } 0049 0050 doSearch(qobject_cast<KIMAP::SelectJob *>(job)->session()); 0051 } 0052 0053 static KIMAP::Term::Relation mapRelation(Akonadi::SearchTerm::Relation relation) 0054 { 0055 if (relation == Akonadi::SearchTerm::RelAnd) { 0056 return KIMAP::Term::And; 0057 } 0058 return KIMAP::Term::Or; 0059 } 0060 0061 static KIMAP::Term recursiveEmailTermMapping(const Akonadi::SearchTerm &term) 0062 { 0063 if (!term.subTerms().isEmpty()) { 0064 QList<KIMAP::Term> subterms; 0065 const QList<Akonadi::SearchTerm> lstSearchTermsList = term.subTerms(); 0066 for (const Akonadi::SearchTerm &subterm : lstSearchTermsList) { 0067 const KIMAP::Term newTerm = recursiveEmailTermMapping(subterm); 0068 if (!newTerm.isNull()) { 0069 subterms << newTerm; 0070 } 0071 } 0072 return KIMAP::Term(mapRelation(term.relation()), subterms); 0073 } else { 0074 const Akonadi::EmailSearchTerm::EmailSearchField field = Akonadi::EmailSearchTerm::fromKey(term.key()); 0075 switch (field) { 0076 case Akonadi::EmailSearchTerm::Message: 0077 return KIMAP::Term(KIMAP::Term::Text, term.value().toString()).setNegated(term.isNegated()); 0078 case Akonadi::EmailSearchTerm::Body: 0079 return KIMAP::Term(KIMAP::Term::Body, term.value().toString()).setNegated(term.isNegated()); 0080 case Akonadi::EmailSearchTerm::Headers: 0081 // FIXME 0082 // return KIMAP::Term(KIMAP::Term::Header, term.value()).setNegated(term.isNegated()); 0083 break; 0084 case Akonadi::EmailSearchTerm::ByteSize: { 0085 int value = term.value().toInt(); 0086 switch (term.condition()) { 0087 case Akonadi::SearchTerm::CondGreaterOrEqual: 0088 value--; 0089 [[fallthrough]]; 0090 case Akonadi::SearchTerm::CondGreaterThan: 0091 return KIMAP::Term(KIMAP::Term::Larger, value).setNegated(term.isNegated()); 0092 case Akonadi::SearchTerm::CondLessOrEqual: 0093 value++; 0094 [[fallthrough]]; 0095 case Akonadi::SearchTerm::CondLessThan: 0096 return KIMAP::Term(KIMAP::Term::Smaller, value).setNegated(term.isNegated()); 0097 case Akonadi::SearchTerm::CondEqual: 0098 return KIMAP::Term(KIMAP::Term::And, 0099 QList<KIMAP::Term>() << KIMAP::Term(KIMAP::Term::Smaller, value + 1) << KIMAP::Term(KIMAP::Term::Larger, value + 1)) 0100 .setNegated(term.isNegated()); 0101 case Akonadi::SearchTerm::CondContains: 0102 qCDebug(IMAPRESOURCE_LOG) << " invalid condition for ByteSize"; 0103 break; 0104 } 0105 break; 0106 } 0107 case Akonadi::EmailSearchTerm::HeaderOnlyDate: 0108 case Akonadi::EmailSearchTerm::HeaderDate: { 0109 QDate value = term.value().toDateTime().date(); 0110 switch (term.condition()) { 0111 case Akonadi::SearchTerm::CondGreaterOrEqual: 0112 value = value.addDays(-1); 0113 [[fallthrough]]; 0114 case Akonadi::SearchTerm::CondGreaterThan: 0115 return KIMAP::Term(KIMAP::Term::SentSince, value).setNegated(term.isNegated()); 0116 case Akonadi::SearchTerm::CondLessOrEqual: 0117 value = value.addDays(1); 0118 [[fallthrough]]; 0119 case Akonadi::SearchTerm::CondLessThan: 0120 return KIMAP::Term(KIMAP::Term::SentBefore, value).setNegated(term.isNegated()); 0121 case Akonadi::SearchTerm::CondEqual: 0122 return KIMAP::Term(KIMAP::Term::SentOn, value).setNegated(term.isNegated()); 0123 case Akonadi::SearchTerm::CondContains: 0124 qCDebug(IMAPRESOURCE_LOG) << " invalid condition for Date"; 0125 return {}; 0126 default: 0127 qCWarning(IMAPRESOURCE_LOG) << "unknown term for date" << term.key(); 0128 return {}; 0129 } 0130 } 0131 case Akonadi::EmailSearchTerm::Subject: 0132 return KIMAP::Term(KIMAP::Term::Subject, term.value().toString()).setNegated(term.isNegated()); 0133 case Akonadi::EmailSearchTerm::HeaderFrom: 0134 return KIMAP::Term(KIMAP::Term::From, term.value().toString()).setNegated(term.isNegated()); 0135 case Akonadi::EmailSearchTerm::HeaderTo: 0136 return KIMAP::Term(KIMAP::Term::To, term.value().toString()).setNegated(term.isNegated()); 0137 case Akonadi::EmailSearchTerm::HeaderCC: 0138 return KIMAP::Term(KIMAP::Term::Cc, term.value().toString()).setNegated(term.isNegated()); 0139 case Akonadi::EmailSearchTerm::HeaderBCC: 0140 return KIMAP::Term(KIMAP::Term::Bcc, term.value().toString()).setNegated(term.isNegated()); 0141 case Akonadi::EmailSearchTerm::MessageStatus: { 0142 const QString termStr = term.value().toString(); 0143 if (termStr == QLatin1StringView(Akonadi::MessageFlags::Flagged)) { 0144 return KIMAP::Term(KIMAP::Term::Flagged).setNegated(term.isNegated()); 0145 } 0146 if (termStr == QLatin1StringView(Akonadi::MessageFlags::Deleted)) { 0147 return KIMAP::Term(KIMAP::Term::Deleted).setNegated(term.isNegated()); 0148 } 0149 if (termStr == QLatin1StringView(Akonadi::MessageFlags::Replied)) { 0150 return KIMAP::Term(KIMAP::Term::Answered).setNegated(term.isNegated()); 0151 } 0152 if (termStr == QLatin1StringView(Akonadi::MessageFlags::Seen)) { 0153 return KIMAP::Term(KIMAP::Term::Seen).setNegated(term.isNegated()); 0154 } 0155 break; 0156 } 0157 case Akonadi::EmailSearchTerm::MessageTag: 0158 break; 0159 case Akonadi::EmailSearchTerm::HeaderReplyTo: 0160 break; 0161 case Akonadi::EmailSearchTerm::HeaderOrganization: 0162 break; 0163 case Akonadi::EmailSearchTerm::HeaderListId: 0164 break; 0165 case Akonadi::EmailSearchTerm::HeaderResentFrom: 0166 break; 0167 case Akonadi::EmailSearchTerm::HeaderXLoop: 0168 break; 0169 case Akonadi::EmailSearchTerm::HeaderXMailingList: 0170 break; 0171 case Akonadi::EmailSearchTerm::HeaderXSpamFlag: 0172 break; 0173 case Akonadi::EmailSearchTerm::Unknown: 0174 default: 0175 qCWarning(IMAPRESOURCE_LOG) << "unknown term " << term.key(); 0176 } 0177 } 0178 return {}; 0179 } 0180 0181 void SearchTask::doSearch(KIMAP::Session *session) 0182 { 0183 qCDebug(IMAPRESOURCE_LOG) << m_query; 0184 0185 Akonadi::SearchQuery query = Akonadi::SearchQuery::fromJSON(m_query.toLatin1()); 0186 auto searchJob = new KIMAP::SearchJob(session); 0187 searchJob->setUidBased(true); 0188 0189 KIMAP::Term term = recursiveEmailTermMapping(query.term()); 0190 if (term.isNull()) { 0191 qCWarning(IMAPRESOURCE_LOG) << "failed to translate query " << m_query; 0192 searchFinished(QList<qint64>()); 0193 cancelTask(i18n("Invalid search")); 0194 return; 0195 } 0196 searchJob->setTerm(term); 0197 0198 connect(searchJob, &KJob::finished, this, &SearchTask::onSearchDone); 0199 searchJob->start(); 0200 } 0201 0202 void SearchTask::onSearchDone(KJob *job) 0203 { 0204 if (job->error()) { 0205 qCWarning(IMAPRESOURCE_LOG) << "Failed to execute search " << job->errorString(); 0206 qCDebug(IMAPRESOURCE_LOG) << m_query; 0207 searchFinished(QList<qint64>()); 0208 cancelTask(job->errorString()); 0209 return; 0210 } 0211 0212 auto searchJob = qobject_cast<KIMAP::SearchJob *>(job); 0213 const QList<qint64> result = searchJob->results(); 0214 qCDebug(IMAPRESOURCE_LOG) << result.count() << "matches"; 0215 0216 searchFinished(result); 0217 taskDone(); 0218 } 0219 0220 #include "moc_searchtask.cpp"