diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b30374a0..4c37aa2c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,168 +1,168 @@ cmake_minimum_required(VERSION 3.0) set(KDEPIM_RUNTIME_VERSION_NUMBER "5.6.40") project(kdepim-runtime VERSION ${KDEPIM_RUNTIME_VERSION_NUMBER}) if (POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() ############### KDEPIM-Runtime version ################ # KDEPIM_RUNTIME_VERSION # Version scheme: "x.y.z build". # # x is the version number. # y is the major release number. # z is the minor release number. # # "x.y.z" follow the kdelibs version kdepim is released with. # # If "z" is 0, it the version is "x.y" # # KDEPIM_RUNTIME_DEV_VERSION # is empty for final versions. For development versions "build" is # something like "pre", "alpha1", "alpha2", "beta1", "beta2", "rc1", "rc2". # # Examples in chronological order: # # 3.0 # 3.0.1 # 3.1 alpha1 # 3.1 beta1 # 3.1 beta2 # 3.1 rc1 # 3.1 # 3.1.1 # 3.2 pre # 3.2 alpha1 if(NOT DEFINED KDEPIM_RUNTIME_DEV_VERSION) set(KDEPIM_RUNTIME_DEV_VERSION "alpha1") endif() set(KDEPIM_RUNTIME_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}${KDEPIM_RUNTIME_DEV_VERSION}") configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) set(KF5_VERSION "5.38.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/ ${ECM_MODULE_PATH}) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMQtDeclareLoggingCategory) include(ECMCoverageOption) set(QT_REQUIRED_VERSION "5.8.0") set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") set(KDEPIMRUNTIME_LIB_SOVERSION "5") set(AKONADI_VERSION "5.6.40") set(KCONTACTS_LIB_VERSION "5.6.40") set(KCALENDARCORE_LIB_VERSION "5.6.40") set(IDENTITYMANAGEMENT_LIB_VERSION "5.6.40") set(KMAILTRANSPORT_LIB_VERSION "5.6.40") set(CALENDARUTILS_LIB_VERSION "5.6.40") set(KDAV_LIB_VERSION "5.6.40") set(KIMAP_LIB_VERSION "5.6.41") set(KMBOX_LIB_VERSION "5.6.40") set(AKONADICALENDAR_LIB_VERSION "5.6.40") set(KONTACTINTERFACE_LIB_VERSION "5.6.40") set(AKONADIKALARM_LIB_VERSION "5.6.40") -set(KMIME_LIB_VERSION "5.6.40") +set(KMIME_LIB_VERSION "5.6.42") set(XMLRPCCLIENT_LIB_VERSION "5.6.40") set(KCONTACTS_LIB_VERSION "5.6.40") set(AKONADIMIME_LIB_VERSION "5.6.40") set(AKONADICONTACT_LIB_VERSION "5.6.40") set(AKONADINOTE_LIB_VERSION "5.6.40") set(PIMCOMMON_LIB_VERSION_LIB "5.6.41") set(KGAPI_LIB_VERSION "5.6.40") set( SHARED_MIME_INFO_MINIMUM_VERSION "1.0" ) find_package( SharedMimeInfo REQUIRED ) find_package(Sasl2) set_package_properties(Sasl2 PROPERTIES TYPE REQUIRED) # QT5 package find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Network Widgets Test XmlPatterns DBus WebEngineWidgets) find_package(Qt5 OPTIONAL_COMPONENTS TextToSpeech) if (NOT Qt5TextToSpeech_FOUND) message(STATUS "Qt5TextToSpeech not found, speech feature will be disabled") else() add_definitions(-DHAVE_TEXTTOSPEECH) endif() # KF5 package find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5NotifyConfig ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) # for KPluralHandlingSpinBox find_package(KF5Notifications ${KF5_VERSION} CONFIG REQUIRED) # pop3 # KdepimLibs package find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KPimKDAV ${KDAV_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiContact ${AKONADICONTACT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AlarmCalendar ${AKONADIKALARM_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mbox ${KMBOX_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiNotes ${AKONADINOTE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiCalendar ${AKONADICALENDAR_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommon ${PIMCOMMON_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KPimGAPI ${KGAPI_LIB_VERSION} CONFIG REQUIRED) option(KDEPIM_RUN_ISOLATED_TESTS "Run the isolated tests." FALSE) add_definitions( -DQT_NO_CAST_FROM_ASCII ) add_definitions( -DQT_NO_CAST_TO_ASCII ) add_definitions( -DQT_NO_URL_CAST_FROM_STRING ) add_definitions( -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT ) #add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0x060000 ) # Extra package find_package(KPimGAPI "5.6.1" CONFIG) find_package(Libkolabxml 1.1 QUIET CONFIG) add_subdirectory(resources) add_subdirectory(agents) add_subdirectory(plugins) add_subdirectory(defaultsetup) add_subdirectory(kioslave) add_subdirectory(migration) add_subdirectory(doc) ## install the MIME type spec file for KDEPIM specific MIME types install(FILES kdepim-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR}) update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR}) install( FILES kdepim-runtime.renamecategories kdepim-runtime.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES ) diff --git a/plugins/akonadi_serializer_mail.cpp b/plugins/akonadi_serializer_mail.cpp index 12595f704..6d50de2c1 100644 --- a/plugins/akonadi_serializer_mail.cpp +++ b/plugins/akonadi_serializer_mail.cpp @@ -1,294 +1,300 @@ /* Copyright (c) 2007 Till Adam 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 "akonadi_serializer_mail.h" #include "akonadi_serializer_mail_debug.h" #include #include #include #include #include #include using namespace Akonadi; using namespace KMime; QString StringPool::sharedValue(const QString &value) { QMutexLocker lock(&m_mutex); QSet::const_iterator it = m_pool.constFind(value); if (it != m_pool.constEnd()) { return *it; } m_pool.insert(value); return value; } template static void parseAddrList(const QVarLengthArray &addrList, T *hdr, int version, StringPool &pool) { hdr->clear(); const int count = addrList.count(); QVarLengthArray addr; for (int i = 0; i < count; ++i) { ImapParser::parseParenthesizedList(addrList[ i ], addr); if (addr.count() != 4) { qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Error parsing envelope address field: " << addrList[ i ]; continue; } KMime::Types::Mailbox addrField; if (version == 0) { addrField.setNameFrom7Bit(addr[0]); } else if (version == 1) { addrField.setName(pool.sharedValue(QString::fromUtf8(addr[0]))); } KMime::Types::AddrSpec addrSpec; addrSpec.localPart = pool.sharedValue(QString::fromUtf8(addr[2])); addrSpec.domain = pool.sharedValue(QString::fromUtf8(addr[3])); addrField.setAddress(addrSpec); hdr->addAddress(addrField); } } template static void parseAddrList(QDataStream &stream, T *hdr, int version, StringPool &pool) { Q_UNUSED(version); hdr->clear(); int count = 0; stream >> count; for (int i = 0; i < count; ++i) { QString str; KMime::Types::Mailbox mbox; KMime::Types::AddrSpec addrSpec; stream >> str; mbox.setName(pool.sharedValue(str)); stream >> str; addrSpec.localPart = pool.sharedValue(str); stream >> str; addrSpec.domain = pool.sharedValue(str); mbox.setAddress(addrSpec); hdr->addAddress(mbox); } } bool SerializerPluginMail::deserialize(Item &item, const QByteArray &label, QIODevice &data, int version) { if (label != MessagePart::Body && label != MessagePart::Envelope && label != MessagePart::Header) { return false; } KMime::Message::Ptr msg; if (!item.hasPayload()) { Message *m = new Message(); msg = KMime::Message::Ptr(m); item.setPayload(msg); } else { msg = item.payload(); } if (label == MessagePart::Body) { QByteArray buffer = data.readAll(); if (buffer.isEmpty()) { return true; } msg->setContent(buffer); msg->parse(); } else if (label == MessagePart::Header) { QByteArray buffer = data.readAll(); if (buffer.isEmpty()) { return true; } if (msg->body().isEmpty() && msg->contents().isEmpty()) { msg->setHead(buffer); msg->parse(); } } else if (label == MessagePart::Envelope) { if (version <= 1) { QByteArray buffer = data.readAll(); if (buffer.isEmpty()) { return true; } QVarLengthArray env; ImapParser::parseParenthesizedList(buffer, env); if (env.count() < 10) { qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Akonadi KMime Deserializer: Got invalid envelope: " << buffer; return false; } Q_ASSERT(env.count() >= 10); // date msg->date()->from7BitString(env[0]); // subject msg->subject()->from7BitString(env[1]); // from QVarLengthArray addrList; ImapParser::parseParenthesizedList(env[2], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->from(), version, m_stringPool); } // sender ImapParser::parseParenthesizedList(env[3], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->sender(), version, m_stringPool); } // reply-to ImapParser::parseParenthesizedList(env[4], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->replyTo(), version, m_stringPool); } // to ImapParser::parseParenthesizedList(env[5], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->to(), version, m_stringPool); } // cc ImapParser::parseParenthesizedList(env[6], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->cc(), version, m_stringPool); } // bcc ImapParser::parseParenthesizedList(env[7], addrList); if (!addrList.isEmpty()) { parseAddrList(addrList, msg->bcc(), version, m_stringPool); } // in-reply-to msg->inReplyTo()->from7BitString(env[8]); // message id msg->messageID()->from7BitString(env[9]); // references if (env.count() > 10) { msg->references()->from7BitString(env[10]); } } else if (version == 2) { QDataStream stream(&data); QDateTime dt; QString str; stream >> dt; msg->date()->setDateTime(dt); stream >> str; msg->subject()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); - stream >> str; - msg->inReplyTo()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); + + QString inReplyTo; + stream >> inReplyTo; + msg->inReplyTo()->fromUnicodeString(inReplyTo, QByteArrayLiteral("UTF-8")); stream >> str; msg->messageID()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); stream >> str; - msg->references()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); + if (str == inReplyTo) { + msg->references()->fromIdent(msg->inReplyTo()); + } else { + msg->references()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); + } parseAddrList(stream, msg->from(), version, m_stringPool); parseAddrList(stream, msg->sender(), version, m_stringPool); parseAddrList(stream, msg->replyTo(), version, m_stringPool); parseAddrList(stream, msg->to(), version, m_stringPool); parseAddrList(stream, msg->cc(), version, m_stringPool); parseAddrList(stream, msg->bcc(), version, m_stringPool); if (stream.status() == QDataStream::ReadCorruptData || stream.status() == QDataStream::ReadPastEnd) { qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Akonadi KMime Deserializer: Got invalid envelope"; return false; } } } return true; } template static void serializeAddrList(QDataStream &stream, T *hdr) { const KMime::Types::Mailbox::List mb = hdr->mailboxes(); stream << mb.size(); for (const KMime::Types::Mailbox &mbox : mb) { stream << mbox.name() << mbox.addrSpec().localPart << mbox.addrSpec().domain; } } void SerializerPluginMail::serialize(const Item &item, const QByteArray &label, QIODevice &data, int &version) { version = 1; KMime::Message::Ptr m = item.payload(); if (label == MessagePart::Body) { data.write(m->encodedContent()); } else if (label == MessagePart::Envelope) { version = 2; QDataStream stream(&data); stream << m->date()->dateTime() << m->subject()->asUnicodeString() << m->inReplyTo()->asUnicodeString() << m->messageID()->asUnicodeString() << m->references()->asUnicodeString(); serializeAddrList(stream, m->from()); serializeAddrList(stream, m->sender()); serializeAddrList(stream, m->replyTo()); serializeAddrList(stream, m->to()); serializeAddrList(stream, m->cc()); serializeAddrList(stream, m->bcc()); } else if (label == MessagePart::Header) { data.write(m->head()); } } QSet SerializerPluginMail::parts(const Item &item) const { QSet set; if (!item.hasPayload()) { return set; } KMime::Message::Ptr msg = item.payload(); if (!msg) { return set; } // FIXME: we really want "has any header" here, but the kmime api doesn't offer that yet if (msg->hasContent() || msg->hasHeader("Message-ID")) { set << MessagePart::Envelope << MessagePart::Header; if (!msg->body().isEmpty() || !msg->contents().isEmpty()) { set << MessagePart::Body; } } return set; } QString SerializerPluginMail::extractGid(const Item &item) const { if (!item.hasPayload()) { return QString(); } const KMime::Message::Ptr msg = item.payload(); KMime::Headers::MessageID *mid = msg->messageID(false); if (mid) { return mid->asUnicodeString(); } else if (KMime::Headers::Base *uid = msg->headerByType("X-Akonotes-UID")) { return uid->asUnicodeString(); } return QString(); } #include "moc_akonadi_serializer_mail.cpp"