X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4729b6937b582a029147ba51298f5c300c5b1891..443245384239776d29f5386050f56247c7404c1e:/src/src/header.c diff --git a/src/src/header.c b/src/src/header.c index 720446bff..97fa44b4e 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ -/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" @@ -29,11 +30,12 @@ Returns: TRUE or FALSE */ BOOL -header_testname(header_line *h, const uschar *name, int len, BOOL notdel) +header_testname(const header_line * h, const uschar * name, int len, + BOOL notdel) { uschar *tt; if (h->type == '*' && notdel) return FALSE; -if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE; +if (!h->text || strncmpic(h->text, name, len) != 0) return FALSE; tt = h->text + len; while (*tt == ' ' || *tt == '\t') tt++; return *tt == ':'; @@ -45,11 +47,11 @@ return *tt == ':'; header_testname() above. */ BOOL -header_testname_incomplete(header_line *h, const uschar *name, +header_testname_incomplete(const header_line * h, const uschar * name, int len, BOOL notdel) { if (h->type == '*' && notdel) return FALSE; -if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE; +if (!h->text || strncmpic(h->text, name, len) != 0) return FALSE; return TRUE; } @@ -102,13 +104,13 @@ gstring gs; if (!header_last) return NULL; -gs.s = buf = store_get(HEADER_ADD_BUFFER_SIZE, FALSE); +gs.s = buf = store_get(HEADER_ADD_BUFFER_SIZE, GET_UNTAINTED); gs.size = HEADER_ADD_BUFFER_SIZE; gs.ptr = 0; if (!string_vformat(&gs, SVFMT_REBUFFER, format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string too long in header_add: " - "%.100s ...", string_from_gstring(&gs)); + "%.100Y ...", &gs); if (gs.s != buf) store_release_above(buf); gstring_release_unused(&gs); @@ -182,7 +184,7 @@ for (p = q = gs.s; *p; p = q) if (*(++q) != ' ' && *q != '\t') break; } - new = store_get(sizeof(header_line), FALSE); + new = store_get(sizeof(header_line), GET_UNTAINTED); new->text = string_copyn(p, q - p); new->slen = q - p; new->type = type; @@ -368,15 +370,15 @@ Returns: cond if the header exists and contains one of the strings; /* First we have a local subroutine to handle a single pattern */ static BOOL -one_pattern_match(uschar *name, int slen, BOOL has_addresses, uschar *pattern) +one_pattern_match(uschar * name, int slen, BOOL has_addresses, uschar * pattern) { BOOL yield = FALSE; -const pcre *re = NULL; +const pcre2_code *re = NULL; /* If the pattern is a regex, compile it. Bomb out if compiling fails; these patterns are all constructed internally and should be valid. */ -if (*pattern == '^') re = regex_must_compile(pattern, TRUE, FALSE); +if (*pattern == '^') re = regex_must_compile(pattern, MCS_CASELESS, FALSE); /* Scan for the required header(s) and scan each one */ @@ -419,10 +421,9 @@ for (header_line * h = header_list; !yield && h; h = h->next) /* Otherwise, test for the pattern; a non-regex must be an exact match */ - yield = !re - ? (strcmpic(next, pattern) == 0) - : (pcre_exec(re, NULL, CS next, Ustrlen(next), 0, PCRE_EOPT, NULL, 0) - >= 0); + yield = re + ? regex_match(re, next, -1, NULL) + : (strcmpic(next, pattern) == 0); } } @@ -431,10 +432,9 @@ for (header_line * h = header_list; !yield && h; h = h->next) else { - yield = (re == NULL)? - (strstric(h->text, pattern, FALSE) != NULL) - : - (pcre_exec(re, NULL, CS h->text, h->slen, 0, PCRE_EOPT, NULL, 0) >= 0); + yield = re + ? regex_match(re, h->text, h->slen, NULL) + : (strstric(h->text, pattern, FALSE) != NULL); } } @@ -445,7 +445,7 @@ return yield; /* The externally visible interface */ BOOL -header_match(uschar *name, BOOL has_addresses, BOOL cond, string_item *strings, +header_match(uschar * name, BOOL has_addresses, BOOL cond, string_item * strings, int count, ...) { va_list ap; @@ -467,4 +467,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 */