diff --git a/CMakeLists.txt b/CMakeLists.txt index 18f988a4e..55d8a5633 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,315 +1,316 @@ cmake_minimum_required(VERSION 3.0) set(PIM_VERSION "5.6.42") project(Akonadi VERSION ${PIM_VERSION}) # ECM setup set(KF5_VERSION "5.37.0") option(RUN_UNINSTALL "test run uninstalled apps (experimental)" FALSE) set(ECM_VERSION ${KF5_VERSION}) # ECM setup if (RUN_UNINSTALL) set(ECM_VERSION "5.38.0") endif() find_package(ECM ${ECM_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(CheckIncludeFiles) include(ECMQtDeclareLoggingCategory) include(CheckSymbolExists) include(ECMCoverageOption) include(AkonadiMacros) set(QT_REQUIRED_VERSION "5.8.0") set(AKONADI_VERSION ${PIM_VERSION}) ecm_setup_version(PROJECT VARIABLE_PREFIX AKONADI VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/akonadi_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfigVersion.cmake" SOVERSION 5) # Find packages find_package(Qt5Core ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Private) find_package(Qt5Sql ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Private) find_package(Qt5DBus ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Network ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Test ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Widgets ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Xml ${QT_REQUIRED_VERSION} REQUIRED) find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${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(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Crash ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5NetworkManagerQt ${KF5_VERSION} CONFIG REQUIRED) find_package(Qt5Designer NO_MODULE) set_package_properties(Qt5Designer PROPERTIES PURPOSE "Required to build the Qt Designer plugins" TYPE OPTIONAL ) find_package(KF5DesignerPlugin ${KF5_VERSION} CONFIG) set_package_properties(KF5DesignerPlugin PROPERTIES DESCRIPTION "KF5 designer plugin" TYPE OPTIONAL) set(Boost_MINIMUM_VERSION "1.34.0") find_package(Boost ${Boost_MINIMUM_VERSION}) set_package_properties(Boost PROPERTIES DESCRIPTION "Boost C++ Libraries" URL "http://www.boost.org" TYPE REQUIRED ) if(BUILD_TESTING) set(AKONADI_TESTS_EXPORT AKONADICORE_EXPORT) endif() configure_file(akonaditests_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/akonaditests_export.h") # Make sure the KF5Akonadi_DATA_DIR is absolute before passing it to KF5AkonadiConfig.cmake.in # otherwise build fails either on OSX CI, or for normal users if (IS_ABSOLUTE "${KDE_INSTALL_DATADIR_KF5}") set(KF5Akonadi_DATA_DIR "${KDE_INSTALL_DATADIR_KF5}/akonadi") else() set(KF5Akonadi_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_DATADIR_KF5}/akonadi") endif() check_symbol_exists(malloc_trim "malloc.h" HAVE_MALLOC_TRIM) ############### Build Options ############### option(AKONADI_BUILD_QSQLITE "Build the Sqlite backend." TRUE) option(BUILD_TOOLS "Build and install tools for development and testing purposes." TRUE) if(BUILD_TESTING) set(BUILD_TOOLS TRUE) endif() set(SMI_MIN_VERSION "1.0") find_package(SharedMimeInfo ${SMI_MIN_VERSION} REQUIRED) find_program(XSLTPROC_EXECUTABLE xsltproc) if(NOT XSLTPROC_EXECUTABLE) message(FATAL_ERROR "\nThe command line XSLT processor program 'xsltproc' could not be found.\nPlease install xsltproc.\n") endif() find_program(MYSQLD_EXECUTABLE NAMES mysqld PATHS /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec /opt/mysql/libexec /usr/mysql/bin /opt/mysql/sbin DOC "The mysqld executable path. ONLY needed at runtime" ) if(MYSQLD_EXECUTABLE) message(STATUS "MySQL Server found: ${MYSQLD_EXECUTABLE}") else() message(STATUS "MySQL Server wasn't found. it is required to use the MySQL backend.") endif() find_path(POSTGRES_PATH NAMES pg_ctl HINTS /usr/lib${LIB_SUFFIX}/postgresql/8.4/bin /usr/lib${LIB_SUFFIX}/postgresql/9.0/bin /usr/lib${LIB_SUFFIX}/postgresql/9.1/bin DOC "The pg_ctl executable path. ONLY needed at runtime by the PostgreSQL backend" ) if(POSTGRES_PATH) message(STATUS "PostgreSQL Server found.") else() message(STATUS "PostgreSQL wasn't found. it is required to use the Postgres backend.") endif() if("${DATABASE_BACKEND}" STREQUAL "SQLITE") set(SQLITE_TYPE "REQUIRED") else() set(SQLITE_TYPE "OPTIONAL") endif() if(AKONADI_BUILD_QSQLITE) set(SQLITE_MIN_VERSION 3.6.23) find_package(Sqlite ${SQLITE_MIN_VERSION}) set_package_properties(Sqlite PROPERTIES URL "http://www.sqlite.org" DESCRIPTION "The Sqlite database library" TYPE ${SQLITE_TYPE} PURPOSE "Required by the Sqlite backend" ) endif() find_program(XMLLINT_EXECUTABLE xmllint) if(NOT XMLLINT_EXECUTABLE) message(STATUS "xmllint not found, skipping akonadidb.xml schema validation") endif() check_include_files(unistd.h HAVE_UNISTD_H) if(HAVE_UNISTD_H) add_definitions(-DHAVE_UNISTD_H) endif() if (IS_ABSOLUTE "${DBUS_INTERFACES_INSTALL_DIR}") set(AKONADI_DBUS_INTERFACES_INSTALL_DIR "${DBUS_INTERFACES_INSTALL_DIR}") else() set(AKONADI_DBUS_INTERFACES_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DBUS_INTERFACES_INSTALL_DIR}") endif() if (IS_ABSOLUTE "${KDE_INSTALL_INCLUDEDIR_KF5}") set(AKONADI_INCLUDE_DIR "${KDE_INSTALL_INCLUDEDIR_KF5}") else() set(AKONADI_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_INCLUDEDIR_KF5}") endif() ############### Build Options ############### include(CTest) # Calls enable_testing(). include(CTestConfig.cmake) if(NOT DEFINED DATABASE_BACKEND) set(DATABASE_BACKEND "MYSQL" CACHE STRING "The default database backend to use for Akonadi. Can be either MYSQL, POSTGRES or SQLITE") endif() ############### CTest options ############### # Set a timeout value of 1 minute per test set(DART_TESTING_TIMEOUT 60) # CTestCustom.cmake has to be in the CTEST_BINARY_DIR. # in the KDE build system, this is the same as CMAKE_BINARY_DIR. configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake COPYONLY) ############### Macros ############### macro(SET_DEFAULT_DB_BACKEND) set(_backend ${ARGV0}) if("${_backend}" STREQUAL "SQLITE") set(AKONADI_DATABASE_BACKEND QSQLITE3) set(AKONADI_BUILD_QSQLITE TRUE) else() if("${_backend}" STREQUAL "POSTGRES") set(AKONADI_DATABASE_BACKEND QPSQL) else() set(AKONADI_DATABASE_BACKEND QMYSQL) endif() endif() message(STATUS "Using default db backend ${AKONADI_DATABASE_BACKEND}") add_definitions(-DAKONADI_DATABASE_BACKEND="${AKONADI_DATABASE_BACKEND}") endmacro() #### DB BACKEND DEFAULT #### set_default_db_backend(${DATABASE_BACKEND}) ############### Compilers flags ############### if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wundef -Wcast-align -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wformat-security -fno-common -pedantic") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C++ compiler during debugfull builds." FORCE) set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C compiler during debugfull builds." FORCE) mark_as_advanced(CMAKE_CXX_FLAGS_DEBUGFULL CMAKE_C_FLAGS_DEBUGFULL) endif() if(MSVC) set(_ENABLE_EXCEPTIONS -EHsc) else() set(_ENABLE_EXCEPTIONS -fexceptions) endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS}") ############### Configure files ############# configure_file(akonadi-prefix.h.cmake ${Akonadi_BINARY_DIR}/akonadi-prefix.h) configure_file(config-akonadi.h.cmake ${Akonadi_BINARY_DIR}/config-akonadi.h) ############### build targets ############### add_definitions(-DTRANSLATION_DOMAIN=\"libakonadi5\") include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} src ) #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_subdirectory(src) add_subdirectory(icons) if(BUILD_TOOLS) # add testrunner (application for managing a self-contained # test environment) add_subdirectory(autotests/libs/testrunner) add_subdirectory(autotests/libs/testresource) add_subdirectory(autotests/libs/testsearchplugin) endif() if(BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() ############### install stuff ############### install(FILES akonadi-mime.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) ############### CMake Config Files ############### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Akonadi") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5AkonadiConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/KF5AkonadiMacros.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5AkonadiTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5AkonadiTargets.cmake NAMESPACE KF5::) install(FILES akonadi.categories akonadi.renamecategories DESTINATION ${KDE_INSTALL_CONFDIR} ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akonadi_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/agentbase/CMakeLists.txt b/src/agentbase/CMakeLists.txt index d770532e4..8a390eedb 100644 --- a/src/agentbase/CMakeLists.txt +++ b/src/agentbase/CMakeLists.txt @@ -1,98 +1,99 @@ set(akonadiagentbase_SRCS agentbase.cpp agentsearchinterface.cpp preprocessorbase.cpp preprocessorbase_p.cpp recursivemover.cpp resourcebase.cpp resourcescheduler.cpp resourcesettings.cpp transportresourcebase.cpp ) ecm_qt_declare_logging_category(akonadiagentbase_SRCS HEADER akonadiagentbase_debug.h IDENTIFIER AKONADIAGENTBASE_LOG CATEGORY_NAME org.kde.pim.akonadiagentbase) ecm_generate_headers(AkonadiAgentBase_HEADERS HEADER_NAMES AgentBase AgentSearchInterface PreprocessorBase ResourceBase ResourceSettings TransportResourceBase REQUIRED_HEADERS AkonadiAgentBase_HEADERS ) KCONFIG_ADD_KCFG_FILES(akonadiagentbase_SRCS resourcebasesettings.kcfgc) qt5_add_dbus_interfaces(akonadiagentbase_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Tracer.xml ) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Resource.xml resourcebase.h Akonadi::ResourceBase resourceadaptor Akonadi__ResourceAdaptor) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Preprocessor.xml preprocessorbase_p.h Akonadi::PreprocessorBasePrivate preprocessoradaptor Akonadi__PreprocessorAdaptor) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Agent.Status.xml agentbase.h Akonadi::AgentBase statusadaptor Akonadi__StatusAdaptor) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Agent.Control.xml agentbase.h Akonadi::AgentBase controladaptor Akonadi__ControlAdaptor) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ../interfaces/org.freedesktop.Akonadi.Resource.Transport.xml transportresourcebase_p.h Akonadi::TransportResourceBasePrivate transportadaptor Akonadi__TransportAdaptor) qt5_add_dbus_adaptor(akonadiagentbase_SRCS ../interfaces/org.freedesktop.Akonadi.Agent.Search.xml agentsearchinterface_p.h Akonadi::AgentSearchInterfacePrivate searchadaptor Akonadi__SearchAdaptor ) add_library(KF5AkonadiAgentBase ${akonadiagentbase_SRCS}) generate_export_header(KF5AkonadiAgentBase BASE_NAME akonadiagentbase) add_library(KF5::AkonadiAgentBase ALIAS KF5AkonadiAgentBase) target_include_directories(KF5AkonadiAgentBase INTERFACE "$") target_link_libraries(KF5AkonadiAgentBase PUBLIC Qt5::DBus Qt5::Widgets # for QApplication KF5::AkonadiCore KF5::ConfigCore KF5::ConfigGui # for KConfigSkeleton PRIVATE KF5::AkonadiPrivate KF5::DBusAddons KF5::I18n + KF5::NetworkManagerQt Qt5::Network ) set_target_properties(KF5AkonadiAgentBase PROPERTIES VERSION ${AKONADI_VERSION_STRING} SOVERSION ${AKONADI_SOVERSION} EXPORT_NAME AkonadiAgentBase ) ecm_generate_pri_file(BASE_NAME AkonadiAgentBase LIB_NAME KF5AkonadiAgentBase DEPS "AkonadiCore AkonadiPrivate ConfigCore ConfigGui" FILENAME_VAR PRI_FILENAME ) install(TARGETS KF5AkonadiAgentBase EXPORT KF5AkonadiTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akonadiagentbase_export.h ${CMAKE_CURRENT_BINARY_DIR}/resourcebasesettings.h ${AkonadiAgentBase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/AkonadiAgentBase COMPONENT Devel ) install(FILES resourcebase.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR} ) diff --git a/src/agentbase/agentbase.cpp b/src/agentbase/agentbase.cpp index 3a19005b8..edd77066d 100644 --- a/src/agentbase/agentbase.cpp +++ b/src/agentbase/agentbase.cpp @@ -1,1304 +1,1338 @@ /* Copyright (c) 2006 Till Adam Copyright (c) 2007 Volker Krause Copyright (c) 2007 Bruno Virlet Copyright (c) 2008 Kevin Krammer 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 "agentbase.h" #include "agentbase_p.h" #include "akonadi_version.h" #include "agentmanager.h" #include "changerecorder.h" #include "controladaptor.h" #include "KDBusConnectionPool" #include "itemfetchjob.h" #include "monitor_p.h" #include "servermanager_p.h" #include "session.h" #include "session_p.h" #include "statusadaptor.h" #include "private/standarddirs_p.h" #include "akonadiagentbase_debug.h" #include #include #include #include #include #include +#include #include #include #include #include +#include #include #include #include #if defined __GLIBC__ # include // for dumping memory information #endif using namespace Akonadi; static AgentBase *sAgentBase = nullptr; AgentBase::Observer::Observer() { } AgentBase::Observer::~Observer() { } void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::itemChanged(const Item &item, const QSet &partIdentifiers) { Q_UNUSED(item); Q_UNUSED(partIdentifiers); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::itemRemoved(const Item &item) { Q_UNUSED(item); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(collection); Q_UNUSED(parent); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionChanged(const Collection &collection) { Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionRemoved(const Collection &collection) { Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) { Q_UNUSED(item); Q_UNUSED(source); Q_UNUSED(dest); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemLinked, sAgentBase->d_ptr, &AgentBasePrivate::itemLinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemUnlinked, sAgentBase->d_ptr, &AgentBasePrivate::itemUnlinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) { Q_UNUSED(collection); Q_UNUSED(source); Q_UNUSED(dest); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes) { Q_UNUSED(changedAttributes); collectionChanged(collection); } void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) { Q_UNUSED(items); Q_UNUSED(addedFlags); Q_UNUSED(removedFlags); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsFlagsChanged, sAgentBase->d_ptr, &AgentBasePrivate::itemsFlagsChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection) { Q_UNUSED(items); Q_UNUSED(sourceCollection); Q_UNUSED(destinationCollection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsMoved, sAgentBase->d_ptr, &AgentBasePrivate::itemsMoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items) { Q_UNUSED(items); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRemoved, sAgentBase->d_ptr, &AgentBasePrivate::itemsRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection) { Q_UNUSED(items); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsLinked, sAgentBase->d_ptr, &AgentBasePrivate::itemsLinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection) { Q_UNUSED(items); Q_UNUSED(collection) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsUnlinked, sAgentBase->d_ptr, &AgentBasePrivate::itemsUnlinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagAdded(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagAdded, sAgentBase->d_ptr, &AgentBasePrivate::tagAdded); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagChanged(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagChanged, sAgentBase->d_ptr, &AgentBasePrivate::tagChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagRemoved(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagRemoved, sAgentBase->d_ptr, &AgentBasePrivate::tagRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet &addedTags, const QSet &removedTags) { Q_UNUSED(items); Q_UNUSED(addedTags); Q_UNUSED(removedTags); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsTagsChanged, sAgentBase->d_ptr, &AgentBasePrivate::itemsTagsChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::relationAdded(const Akonadi::Relation &relation) { Q_UNUSED(relation) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationAdded, sAgentBase->d_ptr, &AgentBasePrivate::relationAdded); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::relationRemoved(const Akonadi::Relation &relation) { Q_UNUSED(relation) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationRemoved, sAgentBase->d_ptr, &AgentBasePrivate::relationRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations) { Q_UNUSED(items) Q_UNUSED(addedRelations) Q_UNUSED(removedRelations) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsRelationsChanged(Akonadi::Item::List,Akonadi::Relation::List,Akonadi::Relation::List)), sAgentBase, SLOT(itemsRelationsChanged(Akonadi::Item::List,Akonadi::Relation::List,Akonadi::Relation::List))); sAgentBase->d_ptr->changeProcessed(); } } //@cond PRIVATE AgentBasePrivate::AgentBasePrivate(AgentBase *parent) : q_ptr(parent) , mStatusCode(AgentBase::Idle) , mProgress(0) , mNeedsNetwork(false) , mOnline(false) , mDesiredOnlineState(false) , mSettings(nullptr) , mChangeRecorder(nullptr) , mTracer(nullptr) , mObserver(nullptr) , mPowerInterface(nullptr) , mTemporaryOfflineTimer(nullptr) , mEventLoopLocker(nullptr) , mNetworkManager(nullptr) { Internal::setClientType(Internal::Agent); } AgentBasePrivate::~AgentBasePrivate() { mChangeRecorder->setConfig(nullptr); delete mSettings; } void AgentBasePrivate::init() { Q_Q(AgentBase); Kdelibs4ConfigMigrator migrate(mId); migrate.setConfigFiles(QStringList() << QStringLiteral("%1rc").arg(mId)); migrate.migrate(); /** * Create a default session for this process. */ SessionPrivate::createDefaultSession(mId.toLatin1()); mTracer = new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server), QStringLiteral("/tracing"), KDBusConnectionPool::threadConnection(), q); new Akonadi__ControlAdaptor(q); new Akonadi__StatusAdaptor(q); if (!KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/"), q, QDBusConnection::ExportAdaptors)) { q->error(i18n("Unable to register object at dbus: %1", KDBusConnectionPool::threadConnection().lastError().message())); } mSettings = new QSettings(QStringLiteral("%1/agent_config_%2").arg(StandardDirs::saveDir("config"), mId), QSettings::IniFormat); mChangeRecorder = new ChangeRecorder(q); mChangeRecorder->setObjectName(QStringLiteral("AgentBaseChangeRecorder")); mChangeRecorder->ignoreSession(Session::defaultSession()); mChangeRecorder->itemFetchScope().setCacheOnly(true); mChangeRecorder->setConfig(mSettings); mDesiredOnlineState = mSettings->value(QStringLiteral("Agent/DesiredOnlineState"), true).toBool(); mOnline = mDesiredOnlineState; // reinitialize the status message now that online state is available mStatusMessage = defaultReadyMessage(); mName = mSettings->value(QStringLiteral("Agent/Name")).toString(); if (mName.isEmpty()) { mName = mSettings->value(QStringLiteral("Resource/Name")).toString(); if (!mName.isEmpty()) { mSettings->remove(QStringLiteral("Resource/Name")); mSettings->setValue(QStringLiteral("Agent/Name"), mName); } } connect(mChangeRecorder, &Monitor::itemAdded, this, &AgentBasePrivate::itemAdded); connect(mChangeRecorder, &Monitor::itemChanged, this, &AgentBasePrivate::itemChanged); connect(mChangeRecorder, &Monitor::collectionAdded, this, &AgentBasePrivate::collectionAdded); connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection)), SLOT(collectionChanged(Akonadi::Collection))); connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection,QSet)), SLOT(collectionChanged(Akonadi::Collection,QSet))); connect(mChangeRecorder, &Monitor::collectionMoved, this, &AgentBasePrivate::collectionMoved); connect(mChangeRecorder, &Monitor::collectionRemoved, this, &AgentBasePrivate::collectionRemoved); connect(mChangeRecorder, &Monitor::collectionSubscribed, this, &AgentBasePrivate::collectionSubscribed); connect(mChangeRecorder, &Monitor::collectionUnsubscribed, this, &AgentBasePrivate::collectionUnsubscribed); connect(q, SIGNAL(status(int,QString)), q, SLOT(slotStatus(int,QString))); connect(q, SIGNAL(percent(int)), q, SLOT(slotPercent(int))); connect(q, SIGNAL(warning(QString)), q, SLOT(slotWarning(QString))); connect(q, SIGNAL(error(QString)), q, SLOT(slotError(QString))); mPowerInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"), QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"), QDBusConnection::sessionBus(), this); if (mPowerInterface->isValid()) { connect(mPowerInterface, SIGNAL(resumingFromSuspend()), q, SLOT(slotResumedFromSuspend())); } else { delete mPowerInterface; mPowerInterface = nullptr; } // Use reference counting to allow agents to finish internal jobs when the // agent is stopped. mEventLoopLocker = new QEventLoopLocker(); mResourceTypeName = AgentManager::self()->instance(mId).type().name(); setProgramName(); QTimer::singleShot(0, q, SLOT(delayedInit())); } void AgentBasePrivate::delayedInit() { Q_Q(AgentBase); const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId); if (!KDBusConnectionPool::threadConnection().registerService(serviceId)) { qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at dbus:" << KDBusConnectionPool::threadConnection().lastError().message(); } q->setOnlineInternal(mDesiredOnlineState); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/Debug"), this, QDBusConnection::ExportScriptableSlots); } void AgentBasePrivate::setProgramName() { // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras) QString programName = mResourceTypeName; if (!mName.isEmpty()) { programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName); } QGuiApplication::setApplicationDisplayName(programName); } void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (mObserver) { mObserver->itemAdded(item, collection); } } void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers) { if (mObserver) { mObserver->itemChanged(item, partIdentifiers); } } void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (mObserver) { // inter-resource moves, requires we know which resources the source and destination are in though if (!source.resource().isEmpty() && !dest.resource().isEmpty()) { if (source.resource() != dest.resource()) { if (source.resource() == q_ptr->identifier()) { // moved away from us Akonadi::Item i(item); i.setParentCollection(source); mObserver->itemRemoved(i); } else if (dest.resource() == q_ptr->identifier()) { // moved to us mObserver->itemAdded(item, dest); } else if (observer2) { observer2->itemMoved(item, source, dest); } else { // not for us, not sure if we should get here at all changeProcessed(); } return; } } // intra-resource move if (observer2) { observer2->itemMoved(item, source, dest); } else { // ### we cannot just call itemRemoved here as this will already trigger changeProcessed() // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway // without using ObserverV2 mObserver->itemAdded(item, dest); // mObserver->itemRemoved( item ); } } } void AgentBasePrivate::itemRemoved(const Akonadi::Item &item) { if (mObserver) { mObserver->itemRemoved(item); } } void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->itemLinked(item, collection); } else { changeProcessed(); } } void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->itemUnlinked(item, collection); } else { changeProcessed(); } } void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsFlagsChanged(items, addedFlags, removedFlags); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsMoved(items, source, destination); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsRemoved(items); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) { if (!mObserver) { changeProcessed(); return; } AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsLinked(items, collection); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) { if (!mObserver) { return; } AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsUnlinked(items, collection); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagAdded(tag); } else { changeProcessed(); } } void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagChanged(tag); } else { changeProcessed(); } } void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagRemoved(tag);; } else { changeProcessed(); } } void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->itemsTagsChanged(items, addedTags, removedTags); } else { changeProcessed(); } } void AgentBasePrivate::relationAdded(const Akonadi::Relation &relation) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->relationAdded(relation); } else { changeProcessed(); } } void AgentBasePrivate::relationRemoved(const Akonadi::Relation &relation) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->relationRemoved(relation); } else { changeProcessed(); } } void AgentBasePrivate::itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations) { if (!mObserver) { return; } AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->itemsRelationsChanged(items, addedRelations, removedRelations); } else { changeProcessed(); } } void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { if (mObserver) { mObserver->collectionAdded(collection, parent); } } void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (mObserver && observer2 == nullptr) { // For ObserverV2 we use the variant with the part identifiers mObserver->collectionChanged(collection); } } void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->collectionChanged(collection, changedAttributes); } } void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->collectionMoved(collection, source, dest); } else if (mObserver) { // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed() // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway // without using ObserverV2 mObserver->collectionAdded(collection, dest); } else { changeProcessed(); } } void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection) { if (mObserver) { mObserver->collectionRemoved(collection); } } void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(collection); Q_UNUSED(parent); changeProcessed(); } void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection) { Q_UNUSED(collection); changeProcessed(); } void AgentBasePrivate::changeProcessed() { mChangeRecorder->changeProcessed(); QTimer::singleShot(0, mChangeRecorder, &ChangeRecorder::replayNext); } void AgentBasePrivate::slotStatus(int status, const QString &message) { mStatusMessage = message; mStatusCode = 0; switch (status) { case AgentBase::Idle: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultReadyMessage(); } mStatusCode = 0; break; case AgentBase::Running: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultSyncingMessage(); } mStatusCode = 1; break; case AgentBase::Broken: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultErrorMessage(); } mStatusCode = 2; break; case AgentBase::NotConfigured: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultUnconfiguredMessage(); } mStatusCode = 3; break; default: Q_ASSERT(!"Unknown status passed"); break; } } void AgentBasePrivate::slotPercent(int progress) { mProgress = progress; } void AgentBasePrivate::slotWarning(const QString &message) { mTracer->warning(QStringLiteral("AgentBase(%1)").arg(mId), message); } void AgentBasePrivate::slotError(const QString &message) { mTracer->error(QStringLiteral("AgentBase(%1)").arg(mId), message); } void AgentBasePrivate::slotNetworkStatusChange(bool isOnline) { Q_UNUSED(isOnline); Q_Q(AgentBase); q->setOnlineInternal(mDesiredOnlineState); } +void AgentBasePrivate::slotConnectivityChanged(NetworkManager::Connectivity connectivity) +{ + Q_Q(AgentBase); + qCDebug(AKONADIAGENTBASE_LOG) << "NM Connectivity changed:" << connectivity; + if (connectivity == NetworkManager::Full) { + q->setOnlineInternal(mDesiredOnlineState); + } else { + q->setOnlineInternal(false); + } +} + void AgentBasePrivate::slotResumedFromSuspend() { if (mNeedsNetwork) { - slotNetworkStatusChange(mNetworkManager->isOnline()); + if (mNetworkManager) { + slotNetworkStatusChange(mNetworkManager->isOnline()); + } else { + slotConnectivityChanged(NetworkManager::connectivity()); + } } } void AgentBasePrivate::slotTemporaryOfflineTimeout() { Q_Q(AgentBase); q->setOnlineInternal(true); } QString AgentBasePrivate::dumpNotificationListToString() const { return mChangeRecorder->dumpNotificationListToString(); } void AgentBasePrivate::dumpMemoryInfo() const { // Send it to stdout, so we can debug user problems. // since you have to explicitly call this // it won't flood users with release builds. QTextStream stream(stdout); stream << dumpMemoryInfoToString(); } QString AgentBasePrivate::dumpMemoryInfoToString() const { // man mallinfo for more info QString str; #if defined __GLIBC__ struct mallinfo mi; mi = mallinfo(); QTextStream stream(&str); stream << "Total non-mmapped bytes (arena): " << mi.arena << '\n' << "# of free chunks (ordblks): " << mi.ordblks << '\n' << "# of free fastbin blocks (smblks>: " << mi.smblks << '\n' << "# of mapped regions (hblks): " << mi.hblks << '\n' << "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n' << "Max. total allocated space (usmblks): " << mi.usmblks << '\n' << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n' << "Total allocated space (uordblks): " << mi.uordblks << '\n' << "Total free space (fordblks): " << mi.fordblks << '\n' << "Topmost releasable block (keepcost): " << mi.keepcost << '\n'; #else str = QLatin1String("mallinfo() not supported"); #endif return str; } AgentBase::AgentBase(const QString &id) : d_ptr(new AgentBasePrivate(this)) { sAgentBase = this; d_ptr->mId = id; d_ptr->init(); } AgentBase::AgentBase(AgentBasePrivate *d, const QString &id) : d_ptr(d) { sAgentBase = this; d_ptr->mId = id; d_ptr->init(); } AgentBase::~AgentBase() { delete d_ptr; } QString AgentBase::parseArguments(int argc, char **argv) { Q_UNUSED(argc); QCommandLineOption identifierOption(QStringLiteral("identifier"), i18n("Agent identifier"), QStringLiteral("argument")); QCommandLineParser parser; parser.addOption(identifierOption); parser.addHelpOption(); parser.addVersionOption(); parser.process(*qApp); parser.setApplicationDescription(i18n("Akonadi Agent")); if (!parser.isSet(identifierOption)) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing"; exit(1); } const QString identifier = parser.value(identifierOption); if (identifier.isEmpty()) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument is empty"; exit(1); } QCoreApplication::setApplicationName(ServerManager::addNamespace(identifier)); QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_VERSION_STRING)); const QFileInfo fi(QString::fromLocal8Bit(argv[0])); // strip off full path and possible .exe suffix const QString catalog = fi.baseName(); QTranslator *translator = new QTranslator(); translator->load(catalog); QCoreApplication::installTranslator(translator); return identifier; } // @endcond int AgentBase::init(AgentBase *r) { KLocalizedString::setApplicationDomain("libakonadi5"); int rv = qApp->exec(); delete r; return rv; } int AgentBase::status() const { Q_D(const AgentBase); return d->mStatusCode; } QString AgentBase::statusMessage() const { Q_D(const AgentBase); return d->mStatusMessage; } int AgentBase::progress() const { Q_D(const AgentBase); return d->mProgress; } QString AgentBase::progressMessage() const { Q_D(const AgentBase); return d->mProgressMessage; } bool AgentBase::isOnline() const { Q_D(const AgentBase); return d->mOnline; } void AgentBase::setNeedsNetwork(bool needsNetwork) { Q_D(AgentBase); if (d->mNeedsNetwork == needsNetwork) { return; } d->mNeedsNetwork = needsNetwork; - if (d->mNeedsNetwork) { - d->mNetworkManager = new QNetworkConfigurationManager(this); - connect(d->mNetworkManager, SIGNAL(onlineStateChanged(bool)), - this, SLOT(slotNetworkStatusChange(bool)), - Qt::UniqueConnection); + if (d->mNeedsNetwork) { + if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.NetworkManager"))) { + connect(NetworkManager::notifier(), SIGNAL(connectivityChanged(NetworkManager::Connectivity)), + this, SLOT(slotConnectivityChanged(NetworkManager::Connectivity)), + Qt::UniqueConnection); + if (d->mDesiredOnlineState && NetworkManager::connectivity() == NetworkManager::Full) { + setOnlineInternal(d->mDesiredOnlineState); + } + } else { + d->mNetworkManager = new QNetworkConfigurationManager(this); + connect(d->mNetworkManager, SIGNAL(onlineStateChanged(bool)), + this, SLOT(slotNetworkStatusChange(bool))); + if (d->mDesiredOnlineState && d->mNetworkManager->isOnline()) { + setOnlineInternal(d->mDesiredOnlineState); + } + } } else { - delete d->mNetworkManager; - d->mNetworkManager = nullptr; + if (d->mNetworkManager) { + delete d->mNetworkManager; + d->mNetworkManager = nullptr; + } else { + disconnect(NetworkManager::notifier(), SIGNAL(connectivityChanged(NetworkManager::Connectivity)), + this, SLOT(slotConnectivityChanged(NetworkManager::Connectivity))); + } setOnlineInternal(d->mDesiredOnlineState); } } void AgentBase::setOnline(bool state) { Q_D(AgentBase); d->mDesiredOnlineState = state; d->mSettings->setValue(QStringLiteral("Agent/DesiredOnlineState"), state); setOnlineInternal(state); } void AgentBase::setTemporaryOffline(int makeOnlineInSeconds) { Q_D(AgentBase); // if not currently online, avoid bringing it online after the timeout if (!d->mOnline) { return; } setOnlineInternal(false); if (!d->mTemporaryOfflineTimer) { d->mTemporaryOfflineTimer = new QTimer(d); d->mTemporaryOfflineTimer->setSingleShot(true); connect(d->mTemporaryOfflineTimer, SIGNAL(timeout()), this, SLOT(slotTemporaryOfflineTimeout())); } d->mTemporaryOfflineTimer->setInterval(makeOnlineInSeconds * 1000); d->mTemporaryOfflineTimer->start(); } void AgentBase::setOnlineInternal(bool state) { Q_D(AgentBase); if (state && d->mNeedsNetwork) { - if (!d->mNetworkManager->isOnline()) { + if ((d->mNetworkManager && !d->mNetworkManager->isOnline()) + || (NetworkManager::connectivity() != NetworkManager::Full)) { //Don't go online if the resource needs network but there is none state = false; } } d->mOnline = state; if (d->mTemporaryOfflineTimer) { d->mTemporaryOfflineTimer->stop(); } const QString newMessage = d->defaultReadyMessage(); if (d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken) { emit status(d->mStatusCode, newMessage); } doSetOnline(state); emit onlineChanged(state); } void AgentBase::doSetOnline(bool online) { Q_UNUSED(online); } void AgentBase::configure(WId windowId) { Q_UNUSED(windowId); emit configurationDialogAccepted(); } #ifdef Q_OS_WIN //krazy:exclude=cpp void AgentBase::configure(qlonglong windowId) { configure(static_cast(windowId)); } #endif WId AgentBase::winIdForDialogs() const { const bool registered = KDBusConnectionPool::threadConnection().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.akonaditray")); if (!registered) { return 0; } QDBusInterface dbus(QStringLiteral("org.freedesktop.akonaditray"), QStringLiteral("/Actions"), QStringLiteral("org.freedesktop.Akonadi.Tray")); const QDBusMessage reply = dbus.call(QStringLiteral("getWinId")); if (reply.type() == QDBusMessage::ErrorMessage) { return 0; } const WId winid = (WId)reply.arguments().at(0).toLongLong(); return winid; } void AgentBase::quit() { Q_D(AgentBase); aboutToQuit(); if (d->mSettings) { d->mChangeRecorder->setConfig(nullptr); d->mSettings->sync(); } delete d->mEventLoopLocker; d->mEventLoopLocker = nullptr; } void AgentBase::aboutToQuit() { } void AgentBase::cleanup() { Q_D(AgentBase); // prevent the monitor from picking up deletion signals for our own data if we are a resource // and thus avoid that we kill our own data as last act before our own death d->mChangeRecorder->blockSignals(true); aboutToQuit(); const QString fileName = d->mSettings->fileName(); /* * First destroy the settings object... */ d->mChangeRecorder->setConfig(nullptr); delete d->mSettings; d->mSettings = nullptr; /* * ... then remove the file from hd. */ if (!QFile::remove(fileName)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << fileName; } /* * ... and remove the changes file from hd. */ const QString changeDataFileName = fileName + QStringLiteral("_changes.dat"); if (!QFile::remove(changeDataFileName)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << changeDataFileName; } /* * ... and also remove the agent configuration file if there is one. */ const QString configFile = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + config()->name(); if (!QFile::remove(configFile)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove config file " << configFile; } delete d->mEventLoopLocker; d->mEventLoopLocker = nullptr; } void AgentBase::registerObserver(Observer *observer) { // TODO in theory we should re-connect change recorder signals here that we disconnected previously d_ptr->mObserver = observer; const bool hasObserverV3 = (dynamic_cast(d_ptr->mObserver) != nullptr); const bool hasObserverV4 = (dynamic_cast(d_ptr->mObserver) != nullptr); disconnect(d_ptr->mChangeRecorder, &Monitor::tagAdded, d_ptr, &AgentBasePrivate::tagAdded); disconnect(d_ptr->mChangeRecorder, &Monitor::tagChanged, d_ptr, &AgentBasePrivate::tagChanged); disconnect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, d_ptr, &AgentBasePrivate::tagRemoved); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, d_ptr, &AgentBasePrivate::itemsTagsChanged); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, d_ptr, &AgentBasePrivate::itemsFlagsChanged); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, d_ptr, &AgentBasePrivate::itemsMoved); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, d_ptr, &AgentBasePrivate::itemsRemoved); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, d_ptr, &AgentBasePrivate::itemsLinked); disconnect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, d_ptr, &AgentBasePrivate::itemsUnlinked); disconnect(d_ptr->mChangeRecorder, &Monitor::itemMoved, d_ptr, &AgentBasePrivate::itemMoved); disconnect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, d_ptr, &AgentBasePrivate::itemRemoved); disconnect(d_ptr->mChangeRecorder, &Monitor::itemLinked, d_ptr, &AgentBasePrivate::itemLinked); disconnect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, d_ptr, &AgentBasePrivate::itemUnlinked); if (hasObserverV4) { connect(d_ptr->mChangeRecorder, &Monitor::tagAdded, d_ptr, &AgentBasePrivate::tagAdded); connect(d_ptr->mChangeRecorder, &Monitor::tagChanged, d_ptr, &AgentBasePrivate::tagChanged); connect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, d_ptr, &AgentBasePrivate::tagRemoved); connect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, d_ptr, &AgentBasePrivate::itemsTagsChanged); } if (hasObserverV3) { connect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, d_ptr, &AgentBasePrivate::itemsFlagsChanged); connect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, d_ptr, &AgentBasePrivate::itemsMoved); connect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, d_ptr, &AgentBasePrivate::itemsRemoved); connect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, d_ptr, &AgentBasePrivate::itemsLinked); connect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, d_ptr, &AgentBasePrivate::itemsUnlinked); } else { // V2 - don't connect these if we have V3 connect(d_ptr->mChangeRecorder, &Monitor::itemMoved, d_ptr, &AgentBasePrivate::itemMoved); connect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, d_ptr, &AgentBasePrivate::itemRemoved); connect(d_ptr->mChangeRecorder, &Monitor::itemLinked, d_ptr, &AgentBasePrivate::itemLinked); connect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, d_ptr, &AgentBasePrivate::itemUnlinked); } } QString AgentBase::identifier() const { return d_ptr->mId; } void AgentBase::setAgentName(const QString &name) { Q_D(AgentBase); if (name == d->mName) { return; } // TODO: rename collection d->mName = name; if (d->mName.isEmpty() || d->mName == d->mId) { d->mSettings->remove(QStringLiteral("Resource/Name")); d->mSettings->remove(QStringLiteral("Agent/Name")); } else { d->mSettings->setValue(QStringLiteral("Agent/Name"), d->mName); } d->mSettings->sync(); d->setProgramName(); emit agentNameChanged(d->mName); } QString AgentBase::agentName() const { Q_D(const AgentBase); if (d->mName.isEmpty()) { return d->mId; } else { return d->mName; } } void AgentBase::changeProcessed() { Q_D(AgentBase); d->changeProcessed(); } ChangeRecorder *AgentBase::changeRecorder() const { return d_ptr->mChangeRecorder; } KSharedConfigPtr AgentBase::config() { return KSharedConfig::openConfig(); } void AgentBase::abort() { emit abortRequested(); } void AgentBase::reconfigure() { emit reloadConfiguration(); } #include "moc_agentbase.cpp" #include "moc_agentbase_p.cpp" diff --git a/src/agentbase/agentbase.h b/src/agentbase/agentbase.h index 2212b354a..aa60454a6 100644 --- a/src/agentbase/agentbase.h +++ b/src/agentbase/agentbase.h @@ -1,810 +1,811 @@ /* This file is part of akonadiresources. Copyright (c) 2006 Till Adam Copyright (c) 2007 Volker Krause Copyright (c) 2008 Kevin Krammer 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 AKONADI_AGENTBASE_H #define AKONADI_AGENTBASE_H #include "akonadiagentbase_export.h" #include "item.h" #include #include #include #include class Akonadi__ControlAdaptor; class Akonadi__StatusAdaptor; namespace Akonadi { class AgentBasePrivate; class ChangeRecorder; class Collection; class Item; /** * @short The base class for all Akonadi agents and resources. * * This class is a base class for all Akonadi agents, which covers the real * agent processes and all resources. * * It provides: * - lifetime management * - change monitoring and recording * - configuration interface * - problem reporting * * Akonadi Server supports several ways to launch agents and resources: * - As a separate application (@see AKONADI_AGENT_MAIN) * - As a thread in the AgentServer * - As a separate process, using the akonadi_agent_launcher * * The idea is this, the agent or resource is written as a plugin instead of an * executable (@see AgentFactory). In the AgentServer case, the AgentServer * looks up the plugin and launches the agent in a separate thread. In the * launcher case, a new akonadi_agent_launcher process is started for each * agent or resource instance. * * When making an Agent or Resource suitable for running in the AgentServer some * extra caution is needed. Because multiple instances of several kinds of agents * run in the same process, one cannot blindly use global objects like KGlobal. * For this reasons several methods where added to avoid problems in this context, * most notably AgentBase::config(). Additionally, * one cannot use QDBusConnection::sessionBus() with dbus < 1.4, because of a * multithreading bug in libdbus. Instead one should use * KDBusConnectionPool::threadConnection() which works around this problem. * * @author Till Adam , Volker Krause */ class AKONADIAGENTBASE_EXPORT AgentBase : public QObject, protected QDBusContext { Q_OBJECT public: /** * @short The interface for reacting on monitored or replayed changes. * * The Observer provides an interface to react on monitored or replayed changes. * * Since the this base class does only tell the change recorder that the change * has been processed, an AgentBase subclass which wants to actually process * the change needs to subclass Observer and reimplement the methods it is * interested in. * * Such an agent specific Observer implementation can either be done * stand-alone, i.e. as a separate object, or by inheriting both AgentBase * and AgentBase::Observer. * * The observer implementation then has registered with the agent, so it * can forward the incoming changes to the observer. * * @note In the multiple inheritance approach the init() method automatically * registers itself as the observer. * * @note Do not call the base implementation of reimplemented virtual methods! * The default implementation disconnected themselves from the Akonadi::ChangeRecorder * to enable internal optimizations for unused notifications. * * Example for stand-alone observer: * @code * class ExampleAgent : public AgentBase * { * public: * ExampleAgent( const QString &id ); * * ~ExampleAgent(); * * private: * AgentBase::Observer *mObserver; * }; * * class ExampleObserver : public AgentBase::Observer * { * protected: * void itemChanged( const Item &item ); * }; * * ExampleAgent::ExampleAgent( const QString &id ) : AgentBase( id ) , mObserver( 0 ) * { * mObserver = new ExampleObserver(); * registerObserver( mObserver ); * } * * ExampleAgent::~ExampleAgent() * { * delete mObserver; * } * * void ExampleObserver::itemChanged( const Item &item ) * { * // do something with item * qCDebug(AKONADIAGENTBASE_LOG) << "Item id=" << item.id(); * * // let base implementation tell the change recorder that we * // have processed the change * AgentBase::Observer::itemChanged( item ); * } * @endcode * * Example for observer through multiple inheritance: * @code * class ExampleAgent : public AgentBase, public AgentBase::Observer * { * public: * ExampleAgent( const QString &id ); * * protected: * void itemChanged( const Item &item ); * }; * * ExampleAgent::ExampleAgent( const QString &id ) : AgentBase( id ) * { * // no need to create or register observer since * // we are the observer and registration happens automatically * // in init() * } * * void ExampleAgent::itemChanged( const Item &item ) * { * // do something with item * qCDebug(AKONADIAGENTBASE_LOG) << "Item id=" << item.id(); * * // let base implementation tell the change recorder that we * // have processed the change * AgentBase::Observer::itemChanged( item ); * } * @endcode * * @author Kevin Krammer * * @deprecated Use ObserverV2 instead */ class AKONADIAGENTBASE_DEPRECATED AKONADIAGENTBASE_EXPORT Observer // krazy:exclude=dpointer { public: /** * Creates an observer instance. */ Observer(); /** * Destroys the observer instance. */ virtual ~Observer(); /** * Reimplement to handle adding of new items. * @param item The newly added item. * @param collection The collection @p item got added to. */ virtual void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection); /** * Reimplement to handle changes to existing items. * @param item The changed item. * @param partIdentifiers The identifiers of the item parts that has been changed. */ virtual void itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers); /** * Reimplement to handle deletion of items. * @param item The deleted item. */ virtual void itemRemoved(const Akonadi::Item &item); /** * Reimplement to handle adding of new collections. * @param collection The newly added collection. * @param parent The parent collection. */ virtual void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent); /** * Reimplement to handle changes to existing collections. * @param collection The changed collection. */ virtual void collectionChanged(const Akonadi::Collection &collection); /** * Reimplement to handle deletion of collections. * @param collection The deleted collection. */ virtual void collectionRemoved(const Akonadi::Collection &collection); }; /** * BC extension of Observer with support for monitoring item and collection moves. * Use this one instead of Observer. * * @since 4.4 */ class AKONADIAGENTBASE_EXPORT ObserverV2 : public Observer // krazy:exclude=dpointer { public: using Observer::collectionChanged; /** * Reimplement to handle item moves. * When using this class in combination with Akonadi::ResourceBase, inter-resource * moves are handled internally already and the corresponding add or delete method * is called instead. * * @param item The moved item. * @param collectionSource The collection the item has been moved from. * @param collectionDestination The collection the item has been moved to. */ virtual void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination); /** * Reimplement to handle item linking. * This is only relevant for virtual resources. * @param item The linked item. * @param collection The collection the item is linked to. */ virtual void itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection); /** * Reimplement to handle item unlinking. * This is only relevant for virtual resources. * @param item The unlinked item. * @param collection The collection the item is unlinked from. */ virtual void itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection); /** * Reimplement to handle collection moves. * When using this class in combination with Akonadi::ResourceBase, inter-resource * moves are handled internally already and the corresponding add or delete method * is called instead. * * @param collection The moved collection. * @param collectionSource The previous parent collection. * @param collectionDestination The new parent collection. */ virtual void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination); /** * Reimplement to handle changes to existing collections. * @param collection The changed collection. * @param changedAttributes The identifiers of the collection parts/attributes that has been changed. */ virtual void collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes); }; /** * BC extension of ObserverV2 with support for batch operations * * @warning When using ObserverV3, you will never get single-item notifications * from AgentBase::Observer, even when you don't reimplement corresponding batch * method from ObserverV3. For instance, when you don't reimplement itemsRemoved() * here, you will not get any notifications about item removal whatsoever! * * @since 4.11 */ class AKONADIAGENTBASE_EXPORT ObserverV3 : public ObserverV2 // krazy:exclude=dpointer { public: /** * Reimplement to handle changes in flags of existing items * * @warning When using ObserverV3, you will never get notifications about * flag changes via Observer::itemChanged(), even when you don't reimplement * itemsFlagsChanged()! * * @param items The changed items * @param addedFlags Flags that have been added to the item * @param removedFlags Flags that have been removed from the item */ virtual void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags); /** * Reimplement to handle batch notification about items deletion. * * @param items List of deleted items */ virtual void itemsRemoved(const Akonadi::Item::List &items); /** * Reimplement to handle batch notification about items move * * @param items List of moved items * @param sourceCollection Collection from where the items were moved * @param destinationCollection Collection to which the items were moved */ virtual void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destinationCollection); /** * Reimplement to handle batch notifications about items linking. * * @param items Linked items * @param collection Collection to which the items have been linked */ virtual void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); /** * Reimplement to handle batch notifications about items unlinking. * * @param items Unlinked items * @param collection Collection from which the items have been unlinked */ virtual void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); }; /** * Observer that adds support for item tagging * * @warning ObserverV4 subclasses ObserverV3 which changes behavior of some of the * virtual methods from Observer and ObserverV2. Please make sure you read * documentation of ObserverV3 and adapt your agent accordingly. * * @since 4.13 */ class AKONADIAGENTBASE_EXPORT ObserverV4 : public ObserverV3 // krazy:exclude=dpointer { public: /** * Reimplement to handle tags additions * * @param tag Newly added tag */ virtual void tagAdded(const Akonadi::Tag &tag); /** * Reimplement to handle tags changes * * @param tag Tag that has been changed */ virtual void tagChanged(const Akonadi::Tag &tag); /** * Reimplement to handle tags removal. * * @note All items that were tagged by @p tag will get a separate notification * about untagging via itemsTagsChanged(). It is guaranteed that the itemsTagsChanged() * notification will be delivered before this one. * * @param tag Tag that has been removed. */ virtual void tagRemoved(const Akonadi::Tag &tag); /** * Reimplement to handle items tagging * * @param items Items that were tagged or untagged * @param addedTags Set of tags that were added to all @p items * @param removedTags Set of tags that were removed from all @p items */ virtual void itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags); /** * Reimplement to handle relations being added */ virtual void relationAdded(const Akonadi::Relation &relation); /** * Reimplement to handle relations being removed */ virtual void relationRemoved(const Akonadi::Relation &relation); /** * Reimplement to handled relations changing on items * @param items Items that had relations added/removed from them * @param addedRelations the list of relations that were added to all @p items * @param removedRelations the list of relations that were removed from all @p items */ virtual void itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations); }; /** * This enum describes the different states the * agent can be in. */ enum Status { Idle = 0, ///< The agent does currently nothing. Running, ///< The agent is working on something. Broken, ///< The agent encountered an error state. NotConfigured ///< The agent is lacking required configuration }; /** * Use this method in the main function of your agent * application to initialize your agent subclass. * This method also takes care of creating a KApplication * object and parsing command line arguments. * * @note In case the given class is also derived from AgentBase::Observer * it gets registered as its own observer (see AgentBase::Observer), e.g. * agentInstance->registerObserver( agentInstance ); * * @code * * class MyAgent : public AgentBase * { * ... * }; * * AKONADI_AGENT_MAIN( MyAgent ) * * @endcode * * @param argc number of arguments * @param argv arguments for the function */ template static int init(int argc, char **argv) { // Disable session management qunsetenv("SESSION_MANAGER"); QApplication app(argc, argv); const QString id = parseArguments(argc, argv); T *r = new T(id); // check if T also inherits AgentBase::Observer and // if it does, automatically register it on itself Observer *observer = dynamic_cast(r); if (observer != 0) { r->registerObserver(observer); } return init(r); } /** * This method returns the current status code of the agent. * * The following return values are possible: * * - 0 - Idle * - 1 - Running * - 2 - Broken * - 3 - NotConfigured */ virtual int status() const; /** * This method returns an i18n'ed description of the current status code. */ virtual QString statusMessage() const; /** * This method returns the current progress of the agent in percentage. */ virtual int progress() const; /** * This method returns an i18n'ed description of the current progress. */ virtual QString progressMessage() const; public Q_SLOTS: /** * This method is called whenever the agent shall show its configuration dialog * to the user. It will be automatically called when the agent is started for * the first time. * * @param windowId The parent window id. * * @note If the method is reimplemented it has to emit the configurationDialogAccepted() * or configurationDialogRejected() signals depending on the users choice. */ virtual void configure(WId windowId); public: /** * This method returns the windows id, which should be used for dialogs. */ WId winIdForDialogs() const; #ifdef Q_OS_WIN /** * Overload of @ref configure needed because WId cannot be automatically casted * to qlonglong on Windows. */ void configure(qlonglong windowId); #endif /** * Returns the instance identifier of this agent. */ QString identifier() const; /** * This method is called when the agent is removed from * the system, so it can do some cleanup stuff. * * @note If you reimplement this in a subclass make sure * to call this base implementation at the end. */ virtual void cleanup(); /** * Registers the given observer for reacting on monitored or recorded changes. * * @param observer The change handler to register. No ownership transfer, i.e. * the caller stays owner of the pointer and can reset * the registration by calling this method with @c 0 */ void registerObserver(Observer *observer); /** * This method is used to set the name of the agent. * * @since 4.3 * @param name name of the agent */ //FIXME_API: make sure location is renamed to this by agentbase void setAgentName(const QString &name); /** * Returns the name of the agent. * * @since 4.3 */ QString agentName() const; Q_SIGNALS: /** * This signal is emitted whenever the name of the agent has changed. * * @param name The new name of the agent. * * @since 4.3 */ void agentNameChanged(const QString &name); /** * This signal should be emitted whenever the status of the agent has been changed. * @param status The new Status code. * @param message A i18n'ed description of the new status. */ void status(int status, const QString &message = QString()); /** * This signal should be emitted whenever the progress of an action in the agent * (e.g. data transfer, connection establishment to remote server etc.) has changed. * * @param progress The progress of the action in percent. */ void percent(int progress); /** * This signal shall be used to report warnings. * * @param message The i18n'ed warning message. */ void warning(const QString &message); /** * This signal shall be used to report errors. * * @param message The i18n'ed error message. */ void error(const QString &message); /** * This signal should be emitted whenever the status of the agent has been changed. * @param status The object that describes the status change. * * @since 4.6 */ void advancedStatus(const QVariantMap &status); /** * Emitted when another application has remotely asked the agent to abort * its current operation. * Connect to this signal if your agent supports abortion. After aborting * and cleaning up, agents should return to Idle status. * * @since 4.4 */ void abortRequested(); /** * Emitted if another application has changed the agent's configuration remotely * and called AgentInstance::reconfigure(). * * @since 4.2 */ void reloadConfiguration(); /** * Emitted when the online state changed. * @param online The online state. * @since 4.2 */ void onlineChanged(bool online); /** * This signal is emitted whenever the user has accepted the configuration dialog. * * @note Implementors of agents/resources are responsible to emit this signal if * the agent/resource reimplements configure(). * * @since 4.4 */ void configurationDialogAccepted(); /** * This signal is emitted whenever the user has rejected the configuration dialog. * * @note Implementors of agents/resources are responsible to emit this signal if * the agent/resource reimplements configure(). * * @since 4.4 */ void configurationDialogRejected(); protected: /** * Creates an agent base. * * @param id The instance id of the agent. */ AgentBase(const QString &id); /** * Destroys the agent base. */ ~AgentBase(); /** * This method is called whenever the agent application is about to * quit. * * Reimplement this method to do session cleanup (e.g. disconnecting * from groupware server). */ virtual void aboutToQuit(); /** * Returns the Akonadi::ChangeRecorder object used for monitoring. * Use this to configure which parts you want to monitor. */ ChangeRecorder *changeRecorder() const; /** * Returns the config object for this Agent. */ KSharedConfigPtr config(); /** * Marks the current change as processes and replays the next change if change * recording is enabled (noop otherwise). This method is called * from the default implementation of the change notification slots. While not * required when not using change recording, it is nevertheless recommended * to call this method when done with processing a change notification. */ void changeProcessed(); /** * Returns whether the agent is currently online. */ bool isOnline() const; /** * Sets whether the agent needs network or not. * * @since 4.2 * @todo use this in combination with QNetworkConfiguration to change * the onLine status of the agent. * @param needsNetwork @c true if the agents needs network. Defaults to @c false */ void setNeedsNetwork(bool needsNetwork); /** * Sets whether the agent shall be online or not. */ void setOnline(bool state); protected: /** * Sets the agent offline but will make it online again after a given time * * Use this method when the agent detects some problem with its backend but it wants * to retry all pending operations after some time - e.g. a server can not be reached currently * * Example usage: * @code * void ExampleResource::onItemRemovedFinished(KJob *job) * { * if (job->error()) { * emit status(Broken, job->errorString()); * deferTask(); * setTemporaryOffline(300); * return; * } * ... * } * @endcode * * @since 4.13 * @param makeOnlineInSeconds timeout in seconds after which the agent changes to online */ void setTemporaryOffline(int makeOnlineInSeconds = 300); //@cond PRIVATE AgentBasePrivate *d_ptr; explicit AgentBase(AgentBasePrivate *d, const QString &id); friend class ObserverV2; //@endcond /** * This method is called whenever the @p online status has changed. * Reimplement this method to react on online status changes. * @param online online status */ virtual void doSetOnline(bool online); private: //@cond PRIVATE static QString parseArguments(int argc, char **argv); static int init(AgentBase *r); void setOnlineInternal(bool state); // D-Bus interface stuff void abort(); void reconfigure(); void quit(); // dbus agent interface friend class ::Akonadi__StatusAdaptor; friend class ::Akonadi__ControlAdaptor; Q_DECLARE_PRIVATE(AgentBase) Q_PRIVATE_SLOT(d_func(), void delayedInit()) Q_PRIVATE_SLOT(d_func(), void slotStatus(int, const QString &)) Q_PRIVATE_SLOT(d_func(), void slotPercent(int)) Q_PRIVATE_SLOT(d_func(), void slotWarning(const QString &)) Q_PRIVATE_SLOT(d_func(), void slotError(const QString &)) Q_PRIVATE_SLOT(d_func(), void slotNetworkStatusChange(bool)) Q_PRIVATE_SLOT(d_func(), void slotResumedFromSuspend()) Q_PRIVATE_SLOT(d_func(), void slotTemporaryOfflineTimeout()) + Q_PRIVATE_SLOT(d_func(), void slotConnectivityChanged(NetworkManager::Connectivity)) //@endcond }; } #ifndef AKONADI_AGENT_MAIN /** * Convenience Macro for the most common main() function for Akonadi agents. */ #define AKONADI_AGENT_MAIN( agentClass ) \ int main( int argc, char **argv ) \ { \ return Akonadi::AgentBase::init( argc, argv ); \ } #endif #endif diff --git a/src/agentbase/agentbase_p.h b/src/agentbase/agentbase_p.h index 45d774fcf..4d08d0135 100644 --- a/src/agentbase/agentbase_p.h +++ b/src/agentbase/agentbase_p.h @@ -1,157 +1,160 @@ /* Copyright (c) 2007 Volker Krause Copyright (c) 2008 Kevin Krammer 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 AKONADI_AGENTBASE_P_H #define AKONADI_AGENTBASE_P_H #include "agentbase.h" #include "tracerinterface.h" +#include + #include class QSettings; class QTimer; class QNetworkConfigurationManager; namespace Akonadi { /** * @internal */ class AgentBasePrivate : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.dfaure") public: explicit AgentBasePrivate(AgentBase *parent); virtual ~AgentBasePrivate(); void init(); virtual void delayedInit(); void slotStatus(int status, const QString &message); void slotPercent(int progress); void slotWarning(const QString &message); void slotError(const QString &message); void slotNetworkStatusChange(bool isOnline); void slotResumedFromSuspend(); void slotTemporaryOfflineTimeout(); + void slotConnectivityChanged(NetworkManager::Connectivity connectivity); virtual void changeProcessed(); QString defaultReadyMessage() const { if (mOnline) { return i18nc("@info:status Application ready for work", "Ready"); } return i18nc("@info:status", "Offline"); } QString defaultSyncingMessage() const { return i18nc("@info:status", "Syncing..."); } QString defaultErrorMessage() const { return i18nc("@info:status", "Error."); } QString defaultUnconfiguredMessage() const { return i18nc("@info:status", "Not configured"); } void setProgramName(); AgentBase *q_ptr; Q_DECLARE_PUBLIC(AgentBase) QString mId; QString mName; QString mResourceTypeName; int mStatusCode; QString mStatusMessage; uint mProgress; QString mProgressMessage; bool mNeedsNetwork; bool mOnline; bool mDesiredOnlineState; QSettings *mSettings; ChangeRecorder *mChangeRecorder; org::freedesktop::Akonadi::Tracer *mTracer; AgentBase::Observer *mObserver; QDBusInterface *mPowerInterface; QTimer *mTemporaryOfflineTimer; QEventLoopLocker *mEventLoopLocker; QNetworkConfigurationManager *mNetworkManager; public Q_SLOTS: // Dump the contents of the current ChangeReplay Q_SCRIPTABLE QString dumpNotificationListToString() const; Q_SCRIPTABLE void dumpMemoryInfo() const; Q_SCRIPTABLE QString dumpMemoryInfoToString() const; virtual void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection); virtual void itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers); virtual void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &destination); virtual void itemRemoved(const Akonadi::Item &item); void itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection); void itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection); virtual void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags); virtual void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination); virtual void itemsRemoved(const Akonadi::Item::List &items); virtual void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); virtual void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); virtual void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent); virtual void collectionChanged(const Akonadi::Collection &collection); virtual void collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes); virtual void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &destination); virtual void collectionRemoved(const Akonadi::Collection &collection); void collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent); void collectionUnsubscribed(const Akonadi::Collection &collection); virtual void tagAdded(const Akonadi::Tag &tag); virtual void tagChanged(const Akonadi::Tag &tag); virtual void tagRemoved(const Akonadi::Tag &tag); virtual void itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags); virtual void relationAdded(const Akonadi::Relation &relation); virtual void relationRemoved(const Akonadi::Relation &relation); virtual void itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations); }; } #endif