diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ae6a5a..dc5b27f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,124 +1,129 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.54.0") # handled by release scripts set(KF5_DEP_VERSION "5.54.0") # handled by release scripts project(KService VERSION ${KF5_VERSION}) # Disallow in-source build if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message(FATAL_ERROR "KService requires an out of source build. Please create a separate build directory and run 'cmake path_to_kservice [options]' there.") endif() # ECM setup include(FeatureSummary) find_package(ECM 5.54.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMAddQch) include(ECMQtDeclareLoggingCategory) include(GenerateExportHeader) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX KSERVICE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kservice_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ServiceConfigVersion.cmake" SOVERSION 5) set(APPLICATIONS_MENU_NAME applications.menu CACHE STRING "Name to install the applications.menu file as.") # Dependencies set(REQUIRED_QT_VERSION 5.10.0) -find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED DBus Xml) +find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Xml) +if (NOT ANDROID) + find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED DBus) +endif() find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Crash ${KF5_DEP_VERSION} REQUIRED) -find_package(KF5DBusAddons ${KF5_DEP_VERSION} REQUIRED) +if (NOT ANDROID) + find_package(KF5DBusAddons ${KF5_DEP_VERSION} REQUIRED) +endif() find_package(KF5I18n ${KF5_DEP_VERSION} REQUIRED) find_package(KF5DocTools ${KF5_DEP_VERSION}) find_package(FLEX REQUIRED) set_package_properties(FLEX PROPERTIES URL "http://flex.sourceforge.net" DESCRIPTION "Fast Lexical Analyzer" TYPE REQUIRED PURPOSE "Required for the Trader parser" ) find_package(BISON REQUIRED) set_package_properties(BISON PROPERTIES URL "http://www.gnu.org/software/bison" DESCRIPTION "general-purpose parser generator" TYPE REQUIRED PURPOSE "Required for the Trader parser" ) add_definitions(-DTRANSLATION_DOMAIN=\"kservice5\") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) if (KF5DocTools_FOUND) kdoctools_install(po) endif() endif() if (KF5DocTools_FOUND) add_subdirectory(docs) endif() add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Service") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Service_QCH FILE KF5ServiceQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5ServiceQchTargets.cmake\")") endif() include(CMakePackageConfigHelpers) configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5ServiceConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5ServiceConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(EXPORT KF5ServiceTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5ServiceTargets.cmake NAMESPACE KF5:: ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5ServiceConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5ServiceConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/KF5ServiceMacros.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kservice_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) # contains list of debug categories, for kdebugsettings install(FILES kservice.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18f02d2..ec753d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,209 +1,215 @@ include(CheckSymbolExists) include(CheckFunctionExists) check_function_exists(mmap HAVE_MMAP) check_symbol_exists(posix_madvise "sys/mman.h" HAVE_MADVISE) configure_file(config-ksycoca.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ksycoca.h ) set(kservice_SRCS kdeinit/ktoolinvocation.cpp services/kautostart.cpp services/kmimetypefactory.cpp services/kmimetypetrader.cpp services/kservice.cpp services/kserviceaction.cpp services/kservicefactory.cpp services/kservicegroup.cpp services/kservicegroupfactory.cpp services/kserviceoffer.cpp services/kservicetype.cpp services/kservicetypefactory.cpp services/kservicetypeprofile.cpp services/kservicetypetrader.cpp services/ktraderparse.cpp services/ktraderparsetree.cpp services/kplugininfo.cpp sycoca/ksycoca.cpp sycoca/ksycocadevices.cpp sycoca/ksycocadict.cpp sycoca/ksycocaentry.cpp sycoca/ksycocafactory.cpp sycoca/kmemfile.cpp sycoca/kbuildmimetypefactory.cpp sycoca/kbuildservicetypefactory.cpp sycoca/kbuildservicefactory.cpp sycoca/kbuildservicegroupfactory.cpp sycoca/kbuildsycoca.cpp sycoca/kctimefactory.cpp sycoca/kmimeassociations.cpp sycoca/vfolder_menu.cpp plugin/kplugintrader.cpp - plugin/kdbusservicestarter.cpp ) +if (TARGET Qt5::DBus) + list(APPEND kservice_SRCS plugin/kdbusservicestarter.cpp) +endif() ecm_qt_declare_logging_category(kservice_SRCS HEADER servicesdebug.h IDENTIFIER SERVICES CATEGORY_NAME kf5.kservice.services) ecm_qt_declare_logging_category(kservice_SRCS HEADER sycocadebug.h IDENTIFIER SYCOCA CATEGORY_NAME kf5.kservice.sycoca) if (WIN32) LIST(APPEND kservice_SRCS kdeinit/ktoolinvocation_win.cpp ) endif() if (UNIX) LIST(APPEND kservice_SRCS kdeinit/ktoolinvocation_x11.cpp ) endif() bison_target(TraderParser services/yacc.y ${CMAKE_CURRENT_BINARY_DIR}/yacc.c COMPILE_FLAGS "-p kiotrader -d" ) flex_target(TraderLexer services/lex.l ${CMAKE_CURRENT_BINARY_DIR}/lex.c COMPILE_FLAGS "-Pkiotrader -B -i" ) add_flex_bison_dependency(TraderLexer TraderParser) list(APPEND kservice_SRCS ${BISON_TraderParser_OUTPUTS} ${FLEX_TraderLexer_OUTPUTS}) set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/yacc.h PROPERTY SKIP_AUTOMOC TRUE) # don't run automoc on this file # kservice cannot depend on kinit (because kinit->kio->kservice), so we need a copy of org.kde.KLauncher.xml here. # And I don't want to have it here as a source file (who wants to edit dbus xml by hand), so it can be # generated from klauncher's implementation header. -qt5_add_dbus_interface(kservice_SRCS kdeinit/org.kde.KLauncher.xml klauncher_iface) +if (TARGET Qt5::DBus) + qt5_add_dbus_interface(kservice_SRCS kdeinit/org.kde.KLauncher.xml klauncher_iface) +endif() add_library(KF5Service ${kservice_SRCS}) if(WIN32) #unistd.h does not exist on windows target_compile_definitions(KF5Service PRIVATE YY_NO_UNISTD_H=1) endif() generate_export_header(KF5Service BASE_NAME KService) add_library(KF5::Service ALIAS KF5Service) set(kservice_includes ${CMAKE_CURRENT_BINARY_DIR}/.. # Since we publicly include kservice_version.h ${CMAKE_CURRENT_SOURCE_DIR}/services ${CMAKE_CURRENT_SOURCE_DIR}/sycoca ${CMAKE_CURRENT_SOURCE_DIR}/plugin ${CMAKE_CURRENT_SOURCE_DIR}/kdeinit ) target_include_directories(KF5Service PUBLIC "$") target_include_directories(KF5Service INTERFACE "$") target_link_libraries(KF5Service PUBLIC KF5::ConfigCore # KConfig and friends KF5::CoreAddons # KShell KPluginLoader PRIVATE KF5::I18n - KF5::DBusAddons # KDEInitInterface Qt5::Xml # (for vfolder menu) QDomDocument ) +if (TARGET KF5::DBusAddons) + target_link_libraries(KF5Service PRIVATE KF5::DBusAddons) # KDEInitInterface +endif() set_target_properties(KF5Service PROPERTIES VERSION ${KSERVICE_VERSION_STRING} SOVERSION ${KSERVICE_SOVERSION} EXPORT_NAME Service ) ecm_generate_headers(KService_HEADERS HEADER_NAMES KPluginTrader KDBusServiceStarter RELATIVE plugin REQUIRED_HEADERS KService_HEADERS ) ecm_generate_headers(KService_HEADERS HEADER_NAMES KSycoca KSycocaEntry KSycocaType RELATIVE sycoca REQUIRED_HEADERS KService_HEADERS ) ecm_generate_headers(KService_HEADERS HEADER_NAMES KToolInvocation RELATIVE kdeinit REQUIRED_HEADERS KService_HEADERS ) ecm_generate_headers(KService_HEADERS HEADER_NAMES KAutostart KMimeTypeTrader KService KServiceAction KServiceGroup KServiceType KServiceTypeProfile KServiceTypeTrader KPluginInfo RELATIVE services REQUIRED_HEADERS KService_HEADERS ) install(FILES services/kplugininfo.desktop services/application.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} ) # Local copy for the unittests add_custom_target(copy_servicetypes) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/data/kservicetypes5) add_custom_command(TARGET copy_servicetypes PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/services/kplugininfo.desktop ${CMAKE_CURRENT_SOURCE_DIR}/services/application.desktop ${CMAKE_BINARY_DIR}/bin/data/kservicetypes5) # not using KDE_INSTALL_KSERVICETYPES5DIR because QStandardPaths wants "data" on Windows add_dependencies(KF5Service copy_servicetypes) if (WIN32) install( FILES applications.menu DESTINATION ${KDE_INSTALL_DATAROOTDIR}/xdg/menus RENAME ${APPLICATIONS_MENU_NAME} ) else () install( FILES applications.menu DESTINATION ${KDE_INSTALL_SYSCONFDIR}/xdg/menus RENAME ${APPLICATIONS_MENU_NAME} ) endif () # Local copy for the unittests add_custom_target(copy_menu) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/data/menus) add_custom_command(TARGET copy_menu PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/applications.menu ${CMAKE_BINARY_DIR}/bin/data/menus/${APPLICATIONS_MENU_NAME}) add_dependencies(KF5Service copy_menu) install(TARGETS KF5Service EXPORT KF5ServiceTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kservice_export.h" ${KService_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KService COMPONENT Devel ) if(BUILD_QCH) ecm_add_qch( KF5Service_QCH NAME KService BASE_NAME KF5Service VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KService_HEADERS} MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS KF5Config_QCH KF5CoreAddons_QCH BLANK_MACROS KSERVICE_EXPORT KSERVICE_DEPRECATED KSERVICE_DEPRECATED_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() add_subdirectory(kbuildsycoca) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KService LIB_NAME KF5Service DEPS "KConfigCore" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KService) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/kdeinit/ktoolinvocation.cpp b/src/kdeinit/ktoolinvocation.cpp index 7e7f263..1735916 100644 --- a/src/kdeinit/ktoolinvocation.cpp +++ b/src/kdeinit/ktoolinvocation.cpp @@ -1,299 +1,309 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Brad Hards Copyright (C) 2006 Thiago Macieira 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 "ktoolinvocation.h" +#ifdef QT_DBUS_LIB #include "klauncher_iface.h" -#include #include +#endif +#include +#include #include +#include #include #include #include #include // for EINVAL class KToolInvocationSingleton { public: KToolInvocation instance; }; Q_GLOBAL_STATIC(KToolInvocationSingleton, s_self) KToolInvocation *KToolInvocation::self() { return &s_self()->instance; } KToolInvocation::KToolInvocation() : QObject(nullptr), d(nullptr) { } KToolInvocation::~KToolInvocation() { } static void printError(const QString &text, QString *error) { if (error) { *error = text; } else { qWarning() << text; } } bool KToolInvocation::isMainThreadActive(QString *error) { if (QCoreApplication::instance() && QCoreApplication::instance()->thread() != QThread::currentThread()) { printError(i18n("Function must be called from the main thread."), error); return false; } return true; } int KToolInvocation::startServiceInternal(const char *_function, const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait, const QString &workdir) { +#ifdef QT_DBUS_LIB QString function = QLatin1String(_function); KToolInvocation::ensureKdeinitRunning(); QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QStringLiteral("org.kde.KLauncher"), function); msg << _name << URLs; if (function == QLatin1String("kdeinit_exec_with_workdir")) { msg << workdir; } // make sure there is id, so that user timestamp exists QStringList envs; QByteArray s = startup_id; emit kapplication_hook(envs, s); msg << envs; msg << QString::fromLatin1(s); if (!function.startsWith(QLatin1String("kdeinit_exec"))) { msg << noWait; } QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX); if (reply.type() != QDBusMessage::ReplyMessage) { QDBusReply replyObj(reply); if (replyObj.error().type() == QDBusError::NoReply) { printError(i18n("Error launching %1. Either KLauncher is not running anymore, or it failed to start the application.", _name), error); } else { const QString rpl = reply.arguments().count() > 0 ? reply.arguments().at(0).toString() : reply.errorMessage(); printError(i18n("KLauncher could not be reached via D-Bus. Error when calling %1:\n%2\n", function, rpl), error); } //qDebug() << reply; return EINVAL; } if (noWait) { return 0; } Q_ASSERT(reply.arguments().count() == 4); if (serviceName) { *serviceName = reply.arguments().at(1).toString(); } if (error) { *error = reply.arguments().at(2).toString(); } if (pid) { *pid = reply.arguments().at(3).toInt(); } return reply.arguments().at(0).toInt(); +#else + return ENOTSUP; +#endif } #ifndef KSERVICE_NO_DEPRECATED int KToolInvocation::startServiceByName(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } #endif #ifndef KSERVICE_NO_DEPRECATED int KToolInvocation::startServiceByName(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } #endif int KToolInvocation::startServiceByDesktopPath(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopPath(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopName(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopName(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::kdeinitExec(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("kdeinit_exec", name, args, error, nullptr, pid, startup_id, false); } int KToolInvocation::kdeinitExecWait(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("kdeinit_exec_wait", name, args, error, nullptr, pid, startup_id, false); } void KToolInvocation::invokeMailer(const QString &address, const QString &subject, const QByteArray &startup_id) { if (!isMainThreadActive()) { return; } invokeMailer(address, QString(), QString(), subject, QString(), QString(), QStringList(), startup_id); } void KToolInvocation::invokeMailer(const QUrl &mailtoURL, const QByteArray &startup_id, bool allowAttachments) { if (!isMainThreadActive()) { return; } QString address = mailtoURL.path(); QString subject; QString cc; QString bcc; QString body; QList > queryItems = QUrlQuery(mailtoURL).queryItems(); const QChar comma = QChar::fromLatin1(','); QStringList attachURLs; for (int i = 0; i < queryItems.count(); ++i) { const QString q = queryItems.at(i).first.toLower(); const QString value = queryItems.at(i).second; if (q == QLatin1String("subject")) { subject = value; } else if (q == QLatin1String("cc")) { cc = cc.isEmpty() ? value : cc + comma + value; } else if (q == QLatin1String("bcc")) { bcc = bcc.isEmpty() ? value : bcc + comma + value; } else if (q == QLatin1String("body")) { body = value; } else if (allowAttachments && q == QLatin1String("attach")) { attachURLs.push_back(value); } else if (allowAttachments && q == QLatin1String("attachment")) { attachURLs.push_back(value); } else if (q == QLatin1String("to")) { address = address.isEmpty() ? value : address + comma + value; } } invokeMailer(address, cc, bcc, subject, body, QString(), attachURLs, startup_id); } void KToolInvocation::ensureKdeinitRunning() { +#ifdef QT_DBUS_LIB KDEInitInterface::ensureKdeinitRunning(); +#endif }