diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f3cde6..97a2c61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,111 +1,111 @@ # SPDX-FileCopyrightText: 2023 Carl Schwan # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) set(PIM_VERSION "5.23.42") project(MimeTreeParserNG VERSION ${PIM_VERSION}) # ECM setup set(KF_MIN_VERSION "5.105.0") find_package(ECM ${KF_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ) set(QT_REQUIRED_VERSION "5.15.2") if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") endif() include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(ECMQmlModule) include(ECMSetupVersion) include(FeatureSummary) include(KDEGitCommitHooks) include(KDEClangFormat) file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) include(ECMQtDeclareLoggingCategory) include(ECMDeprecationSettings) include(ECMAddQch) if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") set(KF_MIN_VERSION "5.240.0") set(KF_MAJOR_VERSION "6") else() set(KF_MAJOR_VERSION "5") endif() set(KPIM_MIME_VERSION "5.23.40") ecm_setup_version(PROJECT VARIABLE_PREFIX MIMETREEPARSERNG VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparserng_version.h" - PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserNgConfigVersion.cmake" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreConfigVersion.cmake" SOVERSION 5 ) configure_file(mimetreeparserng-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/src/mimetreeparserng-version.h @ONLY) 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)") if(BUILD_TESTING) add_definitions(-DBUILD_TESTING) endif() ########### Find packages ########### find_package(Qt${KF_MAJOR_VERSION}Gui ${QT_MIN_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}I18n ${KF_MIN_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}Codecs ${KF_MIN_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}CalendarCore ${KF_MIN_VERSION} CONFIG REQUIRED) find_package(KPim${KF_MAJOR_VERSION}Mime ${KPIM_MIME_VERSION} CONFIG REQUIRED) find_package(Gpgme REQUIRED) find_package(Qt${KF_MAJOR_VERSION}Quick ${QT_MIN_VERSION} CONFIG) find_package(Qt${KF_MAJOR_VERSION}Widgets ${QT_MIN_VERSION} CONFIG) +if (BUILD_TESTING) + find_package(Qt${KF_MAJOR_VERSION}Test ${QT_MIN_VERSION} CONFIG REQUIRED) +endif() + ########### Targets ########### ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.105.0) option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF) if (USE_UNITY_CMAKE_SUPPORT) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON) add_definitions(-DCOMPILE_WITH_UNITY_CMAKE_SUPPORT) endif() add_subdirectory(src) add_subdirectory(examples) -if (BUILD_TESTING) - #add_subdirectory(autotests) -endif () - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparserng_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserCore COMPONENT Devel ) ecm_qt_install_logging_categories( EXPORT MIMETREEPARSER2 FILE mimetreeparser2.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT) ki18n_install(po) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cmake/modules/add_gpg_crypto_test.cmake b/cmake/modules/add_gpg_crypto_test.cmake new file mode 100644 index 0000000..3a29280 --- /dev/null +++ b/cmake/modules/add_gpg_crypto_test.cmake @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2013 Sandro Knauß +# SPDX-License-Identifier: BSD-3-Clause + +set(GNUPGHOME ${CMAKE_BINARY_DIR}/src/core/autotests/gnupg_home) +add_definitions( -DGNUPGHOME="${GNUPGHOME}" ) + +macro (ADD_GPG_CRYPTO_TEST _target _testname) + if (UNIX) + if (APPLE) + set(_library_path_variable "DYLD_LIBRARY_PATH") + elseif (CYGWIN) + set(_library_path_variable "PATH") + else (APPLE) + set(_library_path_variable "LD_LIBRARY_PATH") + endif (APPLE) + + if (APPLE) + # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH + # OSX already has the RPATH in libraries and executables, putting runtime directories in + # DYLD_LIBRARY_PATH actually breaks things + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/") + else (APPLE) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}") + endif (APPLE) + set(_executable "$") + + # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename=${_executable}.shell -D_library_path_variable=${_library_path_variable} + -D_ld_library_path="${_ld_library_path}" -D_executable=${_executable} + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/generate_crypto_test_wrapper.cmake + ) + + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.shell" ) + add_test(NAME ${_testname} COMMAND ${_executable}.shell) + + else (UNIX) + # under windows, set the property WRAPPER_SCRIPT just to the name of the executable + # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}") + set(_executable "$") + + # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename="${_executable}.bat" + -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}" + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/generate_crypto_test_wrapper.cmake + ) + + add_test(NAME ${_testname} COMMAND ${_executable}.bat) + + endif (UNIX) +endmacro (ADD_GPG_CRYPTO_TEST) + diff --git a/cmake/modules/generate_crypto_test_wrapper.cmake b/cmake/modules/generate_crypto_test_wrapper.cmake new file mode 100644 index 0000000..86e25f0 --- /dev/null +++ b/cmake/modules/generate_crypto_test_wrapper.cmake @@ -0,0 +1,39 @@ +# Copyright (c) 2006, Alexander Neundorf, +# Copyright (c) 2013, Sandro Knauß +# Copyright (c) 2018, Christian Mollekopf +# +# SPDX-License-Identifier: BSD-3-Clause + + +if (UNIX) + +file(WRITE "${_filename}" +"#!/usr/bin/env sh +# created by cmake, don't edit, changes will be lost + +# don't mess with a gpg-agent already running on the system +unset GPG_AGENT_INFO + +${_library_path_variable}=${_ld_library_path}\${${_library_path_variable}:+:\$${_library_path_variable}} GNUPGHOME=${_gnupghome} gpg-agent --daemon \"${_executable}\" \"$@\" +_result=$? +GNUPGHOME=${_gnupghome} gpg-connect-agent killagent /bye +exit \$_result +") + +# make it executable +# since this is only executed on UNIX, it is safe to call chmod +exec_program(chmod ARGS ug+x \"${_filename}\" OUTPUT_VARIABLE _dummy ) + +else (UNIX) + +file(TO_NATIVE_PATH "${_ld_library_path}" win_path) +file(TO_NATIVE_PATH "${_gnupghome}" win_gnupghome) + +file(WRITE "${_filename}" +" +set PATH=${win_path};$ENV{PATH} +set GNUPGHOME=${win_gnupghome};$ENV{GNUPGHOME} +gpg-agent --daemon \"${_executable}\" %* +") + +endif (UNIX) diff --git a/cmake/modules/kdepim_add_gpg_crypto_test.cmake b/cmake/modules/kdepim_add_gpg_crypto_test.cmake new file mode 100644 index 0000000..49c2957 --- /dev/null +++ b/cmake/modules/kdepim_add_gpg_crypto_test.cmake @@ -0,0 +1,167 @@ +# SPDX-FileCopyrightText: 2013 Sandro Knauß +# +# SPDX-License-Identifier: BSD-3-Clause + +set(GNUPGHOME ${CMAKE_BINARY_DIR}/src/core/autotests/gnupg_home) +add_definitions(-DGNUPGHOME="${GNUPGHOME}") + +macro (ADD_GPG_CRYPTO_TEST _target _testname) + if (UNIX) + if (APPLE) + set(_library_path_variable "DYLD_LIBRARY_PATH") + elseif (CYGWIN) + set(_library_path_variable "PATH") + else (APPLE) + set(_library_path_variable "LD_LIBRARY_PATH") + endif (APPLE) + + if (APPLE) + # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH + # OSX already has the RPATH in libraries and executables, putting runtime directories in + # DYLD_LIBRARY_PATH actually breaks things + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/") + else (APPLE) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}") + endif (APPLE) + set(_executable "$") + + # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename=${_executable}.shell -D_library_path_variable=${_library_path_variable} + -D_ld_library_path="${_ld_library_path}" -D_executable=$ + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake + ) + + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.shell" ) + add_test(NAME ${_testname} COMMAND ${_executable}.shell) + + else (UNIX) + # under windows, set the property WRAPPER_SCRIPT just to the name of the executable + # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}") + set(_executable "$") + + # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename="${_executable}.bat" + -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}" + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake + ) + + add_test(NAME ${_testname} COMMAND ${_executable}.bat) + + endif (UNIX) + + # can't be parallelized due to gpg-agent + set_tests_properties(${_testname} PROPERTIES RUN_SERIAL TRUE) +endmacro (ADD_GPG_CRYPTO_TEST) + +macro (ADD_GPG_CRYPTO_AKONADI_TEST _target _testname) + set(_executable "$") + + if (UNIX) + if (APPLE) + set(_library_path_variable "DYLD_LIBRARY_PATH") + elseif (CYGWIN) + set(_library_path_variable "PATH") + else (APPLE) + set(_library_path_variable "LD_LIBRARY_PATH") + endif (APPLE) + + if (APPLE) + # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH + # OSX already has the RPATH in libraries and executables, putting runtime directories in + # DYLD_LIBRARY_PATH actually breaks things + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/") + else (APPLE) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}") + endif (APPLE) + + set(_posix "shell") + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.${_posix}" ) + + # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename=${_executable}.${_posix} -D_library_path_variable=${_library_path_variable} + -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}" + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake + ) + + + else (UNIX) + # under windows, set the property WRAPPER_SCRIPT just to the name of the executable + # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found) + set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}") + set(_posix "bat") + + # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time + add_custom_command(TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D_filename="${_executable}.${_posix}" + -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}" + -D_gnupghome="${GNUPGHOME}" + -P ${CMAKE_SOURCE_DIR}/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake + ) + + endif () + + if (NOT DEFINED _testrunner) + find_program(_testrunner NAMES akonaditest akonaditest.exe) + if (NOT _testrunner) + message(WARNING "Could not locate akonaditest executable, isolated Akonadi tests will fail!") + endif() + endif() + + function(_defineTest name backend) + set(backends ${ARGN}) + if (NOT DEFINED AKONADI_RUN_${backend}_ISOLATED_TESTS OR AKONADI_RUN_${backend}_ISOLATED_TESTS) + LIST(LENGTH "${backends}" backendsLen) + string(TOLOWER ${backend} lcbackend) + LIST(FIND "${backends}" ${lcbackend} enableBackend) + if (${backendsLen} EQUAL 0 OR ${enableBackend} GREATER -1) + set(configFile ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml) + if (AKONADI_TESTS_XML) + set(extraOptions -xml -o "${TEST_RESULT_OUTPUT_PATH}/${lcbackend}-${name}.xml") + endif() + set(_test_name akonadi-${lcbackend}-${name}) + add_test(NAME ${_test_name} + COMMAND ${_testrunner} -c "${configFile}" -b ${lcbackend} + "${_executable}.${_posix}" ${extraOptions} + ) + # Taken from ECMAddTests.cmake + if (CMAKE_LIBRARY_OUTPUT_DIRECTORY) + if(CMAKE_HOST_SYSTEM MATCHES "Windows") + set(PATHSEP ";") + else() # e.g. Linux + set(PATHSEP ":") + endif() + set(_plugin_path $ENV{QT_PLUGIN_PATH}) + set(_test_env + QT_PLUGIN_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}${PATHSEP}$ENV{QT_PLUGIN_PATH} + LD_LIBRARY_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}${PATHSEP}$ENV{LD_LIBRARY_PATH} + ) + set_tests_properties(${_test_name} PROPERTIES ENVIRONMENT "${_test_env}") + endif() + set_tests_properties(${_test_name} PROPERTIES RUN_SERIAL TRUE) # can't be parallelized due to gpg-agent + endif() + endif() + endfunction() + + find_program(MYSQLD_EXECUTABLE mysqld /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec /opt/mysql/libexec /usr/mysql/bin) + if (MYSQLD_EXECUTABLE AND NOT WIN32) + _defineTest(${_testname} "MYSQL" ${CONFIG_BACKENDS}) + endif() + + find_program(POSTGRES_EXECUTABLE postgres) + if (POSTGRES_EXECUTABLE AND NOT WIN32) + _defineTest(${_testname} "PGSQL" ${CONFIG_BACKENDS}) + endif() + + _defineTest(${_testname} "SQLITE" ${CONFIG_BACKENDS}) +endmacro (ADD_GPG_CRYPTO_AKONADI_TEST) diff --git a/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake b/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake new file mode 100644 index 0000000..44f75d9 --- /dev/null +++ b/cmake/modules/kdepim_generate_crypto_test_wrapper.cmake @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: 2006 Alexander Neundorf +# SPDX-FileCopyrightText: 2013 Sandro Knauß +# +# SPDX-License-Identifier: BSD-3-Clause + + +if (UNIX) + +file(WRITE "${_filename}" +"#!/bin/sh +# created by cmake, don't edit, changes will be lost + +# don't mess with a gpg-agent already running on the system +unset GPG_AGENT_INFO + +# _gnupghome will contain a socket, and the path to that has a length limit of 108 chars +# which that is easily reached. Therefore shorten this by copying this to a temporary dir. +# This has the convenient side-effect that modifications to the content are not propagated +# to other tests. +tmp_dir=`mktemp -d -t messagelib-test-gnupg-home.XXXXXXXX` || exit 1 +cp -rf ${_gnupghome}/* $tmp_dir + +${_library_path_variable}=${_ld_library_path}\${${_library_path_variable}:+:\$${_library_path_variable}} GNUPGHOME=$tmp_dir \"${_executable}\" \"$@\" +_result=$? + +_pid=`echo GETINFO pid | GNUPGHOME=$tmp_dir gpg-connect-agent | grep 'D' | cut -d' ' -f2` +if [ ! -z \"\$_pid\" ]; then + echo KILLAGENT | GNUPGHOME=$tmp_dir gpg-connect-agent > /dev/null + sleep .3 + if ps -p \"\$_pid\" > /dev/null; then + echo \"Waiting for gpg-agent to terminate (PID: $_pid)...\" + while kill -0 \"\$_pid\"; do + sleep 1 + done + fi +fi +rm -rf $tmp_dir +exit \$_result +") + +# make it executable +# since this is only executed on UNIX, it is safe to call chmod +exec_program(chmod ARGS ug+x \"${_filename}\" OUTPUT_VARIABLE _dummy ) + +else (UNIX) + +file(TO_NATIVE_PATH "${_ld_library_path}" win_path) +file(TO_NATIVE_PATH "${_gnupghome}" win_gnupghome) + +file(WRITE "${_filename}" +" +set PATH=${win_path};$ENV{PATH} +set GNUPGHOME=${win_gnupghome};$ENV{GNUPGHOME} +gpg-agent --daemon \"${_executable}\" %* +") + +endif (UNIX) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5b50e4d..6628a7c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,175 +1,194 @@ # SPDX-FileCopyrightText: 2023 Carl Schwan # SPDX-License-Identifier: BSD-3-Clause ecm_setup_version(PROJECT VARIABLE_PREFIX MIMETREEPARSER_CORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_core_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreConfigVersion.cmake" SOVERSION 5 ) +# private library + +add_library(mimetreeparserprivate STATIC) +set_target_properties(mimetreeparserprivate PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_sources(mimetreeparserprivate PUBLIC + messagepart.h + messagepart.cpp + crypto.h + crypto.cpp + cryptohelper.h + cryptohelper.cpp + utils.h + utils.cpp +) +target_link_libraries(mimetreeparserprivate +PUBLIC + KPim${KF_MAJOR_VERSION}::Mime + KF${KF_MAJOR_VERSION}::I18n + Qt${KF_MAJOR_VERSION}::Gui + KF${KF_MAJOR_VERSION}::Codecs + Gpgme::Gpgme +) + +ecm_qt_declare_logging_category(mimetreeparserprivate + HEADER mimetreeparser_core_debug.h + IDENTIFIER MIMETREEPARSER_CORE_LOG + CATEGORY_NAME org.kde.pim.mimetreeparser.core + DESCRIPTION "mimetreeparser (pim lib)" + EXPORT MIMETREEPARSERNG +) + +# public dynamic library + + add_library(KPim${KF_MAJOR_VERSION}MimeTreeParserCore) add_library(KPim${KF_MAJOR_VERSION}::MimeTreeParserCore ALIAS KPim${KF_MAJOR_VERSION}MimeTreeParserCore ) target_sources(KPim${KF_MAJOR_VERSION}MimeTreeParserCore PRIVATE - crypto.h - crypto.cpp errors.h - async.h attachmentmodel.h bodypartformatter.h bodypartformatterbasefactory.h bodypartformatterbasefactory_p.h - cryptohelper.h enums.h htmlutils.h mailcrypto.h messageparser.h - messagepart.h mimetreeparser_debug.h objecttreeparser.h partmetadata.h partmodel.h - utils.h attachmentmodel.cpp bodypartformatter.cpp bodypartformatter_impl.cpp bodypartformatterbasefactory.cpp - cryptohelper.cpp htmlutils.cpp mailcrypto.cpp messageparser.cpp - messagepart.cpp mimetreeparser_debug.cpp objecttreeparser.cpp partmodel.cpp - utils.cpp -) - -ecm_qt_declare_logging_category(KPim${KF_MAJOR_VERSION}MimeTreeParserCore - HEADER mimetreeparser_core_debug.h - IDENTIFIER MIMETREEPARSER_CORE_LOG - CATEGORY_NAME org.kde.pim.mimetreeparser.core - DESCRIPTION "mimetreeparser (pim lib)" - EXPORT MIMETREEPARSERNG ) if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) set_target_properties(KPim${KF_MAJOR_VERSION}MimeTreeParserCore PROPERTIES UNITY_BUILD ON) endif() generate_export_header(KPim${KF_MAJOR_VERSION}MimeTreeParserCore BASE_NAME mimetreeparser_core) target_include_directories(KPim${KF_MAJOR_VERSION}MimeTreeParserCore INTERFACE "$") target_include_directories(KPim${KF_MAJOR_VERSION}MimeTreeParserCore PUBLIC "$") target_link_libraries(KPim${KF_MAJOR_VERSION}MimeTreeParserCore PUBLIC KPim${KF_MAJOR_VERSION}::Mime KF${KF_MAJOR_VERSION}::I18n Qt${KF_MAJOR_VERSION}::Gui PRIVATE - KF${KF_MAJOR_VERSION}::Codecs - Gpgme::Gpgme + mimetreeparserprivate ) set_target_properties(KPim${KF_MAJOR_VERSION}MimeTreeParserCore PROPERTIES VERSION ${MIMETREEPARSERNG_VERSION} SOVERSION ${MIMETREEPARSERNG_SOVERSION} EXPORT_NAME MimeTreeParserCore ) ecm_generate_pri_file(BASE_NAME MimeTreeParserCore LIB_NAME KPim${KF_MAJOR_VERSION}MimeTreeParserCore DEPS "MimeTreeParserCore" FILENAME_VAR PRI_FILENAME ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) install(TARGETS KPim${KF_MAJOR_VERSION}MimeTreeParserCore EXPORT KPim${KF_MAJOR_VERSION}MimeTreeParserCoreTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) ecm_generate_headers(MimeTreeParserCore_CamelCase_HEADERS HEADER_NAMES AttachmentModel ObjectTreeParser MessageParser PartModel REQUIRED_HEADERS MimeTreeParserCore_HEADERS PREFIX MimeTreeParserCore ) install(FILES ${MimeTreeParserCore_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserCore/MimeTreeParserCore COMPONENT Devel ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_core_export.h ${MimeTreeParserCore_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserCore/mimetreeparsercore COMPONENT Devel ) if (BUILD_QCH) ecm_add_qch( KPim${KF_MAJOR_VERSION}MimeTreeParserCore_QCH NAME MimeTreeParserCore BASE_NAME KPim${KF_MAJOR_VERSION}MimeTreeParserCore VERSION ${PIM_VERSION} ORG_DOMAIN org.kde # using only public headers, to cover only public API SOURCES ${MimeTreeParser_HEADERS} MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS Qt${QT_MAJOR_VERSION}Core_QCH INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} BLANK_MACROS MIMETREEPARSERCORE_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() ########### CMake Config Files ########### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCore") if (BUILD_QCH) ecm_install_qch_export( TARGETS KPim${KF_MAJOR_VERSION}MimeTreeParserCore_QCH FILE KPim${KF_MAJOR_VERSION}MimeTreeParserCoreQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KPimMimeTreeParserCoreConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserCoreConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KPim${KF_MAJOR_VERSION}MimeTreeParserCoreTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPim${KF_MAJOR_VERSION}MimeTreeParserCoreTargets.cmake NAMESPACE KPim${KF_MAJOR_VERSION}:: ) +if (BUILD_TESTING) + add_subdirectory(autotests) +endif() diff --git a/src/core/autotests/CMakeLists.txt b/src/core/autotests/CMakeLists.txt index d19d2c2..b4bdba0 100644 --- a/src/core/autotests/CMakeLists.txt +++ b/src/core/autotests/CMakeLists.txt @@ -1,65 +1,69 @@ # SPDX-FileCopyrightText: 2017 Christian Mollekopf # SPDX-License-Identifier: BSD-3-Clause set(AUTOMOC ON) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) add_definitions(-DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../testdata" ) include(ECMAddTests) include(${CMAKE_SOURCE_DIR}/cmake/modules/add_gpg_crypto_test.cmake) function(add_mimetreeparser_class_unittest _name _additionalSource) add_executable(${_name} ${_name}.cpp setupenv.cpp ${_additionalSource}) target_link_libraries(${_name} PRIVATE Qt::Test - kalendar_mail_static + mimetreeparserprivate + KPim${KF_MAJOR_VERSION}::MimeTreeParserCore ) target_include_directories(${_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) add_test(NAME ${_name} COMMAND $) endfunction() function(add_mimetreeparser_crypto_unittest _name) add_executable(${_name} ${_name}.cpp setupenv.cpp) if (QT_MAJOR_VERSION STREQUAL "6") target_link_libraries(${_name} PRIVATE QGpgmeQt6 Qt6::Core5Compat) else() target_link_libraries(${_name} PRIVATE Gpgme::Gpgme) endif() target_include_directories(${_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${GPGME_INCLUDE_DIRS} ) target_link_libraries(${_name} PRIVATE Qt::Test - kalendar_mail_static + mimetreeparserprivate + KPim${KF_MAJOR_VERSION}::MimeTreeParserCore ) add_gpg_crypto_test(${_name} mimetreeparser-${_name}) endfunction() add_subdirectory(gnupg_home) add_mimetreeparser_crypto_unittest(attachmenttest) add_mimetreeparser_class_unittest(cryptohelpertest "../cryptohelper.cpp") add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../testdata" ) include(${CMAKE_SOURCE_DIR}/cmake/modules/add_gpg_crypto_test.cmake) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ) include_directories(${GPGME_INCLUDE_DIRS}) include(ECMAddTests) add_executable(mimetreeparsertest mimetreeparsertest.cpp) add_gpg_crypto_test(mimetreeparsertest mimetreeparsertest) target_link_libraries(mimetreeparsertest PUBLIC Qt::Test - kalendar_mail_static + mimetreeparserprivate + KPim${KF_MAJOR_VERSION}::MimeTreeParserCore ) add_executable(gpgerrortest gpgerrortest.cpp) target_link_libraries(gpgerrortest PUBLIC Qt::Test - kalendar_mail_static + mimetreeparserprivate + KPim${KF_MAJOR_VERSION}::MimeTreeParserCore ) add_test(NAME gpgerrortest COMMAND $) diff --git a/src/core/autotests/attachmenttest.cpp b/src/core/autotests/attachmenttest.cpp index 34bd08f..4659c89 100644 --- a/src/core/autotests/attachmenttest.cpp +++ b/src/core/autotests/attachmenttest.cpp @@ -1,57 +1,57 @@ // SPDX-FileCopyrightText: 2015 Volker Krause // SPDX-License-Identifier: LGPL-2.0-or-later -#include "objecttreeparser.h" +#include #include "setupenv.h" #include using namespace MimeTreeParser; class AttachmentTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testEncryptedAttachment_data(); void testEncryptedAttachment(); }; QTEST_MAIN(AttachmentTest) QByteArray readMailFromFile(const QString &mailFile) { QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile); file.open(QIODevice::ReadOnly); Q_ASSERT(file.isOpen()); return file.readAll(); } void AttachmentTest::initTestCase() { MimeTreeParser::Test::setupEnv(); } void AttachmentTest::testEncryptedAttachment_data() { QTest::addColumn("mbox"); QTest::newRow("encrypted") << "openpgp-encrypted-two-attachments.mbox"; QTest::newRow("signed") << "openpgp-signed-two-attachments.mbox"; QTest::newRow("signed+encrypted") << "openpgp-signed-encrypted-two-attachments.mbox"; QTest::newRow("encrypted+partial signed") << "openpgp-encrypted-partially-signed-attachments.mbox"; } void AttachmentTest::testEncryptedAttachment() { QFETCH(QString, mbox); ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(mbox)); otp.decryptParts(); otp.print(); auto attachmentParts = otp.collectAttachmentParts(); QCOMPARE(attachmentParts.size(), 2); } #include "attachmenttest.moc" diff --git a/src/core/autotests/gpgerrortest.cpp b/src/core/autotests/gpgerrortest.cpp index e75b0f8..d55ef3e 100644 --- a/src/core/autotests/gpgerrortest.cpp +++ b/src/core/autotests/gpgerrortest.cpp @@ -1,201 +1,200 @@ // SPDX-FileCopyrightText: 2016 Sandro Knauß // SPDX-License-Identifier: LGPL-2.0-or-later -#include +#include #include #include #include -#include QByteArray readMailFromFile(const QString &mailFile) { QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile); file.open(QIODevice::ReadOnly); Q_ASSERT(file.isOpen()); return file.readAll(); } void killAgent(const QString &dir) { QProcess proc; proc.setProgram(QStringLiteral("gpg-connect-agent")); QStringList arguments; arguments << QStringLiteral("-S ") << dir + QStringLiteral("/S.gpg-agent"); proc.start(); proc.waitForStarted(); proc.write("KILLAGENT\n"); proc.write("BYE\n"); proc.closeWriteChannel(); proc.waitForFinished(); } class GpgErrorTest : public QObject { Q_OBJECT private Q_SLOTS: void testGpgConfiguredCorrectly() { setEnv("GNUPGHOME", GNUPGHOME); MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QStringLiteral("openpgp-inline-charset-encrypted.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0]; QVERIFY(bool(part)); QVERIFY(part->text().startsWith(QStringLiteral("asdasd"))); QCOMPARE(part->encryptions().size(), 1); auto enc = part->encryptions()[0]; QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoError); // QCOMPARE((int) enc->recipients().size(), 2); } void testNoGPGInstalled_data() { QTest::addColumn("mailFileName"); QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox"; QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"; QTest::newRow("smime-encrypted") << "smime-encrypted.mbox"; } void testNoGPGInstalled() { QFETCH(QString, mailFileName); setEnv("PATH", "/nonexististing"); setGpgMEfname("/nonexisting/gpg", ""); MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(mailFileName)); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->encryptions().size(), 1); QVERIFY(part->text().isEmpty()); auto enc = part->encryptions()[0]; QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoKeyError); } void testGpgIncorrectGPGHOME_data() { QTest::addColumn("mailFileName"); QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox"; QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"; QTest::newRow("smime-encrypted") << "smime-encrypted.mbox"; } void testGpgIncorrectGPGHOME() { QFETCH(QString, mailFileName); setEnv("GNUPGHOME", QByteArray(GNUPGHOME) + QByteArray("noexist")); MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(mailFileName)); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 0); QVERIFY(part->text().isEmpty()); auto enc = part->encryptions()[0]; QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoKeyError); // QCOMPARE((int) enc->recipients().size(), 2); } public Q_SLOTS: void init() { mResetGpgmeEngine = false; mModifiedEnv.clear(); { gpgme_check_version(nullptr); gpgme_ctx_t ctx = nullptr; gpgme_new(&ctx); gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP); gpgme_engine_info_t info = gpgme_ctx_get_engine_info(ctx); mGpgmeEngine_fname = info->file_name; gpgme_release(ctx); } mEnv = QProcessEnvironment::systemEnvironment(); unsetEnv("GNUPGHOME"); } void cleanup() { QCoreApplication::sendPostedEvents(); const QString &gnupghome = QString::fromUtf8(qgetenv("GNUPGHOME")); if (!gnupghome.isEmpty()) { killAgent(gnupghome); } resetGpgMfname(); resetEnv(); } private: void unsetEnv(const QByteArray &name) { mModifiedEnv << name; qunsetenv(name.data()); } void setEnv(const QByteArray &name, const QByteArray &value) { mModifiedEnv << name; qputenv(name.data(), value); } void resetEnv() { for (const auto &i : std::as_const(mModifiedEnv)) { const auto env = i.data(); if (mEnv.contains(QString::fromUtf8(i))) { qputenv(env, mEnv.value(QString::fromUtf8(i)).toUtf8()); } else { qunsetenv(env); } } } void resetGpgMfname() { if (mResetGpgmeEngine) { gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, mGpgmeEngine_fname.data(), nullptr); } } void setGpgMEfname(const QByteArray &fname, const QByteArray &homedir) { mResetGpgmeEngine = true; gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, fname.data(), homedir.data()); } QSet mModifiedEnv; QProcessEnvironment mEnv; bool mResetGpgmeEngine; QByteArray mGpgmeEngine_fname; }; QTEST_GUILESS_MAIN(GpgErrorTest) #include "gpgerrortest.moc" diff --git a/src/core/autotests/mimetreeparsertest.cpp b/src/core/autotests/mimetreeparsertest.cpp index 1c9c822..bc5f008 100644 --- a/src/core/autotests/mimetreeparsertest.cpp +++ b/src/core/autotests/mimetreeparsertest.cpp @@ -1,776 +1,776 @@ // SPDX-FileCopyrightText: 2016 Sandro Knauß // SPDX-License-Identifier: LGPL-2.0-or-later -#include +#include #include #include #include QByteArray readMailFromFile(const QString &mailFile) { QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile); file.open(QIODevice::ReadOnly); Q_ASSERT(file.isOpen()); return file.readAll(); } class MimeTreeParserTest : public QObject { Q_OBJECT private Q_SLOTS: void testTextMail() { const auto expectedText = QStringLiteral( "If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter " "on our website: http://www.gog.com/newsletter/"); MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("plaintext.mbox"))); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QCOMPARE(part->text(), expectedText); QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit()); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QCOMPARE(otp.collectAttachmentParts().size(), 0); QCOMPARE(otp.plainTextContent(), expectedText); QVERIFY(otp.htmlContent().isEmpty()); } void testAlternative() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("alternative.mbox"))); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->plaintextContent(), QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to " "view the newsletter on our website: http://www.gog.com/newsletter/\n")); QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit()); QCOMPARE(part->htmlContent(), QStringLiteral("

HTML text

\n\n")); QCOMPARE(otp.collectAttachmentParts().size(), 0); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); } void testTextHtml() { auto expectedText = QStringLiteral("

HTML text

"); MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("html.mbox"))); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->htmlContent(), expectedText); QCOMPARE(part->charset(), QStringLiteral("windows-1252").toLocal8Bit()); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); auto contentAttachmentList = otp.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 0); QCOMPARE(otp.htmlContent(), expectedText); QVERIFY(otp.plainTextContent().isEmpty()); } void testSMimeEncrypted() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog.")); QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit()); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 0); auto contentAttachmentList = otp.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 0); } void testOpenPGPEncryptedAttachment() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("test text")); QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit()); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned); auto contentAttachmentList = otp.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 2); // QCOMPARE(contentAttachmentList[0]->availableContents(), QVector() << "text/plain"); // QCOMPARE(contentAttachmentList[0]->content().size(), 1); QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1); QCOMPARE(contentAttachmentList[0]->signatures().size(), 1); QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned); // QCOMPARE(contentAttachmentList[1]->availableContents(), QVector() << "image/png"); // QCOMPARE(contentAttachmentList[1]->content().size(), 1); QCOMPARE(contentAttachmentList[1]->encryptions().size(), 0); QCOMPARE(contentAttachmentList[1]->signatures().size(), 0); QCOMPARE(contentAttachmentList[1]->encryptionState(), MimeTreeParser::KMMsgNotEncrypted); QCOMPARE(contentAttachmentList[1]->signatureState(), MimeTreeParser::KMMsgNotSigned); } void testOpenPGPInline() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-charset-encrypted.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->charset(), QStringLiteral("ISO-8859-15").toLocal8Bit()); QCOMPARE(part->text(), QString::fromUtf8("asdasd asd asd asdf sadf sdaf sadf öäü")); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 1); QCOMPARE(otp.collectAttachmentParts().size(), 0); } void testOpenPPGInlineWithNonEncText() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-encrypted+nonenc.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part1 = partList[0].dynamicCast(); QVERIFY(bool(part1)); QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\nsome random text")); // TODO test if we get the proper subparts with the appropriate encryptions QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit()); QCOMPARE(part1->encryptionState(), MimeTreeParser::KMMsgPartiallyEncrypted); QCOMPARE(part1->signatureState(), MimeTreeParser::KMMsgNotSigned); // QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\n")); // QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit()); // QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit()); // QCOMPARE(contentList[1]->charset(), QStringLiteral("us-ascii").toLocal8Bit()); // QCOMPARE(contentList[1]->encryptions().size(), 1); // QCOMPARE(contentList[1]->signatures().size(), 0); QCOMPARE(otp.collectAttachmentParts().size(), 0); } void testEncryptionBlock() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part1 = partList[0].dynamicCast(); QVERIFY(bool(part1)); QCOMPARE(part1->encryptions().size(), 1); // auto enc = contentList[0]->encryptions()[0]; // QCOMPARE((int) enc->recipients().size(), 2); // auto r = enc->recipients()[0]; // QCOMPARE(r->keyid(),QStringLiteral("14B79E26050467AA")); // QCOMPARE(r->name(),QStringLiteral("kdetest")); // QCOMPARE(r->email(),QStringLiteral("you@you.com")); // QCOMPARE(r->comment(),QStringLiteral("")); // r = enc->recipients()[1]; // QCOMPARE(r->keyid(),QStringLiteral("8D9860C58F246DE6")); // QCOMPARE(r->name(),QStringLiteral("unittest key")); // QCOMPARE(r->email(),QStringLiteral("test@kolab.org")); // QCOMPARE(r->comment(),QStringLiteral("no password")); auto attachmentList = otp.collectAttachmentParts(); QCOMPARE(attachmentList.size(), 2); auto attachment1 = attachmentList[0]; QVERIFY(attachment1->node()); QCOMPARE(attachment1->filename(), QStringLiteral("file.txt")); auto attachment2 = attachmentList[1]; QVERIFY(attachment2->node()); QCOMPARE(attachment2->filename(), QStringLiteral("image.png")); } void testSignatureBlock() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); // QCOMPARE(contentList[0]->signatures().size(), 1); // auto sig = contentList[0]->signatures()[0]; // QCOMPARE(sig->creationDateTime(), QDateTime(QDate(2015,05,01),QTime(15,12,47))); // QCOMPARE(sig->expirationDateTime(), QDateTime()); // QCOMPARE(sig->neverExpires(), true); // auto key = sig->key(); // QCOMPARE(key->keyid(),QStringLiteral("8D9860C58F246DE6")); // QCOMPARE(key->name(),QStringLiteral("unittest key")); // QCOMPARE(key->email(),QStringLiteral("test@kolab.org")); // QCOMPARE(key->comment(),QStringLiteral("no password")); } void testRelatedAlternative() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links.mbox"))); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QCOMPARE(otp.collectAttachmentParts().size(), 1); } void testAttachmentPart() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("attachment.mbox"))); otp.print(); auto partList = otp.collectAttachmentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->mimeType(), "image/jpeg"); QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg")); } void testAttachment2Part() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("attachment2.mbox"))); otp.print(); auto partList = otp.collectAttachmentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->mimeType(), "image/jpeg"); QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg")); } void testCidLink() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links.mbox"))); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); auto resolvedContent = otp.resolveCidLinks(part->htmlContent()); QVERIFY(!resolvedContent.contains(QLatin1String("cid:"))); } void testCidLinkInForwardedInline() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links-forwarded-inline.mbox"))); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); auto resolvedContent = otp.resolveCidLinks(part->htmlContent()); QVERIFY(!resolvedContent.contains(QLatin1String("cid:"))); } void testOpenPGPInlineError() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("inlinepgpgencrypted-error.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QVERIFY(part->error()); } void testEncapsulated() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("encapsulated-with-attachment.mbox"))); otp.decryptParts(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 2); auto part = partList[1].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->from(), QLatin1String("Thomas McGuire ")); QCOMPARE(part->date().toString(), QLatin1String("Wed Aug 5 10:57:58 2009 GMT+0200")); auto subPartList = otp.collectContentParts(part); QCOMPARE(subPartList.size(), 1); qWarning() << subPartList[0]->metaObject()->className(); auto subPart = subPartList[0].dynamicCast(); QVERIFY(bool(subPart)); } void test8bitEncodedInPlaintext() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("8bitencoded.mbox"))); QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("Why Pisa’s Tower"))); QVERIFY(otp.htmlContent().contains(QString::fromUtf8("Why Pisa’s Tower"))); } void testInlineSigned() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-signed.mbox"))); otp.decryptParts(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgNotEncrypted); QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned); QCOMPARE(part->text(), QString::fromUtf8("ohno öäü\n")); QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("ohno öäü"))); auto signaturePart = part->signatures().first(); QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true); QCOMPARE(signaturePart->partMetaData()->keyIsTrusted, true); QCOMPARE(signaturePart->partMetaData()->keyMissing, false); QCOMPARE(signaturePart->partMetaData()->keyExpired, false); QCOMPARE(signaturePart->partMetaData()->keyRevoked, false); QCOMPARE(signaturePart->partMetaData()->sigExpired, false); QCOMPARE(signaturePart->partMetaData()->crlMissing, false); QCOMPARE(signaturePart->partMetaData()->crlTooOld, false); QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"}); QCOMPARE(signaturePart->partMetaData()->signer, QLatin1String{"unittest key (no password) "}); QCOMPARE(signaturePart->partMetaData()->signerMailAddresses, QStringList{{QStringLiteral("test@kolab.org")}}); } void testEncryptedAndSigned() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted+signed.mbox"))); otp.decryptParts(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned); QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("encrypted message text"))); auto signaturePart = part->signatures().first(); QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"}); QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true); } void testOpenpgpMultipartEmbedded() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-multipart-embedded.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(otp.plainTextContent(), QString::fromUtf8("sdflskjsdf\n\n-- \nThis is a HTML signature.\n")); } void testOpenpgpMultipartEmbeddedSigned() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-multipart-embedded-signed.mbox"))); otp.decryptParts(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned); QCOMPARE(otp.plainTextContent(), QString::fromUtf8("test\n\n-- \nThis is a HTML signature.\n")); auto signaturePart = part->signatures().first(); QVERIFY(signaturePart->partMetaData()->keyId.endsWith(QByteArray{"2E3B7787B1B75920"})); // We lack the public key for this message QCOMPARE(signaturePart->partMetaData()->isGoodSignature, false); QCOMPARE(signaturePart->partMetaData()->keyMissing, true); } void testAppleHtmlWithAttachments() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("applehtmlwithattachments.mbox"))); otp.decryptParts(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(part); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QVERIFY(part->isHtml()); QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hi,\n\nThis is an HTML message with attachments.\n\nCheers,\nChristian")); QCOMPARE(otp.htmlContent(), QString::fromUtf8( "
Hi,

This is an HTML message with attachments.

Cheers,
Christian
")); auto attachments = otp.collectAttachmentParts(); QCOMPARE(attachments.size(), 1); } void testAppleHtmlWithAttachmentsMixed() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("applehtmlwithattachmentsmixed.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(part); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QVERIFY(part->isHtml()); QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hello\n\n\n\nRegards\n\nFsdfsdf")); QCOMPARE(otp.htmlContent(), QString::fromUtf8( "Hello


Regards

Fsdfsdf
")); auto attachments = otp.collectAttachmentParts(); QCOMPARE(attachments.size(), 1); } void testInvitation() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("invitation.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(part); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QVERIFY(!part->isHtml()); QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)); auto attachments = otp.collectAttachmentParts(); QCOMPARE(attachments.size(), 0); } void testGmailInvitation() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("gmail-invitation.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(part); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); QVERIFY(part->isHtml()); QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)); auto attachments = otp.collectAttachmentParts(); QCOMPARE(attachments.size(), 1); } void testMemoryHole() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("very secret mesage\n")); QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("hidden subject")); QCOMPARE(part->header("from")->asUnicodeString(), QStringLiteral("you@example.com")); QCOMPARE(part->header("to")->asUnicodeString(), QStringLiteral("me@example.com")); QCOMPARE(part->header("cc")->asUnicodeString(), QStringLiteral("cc@example.com")); QCOMPARE(part->header("message-id")->asUnicodeString(), QStringLiteral("")); QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("")); QCOMPARE(part->header("in-reply-to")->asUnicodeString(), QStringLiteral("")); QCOMPARE(static_cast(part->header("date"))->dateTime(), QDateTime(QDate(2018, 1, 2), QTime(3, 4, 5), QTimeZone::utc())); } /** * Required special handling because the list replaces the toplevel part. */ void testMemoryHoleWithList() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links-forwarded-inline.mbox"))); const auto parts = otp.collectContentParts(); auto part = parts[0]; QVERIFY(part->header("references")); QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("")); } void testMemoryHoleMultipartMixed() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole2.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("\n\n Fsdflkjdslfj\n\n\nHappy Monday!\n\nBelow you will find a quick overview of the current on-goings. Remember\n")); QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("This is the subject")); } void testMIMESignature() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("text+html-maillinglist.mbox"))); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); for (const auto &part : partList) { qWarning() << "found part " << part->metaObject()->className(); } QCOMPARE(partList.size(), 2); // The actual content { auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); } // The signature { auto part = partList[1].dynamicCast(); QVERIFY(bool(part)); QVERIFY(part->text().contains(QStringLiteral("bugzilla mailing list"))); } } void testCRLFEncryptedWithSignature() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature.mbox"))); otp.decryptParts(); otp.print(); QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n")); } void testCRLFEncryptedWithSignatureMultipart() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature-multipart.mbox"))); otp.decryptParts(); otp.print(); // QEXPECT_FAIL("", "because MessagePart::parseInternal uses \n\n to detect encapsulated messages (so 'CRLF file' ends up as header)", Continue); // QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n")); // QVERIFY(!otp.htmlContent().contains(QStringLiteral("\r\n"))); } void testCRLFOutlook() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("outlook.mbox"))); otp.decryptParts(); otp.print(); qWarning() << otp.plainTextContent(); QVERIFY(otp.plainTextContent().startsWith(QStringLiteral("Hi Christian,\n\nhabs gerade getestet:\n\n«This is a test"))); QVERIFY(!otp.htmlContent().contains(QLatin1String("\r\n"))); } void testOpenPGPEncryptedSignedThunderbird() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-signed-thunderbird.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("sdfsdf\n")); QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit()); QCOMPARE(part->encryptions().size(), 1); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true); QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned); auto contentAttachmentList = otp.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 1); // QCOMPARE(contentAttachmentList[0]->content().size(), 1); QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1); QCOMPARE(contentAttachmentList[0]->signatures().size(), 1); QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted); QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned); } void testSignedForwardOpenpgpSignedEncrypted() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("signed-forward-openpgp-signed-encrypted.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 2); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("bla bla bla")); part = partList[1].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QString()); QCOMPARE(part->charset(), QStringLiteral("ISO-8859-1").toLocal8Bit()); QCOMPARE(part->signatures().size(), 1); QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true); auto contentAttachmentList = otp.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 1); } void testSmimeOpaqueSign() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-sign.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("A simple signed only test.")); } void testSmimeEncrypted() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog.")); } void testSmimeSignedApple() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-signed-apple.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); // QCOMPARE(part->text(), QStringLiteral("A simple signed only test.")); } void testSmimeEncryptedOctetStream() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted-octet-stream.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog.")); } void testSmimeOpaqueSignedEncryptedAttachment() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-signed-encrypted-attachment.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("This is an Opaque S/MIME encrypted and signed message with attachment")); } void testSmimeOpaqueEncSign() { MimeTreeParser::ObjectTreeParser otp; otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-enc+sign.mbox"))); otp.print(); otp.decryptParts(); otp.print(); auto partList = otp.collectContentParts(); QCOMPARE(partList.size(), 1); auto part = partList[0].dynamicCast(); QVERIFY(bool(part)); QCOMPARE(part->text(), QStringLiteral("Encrypted and signed mail.")); } }; QTEST_GUILESS_MAIN(MimeTreeParserTest) #include "mimetreeparsertest.moc" diff --git a/src/core/objecttreeparser.h b/src/core/objecttreeparser.h index 8aa3f89..d66a355 100644 --- a/src/core/objecttreeparser.h +++ b/src/core/objecttreeparser.h @@ -1,105 +1,106 @@ // SPDX-FileCopyrightText: 2016 Sandro Knauß // SPDX-FileCopyrightText: 2003 Marc Mutz // SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net // SPDX-FileCopyrightText: 2009 Andras Mantia // SPDX-License-Identifier: LGPL-2.0-or-later #pragma once #include "messagepart.h" +#include "mimetreeparser_core_export.h" #include #include class QString; namespace KMime { class Content; } class QTextCodec; namespace MimeTreeParser { /** Entry point to parse mime messages. Content returned by the ObjectTreeParser (including messageparts), is normalized to not contain any CRLF's but only LF's (just like KMime). */ -class ObjectTreeParser +class MIMETREEPARSER_CORE_EXPORT ObjectTreeParser { // Disable copy ObjectTreeParser(const ObjectTreeParser &other); public: explicit ObjectTreeParser() = default; virtual ~ObjectTreeParser() = default; QString structureAsString() const; void print(); /** * The text of the message, ie. what would appear in the * composer's text editor if this was edited or replied to. * This is usually the content of the first text/plain MIME part. */ QString plainTextContent(); /** * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part. */ QString htmlContent(); /** Parse beginning at a given node and recursively parsing the children of that node and it's next sibling. */ void parseObjectTree(KMime::Content *node); void parseObjectTree(const QByteArray &mimeMessage); MessagePart::Ptr parsedPart() const; KMime::Content *find(const std::function &select); MessagePart::List collectContentParts(); MessagePart::List collectContentParts(MessagePart::Ptr start); MessagePart::List collectAttachmentParts(); /** Decrypt parts and verify signatures */ void decryptAndVerify(); // DEPRECATED calls decryptAndVerify void decryptParts(); /** Import any certificates found in the message */ void importCertificates(); /** Embedd content referenced by cid by inlining */ QString resolveCidLinks(const QString &html); private: /** * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the * top-level content. */ MessagePart::Ptr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart); MessagePart::List processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType); MessagePart::List defaultHandling(KMime::Content *node); const QTextCodec *codecFor(KMime::Content *node) const; KMime::Content *mTopLevelContent{nullptr}; MessagePart::Ptr mParsedPart; KMime::Message::Ptr mMsg; friend class MessagePart; friend class EncryptedMessagePart; friend class SignedMessagePart; friend class EncapsulatedRfc822MessagePart; friend class TextMessagePart; friend class HtmlMessagePart; friend class TextPlainBodyPartFormatter; friend class MultiPartSignedBodyPartFormatter; friend class ApplicationPkcs7MimeBodyPartFormatter; }; }