Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kmime for openSUSE:Factory checked in at 2025-12-15 11:49:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kmime (Old) and /work/SRC/openSUSE:Factory/.kmime.new.1939 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kmime" Mon Dec 15 11:49:00 2025 rev:124 rq:1322420 version:25.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/kmime/kmime.changes 2025-11-08 16:29:12.454837576 +0100 +++ /work/SRC/openSUSE:Factory/.kmime.new.1939/kmime.changes 2025-12-15 11:50:11.334748579 +0100 @@ -1,0 +2,16 @@ +Sat Dec 6 11:40:48 UTC 2025 - Christophe Marin <[email protected]> + +- Update to 25.12.0 + * New feature release + * For more details please see: + * https://kde.org/announcements/gear/25.12.0/ +- No code change since 25.11.80 + +------------------------------------------------------------------- +Sat Nov 15 19:56:51 UTC 2025 - Christophe Marin <[email protected]> + +- Update to 25.11.80 + * New feature release +- Too many changes to list here. + +------------------------------------------------------------------- Old: ---- kmime-25.08.3.tar.xz kmime-25.08.3.tar.xz.sig New: ---- kmime-25.12.0.tar.xz kmime-25.12.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kmime.spec ++++++ --- /var/tmp/diff_new_pack.4KRj51/_old 2025-12-15 11:50:12.398792859 +0100 +++ /var/tmp/diff_new_pack.4KRj51/_new 2025-12-15 11:50:12.406793192 +0100 @@ -16,12 +16,12 @@ # -%define kf6_version 6.14.0 -%define qt6_version 6.8.0 +%define kf6_version 6.19.0 +%define qt6_version 6.9.0 %bcond_without released Name: kmime -Version: 25.08.3 +Version: 25.12.0 Release: 0 Summary: KDE PIM libraries MIME support License: LGPL-2.1-or-later ++++++ kmime-25.08.3.tar.xz -> kmime-25.12.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/.gitattributes new/kmime-25.12.0/.gitattributes --- old/kmime-25.08.3/.gitattributes 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/.gitattributes 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: none + +*.yenc binary diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/.gitlab-ci.yml new/kmime-25.12.0/.gitlab-ci.yml --- old/kmime-25.08.3/.gitlab-ci.yml 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/.gitlab-ci.yml 2025-12-01 07:01:16.000000000 +0100 @@ -15,3 +15,4 @@ - /gitlab-templates/alpine-qt6.yml - /gitlab-templates/xml-lint.yml - /gitlab-templates/yaml-lint.yml + - /gitlab-templates/oss-fuzz.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/CMakeLists.txt new/kmime-25.12.0/CMakeLists.txt --- old/kmime-25.08.3/CMakeLists.txt 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/CMakeLists.txt 2025-12-01 07:01:16.000000000 +0100 @@ -1,15 +1,16 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(PIM_VERSION "6.5.3") +set(PIM_VERSION "6.6.0") project(KMime VERSION ${PIM_VERSION}) # ECM setup -set(KF_MIN_VERSION "6.14.0") +set(KF_MIN_VERSION "6.18.0") find_package(ECM ${KF_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${KMime_SOURCE_DIR}/cmake ${ECM_MODULE_PATH}) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_SCAN_FOR_MODULES OFF) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) include(KDEInstallDirs) @@ -42,10 +43,12 @@ 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)") +option(BUILD_FUZZERS "Build KMime fuzzers" OFF) + ########### Find packages ########### find_package(KF6Codecs ${KF_MIN_VERSION} CONFIG REQUIRED) add_definitions(-DQT_NO_CONTEXTLESS_CONNECT) -ecm_set_disabled_deprecation_versions(QT 6.10.0 KF 6.16.0) +ecm_set_disabled_deprecation_versions(QT 6.10.0 KF 6.20.0) option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF) @@ -62,6 +65,10 @@ add_subdirectory(tests) endif() +if(BUILD_FUZZERS) + add_subdirectory(autotests/ossfuzz) +endif() + ########### CMake Config Files ########### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim6Mime") if(BUILD_QCH) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/contenttest.cpp new/kmime-25.12.0/autotests/contenttest.cpp --- old/kmime-25.08.3/autotests/contenttest.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/contenttest.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -16,6 +16,7 @@ #include <type_traits> using namespace KMime; +using namespace Qt::Literals; QTEST_MAIN(ContentTest) @@ -267,14 +268,46 @@ { auto c = new Content(); c->setBody("\0"); - QVERIFY(c->decodedContent() == QByteArray()); + QVERIFY(c->decodedBody() == QByteArray()); c->setBody(QByteArray()); - QVERIFY(c->decodedContent() == QByteArray()); + QVERIFY(c->decodedBody() == QByteArray()); c->setBody(" "); - QVERIFY(c->decodedContent() == QByteArray(" ")); + QVERIFY(c->decodedBody() == QByteArray(" ")); delete c; } +void ContentTest::testDecodedText() +{ + { + Content c{}; + c.setEncodedBody("plain text"); + QCOMPARE(c.decodedText(), u"plain text"_s); + } + { + Content c{}; + auto cte = std::make_unique<Headers::ContentTransferEncoding>(); + cte->setEncoding(Headers::CEbase64); + c.setHeader(cte.release()); + c.setEncodedBody("YmFzZTY0LWVuY29kZWQgdGV4dA=="); + QCOMPARE(c.decodedText(), u"base64-encoded text"_s); + QCOMPARE(c.decodedBody(), "base64-encoded text\n"); + } + { + Content c{}; + auto cte = std::make_unique<Headers::ContentTransferEncoding>(); + cte->setEncoding(Headers::CEbase64); + c.setHeader(cte.release()); + auto ct = std::make_unique<Headers::ContentType>(); + ct->setMimeType("text/pgp"); + c.setHeader(ct.release()); + c.setEncodedBody("YmFzZTY0LWVuY29kZWQgYmluYXJ5IGJsb2Igb2YgZW5jcnlwdGVkIHRleHQ="); + // content of type text/pgp might be a binary blob of encrypted text; it must not be decoded as text + QCOMPARE(c.decodedText(), QString{}); + QCOMPARE(c.decodedBody(), "base64-encoded binary blob of encrypted text"); + } + +} + void ContentTest::testMultipleHeaderExtraction() { QByteArray data = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/contenttest.h new/kmime-25.12.0/autotests/contenttest.h --- old/kmime-25.08.3/autotests/contenttest.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/contenttest.h 2025-12-01 07:01:16.000000000 +0100 @@ -18,6 +18,7 @@ void testSetContent(); void testEncodedContent(); void testDecodedContent(); + void testDecodedText(); void testMultipartMixed(); void testMultipleHeaderExtraction(); /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-4804196479336448 new/kmime-25.12.0/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-4804196479336448 --- old/kmime-25.08.3/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-4804196479336448 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-4804196479336448 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1 @@ +=ybegin line=name=size=2147483647 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-5617955779182592 new/kmime-25.12.0/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-5617955779182592 --- old/kmime-25.08.3/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-5617955779182592 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-5617955779182592 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,5 @@ + +begin 216 + +M +end.eml \ No newline at end of file Binary files old/kmime-25.08.3/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-6349101081100288 and new/kmime-25.12.0/autotests/data/clusterfuzz-testcase-minimized-kmime_fuzzer-6349101081100288 differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/data/yenc-single-part-corrupt-size.yenc new/kmime-25.12.0/autotests/data/yenc-single-part-corrupt-size.yenc --- old/kmime-25.08.3/autotests/data/yenc-single-part-corrupt-size.yenc 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/data/yenc-single-part-corrupt-size.yenc 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,17 @@ +From: [email protected] +Newsgroups: yenc +Date: 27 Oct 2001 15:07:44 +0200 +Subject: yEnc-Prefix: "testfile.txt" 584 yEnc bytes - yEnc test (1) +Message-ID: <[email protected]> +Path: liebchen.winews.net!not-for-mail +Lines: 16 +X-Newsreader: MyNews + +-- +=ybegin line=128 size=2147483647 name=testfile.txt +�o��JWJ~�������JR[S74k}mssdJ\__XXZ74)('&%$#"! =M =J=I=@���������������������������������������������� +��������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSR +QPONMLKJIHGFEDCBA@?>=}<;:9876543210/=n-,+*74k}mssdJZXX\__74*+,-=n/0123456789:;<=}>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl +mnopqrstuvwxyz{|}~������������������������������������������������������������������������������������������������������������� +�������������������=@=I=J=M !"#$%&'()74o��J��J~�������74 +=yend size=584 crc32=ded29f4f Binary files old/kmime-25.08.3/autotests/data/yenc-single-part.txt and new/kmime-25.12.0/autotests/data/yenc-single-part.txt differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/data/yenc-single-part.yenc new/kmime-25.12.0/autotests/data/yenc-single-part.yenc --- old/kmime-25.08.3/autotests/data/yenc-single-part.yenc 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/data/yenc-single-part.yenc 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,17 @@ +From: [email protected] +Newsgroups: yenc +Date: 27 Oct 2001 15:07:44 +0200 +Subject: yEnc-Prefix: "testfile.txt" 584 yEnc bytes - yEnc test (1) +Message-ID: <[email protected]> +Path: liebchen.winews.net!not-for-mail +Lines: 16 +X-Newsreader: MyNews + +-- +=ybegin line=128 size=584 name=testfile.txt +�o��JWJ~�������JR[S74k}mssdJ\__XXZ74)('&%$#"! =M =J=I=@���������������������������������������������� +��������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSR +QPONMLKJIHGFEDCBA@?>=}<;:9876543210/=n-,+*74k}mssdJZXX\__74*+,-=n/0123456789:;<=}>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl +mnopqrstuvwxyz{|}~������������������������������������������������������������������������������������������������������������� +�������������������=@=I=J=M !"#$%&'()74o��J��J~�������74 +=yend size=584 crc32=ded29f4f diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/messagetest.cpp new/kmime-25.12.0/autotests/messagetest.cpp --- old/kmime-25.08.3/autotests/messagetest.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/messagetest.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -44,10 +44,8 @@ QCOMPARE(msg2->mainBodyPart("text/plain"), text); QCOMPARE(msg2->mainBodyPart("text/html"), (Content *)nullptr); - // Careful with removing content here. If we remove one of the two contents - // (by adding it to another message), the multipart will automatically be - // converted to a single-part, deleting the other content! - msg2->clearContents(false); + msg2->takeContent(text); + msg2->takeContent(html); // multipart/alternative msg->contentType()->setMimeType("multipart/alternative"); @@ -712,6 +710,10 @@ QTest::newRow("digits-overflow") << u"read-digits-overflow.mbox"_s; QTest::newRow("uninitialized-memory") << u"uninitialized-memory-use.mbox"_s; QTest::newRow("infinite-memory") << u"clusterfuzz-testcase-minimized-kmime_fuzzer-5255984894509056"_s; + QTest::newRow("assert") << u"clusterfuzz-testcase-minimized-kmime_fuzzer-5617955779182592"_s; + QTest::newRow("yenc-large-alloc") << u"clusterfuzz-testcase-minimized-kmime_fuzzer-4804196479336448"_s; + QTest::newRow("yenc-corrupt-size") << u"yenc-single-part.yenc"_s; + QTest::newRow("uuencode-no-filename") << u"clusterfuzz-testcase-minimized-kmime_fuzzer-6349101081100288"_s; } void MessageTest::testGarbage() @@ -722,4 +724,16 @@ QVERIFY(msg); } +void MessageTest::testYenc() +{ + KMime::Message::Ptr msg = readAndParseMail(u"yenc-single-part.yenc"_s); + QVERIFY(msg); + + QFile refFile(QLatin1StringView(TEST_DATA_DIR) + "/yenc-single-part.txt"_L1); + QVERIFY(refFile.open(QFile::ReadOnly)); + QCOMPARE(msg->subject()->asUnicodeString(), "yEnc-Prefix: \"testfile.txt\" 584 yEnc bytes - yEnc test (1)"_L1); + QCOMPARE(msg->contents().size(), 2); + QCOMPARE(msg->contents()[1]->decodedBody(), refFile.readAll()); +} + #include "moc_messagetest.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/messagetest.h new/kmime-25.12.0/autotests/messagetest.h --- old/kmime-25.08.3/autotests/messagetest.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/messagetest.h 2025-12-01 07:01:16.000000000 +0100 @@ -43,6 +43,8 @@ void testGarbage_data(); void testGarbage(); + + void testYenc(); private: KMime::Message::Ptr readAndParseMail(const QString &mailFile) const; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/ossfuzz/CMakeLists.txt new/kmime-25.12.0/autotests/ossfuzz/CMakeLists.txt --- old/kmime-25.08.3/autotests/ossfuzz/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/ossfuzz/CMakeLists.txt 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2025 Azhar Momin <[email protected]> +# SPDX-License-Identifier: BSD-2-Clause + +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(fuzzing_engine $ENV{LIB_FUZZING_ENGINE}) +else() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(fuzzing_engine -fsanitize=fuzzer) + else() + message(FATAL_ERROR "Fuzzing engine not supported") + endif() +endif() + + +add_executable(kmime_fuzzer kmime_fuzzer.cc) +target_link_libraries(kmime_fuzzer PRIVATE KPim6Mime ${fuzzing_engine}) + +set_target_properties(kmime_fuzzer PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/fuzzers +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/ossfuzz/README.md new/kmime-25.12.0/autotests/ossfuzz/README.md --- old/kmime-25.08.3/autotests/ossfuzz/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/kmime-25.12.0/autotests/ossfuzz/README.md 2025-12-01 07:01:16.000000000 +0100 @@ -0,0 +1,35 @@ +<!-- SPDX-FileCopyrightText: 2025 Azhar Momin <[email protected]> --> +<!-- SPDX-License-Identifier: CC-BY-SA-4.0 --> + +# KMime OSS-Fuzz Integration + +## Fuzzing Locally +Make sure you're using Clang (by setting `CC` and `CXX`), since fuzzing requires it. Then build KMime with `BUILD_FUZZERS=ON` to generate the `kmime_fuzzer` binary: +```sh +cmake -B build -DBUILD_FUZZERS=ON +cmake --build build +./build/bin/fuzzers/kmime_fuzzer +``` + +## Testing OSS-Fuzz Integration +Testing OSS-Fuzz integration requires: Python & Docker + +First clone the OSS-Fuzz repository: +```sh +git clone https://github.com/google/oss-fuzz.git +``` + +After navigating to the cloned repository, run the following command to build the fuzzers: +```sh +python3 infra/helper.py build_image kmime +python3 infra/helper.py build_fuzzers --sanitizer address kmime +``` + +This may take a while since it builds the whole QtBase dependency alongside with KMime and KCodecs. Once the build is completed, you can run the fuzzers using the following command: +```sh +python3 infra/helper.py run_fuzzer kmime kmime_fuzzer +``` + +The code for preparing the build lives in the `prepare_build.sh` script and the code for building the fuzzers lives in the `build_fuzzers.sh` script (which is also responsible for building the dependencies, creating the seed corpus and copying the dict file). + +For more information on OSS-Fuzz, visit the [official website](https://google.github.io/oss-fuzz/). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/ossfuzz/build_fuzzers.sh new/kmime-25.12.0/autotests/ossfuzz/build_fuzzers.sh --- old/kmime-25.08.3/autotests/ossfuzz/build_fuzzers.sh 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/ossfuzz/build_fuzzers.sh 2025-12-01 07:01:16.000000000 +0100 @@ -3,46 +3,41 @@ # SPDX-FileCopyrightText: 2025 Azhar Momin <[email protected]> # SPDX-License-Identifier: LGPL-2.0-or-later +export PATH="$WORK/bin:$PATH" + cd $SRC/extra-cmake-modules cmake . -G Ninja \ - -DBUILD_TESTING=OFF + -DBUILD_TESTING=OFF \ + -DCMAKE_INSTALL_PREFIX=$WORK ninja install -j$(nproc) cd $SRC/qtbase -./configure -platform linux-clang-libc++ -prefix /usr -static -opensource -confirm-license -debug \ +./configure -platform linux-clang-libc++ -prefix $WORK -static -opensource -confirm-license -debug \ -qt-pcre -qt-zlib \ -no-glib -no-icu -no-feature-gui -no-feature-sql -no-feature-network -no-feature-xml \ - -no-feature-dbus -no-feature-printsupport -ninja install -j$(nproc) - -cd $SRC/qttools -cmake . -G Ninja \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_INSTALL_PREFIX=/usr + -no-feature-dbus -no-feature-printsupport -no-feature-widgets ninja install -j$(nproc) cd $SRC/kcodecs rm -rf poqm cmake . -G Ninja \ --DBUILD_SHARED_LIBS=OFF \ --DBUILD_TESTING=OFF + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_INSTALL_PREFIX=$WORK ninja install -j$(nproc) cd $SRC/kmime rm -rf poqm cmake . -G Ninja \ --DBUILD_SHARED_LIBS=OFF \ --DBUILD_TESTING=OFF + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_FUZZERS=ON \ + -DCMAKE_INSTALL_PREFIX=$WORK ninja install -j$(nproc) -$CXX $CXXFLAGS -std=c++20 -fPIC autotests/ossfuzz/kmime_fuzzer.cc -o $OUT/kmime_fuzzer \ - -I /usr/include/QtCore/ \ - -I /usr/local/include/KPim6/KMime -I /usr/local/include/KPim6/KMime/kmime \ - -lKPim6Mime -lKF6Codecs \ - -lQt6Core -lQt6BundledPcre2 -lQt6BundledZLIB \ - -lm -ldl -lpthread \ - $LIB_FUZZING_ENGINE - +# Copy the fuzzer +cp bin/fuzzers/kmime_fuzzer $OUT/kmime_fuzzer +# Create a seed corpus find . -name "*.mbox" | zip -q $OUT/kmime_fuzzer_seed_corpus.zip -@ - +# Copy the dictionary cp autotests/ossfuzz/kmime_fuzzer.dict $OUT/kmime_fuzzer.dict diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/ossfuzz/kmime_fuzzer.cc new/kmime-25.12.0/autotests/ossfuzz/kmime_fuzzer.cc --- old/kmime-25.08.3/autotests/ossfuzz/kmime_fuzzer.cc 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/ossfuzz/kmime_fuzzer.cc 2025-12-01 07:01:16.000000000 +0100 @@ -6,6 +6,7 @@ #include "message.h" #include <QCoreApplication> +#include <QtEnvironmentVariables> void traverseContent(KMime::Content *content) { for (KMime::Content *c : content->contents()) { @@ -22,6 +23,8 @@ } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + qputenv("QT_ENABLE_REGEXP_JIT", "1"); + int argc = 0; QCoreApplication a(argc, nullptr); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/autotests/ossfuzz/prepare_build.sh new/kmime-25.12.0/autotests/ossfuzz/prepare_build.sh --- old/kmime-25.08.3/autotests/ossfuzz/prepare_build.sh 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/autotests/ossfuzz/prepare_build.sh 2025-12-01 07:01:16.000000000 +0100 @@ -7,6 +7,5 @@ apt-get install -y cmake ninja-build gperf git clone --depth 1 --branch=dev git://code.qt.io/qt/qtbase.git -git clone --depth 1 --branch=dev git://code.qt.io/qt/qttools.git git clone --depth 1 -b master https://invent.kde.org/frameworks/extra-cmake-modules.git git clone --depth 1 -b master https://invent.kde.org/frameworks/kcodecs.git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/poqm/ca/libkmime6_qt.po new/kmime-25.12.0/poqm/ca/libkmime6_qt.po --- old/kmime-25.08.3/poqm/ca/libkmime6_qt.po 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/poqm/ca/libkmime6_qt.po 2025-12-01 07:01:16.000000000 +0100 @@ -82,17 +82,3 @@ "Ha fallat la generació d'una Notificació de processament del missatge (MDN) " "per al missatge enviat el ${date} a ${to} amb l'assumpte «${subject}». El " "motiu s'especifica a la capçalera «Failure:» de sota." - -#~ msgctxt "invalid time specified" -#~ msgid "unknown" -#~ msgstr "desconegut" - -#~ msgid "Today %1" -#~ msgstr "Avui %1" - -#~ msgid "Yesterday %1" -#~ msgstr "Ahir %1" - -#~ msgctxt "1. weekday, 2. time" -#~ msgid "%1 %2" -#~ msgstr "%1 %2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/poqm/pt_BR/libkmime6_qt.po new/kmime-25.12.0/poqm/pt_BR/libkmime6_qt.po --- old/kmime-25.08.3/poqm/pt_BR/libkmime6_qt.po 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/poqm/pt_BR/libkmime6_qt.po 2025-12-01 07:01:16.000000000 +0100 @@ -3,21 +3,21 @@ # This file is distributed under the same license as the PACKAGE package. # # André Marcelo Alvarenga <[email protected]>, 2014. -# SPDX-FileCopyrightText: 2025 Marcus Gama <[email protected]> +# SPDX-FileCopyrightText: 2025 Geraldo Simiao <[email protected]> msgid "" msgstr "" "Project-Id-Version: libkmime5\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2023-11-20 01:59+0000\n" -"PO-Revision-Date: 2025-07-23 10:53-0300\n" -"Last-Translator: Marcus Gama <[email protected]>\n" +"PO-Revision-Date: 2025-08-20 22:32-0300\n" +"Last-Translator: Geraldo Simiao <[email protected]>\n" "Language-Team: Brazilian Portuguese <[email protected]>\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Lokalize 25.04.3\n" +"X-Generator: Lokalize 25.08.0\n" "X-Qt-Contexts: true\n" #: mdn.cpp:53 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/content.cpp new/kmime-25.12.0/src/content.cpp --- old/kmime-25.08.3/src/content.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/content.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -227,12 +227,10 @@ d->body.clear(); } -void Content::clearContents(bool del) +void Content::clearContents() { Q_D(Content); - if (del) { - qDeleteAll(d->multipartContents); - } + qDeleteAll(d->multipartContents); d->multipartContents.clear(); d->clearBodyMessage(); } @@ -321,7 +319,7 @@ return e; } -QByteArray Content::decodedContent() const +QByteArray Content::decodedBody() const { QByteArray ret; const Headers::ContentTransferEncoding *ec = contentTransferEncoding(); @@ -372,6 +370,11 @@ return ret; } +QByteArray Content::decodedContent() const +{ + return decodedBody(); +} + QString Content::decodedText(DecodedTextTrimOption trimOption) const { if (!d_ptr->decodeText(this)) { //this is not a text content !! @@ -554,7 +557,7 @@ } else { // This is non-textual content. Re-encode it. if (e == Headers::CEbase64) { - KCodecs::base64Encode(decodedContent(), d_ptr->body, true); + KCodecs::base64Encode(decodedBody(), d_ptr->body, true); enc->setEncoding(e); d_ptr->m_decoded = false; } else { @@ -672,8 +675,9 @@ { const Headers::ContentTransferEncoding *enc = q->contentTransferEncoding(); - if (const auto ct = q->contentType(); ct && !ct->isText()) { - return false; //non textual data cannot be decoded here => use decodedContent() instead + if (const auto ct = q->contentType(); ct && (!ct->isText() || ct->isSubtype("pgp"))) { + // content of type text/pgp might be a binary blob of encrypted text; it must not be decoded as text + return false; //non textual data cannot be decoded here => use decodedBody() instead } if (m_decoded) { return true; //nothing to do @@ -717,7 +721,7 @@ } } -ContentIndex KMime::Content::indexForContent(Content *content) const +ContentIndex KMime::Content::indexForContent(const Content *content) const { const auto i = d_ptr->contents().indexOf(content); if (i >= 0) { @@ -861,7 +865,8 @@ // This seems to be only a part of the message, so we treat it as "message/partial". ct->setMimeType("message/partial"); //ct->setId( uniqueString() ); not needed yet - ct->setPartialParams(uup.partialCount(), uup.partialNumber()); + ct->setPartialCount(uup.partialCount()); + ct->setPartialNumber(uup.partialNumber()); q->contentTransferEncoding()->setEncoding(Headers::CE7Bit); } else { // This is a complete message, so treat it as "multipart/mixed". @@ -886,7 +891,7 @@ // Now add each of the binary parts as sub-Contents. for (int i = 0; i < uup.binaryParts().count(); ++i) { auto c = new Content(q); - c->contentType()->setMimeType(uup.mimeTypes().at(i)); + c->contentType()->setMimeType(uup.mimeTypes().at(i) == "message/rfc822" ? "text/plain" : uup.mimeTypes().at(i)); c->contentType()->setName(QLatin1StringView(uup.filenames().at(i))); c->contentTransferEncoding()->setEncoding(Headers::CEuuenc); c->contentDisposition()->setDisposition(Headers::CDattachment); @@ -916,7 +921,8 @@ // Assume there is exactly one decoded part. Treat this as "message/partial". ct->setMimeType("message/partial"); //ct->setId( uniqueString() ); not needed yet - ct->setPartialParams(yenc.partialCount(), yenc.partialNumber()); + ct->setPartialCount(yenc.partialCount()); + ct->setPartialNumber(yenc.partialNumber()); q->contentTransferEncoding()->setEncoding(Headers::CEbinary); q->changeEncoding(Headers::CEbase64); // Convert to base64. } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/content.h new/kmime-25.12.0/src/content.h --- old/kmime-25.08.3/src/content.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/content.h 2025-12-01 07:01:16.000000000 +0100 @@ -90,7 +90,7 @@ message. Because of this, it will not have any children, it has no sub-contents and is therefore a leaf content. Only leaf contents have a body that is not empty, i.e. functions that operate - on the body, such as body(), size() and decodedContent(), will work only on + on the body, such as body(), size() and decodedBody(), will work only on leaf contents. - A non-leaf Content: This is a content that itself doesn't have any body, but that does have sub-contents. @@ -213,17 +213,12 @@ void clear(); /** - Removes all sub-Contents from this content. Deletes them if @p del is true. - This is different from calling removeContent() on each sub-Content, because - removeContent() will convert this to a single-part Content if only one - sub-Content is left. Calling clearContents() does NOT make this Content - single-part. + Removes all sub-Contents from this content. sub-Contents will be deleted. + Calling clearContents() does NOT make this Content single-part. - @param del Whether to delete the sub-Contents. - @see removeContent() @since 4.4 */ - void clearContents(bool del = true); + void clearContents(); /** Returns the Content header raw data. @@ -269,18 +264,15 @@ Note that the returned header may be empty. @param create Whether to create the header if it does not exist. @since 4.4. - - KDE5: BIC: FIXME: Why is the default argument false here? That is - inconsistent with the methods in KMime::Message! */ - template <typename T> T *header(bool create = false); + template <typename T> T *header(bool create = true); /** Returns the first header of type @tparam T. Can be @c nullptr if such a header doesn't exist. @since 24.08 */ - template <typename T> [[nodiscard]] T *header() const; + template <typename T> [[nodiscard]] const std::remove_cv_t<T> *header() const; /** Returns all @p type headers in the Content. @@ -542,10 +534,10 @@ * * Note that this will be empty for multipart contents or for encapsulated * messages, after parse() has been called. + * @since 25.12 (previously decodedContent()) */ - // TODO: KDE5: BIC: Rename this to decodedBody(), since only the body is - // returned. In contrast, setContent() sets the head and the body! Also, try - // to make this const. + [[nodiscard]] QByteArray decodedBody() const; + [[deprecated("Use decodedBody() instead")]] [[nodiscard]] QByteArray decodedContent() const; /** Options for Content::decodedText(). @@ -558,7 +550,7 @@ }; /** - Returns the decoded text. Additional to decodedContent(), this also + Returns the decoded text. Additional to decodedBody(), this also applies charset decoding. If this is not a text Content, decodedText() returns an empty QString. @@ -570,7 +562,7 @@ [[nodiscard]] QString decodedText(DecodedTextTrimOption trimOption = NoTrim) const; /** - Returns the decoded text. Additional to decodedContent(), this also + Returns the decoded text. Additional to decodedBody(), this also applies charset decoding. If this is not a text Content, decodedText() returns an empty QString. @@ -662,6 +654,7 @@ */ void prependContent(Content *content); + [[deprecated("Use takeContent/appendContent instead")]] void replaceContent(Content *oldContent, Content *newContent); /** Removes the given sub-Content and, if that actually was a sub-content @@ -699,7 +692,7 @@ if the Content is not found within the hierarchy. @param content the Content object to search. */ - [[nodiscard]] ContentIndex indexForContent(Content *content) const; + [[nodiscard]] ContentIndex indexForContent(const Content *content) const; /** Returns true if this is the top-level node in the MIME tree. The top-level @@ -804,7 +797,7 @@ return static_cast<T *>(h); } -template <typename T> T *Content::header() const +template <typename T> const std::remove_cv_t<T> *Content::header() const { Headers::Base *h = headerByType(T::staticType()); if (h) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/headerparsing.cpp new/kmime-25.12.0/src/headerparsing.cpp --- old/kmime-25.08.3/src/headerparsing.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/headerparsing.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -1089,9 +1089,7 @@ return false; } - // KDE5 TODO: Don't expose displayName as public, but rather add setter for it that - // automatically calls removeBidiControlChars - result.displayName = removeBidiControlChars(maybeDisplayName); + result.setDisplayName(maybeDisplayName); // get obs-mbox-list (may contain empty entries): scursor++; @@ -1152,7 +1150,7 @@ const char *oldscursor = scursor; if (parseMailbox(scursor, send, maybeMailbox, isCRLF)) { // yes, it is: - result.displayName.clear(); + result.setDisplayName({}); result.mailboxList.append(maybeMailbox); return true; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/headers.cpp new/kmime-25.12.0/src/headers.cpp --- old/kmime-25.08.3/src/headers.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/headers.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -371,11 +371,8 @@ // extract the mailboxes and complain if there are groups: for (const auto &it : std::as_const(maybeAddressList)) { - if (!(it).displayName.isEmpty()) { - KMIME_WARN << "mailbox groups in header disallowing them! Name: \"" - << (it).displayName << "\"" - << Qt::endl - ; + if (!(it).displayName().isEmpty()) { + KMIME_WARN << "mailbox groups in header disallowing them! Name:" << (it).displayName(); } d->mailboxList += (it).mailboxList; } @@ -967,10 +964,10 @@ return rv; } -void Ident::fromIdent(const Ident* ident) +void Ident::fromIdent(const Ident &ident) { - d_func()->encCS = ident->d_func()->encCS; - d_func()->msgIdList = ident->d_func()->msgIdList; + d_func()->encCS = ident.d_func()->encCS; + d_func()->msgIdList = ident.d_func()->msgIdList; } void Ident::appendIdentifier(const QByteArray &id) @@ -1730,6 +1727,11 @@ } } +void ContentType::setPartialNumber(int number) +{ + setParameter(QByteArrayLiteral("number"), QString::number(number)); +} + int ContentType::partialCount() const { QByteArray p = parameter("total").toLatin1(); if (!p.isEmpty()) { @@ -1739,8 +1741,8 @@ } } -void ContentType::setPartialParams(int total, int number) { - setParameter(QByteArrayLiteral("number"), QString::number(number)); +void ContentType::setPartialCount(int total) +{ setParameter(QByteArrayLiteral("total"), QString::number(total)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/headers.h new/kmime-25.12.0/src/headers.h --- old/kmime-25.08.3/src/headers.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/headers.h 2025-12-01 07:01:16.000000000 +0100 @@ -502,9 +502,13 @@ [[nodiscard]] bool isEmpty() const override; /** - Initialize this identifier Copy the data from + Initialize this identifier by copying the data from @p ident. */ - void fromIdent(const Ident* ident); + void fromIdent(const Ident &ident); + [[deprecated("Use fromIdent(const Ident&) instead")]] + void fromIdent(const Ident *ident) { + fromIdent(*ident); + } /** Returns the list of identifiers contained in this header. @@ -1113,19 +1117,36 @@ @see isPartial(), partialCount() */ [[nodiscard]] int partialNumber() const; + /** + Sets the position of this part in a multi-part set. + @see partialCount() + @since 25.12 + */ + void setPartialNumber(int number); /** Returns the total number of parts in a multi-part set. @see isPartial(), partialNumber() */ [[nodiscard]] int partialCount() const; + /** + Sets the total number of parts in a multi-part set. + @see partialCount() + @since 25.12 + */ + void setPartialCount(int total); /** Sets parameters of a partial MIME entity. @param total The total number of entities in the multi-part set. @param number The number of this entity in a multi-part set. + @deprecated since 25.12, use setPartialCount/setPartialNumber instead. */ - void setPartialParams(int total, int number); + [[deprecated("Use setPartialCount/setPartialNumber instead")]] + void setPartialParams(int total, int number) { + setPartialNumber(number); + setPartialCount(total); + } protected: bool parse(const char *&scursor, const char *const send, bool isCRLF = false) override; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/parsers.cpp new/kmime-25.12.0/src/parsers.cpp --- old/kmime-25.08.3/src/parsers.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/parsers.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -267,36 +267,29 @@ { } -bool YENCEncoded::yencMeta(QByteArray &src, const QByteArray &name, int *value) +bool YENCEncoded::yencMeta(QByteArrayView src, QByteArrayView name, int *value) { - bool found = false; - QByteArray sought = name + '='; - - auto iPos = src.indexOf(sought); - if (iPos > -1) { - auto pos1 = src.indexOf(' ', iPos); - auto pos2 = src.indexOf('\r', iPos); - auto pos3 = src.indexOf('\t', iPos); - auto pos4 = src.indexOf('\n', iPos); - if (pos2 >= 0 && (pos1 < 0 || pos1 > pos2)) { - pos1 = pos2; - } - if (pos3 >= 0 && (pos1 < 0 || pos1 > pos3)) { - pos1 = pos3; + for (qsizetype idx = 0; idx < src.size() - name.size() - 2;) { + idx = src.indexOf(name, idx); + if (idx < 0 || idx >= src.size() - name.size() - 2) { + return false; } - if (pos4 >= 0 && (pos1 < 0 || pos1 > pos4)) { - pos1 = pos4; + idx += name.size(); + if (src[idx] != '=') { + continue; } - iPos = src.lastIndexOf('=', pos1) + 1; - if (iPos < pos1) { - char c = src.at(iPos); - if (c >= '0' && c <= '9') { - found = true; - *value = src.mid(iPos, pos1 - iPos).toInt(); - } + ++idx; + auto endIdx = idx; + for (; endIdx < src.size() && std::isdigit(src[endIdx]); ++endIdx) {} + if (endIdx <= idx) { + continue; } + + *value = src.mid(idx, endIdx - idx).toInt(); + return true; } - return found; + + return false; } bool YENCEncoded::parse() @@ -307,7 +300,7 @@ qsizetype beginPos = currentPos; qsizetype yencStart = currentPos; bool containsPart = false; - QByteArray fileName; + QByteArrayView fileName; if ((beginPos = m_src.indexOf("=ybegin ", currentPos)) > -1 && (beginPos == 0 || m_src.at(beginPos - 1) == '\n')) { @@ -330,7 +323,7 @@ // Try to identify yenc meta data // Filenames can contain any embedded chars until end of line - QByteArray meta = m_src.mid(beginPos, yencStart - beginPos); + auto meta = QByteArrayView(m_src).mid(beginPos, yencStart - beginPos); qsizetype namePos = meta.indexOf("name="); if (namePos == -1) { success = false; @@ -370,6 +363,10 @@ break; } if (!yencMeta(meta, "total", &m_totalNr)) { + if (m_partNr == std::numeric_limits<int>::max()) { + success = false; + break; + } m_totalNr = m_partNr + 1; } if (yencSize == partEnd - partBegin + 1) { @@ -387,7 +384,8 @@ int lineLength = 0; bool containsEnd = false; QByteArray binary; - binary.resize(yencSize); + // don't allocate more memory than the data we actually have can at most expand to + binary.reserve(std::max<qsizetype>(0, std::min<qsizetype>(m_src.size() * 2, yencSize))); while (pos < len) { int ch = m_src.at(pos); if (ch < 0) { @@ -418,7 +416,8 @@ if (totalSize >= yencSize) { break; } - binary[totalSize++] = ch; + binary.push_back(ch); + ++totalSize; lineLength++; } else { break; @@ -431,7 +430,8 @@ if (totalSize >= yencSize) { break; } - binary[totalSize++] = ch; + binary.push_back(ch); + ++totalSize; lineLength++; pos++; } @@ -454,7 +454,7 @@ success = false; break; } - meta = m_src.mid(pos, eolPos - pos); + meta = QByteArrayView(m_src).mid(pos, eolPos - pos); if (!yencMeta(meta, "size", &totalSize)) { success = false; break; @@ -464,7 +464,7 @@ break; } - m_filenames.append(fileName); + m_filenames.append(fileName.toByteArray()); QMimeDatabase db; m_mimeTypes.append(db.mimeTypeForFile(QString::fromUtf8(fileName), QMimeDatabase::MatchExtension).name().toUtf8()); m_bins.append(binary); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/parsers_p.h new/kmime-25.12.0/src/parsers_p.h --- old/kmime-25.08.3/src/parsers_p.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/parsers_p.h 2025-12-01 07:01:16.000000000 +0100 @@ -94,7 +94,7 @@ [[nodiscard]] bool parse(); private: - static bool yencMeta(QByteArray &src, const QByteArray &name, int *value); + static bool yencMeta(QByteArrayView src, QByteArrayView name, int *value); }; } // namespace Parser diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/types.cpp new/kmime-25.12.0/src/types.cpp --- old/kmime-25.08.3/src/types.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/types.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -230,6 +230,16 @@ return rv.join(QLatin1StringView(", ")); } +QString Address::displayName() const +{ + return m_displayName; +} + +void Address::setDisplayName(const QString &displayName) +{ + m_displayName = removeBidiControlChars(displayName); +} + } // namespace Types } // namespace KMime diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/types.h new/kmime-25.12.0/src/types.h --- old/kmime-25.08.3/src/types.h 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/types.h 2025-12-01 07:01:16.000000000 +0100 @@ -17,9 +17,15 @@ namespace KMime { +/** Basic data types defined in RFC 2822. */ namespace Types { +/** + * Represents an addr-spec according to RFC 2822 §3.4.1. + * That's what you might commonly call an email address (but addressing + * emails is slightly more complex than that). +*/ struct KMIME_EXPORT AddrSpec { [[nodiscard]] QString asString() const; /*! This is the same as asString(), except it decodes IDNs for display */ @@ -153,9 +159,27 @@ typedef QList<Mailbox> MailboxList; -struct KMIME_EXPORT Address { - QString displayName; +/** + Represents an address as defined in RFC 2822 §3.4. + That is, a mailbox or a named group, ie. a named list + of mailboxes. +*/ +class KMIME_EXPORT Address { +public: + /** The display name, in case this is a named group. + * @since 25.12 (previously a public member with the same name) + */ + [[nodiscard]] QString displayName() const; + /** Set the group name. + * This strips bidi control characters. + * @since 25.12 + */ + void setDisplayName(const QString &displayName); + + /** Either a single mailbox or the mailboxes in the named group. */ MailboxList mailboxList; +private: + QString m_displayName; }; typedef QList<Address> AddressList; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kmime-25.08.3/src/util.cpp new/kmime-25.12.0/src/util.cpp --- old/kmime-25.08.3/src/util.cpp 2025-11-01 08:28:58.000000000 +0100 +++ new/kmime-25.12.0/src/util.cpp 2025-12-01 07:01:16.000000000 +0100 @@ -175,6 +175,9 @@ bool isCryptoPart(const Content *content) { + if (!content) { + return false; + } const auto ct = content->contentType(); if (!ct || !ct->isMediatype("application")) { return false;
