+.new
+.vitem &*${headerwrap_*&<&'cols'&>&*_*&<&'limit'&>&*:*&<&'string'&>&*}*&
+.cindex header "wrapping operator"
+.cindex expansion "header wrapping"
+This operator line-wraps its argument in a way useful for headers.
+The &'cols'& value gives the column number to wrap after,
+the &'limit'& gives a limit number of result characters to truncate at.
+Either just the &'limit'& and the preceding underbar, or both, can be omitted;
+the defaults are 80 and 998.
+Wrapping will be inserted at a space if possible before the
+column number is reached.
+Whitespace at a chosen wrap point is removed.
+A line-wrap consists of a newline followed by a tab,
+and the tab is counted as 8 columns.
+.wen
+
+
+
.vitem &*${hex2b64:*&<&'hexstring'&>&*}*&
.cindex "base64 encoding" "conversion from hex"
.cindex "expansion" "hex to base64"
8. New utility exim_msgdate converts message-ids to human readable format.
+ 9. An expansion operator for wrapping long header lines.
+
Version 4.96
------------
* 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 */
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);
-}
/*************************************************
* 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 */
US"expand",
US"h",
US"hash",
+ US"headerwrap",
US"hex2b64",
US"hexquote",
US"ipv6denorm",
EOP_EXPAND,
EOP_H,
EOP_HASH,
+ EOP_HEADERWRAP,
EOP_HEX2B64,
EOP_HEXQUOTE,
EOP_IPV6DENORM,
{
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);
{
uschar *tt = sub;
unsigned long int n = 0;
- while (*tt != 0)
+ while (*tt)
{
uschar *t = Ustrchr(base62_chars, *tt++);
if (!t)
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:
* 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 */
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);
/******************************************************************************/
* 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 */
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 */
hex2b64:${hex2b64:${md5:the quick brown fox}}
hex2b64:${hex2b64:${sha1:the quick brown fox}}
+headerwrap:${headerwrap:}
+headerwrap:${headerwrap:a}
+headerwrap:${headerwrap:ab}
+headerwrap:${headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz}
+headerwrap_79:${headerwrap_79:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz}
+headerwrap:${headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab}
+headerwrap:${headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab}
+headerwrap:${headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab}
+headerwrap:${headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbz}
+headerwrap:${headerwrap:123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(100).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(200).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(300).678901234567890123456789012345678901234567890123456789012345678901234567890123456789(400).67890123456789012345678901234567890123456789012345678901234567890123456789012345\
+67890123456789(500).678901234567890123456789012345678901234567890123456789012345678901234567890123456789(600).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(700).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(800).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(900).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(1000).789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(100).67890123456789}
+headerwrap_81_100:${headerwrap_81_100:123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(100).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(200).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(300).678901234567890123456789012345678901234567890123456789012345678901234567890123456789(400).67890123456789012345678901234567890123456789012345678901234567890123456789012345\
+67890123456789(500).678901234567890123456789012345678901234567890123456789012345678901234567890123456789(600).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(700).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(800).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(900).6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(1000).789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789(100).67890123456789}
+
base32: 0 <${base32:0}>
base32: 1 <${base32:1}>
base32: 31 <${base32:31}>
> hex2b64:MPPJPkZDbetYunCBao7BJA==
> hex2b64:ztcfpyNSMb7Tg/rP3EHE3cwi7PE=
>
+> headerwrap:
+> headerwrap:a
+> headerwrap:ab
+> headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
+> headerwrap_79:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ z
+> headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ b
+> headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
+ Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
+> headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
+ Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
+> headerwrap:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
+ Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbz
+> headerwrap:12345678901234567890123456789012345678901234567890123456789012345678901234567890
+ 1234567890123456789(100).67890123456789012345678901234567890123456789012
+ 34567890123456789012345678901234567890123456789(200).6789012345678901234
+ 567890123456789012345678901234567890123456789012345678901234567890123456
+ 789(300).678901234567890123456789012345678901234567890123456789012345678
+ 901234567890123456789(400).678901234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890123456789(500).67890123456789012
+ 3456789012345678901234567890123456789012345678901234567890123456789(600)
+ .67890123456789012345678901234567890123456789012345678901234567890123456
+ 78901234567890123456789(700).6789012345678901234567890123456789012345678
+ 901234567890123456789012345678901234567890123456789(800).678901234567890
+ 123456789012345678901234567890123456789012345678901234567890123456789012
+ 3456789(900).67890123456789012345678901234567890123456789012345678901234
+ 5678901234567890123456789012
+> headerwrap_81_100:123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ 23456789012345678
+>
> base32: 0 <>
> base32: 1 <b>
> base32: 31 <7>
550-123456789 100 123456789 a really long line to blow the limits
123456789 123456789 123456789 123456789 200 123456789 123456789
123456789 123456789 123456789 123456789 123456789 123456789 123456789 300
- 123456789 123456789 123456789 123456789 123456789 123456789 123456789
+ 123456789 123456789 123456789 123456789 123456789 123456789 123456789
123456789 123456789 400 123456789 123456789 123456789 123456789
123456789 123456789 123456789 123456789 123456789 500 123456789
123456789 123456789 123456789 123456789 123456789 123456789 123456789
123456789 600 123456789 123456789 123456789 123456789 123456789
123456789 123456789 123456789 123456789 700 123456789 123456789
123456789 123456789 123456789 123456789 123456789 123456789 123456789 800
- 123456789 123456789 123456789 123456789 123456789 123456789 123456789
+ 123456789 123456789 123456789 123456789 123456789 123456789 123456789
123456789 123456789 900 123456789 123456789 123456789 123456789
- 123456789 123456789 123456789 123456789 123456789 1000 123456789 12
+ 123456789 123456789 123456789 123456789 123456
Diagnostic-Code: smtp; 550-no mate
550-123456789 100 123456789 a really long line to blow the limits 123456789 123456789 123456789 123456789 200 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 300 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 400 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 500 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 600 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 700 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 800 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 900 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1[truncated]