From a6d90e094d4e2a84d18859cf2005d10c2020e2d4 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 11 Mar 2023 17:48:28 +0000 Subject: Header-wrap expansion. Bug 2843 --- src/src/deliver.c | 67 ++++-------------------------------------- src/src/expand.c | 31 ++++++++++++++++++-- src/src/functions.h | 3 +- src/src/header.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/src/deliver.c b/src/src/deliver.c index 9b77b3619..e2994b116 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -5566,69 +5566,14 @@ Limit to about 1024 chars total. */ static void dsn_put_wrapped(FILE * fp, const uschar * header, const uschar * s) { -const uschar * t; -int llen = fprintf(fp, "%s", CS header), sleft = Ustrlen(s); -int remain = 1022 - llen; +gstring * g = string_cat(NULL, header); -if (*s && remain > 0) - { - for(;;) - { - unsigned ltail; /* source chars to skip */ - - /* Chop at a newline, or end of string */ - - if ((t = Ustrchr(s, '\\')) && t[1] == 'n') - ltail = 2; - else if ((t = Ustrchr(s, '\n'))) - ltail = 1; - else - { - t = s + sleft; - ltail = 0; - } - - /* If that is too long, search backward for a space */ - - if ((llen + t - s) > 78) - { - const uschar * u; - for (u = s + 78 - llen; u > s + 10; --u) if (*u == ' ') break; - if (u > s + 10) - { /* found a space to linebreak at */ - llen = u - s; - remain -= fprintf(fp, "%.*s", (int)llen, s); - s += ++llen; /* skip the space also */ - } - else if (llen < 78) - { /* just linebreak at 78 */ - llen = 78 - llen; - remain -= fprintf(fp, "%.*s", llen, s); - s += llen; - } - else /* header rather long */ - llen = 0; - } - else - { - llen = t - s; - remain -= fprintf(fp, "%.*s", llen, s); - s = t + ltail; - } +g = string_cat(g, s); +gstring_release_unused(g); +fprintf(fp, "%s\n", wrap_header(string_from_gstring(g), 79, 1023, US" ", 1)); +} - sleft -= llen; - remain -= 2; - if (!*s || remain <= 0) - break; - fputs("\n ", fp); - llen = 1; /* one for the leading space output above */ - } - if (s[-1] != '\n') fputs("\n", fp); - } -else - fputs("\n", fp); -} /************************************************* diff --git a/src/src/expand.c b/src/src/expand.c index baf7134cd..6dcd45062 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -237,6 +237,7 @@ static uschar *op_table_main[] = { US"expand", US"h", US"hash", + US"headerwrap", US"hex2b64", US"hexquote", US"ipv6denorm", @@ -284,6 +285,7 @@ enum { EOP_EXPAND, EOP_H, EOP_HASH, + EOP_HEADERWRAP, EOP_HEX2B64, EOP_HEXQUOTE, EOP_IPV6DENORM, @@ -7262,7 +7264,7 @@ NOT_ITEM: ; { uschar *t; unsigned long int n = Ustrtoul(sub, &t, 10); - if (*t != 0) + if (*t) { expand_string_message = string_sprintf("argument for base62 " "operator is \"%s\", which is not a decimal number", sub); @@ -7278,7 +7280,7 @@ NOT_ITEM: ; { uschar *tt = sub; unsigned long int n = 0; - while (*tt != 0) + while (*tt) { uschar *t = Ustrchr(base62_chars, *tt++); if (!t) @@ -7428,6 +7430,29 @@ NOT_ITEM: ; goto EXPAND_FAILED; #endif + /* Line-wrap a string as if it is a header line */ + + case EOP_HEADERWRAP: + { + unsigned col = 80, lim = 998; + uschar * s; + + if (arg) + { + const uschar * list = arg; + int sep = '_'; + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + { + col = atoi(CS s); + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + lim = atoi(CS s); + } + } + if ((s = wrap_header(sub, col, lim, US"\t", 8))) + yield = string_cat(yield, s); + } + break; + /* Convert hex encoding to base64 encoding */ case EOP_HEX2B64: diff --git a/src/src/functions.h b/src/src/functions.h index 37f0a57bc..5fbb426ec 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -678,6 +678,7 @@ extern void version_init(void); extern BOOL write_chunk(transport_ctx *, uschar *, int); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); +extern uschar *wrap_header(const uschar *, unsigned, unsigned, const uschar *, unsigned); /******************************************************************************/ diff --git a/src/src/header.c b/src/src/header.c index a4dd6e72e..e2b3d8a9c 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -466,4 +466,85 @@ va_end(ap); return !cond; } + + +/* Wrap and truncate a string for use as a header. +Convert either the sequence "\n" or a real newline into newline plus indent. +If that still takes us past the column limit, look for the last space +and split there too. +Limit to the given max total char count. + +Return: string or NULL */ + +uschar * +wrap_header(const uschar * s, unsigned cols, unsigned maxchars, + const uschar * indent, unsigned indent_cols) +{ +gstring * g = NULL; + +if (maxchars == 0) maxchars = INT_MAX; +if (cols == 0) cols = INT_MAX; + +if (s && *s) + { + int sleft = Ustrlen(s); + for(unsigned llen = 0; ; llen = indent_cols) + { + const uschar * t; + unsigned ltail = 0, glen; + + if ((t = Ustrchr(s, '\\')) && t[1] == 'n') + ltail = 2; + else if ((t = Ustrchr(s, '\n'))) + ltail = 1; + else + t = s + sleft; + + if ((llen + t - s) > cols) /* more than a linesworth of s */ + { /* look backward for whitespace */ + for (const uschar * u = s + cols - llen; u > s + 10; --u) if (isspace(*u)) + { + llen = u - s; + while (u > s+1 && isspace(u[-1])) --u; /* find start of whitespace */ + g = string_catn(g, s, u - s); + s += ++llen; /* skip the space */ + while (*s && isspace(*s)) /* and any trailing */ + s++, llen++; + goto LDONE; + } + /* no whitespace */ + if (llen < cols) + { /* just linebreak at 80 */ + llen = cols - llen; + g = string_catn(g, s, llen); + s += llen; + } + else + llen = 0; + LDONE: + } + else /* rest of s fits in line */ + { + llen = t - s; + g = string_catn(g, s, llen); + s = t + ltail; + } + + if (!*s) + break; /* no trailing linebreak */ + if ((glen = gstring_length(g)) >= maxchars) + { + gstring_trim(g, glen - maxchars); + break; /* no trailing linebreak */ + } + sleft -= llen; + g = string_catn(g, US"\n", 1); + g = string_catn(g, indent, 1); + } + } +gstring_release_unused(g); +return string_from_gstring(g); +} + + /* End of header.c */ -- cgit v1.2.1