/* Copyright (c) 2011 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mailfilteragent.h" #include "mailcommon/dbusoperators.h" #include "dummykernel.h" #include "filterlogdialog.h" #include "filtermanager.h" #include "mailfilteragentadaptor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "mailfilteragent_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include static bool isFilterableCollection(const Akonadi::Collection &collection) { return MailCommon::Kernel::folderIsInbox(collection); //TODO: check got filter attribute here } MailFilterAgent::MailFilterAgent(const QString &id) : Akonadi::AgentBase(id), m_filterLogDialog(Q_NULLPTR) { Kdelibs4ConfigMigrator migrate(QStringLiteral("mailfilteragent")); migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_mailfilter_agentrc") << QStringLiteral("akonadi_mailfilter_agent.notifyrc")); migrate.migrate(); Akonadi::AttributeFactory::registerAttribute(); mMailFilterKernel = new DummyKernel(this); CommonKernel->registerKernelIf(mMailFilterKernel); //register KernelIf early, it is used by the Filter classes CommonKernel->registerSettingsIf(mMailFilterKernel); //SettingsIf is used in FolderTreeWidget m_filterManager = new FilterManager(this); connect(m_filterManager, &FilterManager::percent, this, &MailFilterAgent::emitProgress); connect(m_filterManager, &FilterManager::progressMessage, this, &MailFilterAgent::emitProgressMessage); Akonadi::Monitor *collectionMonitor = new Akonadi::Monitor(this); collectionMonitor->fetchCollection(true); collectionMonitor->ignoreSession(Akonadi::Session::defaultSession()); collectionMonitor->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); collectionMonitor->setMimeTypeMonitored(KMime::Message::mimeType()); connect(collectionMonitor, &Akonadi::Monitor::collectionAdded, this, &MailFilterAgent::mailCollectionAdded); connect(collectionMonitor, SIGNAL(collectionChanged(Akonadi::Collection)), this, SLOT(mailCollectionChanged(Akonadi::Collection))); connect(collectionMonitor, &Akonadi::Monitor::collectionRemoved, this, &MailFilterAgent::mailCollectionRemoved); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &MailFilterAgent::slotInstanceRemoved); QTimer::singleShot(0, this, &MailFilterAgent::initializeCollections); qDBusRegisterMetaType >(); new MailFilterAgentAdaptor(this); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/MailFilterAgent"), this, QDBusConnection::ExportAdaptors); KDBusConnectionPool::threadConnection().registerService(QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent")); //Enabled or not filterlogdialog KSharedConfig::Ptr config = KSharedConfig::openConfig(); if (config->hasGroup("FilterLog")) { KConfigGroup group(config, "FilterLog"); if (group.hasKey("Enabled")) { if (group.readEntry("Enabled", false)) { m_filterLogDialog = new FilterLogDialog(Q_NULLPTR); const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("view-filter")).pixmap(KIconLoader::SizeSmall, KIconLoader::SizeSmall); KNotification *notify = new KNotification(QStringLiteral("mailfilterlogenabled")); notify->setComponentName(QApplication::applicationDisplayName()); notify->setPixmap(pixmap); notify->setText(i18nc("Notification when the filter log was enabled", "Mail Filter Log Enabled")); notify->sendEvent(); } } } changeRecorder()->itemFetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); changeRecorder()->itemFetchScope().setCacheOnly(true); changeRecorder()->fetchCollection(true); changeRecorder()->setChangeRecordingEnabled(false); mProgressCounter = 0; mProgressTimer = new QTimer(this); connect(mProgressTimer, SIGNAL(timeout()), this, SLOT(emitProgress())); } MailFilterAgent::~MailFilterAgent() { delete m_filterLogDialog; } void MailFilterAgent::configure(WId windowId) { Q_UNUSED(windowId); } void MailFilterAgent::initializeCollections() { m_filterManager->readConfig(); Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this); job->fetchScope().setContentMimeTypes(QStringList() << KMime::Message::mimeType()); connect(job, &Akonadi::CollectionFetchJob::result, this, &MailFilterAgent::initialCollectionFetchingDone); } void MailFilterAgent::initialCollectionFetchingDone(KJob *job) { if (job->error()) { qCWarning(MAILFILTERAGENT_LOG) << job->errorString(); return; //TODO: proper error handling } Akonadi::CollectionFetchJob *fetchJob = qobject_cast(job); const QMap pop3ResourceMap = MailCommon::Kernel::pop3ResourceTargetCollection(); foreach (const Akonadi::Collection &collection, fetchJob->collections()) { if (isFilterableCollection(collection)) { changeRecorder()->setCollectionMonitored(collection, true); } else { QMapIterator i(pop3ResourceMap); while (i.hasNext()) { i.next(); if (collection.id() == i.value()) { changeRecorder()->setCollectionMonitored(collection, true); break; } } } } Q_EMIT status(AgentBase::Idle, i18n("Ready")); Q_EMIT percent(100); QTimer::singleShot(2000, this, &MailFilterAgent::clearMessage); } void MailFilterAgent::clearMessage() { Q_EMIT status(AgentBase::Idle, QString()); } void MailFilterAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { /* The monitor mimetype filter would Q_DECL_OVERRIDE the collection filter, therefor we have to check * for the mimetype of the item here. */ if (item.mimeType() != KMime::Message::mimeType()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemAdded called for a non-message item!"; return; } MailCommon::SearchRule::RequiredPart requiredPart = m_filterManager->requiredPart(collection.resource()); Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); connect(job, &Akonadi::ItemFetchJob::itemsReceived, this, &MailFilterAgent::itemsReceiviedForFiltering); if (requiredPart == MailCommon::SearchRule::CompleteMessage) { job->fetchScope().fetchFullPayload(); } else if (requiredPart == MailCommon::SearchRule::Header) { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); job->fetchScope().fetchAttribute(); job->setProperty("resource", collection.resource()); //TODO: Error handling? } void MailFilterAgent::itemsReceiviedForFiltering(const Akonadi::Item::List &items) { if (items.isEmpty()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemsReceiviedForFiltering items is empty!"; return; } Akonadi::Item item = items.first(); /* * happens when item no longer exists etc, and queue compression didn't happen yet */ if (!item.hasPayload()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemsReceiviedForFiltering item has no payload!"; return; } Akonadi::MessageStatus status; status.setStatusFromFlags(item.flags()); if (status.isRead() || status.isSpam() || status.isIgnored()) { return; } QString resource = sender()->property("resource").toString(); const Akonadi::Pop3ResourceAttribute *pop3ResourceAttribute = item.attribute(); if (pop3ResourceAttribute) { resource = pop3ResourceAttribute->pop3AccountName(); } emitProgressMessage(i18n("Filtering in %1", Akonadi::AgentManager::self()->instance(resource).name())); m_filterManager->process(item, m_filterManager->requiredPart(resource), FilterManager::Inbound, true, resource); emitProgress(++mProgressCounter); mProgressTimer->start(1000); } void MailFilterAgent::mailCollectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &) { if (isFilterableCollection(collection)) { changeRecorder()->setCollectionMonitored(collection, true); } } void MailFilterAgent::mailCollectionChanged(const Akonadi::Collection &collection) { changeRecorder()->setCollectionMonitored(collection, isFilterableCollection(collection)); } void MailFilterAgent::mailCollectionRemoved(const Akonadi::Collection &collection) { changeRecorder()->setCollectionMonitored(collection, false); m_filterManager->mailCollectionRemoved(collection); } QString MailFilterAgent::createUniqueName(const QString &nameTemplate) { return m_filterManager->createUniqueName(nameTemplate); } void MailFilterAgent::filterItems(const QList< qint64 > &itemIds, int filterSet) { Akonadi::Item::List items; foreach (qint64 id, itemIds) { items << Akonadi::Item(id); } m_filterManager->applyFilters(items, static_cast(filterSet)); } void MailFilterAgent::applySpecificFilters(const QList< qint64 > &itemIds, int requires, const QStringList &listFilters) { Akonadi::Item::List items; foreach (qint64 id, itemIds) { items << Akonadi::Item(id); } m_filterManager->applySpecificFilters(items, static_cast(requires), listFilters); } void MailFilterAgent::filterItem(qint64 item, int filterSet, const QString &resourceId) { m_filterManager->filter(Akonadi::Item(item), static_cast(filterSet), resourceId); } void MailFilterAgent::filter(qint64 item, const QString &filterIdentifier, const QString &resourceId) { m_filterManager->filter(Akonadi::Item(item), filterIdentifier, resourceId); } void MailFilterAgent::reload() { Akonadi::Collection::List collections = changeRecorder()->collectionsMonitored(); foreach (const Akonadi::Collection &collection, collections) { changeRecorder()->setCollectionMonitored(collection, false); } initializeCollections(); } void MailFilterAgent::showFilterLogDialog(qlonglong windowId) { if (!m_filterLogDialog) { m_filterLogDialog = new FilterLogDialog(Q_NULLPTR); } #ifndef Q_OS_WIN KWindowSystem::setMainWindow(m_filterLogDialog, windowId); #else KWindowSystem::setMainWindow(m_filterLogDialog, (HWND)windowId); #endif m_filterLogDialog->show(); m_filterLogDialog->raise(); m_filterLogDialog->activateWindow(); m_filterLogDialog->setModal(false); } void MailFilterAgent::emitProgress(int p) { if (p == 0) { mProgressTimer->stop(); Q_EMIT status(AgentBase::Idle, QString()); } mProgressCounter = p; Q_EMIT percent(p); } void MailFilterAgent::emitProgressMessage(const QString &message) { Q_EMIT status(AgentBase::Running, message); } QString MailFilterAgent::printCollectionMonitored() { QString printDebugCollection; Akonadi::Collection::List collections = changeRecorder()->collectionsMonitored(); if (collections.isEmpty()) { printDebugCollection = QStringLiteral("No collection is monitored!"); } else { foreach (const Akonadi::Collection &collection, collections) { if (!printDebugCollection.isEmpty()) { printDebugCollection += QLatin1Char('\n'); } printDebugCollection += QStringLiteral("Collection name: %1\n").arg(collection.name()); printDebugCollection += QStringLiteral("Collection id: %1\n").arg(collection.id()); } } return printDebugCollection; } void MailFilterAgent::showConfigureDialog(qlonglong windowId) { Q_UNUSED(windowId); //TODO } void MailFilterAgent::expunge(qint64 collectionId) { mMailFilterKernel->expunge(collectionId, false); } void MailFilterAgent::slotInstanceRemoved(const Akonadi::AgentInstance &instance) { m_filterManager->agentRemoved(instance.identifier()); } AKONADI_AGENT_MAIN(MailFilterAgent)