Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gpgmepp for openSUSE:Factory checked in at 2026-06-01 17:58:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gpgmepp (Old) and /work/SRC/openSUSE:Factory/.gpgmepp.new.1937 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gpgmepp" Mon Jun 1 17:58:44 2026 rev:2 rq:1356111 version:2.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/gpgmepp/gpgmepp.changes 2025-07-21 19:58:36.290878672 +0200 +++ /work/SRC/openSUSE:Factory/.gpgmepp.new.1937/gpgmepp.changes 2026-06-01 17:58:45.965284382 +0200 @@ -1,0 +2,10 @@ +Sun May 31 06:51:22 UTC 2026 - Andreas Stieger <[email protected]> + +- Update to 2.1.0: + * New methods to query the compliance of encryption results + * New function Subkey::keyGrips to get the list of keygrips for + a subkey using combined algorithms + * New function Context::generateRandomZBase32String to get a + cryptographically strong string of z-base-32 characters + +------------------------------------------------------------------- Old: ---- gpgmepp-2.0.0.tar.xz gpgmepp-2.0.0.tar.xz.sig New: ---- gpgmepp-2.1.0.tar.xz gpgmepp-2.1.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gpgmepp.spec ++++++ --- /var/tmp/diff_new_pack.gp8Szq/_old 2026-06-01 17:58:48.945407975 +0200 +++ /var/tmp/diff_new_pack.gp8Szq/_new 2026-06-01 17:58:48.965408805 +0200 @@ -1,7 +1,7 @@ # # spec file for package gpgmepp # -# Copyright (c) 2025 Andreas Stieger <[email protected]> +# Copyright (c) 2026 Andreas Stieger <[email protected]> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %define sover 7 Name: gpgmepp -Version: 2.0.0 +Version: 2.1.0 Release: 0 Summary: C++ bindings/wrapper for GPGME License: LGPL-2.0-or-later AND LGPL-2.1-or-later ++++++ gpgmepp-2.0.0.tar.xz -> gpgmepp-2.1.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/CMakeLists.txt new/gpgmepp-2.1.0/CMakeLists.txt --- old/gpgmepp-2.0.0/CMakeLists.txt 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/CMakeLists.txt 2026-05-18 12:04:51.000000000 +0200 @@ -21,7 +21,7 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(FULL_VERSION "2.0.0") +set(FULL_VERSION "2.1.0") string(REGEX MATCH "^[0-9]+.[0-9]+.[0-9]+" cmake_compat_version ${FULL_VERSION}) project(gpgmepp VERSION ${cmake_compat_version}) @@ -35,8 +35,8 @@ # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed: AGE=0) -set(LIBGPGMEPP_LT_CURRENT "7") -set(LIBGPGMEPP_LT_AGE "0") +set(LIBGPGMEPP_LT_CURRENT "8") +set(LIBGPGMEPP_LT_AGE "1") set(LIBGPGMEPP_LT_REVISION "0") math(EXPR LIBGPGMEPP_SOVERSION "${LIBGPGMEPP_LT_CURRENT} - ${LIBGPGMEPP_LT_AGE}") @@ -48,6 +48,7 @@ # The following option is used in gpg4win to distinguish the 32-bit build from # the 64-bit build option(PKGCONFIG_HOST "The host value to write in the pkgconfig file" "") +option(CONFIGURE_FOR_DISTRIBUTION "Configure only for `make dist`, etc., ignoring required dependencies" OFF) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) @@ -63,9 +64,13 @@ include(GNUInstallDirs) find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION}) -set_package_properties(LibGpgError PROPERTIES TYPE REQUIRED) find_package(Gpgme ${GPGME_REQUIRED_VERSION}) -set_package_properties(Gpgme PROPERTIES TYPE REQUIRED) +if (CONFIGURE_FOR_DISTRIBUTION) + message(WARNING "Configuring only for running `make dist`, etc., ignoring required dependencies") +else() + set_package_properties(LibGpgError PROPERTIES TYPE REQUIRED) + set_package_properties(Gpgme PROPERTIES TYPE REQUIRED) +endif() g10_get_full_version() @@ -113,10 +118,12 @@ include(G10GitHooks) g10_configure_git_hooks() -add_subdirectory(src) +if (LibGpgError_FOUND AND Gpgme_FOUND) + add_subdirectory(src) -if(BUILD_TESTING AND ENABLE_SHARED) - add_subdirectory(tests) + if(BUILD_TESTING AND ENABLE_SHARED) + add_subdirectory(tests) + endif() endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/ChangeLog new/gpgmepp-2.1.0/ChangeLog --- old/gpgmepp-2.0.0/ChangeLog 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/ChangeLog 2026-05-18 12:04:51.000000000 +0200 @@ -1,3 +1,109 @@ +2026-05-18 Werner Koch <[email protected]> + + Release 2.1.0. + + commit 6a99d7eef09aedd768356792f38aca2cc03659da + + +2026-04-21 Ingo Klöcker <[email protected]> + + Add support for new flags of encryption results. + + commit f12b36209758e82a53a8bfcc4ac0889bc9ecc0ed + * src/encryptionresult.h, src/encryptionresult.cpp (class + EncryptionResult): New methods isDeVs and isBetaCompliance. + * src/encryptionresult.cpp (EncryptionResult::Private): New + field res. + (EncryptionResult::Private::Private): Change type of argument r from + pointer to const-reference. Copy-construct res from r. Clear + invalid_recipients field of res after copying invalid recipients. + (EncryptionResult::init): Adapt call of Private c'tor to changed + argument type. + (operator<<): Add new flags. + +2026-02-18 Ingo Klöcker <[email protected]> + + Improve API to generate random bytes and z-base-32 characters. + + commit fea0862b4f7857f780d417e5e178fb2753f2e901 + * src/context.h (enum class RandomMode): Deprecate. + (generateRandomBytes): Deprecate overload taking RandomMode parameter. + * src/context.h, src/context.cpp (generateRandomBytes): New overload + taking only size_t parameter. + (generateRandomZBase32String): New. + * src/randomresults.h (RandomZBase32StringResult): New. + + * tests/run-genrandom.cpp (main): Use generateRandomZBase32String + instead of deprecated generateRandomBytes. + + Avoid copy of generated random bytes. + + commit 7025cb4b211bcda5e07620f12eb806c14ca95e0d + * src/randomresults.h (class RandomBytesResult): Add constructor for + moving value into result. + * src/context.cpp (Context::generateRandomBytes): Use new constructor. + +2026-02-17 Ingo Klöcker <[email protected]> + + tests: Fix conversion of random zbase32 data to string. + + commit fade4bbe630b7e597d1cd3308819110490cae598 + * tests/run-genrandom.cpp (main): Pass data to std::string constructor + instead of copying it byte by byte. + +2025-12-10 Ingo Klöcker <[email protected]> + + Use DeletionFlags in key deletion API. + + commit 527df234fb2fea51dfc6e9f84f3c98914b38b88e + * src/context.h, src/context.cpp (Context::startKeyDeletion): Add + overload taking DeletionFlags. + * src/context.cpp (Context::startKeyDeletion): Make overload taking a + bool use new overload. + * src/context.h (Context::deleteKey, Context::startKeyDeletion): Mark + overloads taking a bool as deprecated. + + Use new Flags template for new Deletion flags. + + commit d8098f30da95621dff88d815ab29534fec5014e4 + * src/global.h (DeletionFlags): Rename to... + (DeletionFlag): ...this. Change underlying type to unsigned int. Remove + UseDefaults enum value. + (DeletionFlags): New type for combinations of DeletionFlag. Define enum + flag operators for this type. + (operator|): Remove overload for DeletionFlags. + * src/context.cpp (Context::deleteKey): Adapt to above changes. + + Add template class for type-safe enum-based flags. + + commit bb44fc70e48e8be61f5ab6352796f5c0610dd848 + * src/CMakeLists.txt (Gpgmepp_HEADERS): Add new file. + * src/flags.h: New. + * tests/CMakeLists.txt: Add new test. + * tests/t-flags.cpp: New. + +2025-11-19 Tobias Fella <[email protected]> + + Allow passing flags to Context::deleteKey. + + commit 550a0666b25a84534c83237b211053c875fe4795 + GnuPG-Bug-ID: 7538 + +2025-10-29 Ingo Klöcker <[email protected]> + + Add function to get keygrips of subkey using combined algorithms. + + commit 71b7d24a2782d56be9560d2c6a44211aff07e0f1 + * src/key.cpp, src/key.h (Subkey::keyGrips): New. + + Add internal utility function for splitting a string into string views. + + commit fa1e892b1414da07aa467bda295f8981b191374d + * src/util.cpp, src/util.h (split_into_string_views): New. + + * tests/CMakeLists.txt: Add macro _g10_add_test. Add new test. + * tests/t-utils.cpp: New. + +2025-10-24 Ingo Klöcker <[email protected]> + + Use gpgme_ssize_t everywhere. + + commit 268f2feea0b0c7025016170a4a2e3dee2c3aa0fd + * src/editinteractor.cpp (edit_interactor_callback_impl): Replace + ssize_t with gpgme_ssize_t. + + build: Fix install with MSVC. + + commit e0b20e967654446dbe06b09e8d73cc97e02aac2b + * CMakeLists.txt: Add missing DESTINATION keyword to install arguments. + 2025-06-03 Werner Koch <[email protected]> Release 2.0.0. @@ -6,6 +112,13 @@ 2025-06-03 Ingo Klöcker <[email protected]> + build: Add option CONFIGURE_FOR_DISTRIBUTION. + + commit 592efbfcfb61be34ede8ebe66ee8bc3952c131c6 + * CMakeLists.txt: Add option CONFIGURE_FOR_DISTRIBUTION. Set + LibGpgError and Gpgme as required if option CONFIGURE_FOR_DISTRIBUTION + is not set. Add src (and tests) only if required dependencies were + found. + build: Fix logic for appending "-unknown" to version number. + commit 1e948b3f3ee10806eaee282e489609fa370277d8 * cmake/modules/G10GetFullVersion.cmake (G10_GET_FULL_VERSION): Use diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/NEWS new/gpgmepp-2.1.0/NEWS --- old/gpgmepp-2.0.0/NEWS 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/NEWS 2026-05-18 12:04:51.000000000 +0200 @@ -1,3 +1,27 @@ +Noteworthy changes in version 2.1.0 (2026-05-18) [C8/A1/R0] +------------------------------------------------ + + * New methods to query the compliance of encryption results. [T6702] + + * New function Subkey::keyGrips to get the list of keygrips for + a subkey using combined algorithms. + + * New function Context::generateRandomZBase32String to get a + cryptographically strong string of z-base-32 characters. [T8108] + + * Interface changes relative to the 2.0.0 release: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + DeletionFlag NEW. + DeletionFlags NEW. + Context::deleteKey CHANGED: New overload; deprecated old. + Context::startKeyDeletion CHANGED: New overload; deprecated old. + Context::RandomMode DEPRECATED. + Context::generateRandomBytes CHANGED: New overload; deprecated old. + Context::generateRandomZBase32String NEW. + Subkey::keyGrips NEW. + EncryptionResult::isDeVs NEW. + EncryptionResult::isBetaCompliance NEW. + Noteworthy changes in version 2.0.0 (2025-06-03) [C7/A0/R0] ------------------------------------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/VERSION new/gpgmepp-2.1.0/VERSION --- old/gpgmepp-2.0.0/VERSION 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/VERSION 2026-05-18 12:04:51.000000000 +0200 @@ -1,2 +1,2 @@ -2.0.0 -301d39f8b97a79369292280e201e45f472d71e91 +2.1.0 +6a99d7eef09aedd768356792f38aca2cc03659da diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/cmake/modules/g10_sign-release.sh.in new/gpgmepp-2.1.0/cmake/modules/g10_sign-release.sh.in --- old/gpgmepp-2.0.0/cmake/modules/g10_sign-release.sh.in 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/cmake/modules/g10_sign-release.sh.in 2026-05-18 12:04:51.000000000 +0200 @@ -33,12 +33,21 @@ exit 2 fi mysignkey="$x" -files1="@[email protected]" -files2="@[email protected] - @[email protected] +files1= +suffix= +for suf in xz bz2 gz; do + if [ -f "@[email protected].$suf" ]; then + files1="@[email protected].$suf" + files2="@[email protected].$suf.sig" + suffix=$suf + break + fi +done +files2="$files2 + @[email protected] @[email protected]" echo "/* Signing the source tarball ..." -gpg -sbu $mysignkey @[email protected] +gpg -sbu $mysignkey @[email protected].$suffix cat @[email protected] >swdb.snippet echo >>swdb.snippet sha1sum ${files1} >>swdb.snippet diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/CMakeLists.txt new/gpgmepp-2.1.0/src/CMakeLists.txt --- old/gpgmepp-2.0.0/src/CMakeLists.txt 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/CMakeLists.txt 2026-05-18 12:04:51.000000000 +0200 @@ -80,6 +80,7 @@ error.h eventloopinteractor.h exception.h + flags.h global.h gpgaddexistingsubkeyeditinteractor.h gpgadduserideditinteractor.h @@ -195,7 +196,7 @@ if(ENABLE_SHARED) if (MSVC) - install(TARGETS Gpgmepp EXPORT GpgmeppTargets "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") + install(TARGETS Gpgmepp EXPORT GpgmeppTargets DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") else() install(TARGETS Gpgmepp EXPORT GpgmeppTargets) endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/context.cpp new/gpgmepp-2.1.0/src/context.cpp --- old/gpgmepp-2.0.0/src/context.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/context.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -760,14 +760,24 @@ Error Context::deleteKey(const Key &key, bool allowSecretKeyDeletion) { + return deleteKey(key, allowSecretKeyDeletion ? DeletionFlags{DeletionFlag::AllowSecret} : DeletionFlags{}); +} + +Error Context::deleteKey(const Key &key, DeletionFlags flags) +{ d->lastop = Private::Delete; - return Error(d->lasterr = gpgme_op_delete(d->ctx, key.impl(), int(allowSecretKeyDeletion))); + return Error(d->lasterr = gpgme_op_delete_ext(d->ctx, key.impl(), flags.toUnderlyingType())); } Error Context::startKeyDeletion(const Key &key, bool allowSecretKeyDeletion) { + return startKeyDeletion(key, allowSecretKeyDeletion ? DeletionFlags{DeletionFlag::AllowSecret} : DeletionFlags{}); +} + +Error Context::startKeyDeletion(const Key &key, DeletionFlags flags) +{ d->lastop = Private::Delete; - return Error(d->lasterr = gpgme_op_delete_start(d->ctx, key.impl(), int(allowSecretKeyDeletion))); + return Error(d->lasterr = gpgme_op_delete_start(d->ctx, key.impl(), flags.toUnderlyingType())); } Error Context::passwd(const Key &key) @@ -1829,6 +1839,8 @@ return gpgme_get_ctx_flag(d->ctx, name); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" RandomBytesResult Context::generateRandomBytes(size_t count, RandomMode mode) { RandomBytesResult::value_type randomBytes(count); @@ -1837,7 +1849,19 @@ if (d->lasterr) { return RandomBytesResult{Error{d->lasterr}}; } - return RandomBytesResult{randomBytes}; + return RandomBytesResult{std::move(randomBytes)}; +} +#pragma GCC diagnostic pop + +RandomBytesResult Context::generateRandomBytes(size_t count) +{ + RandomBytesResult::value_type randomBytes(count); + d->lasterr = gpgme_op_random_bytes(d->ctx, GPGME_RANDOM_MODE_NORMAL, + reinterpret_cast<char *>(randomBytes.data()), count); + if (d->lasterr) { + return RandomBytesResult{Error{d->lasterr}}; + } + return RandomBytesResult{std::move(randomBytes)}; } RandomValueResult Context::generateRandomValue(unsigned int limit) @@ -1850,6 +1874,17 @@ return RandomValueResult{static_cast<unsigned int>(randomValue)}; } +RandomZBase32StringResult Context::generateRandomZBase32String() +{ + std::string randomString(30, '\0'); + d->lasterr = gpgme_op_random_bytes(d->ctx, GPGME_RANDOM_MODE_ZBASE32, + randomString.data(), 31); + if (d->lasterr) { + return RandomZBase32StringResult{Error{d->lasterr}}; + } + return RandomZBase32StringResult{std::move(randomString)}; +} + // Engine Spawn stuff Error Context::spawn(const char *file, const char *argv[], Data &input, Data &output, Data &err, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/context.h new/gpgmepp-2.1.0/src/context.h --- old/gpgmepp-2.0.0/src/context.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/context.h 2026-05-18 12:04:51.000000000 +0200 @@ -57,6 +57,7 @@ class VfsMountResult; class RandomBytesResult; class RandomValueResult; +class RandomZBase32StringResult; class EngineInfo; @@ -249,8 +250,8 @@ // Key Deletion // - GpgME::Error deleteKey(const Key &key, bool allowSecretKeyDeletion = false); - GpgME::Error startKeyDeletion(const Key &key, bool allowSecretKeyDeletion = false); + GpgME::Error deleteKey(const Key &key, DeletionFlags flags); + GpgME::Error startKeyDeletion(const Key &key, DeletionFlags flags); // // Passphrase changing @@ -537,14 +538,41 @@ // Random values // // - enum class RandomMode { + enum class GPGMEPP_DEPRECATED RandomMode { Normal = 0, ZBase32 = 1 }; - RandomBytesResult generateRandomBytes(size_t count, RandomMode mode = RandomMode::Normal); + /*! + * Generate random bytes. + * + * This function creates a buffer of \a count bytes and fills this buffer by calling + * gpgme_op_random_bytes. \a count must be at most \c 1024. If \a mode is \c ZBase32 + * then \a count must be at least \c 31. In this case the buffer is filled with 30 + * ASCII characters followed by a null byte; the remainder of the buffer is uninitialized. + * + * This function is deprecated. For \c Normal mode use the other overload of + * generateRandomBytes. For \c ZBase32 mode use generateRandomZBase32String. + */ + GPGMEPP_DEPRECATED RandomBytesResult generateRandomBytes(size_t count, RandomMode mode); + /*! + * Generate random bytes. + * + * This function creates a buffer of \a count bytes and fills this buffer with random + * bytes retrieved from gpg. \a count must be at most \c 1024. + */ + RandomBytesResult generateRandomBytes(size_t count); + + /*! + * Generate an unbiased random value in the range [0, \a limit). + */ RandomValueResult generateRandomValue(unsigned int limit); + /*! + * Generate a string with 30 random z-base-32 characters. + */ + RandomZBase32StringResult generateRandomZBase32String(); + // // // G13 crypto container operations @@ -632,6 +660,8 @@ unsigned long reserved, unsigned long expires, unsigned int flags); + GPGMEPP_DEPRECATED GpgME::Error deleteKey(const Key &key, bool allowSecretKeyDeletion = false); + GPGMEPP_DEPRECATED GpgME::Error startKeyDeletion(const Key &key, bool allowSecretKeyDeletion = false); private: // Helper functions that need to be context because they rely diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/editinteractor.cpp new/gpgmepp-2.1.0/src/editinteractor.cpp --- old/gpgmepp-2.0.0/src/editinteractor.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/editinteractor.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -134,7 +134,7 @@ // if there's a result, write it: if (*result) { gpgme_err_set_errno(0); - const ssize_t len = std::strlen(result); + const gpgme_ssize_t len = std::strlen(result); if (writeAll(fd, result, len) != len) { err = Error::fromSystemError(); if (ei->debug) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/encryptionresult.cpp new/gpgmepp-2.1.0/src/encryptionresult.cpp --- old/gpgmepp-2.0.0/src/encryptionresult.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/encryptionresult.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -43,12 +43,9 @@ class GpgME::EncryptionResult::Private { public: - explicit Private(const gpgme_encrypt_result_t r) + explicit Private(const _gpgme_op_encrypt_result &r) : res(r) { - if (!r) { - return; - } - for (gpgme_invalid_key_t ik = r->invalid_recipients ; ik ; ik = ik->next) { + for (gpgme_invalid_key_t ik = res.invalid_recipients ; ik ; ik = ik->next) { gpgme_invalid_key_t copy = new _gpgme_invalid_key(*ik); if (ik->fpr) { copy->fpr = strdup(ik->fpr); @@ -56,6 +53,7 @@ copy->next = nullptr; invalid.push_back(copy); } + res.invalid_recipients = nullptr; } ~Private() { @@ -65,6 +63,7 @@ } } + _gpgme_op_encrypt_result res; std::vector<gpgme_invalid_key_t> invalid; }; @@ -89,11 +88,21 @@ if (!res) { return; } - d.reset(new Private(res)); + d.reset(new Private(*res)); } make_standard_stuff(EncryptionResult) +bool GpgME::EncryptionResult::isDeVs() const +{ + return d && d->res.is_de_vs; +} + +bool GpgME::EncryptionResult::isBetaCompliance() const +{ + return d && d->res.beta_compliance; +} + unsigned int GpgME::EncryptionResult::numInvalidRecipients() const { return d ? d->invalid.size() : 0 ; @@ -144,7 +153,9 @@ { os << "GpgME::EncryptionResult("; if (!result.isNull()) { - os << "\n error: " << result.error() + os << "\n error: " << result.error() + << "\n isDeVs: " << result.isDeVs() + << "\n isBetaCompliance: " << result.isBetaCompliance() << "\n invalid recipients:\n"; const std::vector<InvalidRecipient> ir = result.invalidEncryptionKeys(); std::copy(ir.begin(), ir.end(), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/encryptionresult.h new/gpgmepp-2.1.0/src/encryptionresult.h --- old/gpgmepp-2.0.0/src/encryptionresult.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/encryptionresult.h 2026-05-18 12:04:51.000000000 +0200 @@ -64,6 +64,9 @@ bool isNull() const; + bool isDeVs() const; + bool isBetaCompliance() const; + unsigned int numInvalidRecipients() const; InvalidRecipient invalidEncryptionKey(unsigned int index) const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/flags.h new/gpgmepp-2.1.0/src/flags.h --- old/gpgmepp-2.0.0/src/flags.h 1970-01-01 01:00:00.000000000 +0100 +++ new/gpgmepp-2.1.0/src/flags.h 2026-05-18 12:04:51.000000000 +0200 @@ -0,0 +1,235 @@ +/* + flags.h - helper for bitwise flag-like operations on scoped enums + + Copyright (c) 2024 Zaven Maradyan <[email protected]> + Copyright (c) 2025 g10 Code GmbH + Software engineering by Ingo Klöcker <[email protected]> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this source file (the "Materials"), to deal in the Materials + without restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies of the + Materials, and to permit persons to whom the Materials is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Materials. + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER + DEALINGS IN THE MATERIALS. + + SPDX-License-Identifier: MIT +*/ + +#ifndef __GPGMEPP_FLAGS_H__ +#define __GPGMEPP_FLAGS_H__ + +// Based on the BitFlags class template published at +// https://voithos.io/articles/type-safe-enum-class-bit-flags/ +// under the MIT license. +// The names of the member functions were changed to match the API of std::bitset. + +#include <bitset> +#include <initializer_list> +#include <iosfwd> +#include <type_traits> + +namespace GpgME +{ + +// Helper class for bitwise flag-like operations on scoped enums. +// +// This class provides a way to represent combinations of enum values without +// directly overloading operators on the enum type itself. This approach +// avoids ambiguity in the type system and allows the enum type to continue +// representing a single value, while the Flags can hold a combination +// of enum values. +// +// Example usage: +// +// enum class DeletionFlag : unsigned int { +// AllowSecret = (1 << 0), +// Force = (1 << 1), +// }; +// using DeletionFlags = Flags<DeletionFlag>; +// +// DeletionFlags flags = { DeletionFlag::AllowSecret, DeletionFlag::Force }; +// flags.reset(DeletionFlag::Force); +// if (flags.test(DeletionFlag::AllowSecret)) { +// // ... +// } +// flags |= DeletionFlag::Force; +// +// Optionally, you can define binary operators for two flags for more +// convenient combination of multiple flags: +// +// GPGMEPP_DEFINE_ENUM_FLAG_OPERATORS(DeletionFlags) +// +// DeletionFlags forceDeleteOfSecret = DeletionFlag::AllowSecret | DeletionFlag::Force; +template <typename Enum, std::size_t _Nb = sizeof(Enum) * 8> +class Flags { + using UnderlyingT = std::underlying_type_t<Enum>; + +public: + using enum_type = Enum; + + static const std::size_t number_of_bits = _Nb; + + // Rule of zero: default copy/move constructor/assignment operators are fine + constexpr inline Flags() = default; + constexpr inline explicit Flags(Enum flag) + : mFlags(toUnderlying(flag)) + { + } + constexpr inline Flags(std::initializer_list<Enum> flags) + { + for (Enum flag : flags) { + mFlags |= toUnderlying(flag); + } + } + + static constexpr inline Flags fromUnderlyingType(UnderlyingT flags) + { + return Flags{flags}; + } + constexpr inline UnderlyingT toUnderlyingType() const + { + return mFlags; + } + + //! Tests if flag \a flag is set. + constexpr inline bool test(Enum flag) const + { + return (mFlags & toUnderlying(flag)) == toUnderlying(flag); + } + //! Sets the flag \a flag if \a value is \c true. Otherwise, the flag is reset. + constexpr inline Flags &set(Enum flag, bool value = true) + { + return value ? (*this |= flag) : (*this &= ~Flags(flag)); + } + //! Sets the flag \a flag to \c false. + constexpr inline Flags &reset(Enum flag) + { + return (*this &= ~Flags(flag)); + } + //! Sets all flags to false. + constexpr inline Flags &reset() + { + mFlags = static_cast<UnderlyingT>(0); + return *this; + } + + constexpr inline explicit operator bool() const { + return mFlags != static_cast<UnderlyingT>(0); + } + + friend constexpr inline Flags operator|(Flags lhs, Flags rhs) { + return Flags(lhs.mFlags | rhs.mFlags); + } + friend constexpr inline Flags operator|(Flags lhs, Enum rhs) { + return Flags(lhs.mFlags | toUnderlying(rhs)); + } + friend constexpr inline Flags operator|(Enum lhs, Flags rhs) { + return Flags(toUnderlying(lhs) | rhs.mFlags); + } + + friend constexpr inline Flags operator&(Flags lhs, Flags rhs) { + return Flags(lhs.mFlags & rhs.mFlags); + } + friend constexpr inline Flags operator&(Flags lhs, Enum rhs) { + return Flags(lhs.mFlags & toUnderlying(rhs)); + } + friend constexpr inline Flags operator&(Enum lhs, Flags rhs) { + return Flags(toUnderlying(lhs) & rhs.mFlags); + } + + friend constexpr inline Flags operator^(Flags lhs, Flags rhs) { + return Flags(lhs.mFlags ^ rhs.mFlags); + } + friend constexpr inline Flags operator^(Flags lhs, Enum rhs) { + return Flags(lhs.mFlags ^ toUnderlying(rhs)); + } + friend constexpr inline Flags operator^(Enum lhs, Flags rhs) { + return Flags(toUnderlying(lhs) ^ rhs.mFlags); + } + + constexpr inline Flags &operator|=(Flags other) { + mFlags |= other.mFlags; + return *this; + } + constexpr inline Flags &operator|=(Enum flag) { + mFlags |= toUnderlying(flag); + return *this; + } + constexpr inline Flags &operator&=(Flags other) { + mFlags &= other.mFlags; + return *this; + } + constexpr inline Flags &operator&=(Enum flag) { + mFlags &= toUnderlying(flag); + return *this; + } + constexpr inline Flags &operator^=(Flags other) { + mFlags ^= other.mFlags; + return *this; + } + constexpr inline Flags &operator^=(Enum flag) { + mFlags ^= toUnderlying(flag); + return *this; + } + + constexpr inline Flags operator~() const { + return Flags(~mFlags & (~std::bitset<_Nb>()).to_ulong()); + } + + friend constexpr inline bool operator==(const Flags &lhs, const Flags &rhs) { + return lhs.mFlags == rhs.mFlags; + } + friend constexpr inline bool operator!=(const Flags &lhs, const Flags &rhs) { + return !(lhs == rhs); + } + +private: + constexpr inline explicit Flags(UnderlyingT flags) + : mFlags(flags) + { + } + + static constexpr inline UnderlyingT toUnderlying(Enum flag) { + return static_cast<UnderlyingT>(flag); + } + +private: + UnderlyingT mFlags = static_cast<UnderlyingT>(0); +}; + +#define GPGMEPP_DEFINE_ENUM_FLAG_OPERATORS(FlagsType) \ +constexpr inline FlagsType operator|(FlagsType::enum_type lhs, FlagsType::enum_type rhs) \ +{ \ + return FlagsType{lhs} | rhs; \ +} \ +constexpr inline FlagsType operator&(FlagsType::enum_type lhs, FlagsType::enum_type rhs) \ +{ \ + return FlagsType{lhs} & rhs; \ +} \ +constexpr inline FlagsType operator^(FlagsType::enum_type lhs, FlagsType::enum_type rhs) \ +{ \ + return FlagsType{lhs} ^ rhs; \ +} + +template <typename Enum, std::size_t _Nb> +std::ostream &operator<<(std::ostream &os, const Flags<Enum, _Nb> &flags) +{ + // Write out a bitset representation. + os << std::bitset<_Nb>(flags.toUnderlyingType()); + return os; +} + +} // namespace GpgME + +#endif // __GPGMEPP_FLAGS_H__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/global.h new/gpgmepp-2.1.0/src/global.h --- old/gpgmepp-2.0.0/src/global.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/global.h 2026-05-18 12:04:51.000000000 +0200 @@ -26,6 +26,7 @@ #ifndef __GPGMEPP_GLOBAL_H__ #define __GPGMEPP_GLOBAL_H__ +#include "flags.h" #include "gpgmefw.h" #include "gpgmepp_export.h" @@ -89,6 +90,14 @@ NoLongerUsed = 3 }; +enum class DeletionFlag : unsigned int { + // Keep in line with GPGME_DELETE_* flags + AllowSecret = (1 << 0), + Force = (1 << 1), +}; +using DeletionFlags = Flags<DeletionFlag>; +GPGMEPP_DEFINE_ENUM_FLAG_OPERATORS(DeletionFlags) + GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Protocol proto); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Engine eng); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, KeyListMode mode); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/key.cpp new/gpgmepp-2.1.0/src/key.cpp --- old/gpgmepp-2.0.0/src/key.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/key.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -611,6 +611,14 @@ return subkey ? subkey->keygrip : nullptr; } +std::vector<std::string_view> Subkey::keyGrips() const +{ + if (!subkey || !subkey->keygrip) { + return {}; + } + return _gpgmepp::split_into_string_views(subkey->keygrip, ','); +} + bool Subkey::isSecret() const { return subkey && subkey->secret; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/key.h new/gpgmepp-2.1.0/src/key.h --- old/gpgmepp-2.0.0/src/key.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/key.h 2026-05-18 12:04:51.000000000 +0200 @@ -32,6 +32,7 @@ #include <algorithm> #include <memory> #include <string> +#include <string_view> #include <vector> #include <ctime> @@ -338,8 +339,21 @@ const char *cardSerialNumber() const; + /*! + * Returns the keygrip(s) of the subkey in hex digit form or NULL if not available. + * For combined algorithms the keygrips are delimited by comma. + * + * \sa keyGrips + */ const char *keyGrip() const; + /*! + * Returns the list of keygrips of the subkey in hex digit form. + * + * \note The individual keygrips are not necessarily null-terminated. + */ + std::vector<std::string_view> keyGrips() const; + private: shared_gpgme_key_t key; gpgme_sub_key_t subkey; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/randomresults.h new/gpgmepp-2.1.0/src/randomresults.h --- old/gpgmepp-2.0.0/src/randomresults.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/randomresults.h 2026-05-18 12:04:51.000000000 +0200 @@ -42,6 +42,10 @@ : mValue{value} { } + explicit RandomBytesResult(value_type &&value) + : mValue{std::move(value)} + { + } explicit RandomBytesResult(const Error &err) : Result{err} { @@ -81,6 +85,35 @@ unsigned int mValue = 0; }; +class GPGMEPP_EXPORT RandomZBase32StringResult : public Result +{ +public: + RandomZBase32StringResult() + : Result{GPG_ERR_NO_VALUE} + { + } + explicit RandomZBase32StringResult(const std::string &value) + : mValue{value} + { + } + explicit RandomZBase32StringResult(std::string &&value) + : mValue{std::move(value)} + { + } + explicit RandomZBase32StringResult(const Error &err) + : Result{err} + { + } + + const std::string &value() const + { + return mValue; + } + +private: + std::string mValue; +}; + } // namespace GpgME #endif /* __GPGMEPP_RANDOMRESULTS_H__ */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/util.cpp new/gpgmepp-2.1.0/src/util.cpp --- old/gpgmepp-2.0.0/src/util.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/util.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -29,6 +29,26 @@ #include <functional> +#include <cstring> + +std::vector<std::string_view> _gpgmepp::split_into_string_views(const char *s, char delimiter) +{ + std::vector<std::string_view> result; + if (!s) { + return result; + } + while (const char *segment_end = std::strchr(s, delimiter)) { + if (const auto segment_size = segment_end - s) { + result.emplace_back(s, segment_size); + } + s = segment_end + 1; + }; + if (const auto segment_size = std::strlen(s)) { + result.emplace_back(s, segment_size); + } + return result; +} + StringsToCStrings::StringsToCStrings(const std::vector<std::string>& v) : m_strings{v} { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/src/util.h new/gpgmepp-2.1.0/src/util.h --- old/gpgmepp-2.0.0/src/util.h 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/src/util.h 2026-05-18 12:04:51.000000000 +0200 @@ -190,6 +190,16 @@ return result; } +namespace _gpgmepp { + +/*! + * Splits \a s into substrings whereever \a delimiter occurs. + * Empty substrings are omitted. + */ +GPGMEPP_EXPORT std::vector<std::string_view> split_into_string_views(const char *s, char delimiter); + +} // namespace _gpgmepp + /** * Adapter for passing a vector of strings as NULL-terminated array of * const char* to the C-interface of gpgme. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/tests/CMakeLists.txt new/gpgmepp-2.1.0/tests/CMakeLists.txt --- old/gpgmepp-2.0.0/tests/CMakeLists.txt 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/tests/CMakeLists.txt 2026-05-18 12:04:51.000000000 +0200 @@ -22,12 +22,21 @@ get_filename_component(_name ${_source} NAME_WE) add_executable(${_name} ${_source}) target_link_libraries(${_name} Gpgmepp) - set_target_properties(${_target} PROPERTIES + set_target_properties(${_name} PROPERTIES WIN32_EXECUTABLE FALSE # don't build as GUI app on Windows MACOSX_BUNDLE FALSE # don't build as GUI app on macOS ) endmacro() +macro(_g10_add_test _source) + _g10_add_testprogram(${_source}) + get_filename_component(_name ${_source} NAME_WE) + add_test(NAME ${_name} COMMAND ${_name}) +endmacro() + +_g10_add_test(t-flags.cpp) +_g10_add_test(t-utils.cpp) + _g10_add_testprogram(run-createkey.cpp) _g10_add_testprogram(run-genrandom.cpp) _g10_add_testprogram(run-getkey.cpp) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/tests/run-genrandom.cpp new/gpgmepp-2.1.0/tests/run-genrandom.cpp --- old/gpgmepp-2.0.0/tests/run-genrandom.cpp 2025-06-03 15:44:34.000000000 +0200 +++ new/gpgmepp-2.1.0/tests/run-genrandom.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -143,14 +143,11 @@ break; } case ZBase32: { - const auto result = ctx->generateRandomBytes(31, Context::RandomMode::ZBase32); + const auto result = ctx->generateRandomZBase32String(); if (result.error()) { std::cerr << "Error: Failed to generate random zbase32 characters: " << result.error().asStdString() << std::endl; } else { - std::string password; - password.reserve(result.value().size()); - std::copy(result.value().begin(), result.value().end(), std::back_inserter(password)); - std::cout << password << std::endl; + std::cout << result.value() << std::endl; } break; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/tests/t-flags.cpp new/gpgmepp-2.1.0/tests/t-flags.cpp --- old/gpgmepp-2.0.0/tests/t-flags.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/gpgmepp-2.1.0/tests/t-flags.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -0,0 +1,240 @@ +/* + t-flags.cpp + + This file is part of GPGME++'s test suite. + Copyright (c) 2025 g10 Code GmbH + Software engineering by Ingo Klöcker <[email protected]> + + GPGME++ 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. + + GPGME++ 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 GPGME++; 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 "flags.h" + +#include <iostream> +#include <sstream> + +static void +print_error_and_exit(std::string_view message, std::string_view file, int line) +{ + std::cerr << file << ':' << line << ": " << message << std::endl; + exit(1); +} + +template<typename T> +static void +compare_helper(T actualValue, T expectedValue, std::string_view expression, std::string_view expected, std::string_view filename, int line) +{ + if (actualValue != expectedValue) { + std::stringstream stream; + stream << "Expression is not equal to " << expected << ": " << expression << std::endl; + stream << "expected: " << expectedValue << std::endl; + stream << "actual : " << actualValue << std::endl; + print_error_and_exit(stream.str(), filename, line); + } +} + +#define ASSERT_TRUE(expression) do { if (!(expression)) { print_error_and_exit("Expression is not true: " #expression, __FILE__, __LINE__); } } while (false) +#define ASSERT_FALSE(expression) do { if ((expression)) { print_error_and_exit("Expression is not false: " #expression, __FILE__, __LINE__); } } while (false) +#define ASSERT_EQUAL(expression, expected) do { compare_helper(expression, expected, #expression, #expected, __FILE__, __LINE__); } while (false) + +enum class TestFlag : unsigned int { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, +}; +using TestFlags = GpgME::Flags<TestFlag, 3>; + +static void +test_construction() +{ + { + const TestFlags flags; + ASSERT_EQUAL(flags.toUnderlyingType(), 0u); + ASSERT_TRUE(!flags); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + } + { + const TestFlags flags{TestFlag::A}; + ASSERT_EQUAL(flags.toUnderlyingType(), 1u); + ASSERT_TRUE(flags); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + } + { + const TestFlags flags{TestFlag::B, TestFlag::C}; + ASSERT_EQUAL(flags.toUnderlyingType(), 6u); + ASSERT_TRUE(flags); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_TRUE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + } + { + const TestFlags flags = TestFlags::fromUnderlyingType(3u); + ASSERT_EQUAL(flags.toUnderlyingType(), 3u); + ASSERT_TRUE(flags); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_TRUE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + } +} + +static void +test_set_reset() +{ + { + TestFlags flags; + flags.set(TestFlag::A); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + flags.set(TestFlag::C); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + flags.set(TestFlag::B); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_TRUE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + flags.set(TestFlag::A, false); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_TRUE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + } + { + TestFlags flags{TestFlag::A, TestFlag::B, TestFlag::C}; + flags.reset(TestFlag::B); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + flags.reset(); + ASSERT_TRUE(!flags); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + } + { + TestFlags flags; + flags.set(TestFlag::A).set(TestFlag::B); + ASSERT_TRUE(flags.test(TestFlag::A)); + ASSERT_TRUE(flags.test(TestFlag::B)); + ASSERT_FALSE(flags.test(TestFlag::C)); + } + { + TestFlags flags; + flags.set(TestFlag::A).set(TestFlag::B).reset(TestFlag::A).set(TestFlag::B, false).set(TestFlag::C); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + } + { + TestFlags flags; + flags.set(TestFlag::A).set(TestFlag::B).reset().set(TestFlag::C); + ASSERT_FALSE(flags.test(TestFlag::A)); + ASSERT_FALSE(flags.test(TestFlag::B)); + ASSERT_TRUE(flags.test(TestFlag::C)); + } +} + +static void +test_operators() +{ + const TestFlags flagsA{TestFlag::A}; + const TestFlags flagsB{TestFlag::B}; + const TestFlags flagsC{TestFlag::C}; + const TestFlags flagsAB{TestFlag::A, TestFlag::B}; + const TestFlags flagsAC{TestFlag::A, TestFlag::C}; + const TestFlags flagsBC{TestFlag::B, TestFlag::C}; + const TestFlags flagsABC{TestFlag::A, TestFlag::B, TestFlag::C}; + { + ASSERT_EQUAL(flagsA | flagsB, flagsAB); + ASSERT_EQUAL(flagsA | TestFlag::B, flagsAB); + ASSERT_EQUAL(TestFlag::A | flagsB, flagsAB); + TestFlags flags = flagsA; + flags |= flagsB; + ASSERT_EQUAL(flags, flagsAB); + flags |= TestFlag::C; + ASSERT_EQUAL(flags, flagsABC); + } + { + ASSERT_EQUAL(flagsAB & flagsB, flagsB); + ASSERT_EQUAL(flagsAB & TestFlag::B, flagsB); + ASSERT_EQUAL(TestFlag::B & flagsAB, flagsB); + TestFlags flags = flagsABC; + flags &= flagsBC; + ASSERT_EQUAL(flags, flagsBC); + flags &= TestFlag::C; + ASSERT_EQUAL(flags, flagsC); + } + { + ASSERT_EQUAL(flagsAB ^ flagsAC, flagsBC); + ASSERT_EQUAL(flagsAB ^ TestFlag::B, flagsA); + ASSERT_EQUAL(TestFlag::B ^ flagsAB, flagsA); + TestFlags flags = flagsAB; + flags ^= flagsAC; + ASSERT_EQUAL(flags, flagsBC); + flags ^= TestFlag::C; + ASSERT_EQUAL(flags, flagsB); + } + { + TestFlags flags = ~flagsAB; + ASSERT_FALSE(flags & flagsA); + ASSERT_FALSE(flags & flagsB); + ASSERT_TRUE(flags & flagsC); + } + { + TestFlags flags = ~flagsABC; + ASSERT_FALSE(flags & flagsA); + ASSERT_FALSE(flags & flagsB); + ASSERT_FALSE(flags & flagsC); + ASSERT_FALSE(flags); + } + { + ASSERT_TRUE(flagsAB != flagsBC); + ASSERT_FALSE(flagsAB != flagsAB); + } +} + +GPGMEPP_DEFINE_ENUM_FLAG_OPERATORS(TestFlags) + +static void +test_additional_flag_operators() +{ + const TestFlags flagsA{TestFlag::A}; + const TestFlags flagsAB{TestFlag::A, TestFlag::B}; + ASSERT_EQUAL(TestFlag::A | TestFlag::A, flagsA); + ASSERT_EQUAL(TestFlag::A | TestFlag::B, flagsAB); + ASSERT_EQUAL(TestFlag::A & TestFlag::A, flagsA); + ASSERT_EQUAL(TestFlag::A & TestFlag::B, TestFlags()); + ASSERT_EQUAL(TestFlag::A ^ TestFlag::A, TestFlags()); + ASSERT_EQUAL(TestFlag::A ^ TestFlag::B, flagsAB); +} + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_construction(); + test_set_reset(); + test_operators(); + test_additional_flag_operators(); + + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gpgmepp-2.0.0/tests/t-utils.cpp new/gpgmepp-2.1.0/tests/t-utils.cpp --- old/gpgmepp-2.0.0/tests/t-utils.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/gpgmepp-2.1.0/tests/t-utils.cpp 2026-05-18 12:04:51.000000000 +0200 @@ -0,0 +1,106 @@ +/* + t-utils.cpp + + This file is part of GPGME++'s test suite. + Copyright (c) 2025 g10 Code GmbH + Software engineering by Ingo Klöcker <[email protected]> + + GPGME++ 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. + + GPGME++ 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 GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "global.h" +#include "util.h" + +#include <iostream> + +using namespace _gpgmepp; + +static void +print_error_and_exit(std::string_view message, std::string_view file, int line) +{ + std::cerr << file << ':' << line << ": " << message << std::endl; + exit(1); +} + +static void +test_split_into_string_views() +{ + { + const auto result = split_into_string_views(nullptr, ','); + if (!result.empty()) { + print_error_and_exit("result not empty", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views("", ','); + if (!result.empty()) { + print_error_and_exit("result not empty", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views("abc", ','); + if ((result.size() != 1) || (result[0] != "abc")) { + print_error_and_exit("unexpected result", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views("abc,def", ','); + if ((result.size() != 2) || (result[0] != "abc") || (result[1] != "def")) { + print_error_and_exit("unexpected result", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views(",abc", ','); + if ((result.size() != 1) || (result[0] != "abc")) { + print_error_and_exit("unexpected result", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views("abc,", ','); + if ((result.size() != 1) || (result[0] != "abc")) { + print_error_and_exit("unexpected result", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views("abc,,def", ','); + if ((result.size() != 2) || (result[0] != "abc") || (result[1] != "def")) { + print_error_and_exit("unexpected result", __FILE__, __LINE__); + } + } + { + const auto result = split_into_string_views(",", ','); + if (!result.empty()) { + print_error_and_exit("result not empty", __FILE__, __LINE__); + } + } +} + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + GpgME::initializeLibrary(); + + test_split_into_string_views(); + + return 0; +} ++++++ gpgmepp.keyring ++++++ --- /var/tmp/diff_new_pack.gp8Szq/_old 2026-06-01 17:58:49.801443477 +0200 +++ /var/tmp/diff_new_pack.gp8Szq/_new 2026-06-01 17:58:49.829444638 +0200 @@ -77,7 +77,19 @@ IPBELwEAq1lsS4pO0+WCQtSAyV5Nxkn+8SEkT4a99D2jmBYKoQWIdQQQFgoAHRYh BMHTS2khnkruwLocIeP9/yGORbcrBQJhaU9aAAoJEOP9/yGORbcrHh4BAOnpoZI3 99TjEuXxFK9BRWNUD5oJOsxBHeFHCrDyWIXbAQDc7cQLHPp7X4Ogi1igkEEsovB6 -IiewZn/6HldiuldOAA== -=gHNs +IiewZn/6HldiuldOAJhzBGmcEd0TCSskAwMCCAEBCwMDBFT19o0ILvhUHY7++XKs +7VXucAoXgkq5WEvqBsIzGGSrWlC6fc+L6bDS6YSSw2yRMWmeY7fukPLaDY/BtAFe +6LYHos4tyUkjHcUliMyEPvnIUapDL+ZS8I+ihkt4wlfWPrQkR251UEcuY29tIChS +ZWxlYXNlIFNpZ25pbmcgS2V5IDIwMjYpiNUEExMJAF0WIQQUkyad5h8SSqaaMW46 +3zTr27IApAUCaZwU6xsUgAAAAAAEAA5tYW51MiwyLjUrMS4xMSwyLDICGwMFCQ8M +a4AFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQOt8069uyAKS3FAF/d81D +kMhp98EHpDa/ZLbvIZuTDvz/wngLOzHvMLy+zAm1UQKU4nRT9YCjiz7/hS55AX4w +h36hfXlNvTpP8tXjY+MNo7oMuHx+a23KgB77KTxKCMoF/ZAetYFhVwcubFRk5KOI +dQQQEwgAHRYhBALzjf9zH/l8sDmh2lSeaV6QW6IIBQJpnBZZAAoJEFSeaV6QW6II ++a4BAJsGiYwA0Vwt0p2pxeSh/IGJTSHfFWYkEMf3pt5HGBGBAP9Hxc04lXgkr5yS +AMOkqdhB2Y/VsrU5IltHfPNRWrBUsYh1BBAWCgAdFiEEbapuZKdtKEBXG0kCUoiX +uCZAOtoFAmmcFpkACgkQUoiXuCZAOtq2fQEA4HuVHLXfTB9WNLA07Tuc8jziaGjG +d3g9PrWIQiUMfsEA/2MPh5wwTaX+8YsBc+DwBGtNshzReN+OBDpnyk/ILEoM +=rCfC -----END PGP PUBLIC KEY BLOCK-----
