diff --git a/CMakeLists.txt b/CMakeLists.txt index bfe1d44b..01b0ba5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,141 +1,140 @@ cmake_minimum_required(VERSION 3.5) set(PIM_VERSION "5.12.62") if (POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() project(Messagelib VERSION ${PIM_VERSION}) option(MIMETREEPARSER_ONLY_BUILD "Build only mimetreeparser" FALSE) option(KDEPIM_ENTERPRISE_BUILD "Enable features specific to the enterprise branch, which are normally disabled. Also, it disables many components not needed for Kontact such as the Kolab client." FALSE) option(KDEPIM_RUN_AKONADI_TEST "Enable autotest based on Akonadi." TRUE) -option(DKIM_CHECKER_BUILD "DKIM CHECKER (experimental)" FALSE) set(KF5_MIN_VERSION "5.63.0") set(MESSAGELIB_LIB_VERSION ${PIM_VERSION}) set(AKONADIMIME_LIB_VERSION "5.12.40") set(QT_REQUIRED_VERSION "5.11.0") set(AKONADI_VERSION "5.12.40") set(GRANTLEETHEME_LIB_VERSION "5.12.40") set(GRAVATAR_LIB_VERSION "5.12.40") set(IDENTITYMANAGEMENT_LIB_VERSION "5.12.40") set(KDEPIM_APPS_LIB_VERSION "5.12.40") set(KLDAP_LIB_VERSION "5.12.40") set(KMAILTRANSPORT_LIB_VERSION "5.12.40") set(KMBOX_LIB_VERSION "5.12.40") set(KMIME_LIB_VERSION "5.12.40") set(KPIMTEXTEDIT_LIB_VERSION "5.12.42") set(LIBKDEPIM_LIB_VERSION "5.12.40") set(LIBKLEO_LIB_VERSION "5.12.40") set(PIMCOMMON_LIB_VERSION "5.12.40") set(GPGME_LIB_VERSION "1.11.1") set(AKONADI_CONTACT_VERSION "5.12.42") if (${MIMETREEPARSER_ONLY_BUILD}) set(ECM_VERSION "5.26.0") set(KMIME_LIB_VERSION "5.1.40") else() set(ECM_VERSION ${KF5_MIN_VERSION}) endif() find_package(ECM ${ECM_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${Messagelib_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) set(LIBRARY_NAMELINK) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(FeatureSummary) include(ECMQtDeclareLoggingCategory) include(ECMAddTests) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Gui Test) find_package(KF5Codecs ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5NewStuff ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(QGpgme ${GPGME_LIB_VERSION} CONFIG REQUIRED) if (NOT ${MIMETREEPARSER_ONLY_BUILD}) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Network PrintSupport WebEngine WebEngineWidgets) find_package(KF5Archive ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Completion ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemViews ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5JobWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Service ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Sonnet ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5SyntaxHighlighting ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(Grantlee5 "5.1" CONFIG REQUIRED) find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Contacts ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED) find_package(KF5FollowupReminder ${KDEPIM_APPS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5GrantleeTheme ${GRANTLEETHEME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Gravatar ${GRAVATAR_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5KaddressbookGrantlee ${KDEPIM_APPS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Ldap ${KLDAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5LibkdepimAkonadi ${LIBKDEPIM_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Libkleo ${LIBKLEO_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mbox ${KMBOX_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommonAkonadi ${PIMCOMMON_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5SendLater ${KDEPIM_APPS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiSearch "5.12.40" CONFIG REQUIRED) set_package_properties(KF5AkonadiSearch PROPERTIES DESCRIPTION "The Akonadi Search libraries" URL "https://kde.org/" TYPE REQUIRED PURPOSE "Provides search capabilities in KMail and Akonadi") endif() set(CMAKE_CXX_STANDARD 14) if (EXISTS "${CMAKE_SOURCE_DIR}/.git") add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) endif() #add_definitions( # -DKCOREADDONS_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000 # -DKWINDOWSYSTEM_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000 # -DKWIDGETSADDONS_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000 # -DKPARTS_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000 # -DKTEXTWIDGETS_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000 # -DKSERVICE_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000) if(BUILD_TESTING) add_definitions(-DBUILD_TESTING) endif() add_subdirectory(mimetreeparser) if (NOT ${MIMETREEPARSER_ONLY_BUILD}) add_subdirectory(messageviewer) add_subdirectory(templateparser) add_subdirectory(messagecomposer) add_subdirectory(messagecore) add_subdirectory(messagelist) add_subdirectory(webengineviewer) endif() install(FILES messagelib.renamecategories messagelib.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/messageviewer/src/CMakeLists.txt b/messageviewer/src/CMakeLists.txt index ce57b90f..5da12585 100644 --- a/messageviewer/src/CMakeLists.txt +++ b/messageviewer/src/CMakeLists.txt @@ -1,558 +1,555 @@ add_definitions(-DTRANSLATION_DOMAIN=\"libmessageviewer\") add_subdirectory(messagepartthemes/grantlee) # KCFG files: # The main messageviewer.kcfg is configured by CMake and put in the build directory. if(KDEPIM_ENTERPRISE_BUILD) set(LEGACY_MANGLE_FROM_TO_HEADERS true) set(LEGACY_BODY_INVITES true) set(EXCHANGE_COMPATIBLE_INVITATIONS true) else() set(LEGACY_MANGLE_FROM_TO_HEADERS false) set(LEGACY_BODY_INVITES false) set(EXCHANGE_COMPATIBLE_INVITATIONS false) endif() -if (DKIM_CHECKER_BUILD) - set(USE_DKIM_CHECKER 1) -endif() - - configure_file(settings/messageviewer.kcfg.cmake ${CMAKE_CURRENT_BINARY_DIR}/messageviewer.kcfg) include(CheckIncludeFiles) find_package(Inotify) set_package_properties(Inotify PROPERTIES PURPOSE "Filesystem alteration notifications using inotify") if(Inotify_FOUND) set(HAVE_SYS_INOTIFY_H 1) else() set(HAVE_SYS_INOTIFY_H 0) endif() configure_file(config-messageviewer.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-messageviewer.h) # target_include_directories does not handle empty include paths include_directories(${GPGME_INCLUDES}) if(BUILD_TESTING) add_subdirectory(header/autotests) add_subdirectory(scamdetection/autotests) add_subdirectory(scamdetection/tests) add_subdirectory(viewerplugins/tests/) add_subdirectory(htmlwriter/autotests) add_subdirectory(viewer/webengine/tests) add_subdirectory(messagepartthemes/default/autotests) add_subdirectory(widgets/autotests/) add_subdirectory(utils/autotests) endif() add_subdirectory(pics) add_subdirectory(kconf_update) add_subdirectory(about) add_subdirectory(messageviewerheaderplugins) -if (DKIM_CHECKER_BUILD) - find_package(Qca-qt5 2.1.0) - set_package_properties(Qca-qt5 PROPERTIES DESCRIPTION "Qt Cryptographic Architecture" - URL "https:/download.kde.org/stable/qca-qt5" TYPE OPTIONAL - PURPOSE "Needed for most of the algorithms of the checksum tool") +find_package(Qca-qt5 2.1.0) +set_package_properties(Qca-qt5 PROPERTIES DESCRIPTION "Qt Cryptographic Architecture" + URL "https:/download.kde.org/stable/qca-qt5" TYPE OPTIONAL + PURPOSE "Needed for kdim support.") +if (${Qca-qt5_FOUND}) + set(USE_DKIM_CHECKER 1) if (BUILD_TESTING) add_subdirectory(dkim-verify/autotests) add_subdirectory(dkim-verify/tests) endif() set(dkim_verify_SRCS dkim-verify/dkiminfo.cpp dkim-verify/dkimmanagerkey.cpp dkim-verify/dkimmanagerkeywidget.cpp dkim-verify/dkimmanagerkeydialog.cpp dkim-verify/dkimdownloadkeyjob.cpp dkim-verify/dkimchecksignaturejob.cpp dkim-verify/dkimcheckauthenticationstatusjob.cpp dkim-verify/dkimauthenticationstatusinfo.cpp dkim-verify/dkimutil.cpp dkim-verify/dkimkeyrecord.cpp dkim-verify/dkimmanager.cpp dkim-verify/dkimresultattribute.cpp dkim-verify/dkimwidgetinfo.cpp dkim-verify/dkimstoreresultjob.cpp + dkim-verify/dkimheaderparser.cpp ) endif() if(DEBUG_SIGNATURE) add_definitions(-DDEBUG_SIGNATURE) endif() set(libmessageviewer_mailviewer_SRCS viewer/webengine/mailwebengineview.cpp viewer/webengine/mailwebenginepage.cpp viewer/webengine/loadexternalreferencesurlinterceptor/loadexternalreferencesurlinterceptor.cpp viewer/webengine/cidreferencesurlinterceptor/cidreferencesurlinterceptor.cpp viewer/webengine/blockexternalresourcesurlinterceptor/blockexternalresourcesurlinterceptor.cpp viewer/webengine/blockmailtrackingurlinterceptor/blockmailtrackingurlinterceptor.cpp ) set(libmessageviewer_viewer_SRCS viewer/attachmentstrategy.cpp viewer/csshelper.cpp viewer/csshelperbase.cpp viewer/editorwatcher.cpp viewer/objecttreeemptysource.cpp viewer/objecttreeviewersource.cpp viewer/viewer.cpp viewer/viewer_p.cpp viewer/messagedisplayformatattribute.cpp viewer/urlhandlermanager.cpp viewer/mimeparttree/mimeparttreeview.cpp viewer/mimeparttree/mimetreemodel.cpp ) set(libmessageviewer_widgets_SRCS widgets/attachmentdialog.cpp widgets/configurewidget.cpp widgets/printingsettings.cpp widgets/htmlstatusbar.cpp widgets/vcardviewer.cpp widgets/invitationsettings.cpp widgets/openattachmentfolderwidget.cpp widgets/mailsourceviewtextbrowserwidget.cpp widgets/submittedformwarningwidget.cpp widgets/mailtrackingwarningwidget.cpp widgets/mailtrackingdetailsdialog.cpp widgets/shownextmessagewidget.cpp ) set(libmessageviewer_widgets_webengine_SRCS widgets/mailsourcewebengineviewer.cpp ) set(libmessageviewer_header_SRCS header/contactdisplaymessagememento.cpp header/headerstrategy.cpp header/richheaderstrategy.cpp header/headerstyle.cpp header/grantleeheaderstyle.cpp header/plainheaderstyle.cpp header/headerstyle_util.cpp header/grantleeheaderformatter.cpp header/grantleeheaderteststyle.cpp header/kxface.cpp header/headerstyleplugin.cpp header/headerstylepluginmanager.cpp header/headerstyleinterface.cpp header/headerstylemenumanager.cpp ) set(libmessageviewer_scamdetection_SRCS scamdetection/scamdetectionwarningwidget.cpp scamdetection/scamdetectiondetailsdialog.cpp scamdetection/scamattribute.cpp scamdetection/scamcheckshorturl.cpp scamdetection/scamexpandurljob.cpp scamdetection/scamcheckshorturlmanager.cpp ) set(libmessageviewer_scamdetection_webengine_SRCS scamdetection/scamdetectionwebengine.cpp ) set(libmessageviewer_findbar_SRCS findbar/findbarsourceview.cpp ) set(libmessageviewer_utils_SRCS utils/iconnamecache.cpp utils/markmessagereadhandler.cpp utils/messageviewerutil.cpp utils/mimetype.cpp ) set(libmessageviewer_htmlwriter_webengine_SRCS htmlwriter/webengineparthtmlwriter.cpp htmlwriter/webengineembedpart.cpp ) set(libmessageviewer_htmlwriter_SRCS ${libmessageviewer_htmlwriter_webengine_SRCS} htmlwriter/bufferedhtmlwriter.cpp htmlwriter/filehtmlwriter.cpp ) set(libmessageviewer_antispam_SRCS antispam/spamheaderanalyzer.cpp antispam/antispamconfig.cpp ) set(libmessageviewer_job_SRCS job/modifymessagedisplayformatjob.cpp ) set(libmessageviewer_viewerplugins_SRCS viewerplugins/viewerpluginmanager.cpp viewerplugins/viewerplugin.cpp viewerplugins/viewerplugininterface.cpp viewerplugins/viewerplugintoolmanager.cpp ) set(libmessageviewer_configureplugins_SRCS messageviewerconfigureplugins/messageviewerconfiguresettingsplugin.cpp messageviewerconfigureplugins/messageviewerconfiguresettingspluginmanager.cpp messageviewerconfigureplugins/messageviewerconfiguresettingspluginwidget.cpp ) set(libmessageviewer_messagepartthemes_default_SRCS messagepartthemes/default/converthtmltoplaintext.cpp messagepartthemes/default/defaultrenderer.cpp messagepartthemes/default/htmlblock.cpp messagepartthemes/default/messagepartrenderermanager.cpp messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp messagepartthemes/default/plugins/messagepartrenderer.cpp messagepartthemes/default/plugins/textmessagepartrenderer.cpp messagepartthemes/default/plugins/quotehtml.cpp messagepartthemes/default/messagepartrenderbase.cpp messagepartthemes/default/messagepartrenderplugin.cpp messagepartthemes/default/messagepartrendererfactory.cpp ) set(libmessageviewer_interfaces_SRCS interfaces/htmlwriter.cpp ) set(libmessageviewer_SRCS ${dkim_verify_SRCS} ${libmessageviewer_messagepartthemes_default_SRCS} ${libmessageviewer_htmlwriter_SRCS} ${libmessageviewer_messagepartthemes_SRCS} ${libmessageviewer_scamdetection_webengine_SRCS} ${libmessageviewer_widgets_webengine_SRCS} ${libmessageviewer_viewer_SRCS} ${libmessageviewer_widgets_SRCS} ${libmessageviewer_header_SRCS} ${libmessageviewer_scamdetection_SRCS} ${libmessageviewer_findbar_SRCS} ${libmessageviewer_utils_SRCS} ${libmessageviewer_antispam_SRCS} ${libmessageviewer_job_SRCS} ${libmessageviewer_viewerplugins_SRCS} settings/messageviewersettings.cpp ${libmessageviewer_mailviewer_SRCS} ${libmessageviewer_interfaces_SRCS} ${libmessageviewer_configureplugins_SRCS} ) qt5_add_resources(libmessageviewer_SRCS messagepartthemes.qrc) ecm_qt_declare_logging_category(libmessageviewer_SRCS HEADER messageviewer_debug.h IDENTIFIER MESSAGEVIEWER_LOG CATEGORY_NAME org.kde.pim.messageviewer) #temporary ecm_qt_declare_logging_category(libmessageviewer_SRCS HEADER messageviewer_dkimcheckerdebug.h IDENTIFIER MESSAGEVIEWER_DKIMCHECKER_LOG CATEGORY_NAME org.kde.pim.messageviewer_dkimchecker) kconfig_add_kcfg_files(libmessageviewer_SRCS settings/globalsettings_messageviewer.kcfgc ) ki18n_wrap_ui(libmessageviewer_SRCS ui/settings.ui ui/invitationsettings.ui ui/printingsettings.ui ) add_library(KF5MessageViewer ${libmessageviewer_SRCS}) generate_export_header(KF5MessageViewer BASE_NAME messageviewer) add_library(KF5::MessageViewer ALIAS KF5MessageViewer) target_include_directories(KF5MessageViewer INTERFACE "$;${Inotify_INCLUDE_DIRS}") -if (DKIM_CHECKER_BUILD) +if (${Qca-qt5_FOUND}) set(OPTIONAL_PRIVATE qca-qt5) endif() target_link_libraries(KF5MessageViewer PUBLIC KF5::MessageCore KF5::PimCommon KF5::AkonadiCore KF5::AkonadiMime KF5::Contacts KF5::Libkleo KF5::MimeTreeParser PRIVATE KF5::SyntaxHighlighting KF5::ItemViews Qt5::Network KF5::WebEngineViewer KF5::LibkdepimAkonadi KF5::GrantleeTheme KF5::KaddressbookGrantlee KF5::MailTransportAkonadi KF5::Mime KF5::Mbox KF5::PimTextEdit KF5::Gravatar KF5::IconThemes KF5::I18n KF5::KIOFileWidgets KF5::KIOWidgets KF5::XmlGui Grantlee5::TextDocument Grantlee5::Templates Qt5::PrintSupport QGpgme ${Inotify_LIBRARIES} ${OPTIONAL_PRIVATE} ) set(OPTIONAL_PRIVATE qca-qt5) set_target_properties(KF5MessageViewer PROPERTIES VERSION ${MESSAGEVIEWER_VERSION_STRING} SOVERSION ${MESSAGEVIEWER_SOVERSION} EXPORT_NAME MessageViewer ) install(TARGETS KF5MessageViewer EXPORT KF5MessageViewerTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} ) ecm_generate_headers(MessageViewer_Camelblockmailtrackingurlinterceptor_HEADERS HEADER_NAMES BlockMailTrackingUrlInterceptor REQUIRED_HEADERS MessageViewer_blockmailtrackingurlinterceptor_HEADERS PREFIX MessageViewer RELATIVE viewer/webengine/blockmailtrackingurlinterceptor ) ecm_generate_headers(MessageViewer_Camelcasewebengine_HEADERS HEADER_NAMES MailWebEnginePage MailWebEngineView REQUIRED_HEADERS MessageViewer_webengine_HEADERS PREFIX MessageViewer RELATIVE viewer/webengine ) ecm_generate_headers(MessageViewer_Camelcasescam_HEADERS HEADER_NAMES ScamExpandUrlJob ScamCheckShortUrlManager ScamCheckShortUrl REQUIRED_HEADERS MessageViewer_scam_HEADERS PREFIX MessageViewer RELATIVE scamdetection ) -if (DKIM_CHECKER_BUILD) +if (${Qca-qt5_FOUND}) ecm_generate_headers(MessageViewer_Cameldkimverify_HEADERS HEADER_NAMES DKIMManagerKey DKIMCheckSignatureJob DKIMManager DKIMManagerKeyWidget DKIMManagerKeyDialog DKIMKeyRecord DKIMInfo DKIMWidgetInfo REQUIRED_HEADERS MessageViewer_dkimverify_HEADERS PREFIX MessageViewer RELATIVE dkim-verify ) endif() ecm_generate_headers(MessageViewer_Camelcaseviewer_HEADERS HEADER_NAMES AttachmentStrategy Viewer CSSHelperBase CSSHelper ObjectTreeEmptySource EditorWatcher Stl_Util REQUIRED_HEADERS MessageViewer_viewer_HEADERS PREFIX MessageViewer RELATIVE viewer ) ecm_generate_headers(MessageViewer_Camelcasewidgets_HEADERS HEADER_NAMES InvitationSettings PrintingSettings ConfigureWidget REQUIRED_HEADERS MessageViewer_widgets_HEADERS PREFIX MessageViewer RELATIVE widgets ) ecm_generate_headers(MessageViewer_Camelcaseutils_HEADERS HEADER_NAMES IconNameCache MarkMessageReadHandler MessageViewerUtil MimeType REQUIRED_HEADERS MessageViewer_utils_HEADERS PREFIX MessageViewer RELATIVE utils ) ecm_generate_headers(MessageViewer_Camelcaseantispam_HEADERS HEADER_NAMES SpamHeaderAnalyzer REQUIRED_HEADERS MessageViewer_antispam_HEADERS PREFIX MessageViewer RELATIVE antispam ) ecm_generate_headers(MessageViewer_Camelcaseinterfaces_HEADERS HEADER_NAMES HtmlWriter BodyPartURLHandler URLHandler REQUIRED_HEADERS MessageViewer_interfaces_HEADERS PREFIX MessageViewer RELATIVE interfaces ) ecm_generate_headers(MessageViewer_Camelcasehtmlwriter_HEADERS HEADER_NAMES BufferedHtmlWriter FileHtmlWriter REQUIRED_HEADERS MessageViewer_htmlwriter_HEADERS PREFIX MessageViewer RELATIVE htmlwriter ) ecm_generate_headers(MessageViewer_Camelcasesettings_HEADERS HEADER_NAMES MessageViewerSettings REQUIRED_HEADERS MessageViewer_settings_HEADERS PREFIX MessageViewer RELATIVE settings ) ecm_generate_headers(MessageViewer_CamelcaseConfigurePlugins_HEADERS HEADER_NAMES MessageViewerConfigureSettingsPluginManager MessageViewerConfigureSettingsPlugin MessageViewerConfigureSettingsPluginWidget REQUIRED_HEADERS MessageViewer_ConfigurePlugins_HEADERS PREFIX MessageViewer RELATIVE messageviewerconfigureplugins ) ecm_generate_headers(MessageViewer_Camelcaseheader_HEADERS HEADER_NAMES HeaderStrategy GrantleeHeaderTestStyle GrantleeHeaderStyle HeaderStyle KXFace HeaderStyle_Util HeaderStylePlugin HeaderStyleInterface PlainHeaderStyle RichHeaderStrategy HeaderStylePluginManager HeaderStyleMenuManager REQUIRED_HEADERS MessageViewer_header_HEADERS PREFIX MessageViewer RELATIVE header ) ecm_generate_headers(MessageViewer_Camelcaseviewerplugin_HEADERS HEADER_NAMES ViewerPluginManager ViewerPlugin ViewerPluginInterface ViewerPluginToolManager REQUIRED_HEADERS MessageViewer_viewerplugin_HEADERS PREFIX MessageViewer RELATIVE viewerplugins ) ecm_generate_headers(MessageViewer_Camelcaserenderer_HEADERS HEADER_NAMES HtmlBlock MessagePartRendererBase MessagePartRendererManager MessagePartRenderPlugin REQUIRED_HEADERS MessageViewer_renderer_HEADERS PREFIX MessageViewer RELATIVE messagepartthemes/default ) ecm_generate_pri_file(BASE_NAME MessageViewer LIB_NAME KF5MessageViewer DEPS "PimCommon MessageCore AkonadiCore AkonadiMime Contacts Libkleo MimeTreeParser" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/MessageViewer ) -if (DKIM_CHECKER_BUILD) +if (${Qca-qt5_FOUND}) install(FILES ${MessageViewer_Cameldkimverify_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/MessageViewer COMPONENT Devel ) install(FILES ${MessageViewer_dkimverify_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/messageviewer COMPONENT Devel ) endif() install(FILES ${MessageViewer_CamelcaseConfigurePlugins_HEADERS} ${MessageViewer_Camelcasewebengine_HEADERS} ${MessageViewer_Camelcaseheader_HEADERS} ${MessageViewer_Camelcaseviewerplugin_HEADERS} ${MessageViewer_Camelcasesettings_HEADERS} ${MessageViewer_Camelcaseutils_HEADERS} ${MessageViewer_Camelcaseinterfaces_HEADERS} ${MessageViewer_Camelcasehtmlwriter_HEADERS} ${MessageViewer_Camelcaseviewer_HEADERS} ${MessageViewer_Camelcasewidgets_HEADERS} ${MessageViewer_Camelcaseantispam_HEADERS} ${MessageViewer_Camelfindbar_HEADERS} ${MessageViewer_Camelcasescam_HEADERS} ${MessageViewer_Camelcaserenderer_HEADERS} ${MessageViewer_Camelblockmailtrackingurlinterceptor_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/MessageViewer COMPONENT Devel ) install(FILES ${MessageViewer_ConfigurePlugins_HEADERS} ${MessageViewer_webengine_HEADERS} ${MessageViewer_scam_HEADERS} ${MessageViewer_viewerplugin_HEADERS} ${MessageViewer_settings_HEADERS} ${MessageViewer_header_HEADERS} ${MessageViewer_utils_HEADERS} ${MessageViewer_interfaces_HEADERS} ${MessageViewer_htmlwriter_HEADERS} ${MessageViewer_HEADERS} ${MessageViewer_viewer_HEADERS} ${MessageViewer_widgets_HEADERS} ${MessageViewer_antispam_HEADERS} ${MessageViewer_findbar_HEADERS} ${MessageViewer_renderer_HEADERS} ${MessageViewer_blockmailtrackingurlinterceptor_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/messageviewer_export.h ${CMAKE_CURRENT_BINARY_DIR}/globalsettings_messageviewer.h ${CMAKE_CURRENT_BINARY_DIR}/messageviewer_debug.h ${CMAKE_CURRENT_BINARY_DIR}/config-messageviewer.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/messageviewer COMPONENT Devel ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) install(FILES header/data/messageviewer_header_themes.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR} ) install(FILES notify/messageviewer.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) install(FILES scamdetection/data/longurlServices.json DESTINATION ${KDE_INSTALL_DATADIR}/messageviewer ) diff --git a/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp b/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp index 64881b78..e0d1aedd 100644 --- a/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp +++ b/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp @@ -1,617 +1,643 @@ /* Copyright (C) 2018-2019 Laurent Montel 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 "dkimchecksignaturejob.h" #include "dkimdownloadkeyjob.h" #include "dkimmanagerkey.h" #include "dkiminfo.h" #include "dkimutil.h" #include "dkimkeyrecord.h" #include "messageviewer_dkimcheckerdebug.h" +#include "dkimheaderparser.h" #include #include #include //see https://tools.ietf.org/html/rfc6376 using namespace MessageViewer; DKIMCheckSignatureJob::DKIMCheckSignatureJob(QObject *parent) : QObject(parent) { } DKIMCheckSignatureJob::~DKIMCheckSignatureJob() { } MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult DKIMCheckSignatureJob::createCheckResult() { MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult result; result.error = mError; result.warning = mWarning; result.status = mStatus; result.item = mMessageItem; return result; } QString DKIMCheckSignatureJob::bodyCanonizationResult() const { return mBodyCanonizationResult; } QString DKIMCheckSignatureJob::headerCanonizationResult() const { return mHeaderCanonizationResult; } void DKIMCheckSignatureJob::start() { if (!mMessage) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (mMessageItem.isValid() && !mMessage) { if (mMessageItem.hasPayload()) { mMessage = mMessageItem.payload(); } } else if (mMessage) { //Nothing } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Item has not a message"; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (auto hrd = mMessage->headerByType("DKIM-Signature")) { mDkimValue = hrd->asUnicodeString(); } if (mDkimValue.isEmpty()) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::EmailNotSigned; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (!mDkimInfo.parseDKIM(mDkimValue)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to parse header" << mDkimValue; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } const MessageViewer::DKIMCheckSignatureJob::DKIMStatus status = checkSignature(mDkimInfo); if (status != MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Valid) { mStatus = status; Q_EMIT result(createCheckResult()); deleteLater(); return; } //ComputeBodyHash now. switch (mDkimInfo.bodyCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidBodyCanonicalization; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mBodyCanonizationResult = bodyCanonizationSimple(); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mBodyCanonizationResult = bodyCanonizationRelaxed(); break; } //qDebug() << " bodyCanonizationResult "<< mBodyCanonizationResult << " algorithm " << mDkimInfo.hashingAlgorithm() << mDkimInfo.bodyHash(); if (mDkimInfo.bodyLengthCount() != -1) { //Verify it. if (mDkimInfo.bodyLengthCount() < mBodyCanonizationResult.length()) { // length tag exceeds body size mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::SignatureTooLarge; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } else if (mDkimInfo.bodyLengthCount() > mBodyCanonizationResult.length()) { mWarning = MessageViewer::DKIMCheckSignatureJob::DKIMWarning::SignatureTooSmall; } // truncated body to the length specified in the "l=" tag mBodyCanonizationResult = mBodyCanonizationResult.left(mDkimInfo.bodyLengthCount()); } QByteArray resultHash; if (mDkimInfo.hashingAlgorithm() == QLatin1String("sha1")) { resultHash = MessageViewer::DKIMUtil::generateHash(mBodyCanonizationResult.toLatin1(), QCryptographicHash::Sha1); } else if (mDkimInfo.hashingAlgorithm() == QLatin1String("sha256")) { resultHash = MessageViewer::DKIMUtil::generateHash(mBodyCanonizationResult.toLatin1(), QCryptographicHash::Sha256); } else { mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InsupportedHashAlgorithm; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } // compare body hash qDebug() << "resultHash " << resultHash << "mDkimInfo.bodyHash()" << mDkimInfo.bodyHash(); if (resultHash != mDkimInfo.bodyHash().toLatin1()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Corrupted body hash"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::CorruptedBodyHash; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } //Compute Hash Header switch (mDkimInfo.headerCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidHeaderCanonicalization; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mHeaderCanonizationResult = headerCanonizationSimple(); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mHeaderCanonizationResult = headerCanonizationRelaxed(); break; } // In hash step 2, the Signer/Verifier MUST pass the following to the // hash algorithm in the indicated order. // 1. The header fields specified by the "h=" tag, in the order // specified in that tag, and canonicalized using the header // canonicalization algorithm specified in the "c=" tag. Each // header field MUST be terminated with a single CRLF. // 2. The DKIM-Signature header field that exists (verifying) or will // be inserted (signing) in the message, with the value of the "b=" // tag (including all surrounding whitespace) deleted (i.e., treated // as the empty string), canonicalized using the header // canonicalization algorithm specified in the "c=" tag, and without // a trailing CRLF. // add DKIM-Signature header to the hash input // with the value of the "b=" tag (including all surrounding whitespace) deleted qDebug() << " headerCanonizationResult" << mHeaderCanonizationResult; //Add dkim-signature as lowercase QString dkimValue = mDkimValue; dkimValue = dkimValue.left(dkimValue.indexOf(QLatin1String("b=")) + 2); switch (mDkimInfo.headerCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mHeaderCanonizationResult += QLatin1String("\r\n") + MessageViewer::DKIMUtil::headerCanonizationSimple(QLatin1String("dkim-signature"), dkimValue); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mHeaderCanonizationResult += QLatin1String("\r\n") + MessageViewer::DKIMUtil::headerCanonizationRelaxed(QLatin1String("dkim-signature"), dkimValue); break; } qDebug() << " headerCanonizationResult after " << mHeaderCanonizationResult; if (mSaveKey) { const QString keyValue = MessageViewer::DKIMManagerKey::self()->keyValue(mDkimInfo.selector(), mDkimInfo.domain()); if (keyValue.isEmpty()) { downloadKey(mDkimInfo); } else { parseDKIMKeyRecord(keyValue, mDkimInfo.domain(), mDkimInfo.selector(), false); } } else { downloadKey(mDkimInfo); } } QString DKIMCheckSignatureJob::bodyCanonizationSimple() const { /* * canonicalize the body using the simple algorithm * specified in Section 3.4.3 of RFC 6376 */ // The "simple" body canonicalization algorithm ignores all empty lines // at the end of the message body. An empty line is a line of zero // length after removal of the line terminator. If there is no body or // no trailing CRLF on the message body, a CRLF is added. It makes no // other changes to the message body. In more formal terms, the // "simple" body canonicalization algorithm converts "*CRLF" at the end // of the body to a single "CRLF". // Note that a completely empty or missing body is canonicalized as a // single "CRLF"; that is, the canonicalized length will be 2 octets. return MessageViewer::DKIMUtil::bodyCanonizationSimple(QString::fromLatin1(mMessage->body())); } QString DKIMCheckSignatureJob::bodyCanonizationRelaxed() const { /* * canonicalize the body using the relaxed algorithm * specified in Section 3.4.4 of RFC 6376 */ /* a. Reduce whitespace: * Ignore all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line. * Reduce all sequences of WSP within a line to a single SP character. b. Ignore all empty lines at the end of the message body. "Empty line" is defined in Section 3.4.3. If the body is non-empty but does not end with a CRLF, a CRLF is added. (For email, this is only possible when using extensions to SMTP or non-SMTP transport mechanisms.) */ return MessageViewer::DKIMUtil::bodyCanonizationRelaxed(QString::fromUtf8(mMessage->body())); } QString DKIMCheckSignatureJob::headerCanonizationSimple() const { QString headers; + DKIMHeaderParser parser; + parser.setHead(mMessage->head()); + parser.parse(); + for (const QString &header : mDkimInfo.listSignedHeader()) { - if (auto hrd = mMessage->headerByType(header.toLatin1().constData())) { - headers += MessageViewer::DKIMUtil::headerCanonizationSimple(header, hrd->asUnicodeString()); + const QString str = parser.headerType(header.toLower()); + if (!str.isEmpty()) { + if (!headers.isEmpty()) { + headers += QLatin1String("\r\n"); + } + headers += MessageViewer::DKIMUtil::headerCanonizationSimple(header, str); } } return headers; } QString DKIMCheckSignatureJob::headerCanonizationRelaxed() const { // The "relaxed" header canonicalization algorithm MUST apply the // following steps in order: // o Convert all header field names (not the header field values) to // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". // o Unfold all header field continuation lines as described in // [RFC5322]; in particular, lines with terminators embedded in // continued header field values (that is, CRLF sequences followed by // WSP) MUST be interpreted without the CRLF. Implementations MUST // NOT remove the CRLF at the end of the header field value. // o Convert all sequences of one or more WSP characters to a single SP // character. WSP characters here include those before and after a // line folding boundary. // o Delete all WSP characters at the end of each unfolded header field // value. // o Delete any WSP characters remaining before and after the colon // separating the header field name from the header field value. The // colon separator MUST be retained. QString headers; - QStringList headerAlreadyAdded; //Add support for multi headers + DKIMHeaderParser parser; + parser.setHead(mMessage->head()); + parser.parse(); for (const QString &header : mDkimInfo.listSignedHeader()) { +#if 1 + const QString str = parser.headerType(header.toLower()); +// qDebug() << " str " << str << " header search " << header; +// auto hrd = mMessage->headerByType(header.toLatin1().constData()); +// if (hrd) { +// qDebug() << " hrd " << hrd->asUnicodeString(); +// } + if (!str.isEmpty()) { + if (!headers.isEmpty()) { + headers += QLatin1String("\r\n"); + } + headers += MessageViewer::DKIMUtil::headerCanonizationRelaxed(header, str); + } +#else qDebug() << " header" << header; if (headerAlreadyAdded.contains(header)) { continue; } if (auto hrd = mMessage->headerByType(header.toLatin1().constData())) { QString str; const QByteArray Allheaders = mMessage->head(); - qDebug() << " all headers " << Allheaders; + //qDebug() << " all headers " << Allheaders; if (header == QLatin1String("date")) { int index = Allheaders.indexOf("Date:"); if (index == -1) { index = Allheaders.indexOf("date:"); if (index == -1) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to find \'date:\'"; continue; } } index += 5; const int end = Allheaders.indexOf("\n", index); if (end != -1) { str = QString::fromLatin1(Allheaders.mid(index, (end - index))); if (str.startsWith(QLatin1Char(' '))) { str = str.right(str.length() - 1); } } } else if (header == QLatin1String("from:")) { int index = Allheaders.indexOf("from:"); if (index == -1) { index = Allheaders.indexOf("From:"); if (index == -1) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to find \'from:\'"; continue; } } index += 5; const int end = Allheaders.indexOf("\n", index); if (end != -1) { str = QString::fromLatin1(Allheaders.mid(index, (end - index))); if (str.startsWith(QLatin1Char(' '))) { str = str.right(str.length() - 1); } } qDebug() <<" FROM !!!!!!!!!!!!!!!!!!!" << str; } else { str = hrd->asUnicodeString(); } headerAlreadyAdded << header; if (!headers.isEmpty()) { headers += QLatin1String("\r\n"); } headers += MessageViewer::DKIMUtil::headerCanonizationRelaxed(header, str); } +#endif } return headers; } void DKIMCheckSignatureJob::downloadKey(const DKIMInfo &info) { DKIMDownloadKeyJob *job = new DKIMDownloadKeyJob(this); job->setDomainName(info.domain()); job->setSelectorName(info.selector()); connect(job, &DKIMDownloadKeyJob::error, this, [this](const QString &errorString) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to start downloadkey: error returned: " << errorString; deleteLater(); }); connect(job, &DKIMDownloadKeyJob::success, this, &DKIMCheckSignatureJob::slotDownloadKeyDone); if (!job->start()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to start downloadkey"; deleteLater(); } } void DKIMCheckSignatureJob::slotDownloadKeyDone(const QList &lst, const QString &domain, const QString &selector) { QByteArray ba; if (lst.count() != 1) { for (const QByteArray &b : lst) { ba += b; } qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Key result has more that 1 element" << lst; } else { ba = lst.at(0); } parseDKIMKeyRecord(QString::fromLocal8Bit(ba), domain, selector, true); } void DKIMCheckSignatureJob::parseDKIMKeyRecord(const QString &str, const QString &domain, const QString &selector, bool storeKeyValue) { //qDebug() << "void DKIMCheckSignatureJob::parseDKIMKeyRecord(const QString &str, const QString &domain, const QString &selector, bool storeKeyValue) " << str; if (!mDkimKeyRecord.parseKey(str)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to parse key record " << str; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (mDkimKeyRecord.keyType() != QLatin1String("rsa")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "mDkimKeyRecord key type is unknown " << mDkimKeyRecord.keyType() << " str " << str; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } // if s flag is set in DKIM key record // AUID must be from the same domain as SDID (and not a subdomain) if (mDkimKeyRecord.flags().contains(QLatin1String("s"))) { // s Any DKIM-Signature header fields using the "i=" tag MUST have // the same domain value on the right-hand side of the "@" in the // "i=" tag and the value of the "d=" tag. That is, the "i=" // domain MUST NOT be a subdomain of "d=". Use of this flag is // RECOMMENDED unless subdomaining is required. if (mDkimInfo.iDomain() != mDkimInfo.domain()) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::DomainI; Q_EMIT result(createCheckResult()); deleteLater(); return; } } // check that the testing flag is not set if (mDkimKeyRecord.flags().contains(QLatin1String("y"))) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Testing mode!"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::TestKeyMode; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (mDkimKeyRecord.publicKey().isEmpty()) { // empty value means that this public key has been revoked qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "mDkimKeyRecord public key is empty. It was revoked "; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::PublicKeyWasRevoked; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (storeKeyValue) { Q_EMIT storeKey(str, domain, selector); } verifyRSASignature(); } void DKIMCheckSignatureJob::verifyRSASignature() { QCA::ConvertResult conversionResult; //qDebug() << "mDkimKeyRecord.publicKey() " < currentDate) { mWarning = DKIMCheckSignatureJob::DKIMWarning::SignatureCreatedInFuture; } if (info.signature().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Signature doesn't exist"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::MissingSignature; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (!info.listSignedHeader().contains(QLatin1String("from"), Qt::CaseInsensitive)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "From is not include in headers list"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::MissingFrom; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.domain().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Domain is not defined."; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::DomainNotExist; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.query() != QLatin1String("dns/txt")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Query is incorrect: " << info.query(); mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidQueryMethod; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.hashingAlgorithm().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "body header algorithm is empty"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidBodyHashAlgorithm; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.signingAlgorithm().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "signature algorithm is empty"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidSignAlgorithm; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.hashingAlgorithm() == QLatin1String("sha1")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "hash algorithm is not secure sha1"; mWarning = MessageViewer::DKIMCheckSignatureJob::DKIMWarning::HashAlgorithmUnsafe; } //Add more test //TODO check if info is valid return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Valid; } DKIMCheckSignatureJob::DKIMError DKIMCheckSignatureJob::error() const { return mError; } DKIMCheckSignatureJob::DKIMStatus DKIMCheckSignatureJob::status() const { return mStatus; } void DKIMCheckSignatureJob::setStatus(const DKIMCheckSignatureJob::DKIMStatus &status) { mStatus = status; } QString DKIMCheckSignatureJob::dkimValue() const { return mDkimValue; } diff --git a/messageviewer/src/dkim-verify/dkimheaderparser.cpp b/messageviewer/src/dkim-verify/dkimheaderparser.cpp new file mode 100644 index 00000000..03e3cbcd --- /dev/null +++ b/messageviewer/src/dkim-verify/dkimheaderparser.cpp @@ -0,0 +1,220 @@ +/* + Copyright (C) 2019 Laurent Montel + + 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 "dkimheaderparser.h" + +#include +#include +#include + +using namespace MessageViewer; + +DKIMHeaderParser::DKIMHeaderParser() +{ + +} + +DKIMHeaderParser::~DKIMHeaderParser() +{ + qDeleteAll(mListHeaders); +} + +void DKIMHeaderParser::parse() +{ + if (mHead.isEmpty()) { + return; + } + int cursor = 0; + while (cursor < mHead.size()) { + const int headerStart = cursor; + int endOfFieldBody; + if (auto header = extractHeader(mHead, headerStart, endOfFieldBody)) { + mListHeaders << header; + cursor = endOfFieldBody + 1; + } else { + break; + } + } + +} + +MessageViewer::DKIMHeaderParser::Header *DKIMHeaderParser::extractHeader(const QByteArray &head, const int headerStart, int &endOfFieldBody) +{ + + int startOfFieldBody = head.indexOf(':', headerStart); + if (startOfFieldBody < 0) { + return nullptr; + } + + MessageViewer::DKIMHeaderParser::Header *header = new MessageViewer::DKIMHeaderParser::Header; + const char *rawType = head.constData() + headerStart; + const size_t rawTypeLen = startOfFieldBody - headerStart; + + startOfFieldBody++; //skip the ':' + if (startOfFieldBody < head.size() - 1 && head[startOfFieldBody] == ' ') { // skip the space after the ':', if there's any + startOfFieldBody++; + } + + bool folded = false; + endOfFieldBody = findHeaderLineEnd(head, startOfFieldBody, &folded); + + //Store it as lowercase + header->headerName = QString::fromLatin1(QByteArray::fromRawData(rawType, rawTypeLen)).toLower(); + if (folded) { + const auto unfoldedBody = unfoldHeader(head.constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody); + const QString str = KCodecs::decodeRFC2047String(unfoldedBody, &header->codec, QByteArrayLiteral("ISO-8859-1")); + header->HeaderValue = str; + } else { + QByteArray ba = QByteArray::fromRawData(head.constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody); + const QString str = KCodecs::decodeRFC2047String(ba, &header->codec, QByteArrayLiteral("ISO-8859-1")); + header->HeaderValue = str; + } + return header; +} + +QByteArray DKIMHeaderParser::unfoldHeader(const char *header, size_t headerSize) +{ + QByteArray result; + if (headerSize == 0) { + return result; + } + + // unfolding skips characters so result will be at worst headerSize long + result.reserve(headerSize); + + const char *end = header + headerSize; + const char *pos = header, *foldBegin = nullptr, *foldMid = nullptr, *foldEnd = nullptr; + while ((foldMid = strchr(pos, '\n')) && foldMid < end) { + foldBegin = foldEnd = foldMid; + // find the first space before the line-break + while (foldBegin) { + if (!QChar::isSpace(*(foldBegin - 1))) { + break; + } + --foldBegin; + } + // find the first non-space after the line-break + while (foldEnd <= end - 1) { + if (QChar::isSpace(*foldEnd)) { + ++foldEnd; + } else if (foldEnd && *(foldEnd - 1) == '\n' && + *foldEnd == '=' && foldEnd + 2 < (header + headerSize - 1) && + ((*(foldEnd + 1) == '0' && + *(foldEnd + 2) == '9') || + (*(foldEnd + 1) == '2' && + *(foldEnd + 2) == '0'))) { + // bug #86302: malformed header continuation starting with =09/=20 + foldEnd += 3; + } else { + break; + } + } + + result.append(pos, foldBegin - pos); + if (foldEnd < end - 1) { + result += ' '; + } + pos = foldEnd; + } + if (end > pos) { + result.append(pos, end - pos); + } + return result; +} + +QByteArray DKIMHeaderParser::head() const +{ + return mHead; +} + +void DKIMHeaderParser::setHead(const QByteArray &head) +{ + mHead = head; +} + +int DKIMHeaderParser::findHeaderLineEnd(const QByteArray &src, int &dataBegin, bool *folded) +{ + int end = dataBegin; + int len = src.length() - 1; + + if (folded) { + *folded = false; + } + + if (dataBegin < 0) { + // Not found + return -1; + } + + if (dataBegin > len) { + // No data available + return len + 1; + } + + // If the first line contains nothing, but the next line starts with a space + // or a tab, that means a stupid mail client has made the first header field line + // entirely empty, and has folded the rest to the next line(s). + if (src.at(end) == '\n' && end + 1 < len && + (src[end + 1] == ' ' || src[end + 1] == '\t')) { + + // Skip \n and first whitespace + dataBegin += 2; + end += 2; + } + + if (src.at(end) != '\n') { // check if the header is not empty + while (true) { + end = src.indexOf('\n', end + 1); + if (end == -1 || end == len) { + // end of string + break; + } else if (src[end + 1] == ' ' || src[end + 1] == '\t' || + (src[end + 1] == '=' && end + 3 <= len && + ((src[end + 2] == '0' && src[end + 3] == '9') || + (src[end + 2] == '2' && src[end + 3] == '0')))) { + // next line is header continuation or starts with =09/=20 (bug #86302) + if (folded) { + *folded = true; + } + } else { + // end of header (no header continuation) + break; + } + } + } + + if (end < 0) { + end = len + 1; //take the rest of the string + } + return end; +} + +QString DKIMHeaderParser::headerType(const QString &str) +{ + for (int i = mListHeaders.count() -1; i >=0; --i) { + qDebug() << " mListHeaders.at(i)->headerName" <headerName; + if (mListHeaders.at(i)->headerName == str) { + DKIMHeaderParser::Header *header = mListHeaders.takeAt(i); + const QString headerValue = header->HeaderValue; + delete header; + return headerValue; + } + } + return {}; +} diff --git a/messageviewer/src/dkim-verify/dkimheaderparser.h b/messageviewer/src/dkim-verify/dkimheaderparser.h new file mode 100644 index 00000000..98b9fe79 --- /dev/null +++ b/messageviewer/src/dkim-verify/dkimheaderparser.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2019 Laurent Montel + + 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 DKIMHEADERPARSER_H +#define DKIMHEADERPARSER_H + +#include "messageviewer_export.h" + +#include +#include +namespace MessageViewer { +class MESSAGEVIEWER_EXPORT DKIMHeaderParser +{ +public: + DKIMHeaderParser(); + ~DKIMHeaderParser(); + void parse(); + void setHead(const QByteArray &head); + + Q_REQUIRED_RESULT QByteArray head() const; + + Q_REQUIRED_RESULT QString headerType(const QString &str); +private: + struct Header { + QString headerName; + QString HeaderValue; + QByteArray codec; + }; + int findHeaderLineEnd(const QByteArray &src, int &dataBegin, bool *folded); + MessageViewer::DKIMHeaderParser::Header *extractHeader(const QByteArray &head, const int headerStart, int &endOfFieldBody); + QByteArray unfoldHeader(const char *header, size_t headerSize); + QByteArray mHead; + QList mListHeaders; +}; +} +#endif // DKIMHEADERPARSER_H diff --git a/messageviewer/src/dkim-verify/dkiminfo.cpp b/messageviewer/src/dkim-verify/dkiminfo.cpp index 811a8c86..c1e2091b 100644 --- a/messageviewer/src/dkim-verify/dkiminfo.cpp +++ b/messageviewer/src/dkim-verify/dkiminfo.cpp @@ -1,377 +1,376 @@ /* Copyright (C) 2018-2019 Laurent Montel 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 "dkiminfo.h" #include "dkimutil.h" #include "messageviewer_dkimcheckerdebug.h" #include #include #include "messageviewer_debug.h" using namespace MessageViewer; DKIMInfo::DKIMInfo() { } bool DKIMInfo::parseDKIM(const QString &header) { if (header.isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Error: trying to parse empty header"; return false; } const QStringList items = header.split(QLatin1String("; ")); bool foundCanonizations = false; for (int i = 0; i < items.count(); ++i) { const QString elem = items.at(i).trimmed(); if (elem.startsWith(QLatin1String("v="))) { mVersion = elem.right(elem.length() - 2).toInt(); if (mVersion != 1) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Version is not correct " << mVersion; } } else if (elem.startsWith(QLatin1String("a="))) { //Parse it as "algorithm.signature-algorithm.hash parseAlgorithm(elem.right(elem.length() - 2)); } else if (elem.startsWith(QLatin1String("t="))) { mSignatureTimeStamp = elem.right(elem.length() - 2).toLong(); } else if (elem.startsWith(QLatin1String("c="))) { //Parse header/body canonicalization (example c=relaxed/simple) only relaxed and simple. parseCanonicalization(elem.right(elem.length() - 2)); foundCanonizations = true; } else if (elem.startsWith(QLatin1String("bh="))) { mBodyHash = elem.right(elem.length() - 3).replace(QStringLiteral(" "), QString()); } else if (elem.startsWith(QLatin1String("l="))) { mBodyLengthCount = elem.right(elem.length() - 2).toInt(); } else if (elem.startsWith(QLatin1String("i="))) { mAgentOrUserIdentifier = elem.right(elem.length() - 2); } else if (elem.startsWith(QLatin1String("q="))) { mQuery = elem.right(elem.length() - 2); if (mQuery != QLatin1String("dns/txt")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Query is not correct and not supported " << mQuery; } } else if (elem.startsWith(QLatin1String("d="))) { mDomain = elem.right(elem.length() - 2); } else if (elem.startsWith(QLatin1String("s="))) { mSelector = elem.right(elem.length() - 2); } else if (elem.startsWith(QLatin1String("b="))) { mSignature = elem.right(elem.length() - 2); } else if (elem.startsWith(QLatin1String("h="))) { const QString str = MessageViewer::DKIMUtil::cleanString(elem.right(elem.length() - 2)); - qDebug() << " str************************ " << str; mListSignedHeader = str.split(QLatin1Char(':')); } else if (elem.startsWith(QLatin1String("x="))) { mExpireTime = elem.right(elem.length() - 2).toLong(); } else if (elem.startsWith(QLatin1String("z="))) { mCopiedHeaderField = elem.right(elem.length() - 2).split(QLatin1Char(':')); } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Unknown element type" << elem; } } if (!foundCanonizations) { //Default mHeaderCanonization = Simple; mBodyCanonization = Simple; } if (mVersion == -1) { mVersion = 1; } if (mQuery.isEmpty()) { mQuery = QLatin1String("dns/txt"); } if (mAgentOrUserIdentifier.isEmpty()) { mAgentOrUserIdentifier = QLatin1Char('@') + mDomain; mIDomain = mDomain; } else { const QStringList lst = mAgentOrUserIdentifier.split(QLatin1Char('@')); if (lst.count() == 2) { mAgentOrUserIdentifier = lst.at(0); mIDomain = lst.at(1); } } return true; } void DKIMInfo::parseAlgorithm(const QString &str) { // currently only "rsa-sha1" or "rsa-sha256" //FIXME const QStringList lst = str.split(QLatin1Char('-')); if (lst.count() != 2) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "algorithm is invalid " << str; //Error } else { mSigningAlgorithm = lst.at(0); mHashingAlgorithm = lst.at(1); } } QString DKIMInfo::iDomain() const { return mIDomain; } void DKIMInfo::setIDomain(const QString &iDomain) { mIDomain = iDomain; } void DKIMInfo::parseCanonicalization(const QString &str) { if (!str.isEmpty()) { const QStringList canonicalizations = str.split(QLatin1Char('/')); //qDebug() << " canonicalizations "<< canonicalizations; if (canonicalizations.count() >= 1) { if (canonicalizations.at(0) == QLatin1String("relaxed")) { mHeaderCanonization = DKIMInfo::Relaxed; } else if (canonicalizations.at(0) == QLatin1String("simple")) { mHeaderCanonization = DKIMInfo::Simple; } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "canonicalizations for header unknown " << canonicalizations.at(0); mHeaderCanonization = DKIMInfo::Unknown; return; } if (canonicalizations.count() == 1) { mBodyCanonization = DKIMInfo::Simple; } else if (canonicalizations.count() == 2) { if (canonicalizations.at(1) == QLatin1String("relaxed")) { mBodyCanonization = DKIMInfo::Relaxed; } else if (canonicalizations.at(1) == QLatin1String("simple")) { mBodyCanonization = DKIMInfo::Simple; } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "canonicalizations for body unknown " << canonicalizations.at(1); mBodyCanonization = DKIMInfo::Unknown; return; } } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Problem during parsing canonicalizations " << str; mHeaderCanonization = DKIMInfo::Unknown; mBodyCanonization = DKIMInfo::Unknown; } } } } QStringList DKIMInfo::copiedHeaderField() const { return mCopiedHeaderField; } void DKIMInfo::setCopiedHeaderField(const QStringList &copiedHeaderField) { mCopiedHeaderField = copiedHeaderField; } DKIMInfo::CanonicalizationType DKIMInfo::bodyCanonization() const { return mBodyCanonization; } void DKIMInfo::setBodyCanonization(const CanonicalizationType &bodyCanonization) { mBodyCanonization = bodyCanonization; } bool DKIMInfo::operator==(const DKIMInfo &other) const { return mVersion == other.version() && mHashingAlgorithm == other.hashingAlgorithm() && mSigningAlgorithm == other.signingAlgorithm() && mDomain == other.domain() && mSelector == other.selector() && mBodyHash == other.bodyHash() && mSignatureTimeStamp == other.signatureTimeStamp() && mExpireTime == other.expireTime() && mQuery == other.query() && mSignature == other.signature() && mAgentOrUserIdentifier == other.agentOrUserIdentifier() && mBodyLengthCount == other.bodyLengthCount() && mListSignedHeader == other.listSignedHeader() && mHeaderCanonization == other.headerCanonization() && mBodyCanonization == other.bodyCanonization() && mIDomain == other.iDomain(); } DKIMInfo::CanonicalizationType DKIMInfo::headerCanonization() const { return mHeaderCanonization; } void DKIMInfo::setHeaderCanonization(const CanonicalizationType &headerCanonization) { mHeaderCanonization = headerCanonization; } int DKIMInfo::version() const { return mVersion; } void DKIMInfo::setVersion(int version) { mVersion = version; } QString DKIMInfo::hashingAlgorithm() const { return mHashingAlgorithm; } void DKIMInfo::setHashingAlgorithm(const QString &hashingAlgorithm) { mHashingAlgorithm = hashingAlgorithm; } QString DKIMInfo::domain() const { return mDomain; } void DKIMInfo::setDomain(const QString &domain) { mDomain = domain; } QString DKIMInfo::selector() const { return mSelector; } void DKIMInfo::setSelector(const QString &selector) { mSelector = selector; } QString DKIMInfo::bodyHash() const { return mBodyHash; } void DKIMInfo::setBodyHash(const QString &bodyHash) { mBodyHash = bodyHash; } bool DKIMInfo::isValid() const { if (mBodyCanonization == DKIMInfo::Unknown || mHeaderCanonization == DKIMInfo::Unknown) { return false; } return !mSelector.isEmpty() && !mDomain.isEmpty() && !mBodyHash.isEmpty() && !mHashingAlgorithm.isEmpty(); } QStringList DKIMInfo::listSignedHeader() const { return mListSignedHeader; } void DKIMInfo::setListSignedHeader(const QStringList &listSignedHeader) { mListSignedHeader = listSignedHeader; } QString DKIMInfo::signingAlgorithm() const { return mSigningAlgorithm; } void DKIMInfo::setSigningAlgorithm(const QString &signingAlgorithm) { mSigningAlgorithm = signingAlgorithm; } qint64 DKIMInfo::signatureTimeStamp() const { return mSignatureTimeStamp; } void DKIMInfo::setSignatureTimeStamp(qint64 signatureTimeStamp) { mSignatureTimeStamp = signatureTimeStamp; } QString DKIMInfo::query() const { return mQuery; } void DKIMInfo::setQuery(const QString &query) { mQuery = query; } qint64 DKIMInfo::expireTime() const { return mExpireTime; } void DKIMInfo::setExpireTime(qint64 expireTime) { mExpireTime = expireTime; } QString DKIMInfo::signature() const { return mSignature; } void DKIMInfo::setSignature(const QString &signature) { mSignature = signature; } QString DKIMInfo::agentOrUserIdentifier() const { return mAgentOrUserIdentifier; } void DKIMInfo::setAgentOrUserIdentifier(const QString &userAgent) { mAgentOrUserIdentifier = userAgent; } int DKIMInfo::bodyLengthCount() const { return mBodyLengthCount; } void DKIMInfo::setBodyLengthCount(int bodyLengthCount) { mBodyLengthCount = bodyLengthCount; } QDebug operator <<(QDebug d, const DKIMInfo &t) { d << "mVersion " << t.version(); d << "mHashingAlgorithm " << t.hashingAlgorithm(); d << "mSigningAlgorithm " << t.signingAlgorithm(); d << "mDomain " << t.domain(); d << "mSelector " << t.selector(); d << "mBodyHash " << t.bodyHash(); d << "mSignatureTimeStamp " << t.signatureTimeStamp(); d << "mExpireTime " << t.expireTime(); d << "mQuery " << t.query(); d << "mSignature " << t.signature(); d << "mAgentOrUserIdentifier " << t.agentOrUserIdentifier(); d << "mBodyLengthCount " << t.bodyLengthCount(); d << "mListSignedHeader " << t.listSignedHeader(); d << "mHeaderCanonization " << t.headerCanonization(); d << "mBodyCanonization " << t.bodyCanonization(); d << "mIdomain " << t.iDomain(); return d; } diff --git a/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp b/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp index 67f714bd..4800b475 100644 --- a/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp +++ b/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp @@ -1,165 +1,165 @@ /* Copyright (C) 2019 Laurent Montel 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 "dkimwidgetinfo.h" #include "dkimmanager.h" #include #include #include #include using namespace MessageViewer; DKIMWidgetInfo::DKIMWidgetInfo(QWidget *parent) : QWidget(parent) { QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->setObjectName(QStringLiteral("mainLayout")); mainLayout->setContentsMargins(0, 0, 0, 0); mLabel = new QLabel(this); mLabel->setAutoFillBackground(true); mLabel->setObjectName(QStringLiteral("label")); mainLayout->addWidget(mLabel); connect(DKIMManager::self(), &DKIMManager::result, this, &DKIMWidgetInfo::setResult); connect(DKIMManager::self(), &DKIMManager::clearInfo, this, &DKIMWidgetInfo::clear); } DKIMWidgetInfo::~DKIMWidgetInfo() { } void DKIMWidgetInfo::setResult(const DKIMCheckSignatureJob::CheckSignatureResult &checkResult) { if (mResult != checkResult) { mResult = checkResult; updateInfo(); } } void DKIMWidgetInfo::clear() { mLabel->clear(); mLabel->setToolTip(QString()); QPalette pal = mLabel->palette(); pal.setColor(backgroundRole(), palette().window().color()); mLabel->setPalette(pal); } void DKIMWidgetInfo::updateInfo() { QPalette pal = mLabel->palette(); switch (mResult.status) { case DKIMCheckSignatureJob::DKIMStatus::Unknown: pal.setColor(backgroundRole(), palette().window().color()); mLabel->setPalette(pal); mLabel->setText(i18n("Unknown")); break; case DKIMCheckSignatureJob::DKIMStatus::Valid: mLabel->setText(i18n("KDIM: valid")); pal.setColor(backgroundRole(), KColorScheme(QPalette::Active).background(KColorScheme::PositiveBackground).color()); mLabel->setPalette(pal); break; case DKIMCheckSignatureJob::DKIMStatus::Invalid: pal.setColor(backgroundRole(), KColorScheme(QPalette::Active).background(KColorScheme::NegativeBackground).color()); mLabel->setPalette(pal); mLabel->setText(i18n("KDIM: invalid")); break; case DKIMCheckSignatureJob::DKIMStatus::EmailNotSigned: - mLabel->setText(i18n("KDIM: Unsigned")); + mLabel->setText(i18n("KDIM: Not signed")); pal.setColor(backgroundRole(), palette().window().color()); mLabel->setPalette(pal); break; } updateToolTip(); } void DKIMWidgetInfo::updateToolTip() { QString tooltip; if (mResult.status == DKIMCheckSignatureJob::DKIMStatus::Invalid) { switch (mResult.error) { case DKIMCheckSignatureJob::DKIMError::Any: break; case DKIMCheckSignatureJob::DKIMError::CorruptedBodyHash: tooltip = i18n("Body Hash was corrupted."); break; case DKIMCheckSignatureJob::DKIMError::DomainNotExist: tooltip = i18n("The domain doesn't exist."); break; case DKIMCheckSignatureJob::DKIMError::MissingFrom: tooltip = i18n("Missing header From."); break; case DKIMCheckSignatureJob::DKIMError::MissingSignature: tooltip = i18n("Missing signature."); break; case DKIMCheckSignatureJob::DKIMError::InvalidQueryMethod: tooltip = i18n("Invalid query method."); break; case DKIMCheckSignatureJob::DKIMError::InvalidHeaderCanonicalization: tooltip = i18n("Invalid header canonicalization."); break; case DKIMCheckSignatureJob::DKIMError::InvalidBodyCanonicalization: tooltip = i18n("Invalid body canonicalization."); break; case DKIMCheckSignatureJob::DKIMError::InvalidBodyHashAlgorithm: tooltip = i18n("Unknown Body Hash Algorithm."); break; case DKIMCheckSignatureJob::DKIMError::InvalidSignAlgorithm: tooltip = i18n("Signature algorithm is invalid."); break; case DKIMCheckSignatureJob::DKIMError::PublicKeyWasRevoked: tooltip = i18n("The public key was revoked."); break; case DKIMCheckSignatureJob::DKIMError::SignatureTooLarge: tooltip = i18n("Signature is too large."); break; case DKIMCheckSignatureJob::DKIMError::InsupportedHashAlgorithm: tooltip = i18n("Hash Algorithm is unsupported."); break; case DKIMCheckSignatureJob::DKIMError::PublicKeyTooSmall: break; case DKIMCheckSignatureJob::DKIMError::ImpossibleToVerifySignature: tooltip = i18n("Impossible to verify signature."); break; case DKIMCheckSignatureJob::DKIMError::DomainI: tooltip = i18n("AUID must be in the same domain as SDID (s-flag set in key record)."); break; case DKIMCheckSignatureJob::DKIMError::TestKeyMode: tooltip = i18n("The signing domain is only testing DKIM."); break; } } switch (mResult.warning) { case DKIMCheckSignatureJob::DKIMWarning::Any: break; case DKIMCheckSignatureJob::DKIMWarning::SignatureExpired: tooltip = i18n("Signature expired"); break; case DKIMCheckSignatureJob::DKIMWarning::SignatureCreatedInFuture: tooltip = i18n("Signature created in the future"); break; case DKIMCheckSignatureJob::DKIMWarning::SignatureTooSmall: tooltip = i18n("Signature too small"); break; case DKIMCheckSignatureJob::DKIMWarning::HashAlgorithmUnsafe: tooltip = i18n("Hash Algorithm unsafe (sha1)"); break; } mLabel->setToolTip(tooltip); }