diff options
author | Werner Koch <[email protected]> | 2025-05-30 10:15:37 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2025-05-30 12:19:03 +0000 |
commit | 61514f7cd8cf5a747398d6ef057413ea980376b7 (patch) | |
tree | 80c59f14ae3f12affc5e4124ff2ff654225a3878 | |
parent | common: Improve helpfile.c to provide a generic template API. (diff) | |
download | gnupg-61514f7cd8cf5a747398d6ef057413ea980376b7.tar.gz gnupg-61514f7cd8cf5a747398d6ef057413ea980376b7.zip |
tools: Add a quoted-printable encoding function.
* tools/mime-maker.c (mime_maker_qp_encode): New.
* tools/t-mime-maker.c: New.
* tools/Makefile.am (TESTS): New.
(module_tests): Add the first test.
-rw-r--r-- | tools/Makefile.am | 26 | ||||
-rw-r--r-- | tools/mime-maker.c | 86 | ||||
-rw-r--r-- | tools/mime-maker.h | 1 | ||||
-rw-r--r-- | tools/t-mime-maker.c | 116 |
4 files changed, 227 insertions, 2 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am index 3769d6606..3807bd14c 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -51,6 +51,12 @@ gpg-wks-client-w32info.o : gpg-wks-client.w32-manifest \ endif +if DISABLE_TESTS +TESTS = +else +TESTS = $(module_tests) +endif + AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) sbin_SCRIPTS = addgnupghome applygnupgdefaults @@ -76,7 +82,7 @@ if !HAVE_W32_SYSTEM libexec_PROGRAMS += gpg-auth endif -noinst_PROGRAMS = clean-sat make-dns-cert +noinst_PROGRAMS = clean-sat make-dns-cert $(module_tests) if BUILD_GPGTAR bin_PROGRAMS += gpgtar @@ -216,6 +222,24 @@ gpg_auth_LDADD = $(common_libs) \ $(GPG_ERROR_LIBS) \ $(LIBINTL) $(NETLIBS) $(LIBICONV) +# +# Module tests +# +module_tests = t-mime-maker + +t_extra_src = +t_common_cflags = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) +t_common_ldadd = $(libcommon) \ + $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) + +t_mime_maker_SOURCES = t-mime-maker.c \ + rfc822parse.c rfc822parse.h \ + mime-maker.c mime-maker.h $(t_extra_src) +t_mime_maker_LDADD = $(t_common_ldadd) + + + # Instead of a symlink we install a simple wrapper script for the new # gpg-wks-client location. We assume bin is a sibling of libexec. install-exec-local: diff --git a/tools/mime-maker.c b/tools/mime-maker.c index 12dae910e..e631baba4 100644 --- a/tools/mime-maker.c +++ b/tools/mime-maker.c @@ -1,5 +1,5 @@ /* mime-maker.c - Create MIME structures - * Copyright (C) 2016 g10 Code GmbH + * Copyright (C) 2016, 2025 g10 Code GmbH * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. @@ -16,6 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <config.h> @@ -348,6 +349,89 @@ add_header (part_t part, const char *name, const char *value) } +/* Return a new string which is QP encoded. Also encodes the F in a + * leading "From " and a line with a single dot. Return NULL and sets + * ERRNO on error. */ +char * +mime_maker_qp_encode (const char *string) +{ + /* Check whether the current character is followed by a line ending. + * The end of the string also considered a line ending. */ +#define nextislf(a) (*a && ((a[1] == '\r' && a[2] == '\n') \ + || a[1] == '\n' || !a[1])) + +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + + char *buffer, *p; + const unsigned char *s; + int lastwaslf; + size_t n; + + /* Count the required length. */ + for (lastwaslf=1, n=0, s = string; *s; s++) + { + if (*s == '.' && nextislf (s)) + { + n += 3; + } + else if (!strncmp (s, "From ", 5)) + { + n += 3 + 4; + s += 4; + } + else if (*s == '=') + { + n += 3; + } + else if (strchr ("\r\n\t", *s) || (*s >= 0x20 && *s <= 0x7e)) + n++; + else + n += 3; + } + n++; + + /* Now encode. */ + p = buffer = xtrymalloc (n); + if (!buffer) + return NULL; + for (lastwaslf=1, s = string; *s; s++) + { + if (lastwaslf && *s == '.' && nextislf (s)) + { + *p++ = '='; + *p++ = '2'; + *p++ = 'E'; + } + else if (lastwaslf && !strncmp (s, "From ", 5)) + { + memcpy (p, "=46rom ", 7); + p += 7; + s += 4; + } + else if (*s == '=') + { + *p++ = '='; + *p++ = '3'; + *p++ = 'D'; + } + else if (strchr ("\r\n\t", *s) || (*s >= 0x20 && *s <= 0x7e)) + *p++ = *s; + else + { + *p++ = '='; + *p++ = tohex ((*s>>4)&15); + *p++ = tohex (*s&15); + } + lastwaslf = (*s == '\n'); + } + *p = 0; + + return buffer; +#undef tohex +#undef nextislf +} + + /* Add a header with NAME and VALUE to the current mail. A LF in the * VALUE will be handled automagically. If NULL is used for VALUE it * is expected that the NAME has the format "NAME=VALUE" and VALUE is diff --git a/tools/mime-maker.h b/tools/mime-maker.h index c0ddaeaa5..f7da1501b 100644 --- a/tools/mime-maker.h +++ b/tools/mime-maker.h @@ -31,6 +31,7 @@ void mime_maker_set_verbose (mime_maker_t ctx, int level); void mime_maker_dump_tree (mime_maker_t ctx); +char *mime_maker_qp_encode (const char *string); gpg_error_t mime_maker_add_header (mime_maker_t ctx, const char *name, const char *value); gpg_error_t mime_maker_add_body (mime_maker_t ctx, const char *string); diff --git a/tools/t-mime-maker.c b/tools/t-mime-maker.c new file mode 100644 index 000000000..8af0a1986 --- /dev/null +++ b/tools/t-mime-maker.c @@ -0,0 +1,116 @@ +/* t-mime-maker.c - Module test for mime-maker.c + * Copyright (C) 2025 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "../common/util.h" +#include "../common/init.h" +#include "mime-maker.h" + + +static int verbose; + + + +static void +test_qp_encode (void) +{ + static struct + { + const char *plain; + const char *encoded; + } samples[] = + { + { "", + "" + },{ + "From someone\n" + " I received this mail\n", + "=46rom someone\n" + " I received this mail\n" + },{ + " From someone\n", + " From someone\n" + },{ + "Foo\n" + ".\n", + "Foo\n" + "=2E\n" + },{ + "Foo\n" + ".", + "Foo\n" + "=2E" + },{ + "Hello ÄÖܧäöüß my dear umlauts", + "Hello =C3=84=C3=96=C3=9C=C2=A7=C3=A4=C3=B6=C3=BC=C3=9F " + "my dear umlauts" + },{ + "👀\tⒶ", + "=F0=9F=91=80\t=E2=92=B6" + } + }; + int idx; + char *result; + int oops = 0; + + for (idx=0; idx < DIM (samples); idx++) + { + result = mime_maker_qp_encode (samples[idx].plain); + if (!result) + { + log_error ("%s:test %d: error: %s\n", + __func__, idx, strerror (errno)); + exit (1); + } + if (strcmp (samples[idx].encoded, result)) + { + log_error ("%s:test %d: error\nwant ===>%s<===\n got ===>%s<===\n", + __func__, idx, samples[idx].plain, result); + oops = 1; + } + } + + if (oops) + exit (1); +} + + +int +main (int argc, char **argv) +{ + log_set_prefix ("t-mime-maker", GPGRT_LOG_WITH_PREFIX); + init_common_subsystems (&argc, &argv); + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_qp_encode (); + + return 0; +} |