diff --git a/CMakeLists.txt b/CMakeLists.txt index 6612ed7..da06e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,120 +1,120 @@ cmake_minimum_required(VERSION 3.0) -set(PIM_VERSION "5.6.41") +set(PIM_VERSION "5.6.42") project(mailcommon VERSION ${PIM_VERSION}) set(KF5_VERSION "5.36.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${mailcommon_SOURCE_DIR}/cmake/modules/ ${ECM_MODULE_PATH}) set(LIBRARY_NAMELINK) include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMQtDeclareLoggingCategory) include(ECMAddTests) include(ECMCoverageOption) set(MAILCOMMON_LIB_VERSION ${PIM_VERSION}) set(AKONADIMIME_LIB_VERSION "5.6.40") set(MESSAGELIB_LIB_VERSION "5.6.42") set(QT_REQUIRED_VERSION "5.7.0") set(KMIME_LIB_VERSION "5.6.41") set(KMAILTRANSPORT_LIB_VERSION "5.6.40") set(MAILIMPORTER_LIB_VERSION "5.6.40") set(LIBKDEPIM_LIB_VERSION "5.6.40") set(PIMCOMMON_LIB_VERSION "5.6.40") set(AKONADI_VERSION "5.6.40") find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets DBus Test Xml) find_package(KF5Archive ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ItemViews ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5LibkdepimAkonadi ${LIBKDEPIM_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailImporter ${MAILIMPORTER_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransport ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MessageComposer ${MESSAGELIB_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MessageCore ${MESSAGELIB_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MessageViewer ${MESSAGELIB_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommonAkonadi ${PIMCOMMON_LIB_VERSION} CONFIG REQUIRED) find_package(KF5TemplateParser ${MESSAGELIB_LIB_VERSION} CONFIG REQUIRED) find_package(Phonon4Qt5 CONFIG REQUIRED) find_package(Qt5Designer CONFIG) set_package_properties(Qt5Designer PROPERTIES PURPOSE "Required to build the Qt Designer plugins" TYPE OPTIONAL ) if (Qt5Designer_FOUND) find_package(KF5DesignerPlugin ${KF5_VERSION} CONFIG REQUIRED) endif() ecm_setup_version(PROJECT VARIABLE_PREFIX MAILCOMMON VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mailcommon_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5MailCommonConfigVersion.cmake" SOVERSION 5 ) find_package(Xsltproc) set_package_properties(Xsltproc PROPERTIES DESCRIPTION "XSLT processor from libxslt" TYPE REQUIRED PURPOSE "Required to generate D-Bus interfaces for all Akonadi resources.") ########### Targets ########### #add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions("-DQT_NO_CAST_TO_ASCII") remove_definitions( -DQT_NO_CAST_FROM_ASCII ) #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) ########### CMake Config Files ########### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5MailCommon") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5MailCommonConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5MailCommonConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5MailCommonConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5MailCommonConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5MailCommonTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5MailCommonTargets.cmake NAMESPACE KF5::) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mailcommon_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) add_subdirectory(src) add_subdirectory(autotests) install( FILES mailcommon.renamecategories mailcommon.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml b/src/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml index e51212e..baa5c63 100644 --- a/src/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml +++ b/src/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml @@ -1,38 +1,51 @@ + + + + + + + + + + + + + diff --git a/src/filter/filtermanager.cpp b/src/filter/filtermanager.cpp index 33950c2..41a8716 100644 --- a/src/filter/filtermanager.cpp +++ b/src/filter/filtermanager.cpp @@ -1,316 +1,350 @@ /* 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 "filtermanager.h" #include "mailcommon_debug.h" #include "filteractions/filteraction.h" #include "filteractions/filteractiondict.h" #include "filterimporterexporter.h" #include "mailfilteragentinterface.h" #include #include #include #include #include #include #include namespace MailCommon { class Q_DECL_HIDDEN FilterManager::Private { public: Private(FilterManager *qq) : q(qq) , mMailFilterAgentInterface(nullptr) , mMonitor(new Akonadi::Monitor) , mInitialized(false) { const auto service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent")); mMailFilterAgentInterface = new org::freedesktop::Akonadi::MailFilterAgent(service, QStringLiteral("/MailFilterAgent"), QDBusConnection::sessionBus(), q); } void readConfig(); void writeConfig(bool withSync = true) const; void clear(); QMap mTagList; static FilterManager *mInstance; static FilterActionDict *mFilterActionDict; FilterManager *q; OrgFreedesktopAkonadiMailFilterAgentInterface *mMailFilterAgentInterface; QList mFilters; Akonadi::Monitor *mMonitor; bool mInitialized; }; void FilterManager::Private::readConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig( Akonadi::ServerManager::addNamespace(QStringLiteral("akonadi_mailfilter_agent")) + QStringLiteral("rc")); clear(); QStringList emptyFilters; mFilters = FilterImporterExporter::readFiltersFromConfig(config, emptyFilters); Q_EMIT q->filtersChanged(); } void FilterManager::Private::writeConfig(bool withSync) const { KSharedConfig::Ptr config = KSharedConfig::openConfig( Akonadi::ServerManager::addNamespace(QStringLiteral("akonadi_mailfilter_agent")) + QStringLiteral("rc")); // Now, write out the new stuff: FilterImporterExporter::writeFiltersToConfig(mFilters, config); KConfigGroup group = config->group("General"); if (withSync) { group.sync(); } } void FilterManager::Private::clear() { qDeleteAll(mFilters); mFilters.clear(); } } using namespace MailCommon; FilterManager *FilterManager::Private::mInstance = nullptr; FilterActionDict *FilterManager::Private::mFilterActionDict = nullptr; FilterManager *FilterManager::instance() { if (!FilterManager::Private::mInstance) { FilterManager::Private::mInstance = new FilterManager; } return FilterManager::Private::mInstance; } FilterActionDict *FilterManager::filterActionDict() { if (!FilterManager::Private::mFilterActionDict) { FilterManager::Private::mFilterActionDict = new FilterActionDict; } return FilterManager::Private::mFilterActionDict; } FilterManager::FilterManager() : d(new Private(this)) { updateTagList(); d->mMonitor->setTypeMonitored(Akonadi::Monitor::Tags); d->mMonitor->tagFetchScope().fetchAttribute(); connect(d->mMonitor, &Akonadi::Monitor::tagAdded, this, &FilterManager::slotTagAdded); connect(d->mMonitor, &Akonadi::Monitor::tagRemoved, this, &FilterManager::slotTagRemoved); connect(d->mMonitor, &Akonadi::Monitor::tagChanged, this, &FilterManager::slotTagChanged); qDBusRegisterMetaType >(); Akonadi::ServerManager::State state = Akonadi::ServerManager::self()->state(); if (state == Akonadi::ServerManager::Running) { QTimer::singleShot(0, this, &FilterManager::slotReadConfig); } else { connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &FilterManager::slotServerStateChanged); } } FilterManager::~FilterManager() { cleanup(); } void FilterManager::cleanup() { d->clear(); } void FilterManager::slotServerStateChanged(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { d->readConfig(); disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State))); } } void FilterManager::updateTagList() { Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(this); fetchJob->fetchScope().fetchAttribute(); connect(fetchJob, &Akonadi::TagFetchJob::result, this, &FilterManager::slotFinishedTagListing); } bool FilterManager::initialized() const { return d->mInitialized; } void FilterManager::slotReadConfig() { d->readConfig(); d->mInitialized = true; Q_EMIT loadingFiltersDone(); } void FilterManager::slotFinishedTagListing(KJob *job) { if (job->error()) { qCWarning(MAILCOMMON_LOG) << "failed to retrieve tags " << job->errorString(); } Akonadi::TagFetchJob *fetchJob = static_cast(job); const Akonadi::Tag::List lstTags = fetchJob->tags(); for (const Akonadi::Tag &tag : lstTags) { d->mTagList.insert(tag.url(), tag.name()); } Q_EMIT tagListingFinished(); } void FilterManager::slotTagAdded(const Akonadi::Tag &tag) { d->mTagList.insert(tag.url(), tag.name()); Q_EMIT tagListingFinished(); } void FilterManager::slotTagChanged(const Akonadi::Tag &tag) { if (d->mTagList.contains(tag.url())) { d->mTagList.insert(tag.url(), tag.name()); } Q_EMIT tagListingFinished(); } void FilterManager::slotTagRemoved(const Akonadi::Tag &tag) { d->mTagList.remove(tag.url()); Q_EMIT tagListingFinished(); } QMap FilterManager::tagList() const { return d->mTagList; } bool FilterManager::isValid() const { return d->mMailFilterAgentInterface->isValid(); } QString FilterManager::createUniqueFilterName(const QString &name) const { return d->mMailFilterAgentInterface->createUniqueName(name); } void FilterManager::showFilterLogDialog(qlonglong windowId) { d->mMailFilterAgentInterface->showFilterLogDialog(windowId); } void FilterManager::filter(const Akonadi::Item &item, const QString &identifier, const QString &resourceId) const { d->mMailFilterAgentInterface->filter(item.id(), identifier, resourceId); } void FilterManager::filter(const Akonadi::Item &item, FilterSet set, bool account, const QString &resourceId) const { d->mMailFilterAgentInterface->filterItem(item.id(), static_cast(set), account ? resourceId : QString()); } +void FilterManager::filter(const Akonadi::Collection &collection, MailCommon::FilterManager::FilterSet set) const +{ + filter({ collection }, set); +} + +void FilterManager::filter(const Akonadi::Collection::List &collections, FilterManager::FilterSet set) const +{ + QList colIds; + colIds.reserve(collections.size()); + for (const auto col : collections) { + colIds << col.id(); + } + + d->mMailFilterAgentInterface->filterCollections(colIds, static_cast(set)); +} + +void FilterManager::filter(const Akonadi::Collection &collection, const QStringList &listFilters) const +{ + filter({ collection }, listFilters); +} + + +void FilterManager::filter(const Akonadi::Collection::List &collections, const QStringList &listFilters) const +{ + QList colIds; + colIds.reserve(collections.size()); + for (const auto col : collections) { + colIds << col.id(); + } + + d->mMailFilterAgentInterface->applySpecificFiltersOnCollections(colIds, listFilters); +} + + void FilterManager::filter(const Akonadi::Item::List &messages, FilterManager::FilterSet set) const { QList itemIds; itemIds.reserve(messages.size()); for (const Akonadi::Item &item : messages) { itemIds << item.id(); } d->mMailFilterAgentInterface->filterItems(itemIds, static_cast(set)); } void FilterManager::filter(const Akonadi::Item::List &messages, SearchRule::RequiredPart requiredPart, const QStringList &listFilters) const { QList itemIds; itemIds.reserve(messages.size()); for (const Akonadi::Item &item : messages) { itemIds << item.id(); } d->mMailFilterAgentInterface->applySpecificFilters(itemIds, static_cast(requiredPart), listFilters); } void FilterManager::setFilters(const QList &filters) { beginUpdate(); d->clear(); d->mFilters = filters; endUpdate(); } QList FilterManager::filters() const { return d->mFilters; } void FilterManager::appendFilters(const QList &filters, bool replaceIfNameExists) { beginUpdate(); if (replaceIfNameExists) { for (const MailCommon::MailFilter *newFilter : filters) { int numberOfFilters = d->mFilters.count(); for (int i = 0; i < numberOfFilters; ++i) { MailCommon::MailFilter *filter = d->mFilters.at(i); if (newFilter->name() == filter->name()) { d->mFilters.removeAll(filter); i = 0; numberOfFilters = d->mFilters.count(); } } } } d->mFilters += filters; endUpdate(); } void FilterManager::removeFilter(MailCommon::MailFilter *filter) { beginUpdate(); d->mFilters.removeAll(filter); endUpdate(); } void FilterManager::beginUpdate() { } void FilterManager::endUpdate() { d->writeConfig(true); d->mMailFilterAgentInterface->reload(); Q_EMIT filtersChanged(); } diff --git a/src/filter/filtermanager.h b/src/filter/filtermanager.h index fcc3981..1939006 100644 --- a/src/filter/filtermanager.h +++ b/src/filter/filtermanager.h @@ -1,187 +1,211 @@ /* 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. */ #ifndef MAILCOMMON_FILTERMANAGER_H #define MAILCOMMON_FILTERMANAGER_H #include "mailcommon_export.h" #include "mailfilter.h" #include #include #include namespace MailCommon { class FilterActionDict; /** * @short A wrapper class that allows easy access to the mail filters * * This class communicates with the mailfilter agent via DBus. */ class MAILCOMMON_EXPORT FilterManager : public QObject { Q_OBJECT public: ~FilterManager(); /** * Describes the list of filters. */ enum FilterSet { NoSet = 0x0, Inbound = 0x1, Outbound = 0x2, Explicit = 0x4, BeforeOutbound = 0x8, AllFolders = 0x16, ///< Apply the filter on all folders, not just inbox All = Inbound | BeforeOutbound | Outbound | Explicit | AllFolders }; /** * Returns the global filter manager object. */ static FilterManager *instance(); /** * Returns whether the filter manager is in a usable state. */ bool isValid() const; /** * Checks for existing filters with the @p name and extend the * "name" to "name (i)" until no match is found for i=1..n */ QString createUniqueFilterName(const QString &name) const; /** * Returns the global filter action dictionary. */ static FilterActionDict *filterActionDict(); /** * Shows the filter log dialog. * * This is used to debug problems with filters. */ void showFilterLogDialog(qlonglong windowId); /// Apply filters interface /** * Applies filter with the given @p identifier on the message @p item. * @return @c true on success, @c false otherwise. */ void filter(const Akonadi::Item &item, const QString &identifier, const QString &resourceId) const; /** * Process given message item by applying the filter rules one by * one. You can select which set of filters (incoming or outgoing) * should be used. * * @param item The message item to process. * @param set Select the filter set to use. * @param account @c true if an account id is specified else @c false * @param accountId The id of the resource that the message was retrieved from */ void filter(const Akonadi::Item &item, FilterSet set = Inbound, bool account = false, const QString &resourceId = QString()) const; + /** + * Process all messages in given collection by applying the filters rules one + * by one. You can select which set of filters (incoming or outgoing) + * should be used. + */ + void filter(const Akonadi::Collection &collection, FilterSet set = Inbound) const; + + /** + * Apply specified filters on all messages in given collection + */ + void filter(const Akonadi::Collection &collection, const QStringList &listFilters) const; + + /** + * Process all messages in given collections by applying the filters rules one + * by one. You can select which set of filters (incoming or outgoing) + * should be used. + */ + void filter(const Akonadi::Collection::List &collections, FilterSet set = Inbound) const; + + /** + * Apply specified filters on all messages in given collection + */ + void filter(const Akonadi::Collection::List &collections, const QStringList &listFilters) const; + /** * Process given @p messages by applying the filter rules one by * one. You can select which set of filters (incoming or outgoing) * should be used. * * @param item The message item to process. * @param set Select the filter set to use. */ void filter(const Akonadi::Item::List &messages, FilterSet set = Explicit) const; void filter(const Akonadi::Item::List &messages, SearchRule::RequiredPart requiredPart, const QStringList &listFilters) const; /// Manage filters interface /** * Appends the list of @p filters to the current list of filters and * write everything back into the configuration. The filter manager * takes ownership of the filters in the list. */ void appendFilters(const QList &filters, bool replaceIfNameExists = false); /** * Removes the given @p filter from the list. * The filter object is not deleted. */ void removeFilter(MailCommon::MailFilter *filter); /** * Replace the list of filters of the filter manager with the given list of @p filters. * The manager takes ownership of the filters. */ void setFilters(const QList &filters); /** * Returns the filter list of the manager. */ QList filters() const; /** * Should be called at the beginning of an filter list update. */ void beginUpdate(); /** * Should be called at the end of an filter list update. */ void endUpdate(); QMap tagList() const; bool initialized() const; void cleanup(); private Q_SLOTS: void slotServerStateChanged(Akonadi::ServerManager::State); void slotFinishedTagListing(KJob *); void slotReadConfig(); void updateTagList(); void slotTagAdded(const Akonadi::Tag &); void slotTagChanged(const Akonadi::Tag &); void slotTagRemoved(const Akonadi::Tag &); Q_SIGNALS: /** * This signal is emitted whenever the filter list has been updated. */ void filtersChanged(); void tagListingFinished(); void loadingFiltersDone(); private: FilterManager(); class Private; Private *const d; }; } #endif