aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-05-30 10:15:37 +0000
committerWerner Koch <[email protected]>2025-05-30 12:19:03 +0000
commit61514f7cd8cf5a747398d6ef057413ea980376b7 (patch)
tree80c59f14ae3f12affc5e4124ff2ff654225a3878
parentcommon: Improve helpfile.c to provide a generic template API. (diff)
downloadgnupg-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.am26
-rw-r--r--tools/mime-maker.c86
-rw-r--r--tools/mime-maker.h1
-rw-r--r--tools/t-mime-maker.c116
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;
+}