2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-29 13:18:12 +00:00

postfix-3.6-20201101

This commit is contained in:
Wietse Venema 2020-11-01 00:00:00 -05:00 committed by Viktor Dukhovni
parent 5671f91c4d
commit 225d645d4e
9 changed files with 210 additions and 35 deletions

View File

@ -25251,4 +25251,10 @@ Apologies for any names omitted.
Cleanup: changed the postdrop numerical UID prefix from "#"
to "uid:", and tweaked some local_login_sender_maps
documentatin. Files: proto/postconf.proto, postdrop/postdrop.c.
documentation. Files: proto/postconf.proto, postdrop/postdrop.c.
20201031
Cleanup: don't split a space-comma separated address list
on on space or comma inside a quoted string. Files:
util/mystrtok.c, util/mystetok.ref, global/login_sender_match.c.

View File

@ -53,7 +53,7 @@ r* The lookup table(s) with (login name, sender patterns) entries.
/* .IP ext_delimiters
/* The set of address extension delimiters.
/* .IP null_sender
/* If a sender pattern equals the null_sender pattern, then
/* If a sender pattern equals the null_sender pattern, then
/* the empty address is matched.
/* .IP wildcard
/* Null pointer, or non-empty string with a wildcard pattern.
@ -196,16 +196,16 @@ int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name,
/* flags= */ 0)) != 0) {
/*
* Match the sender. TODO: don't break a sender pattern on a
* comma/space inside a quoted localpart.
* Match the sender. Don't break a sender pattern between double
* quotes.
*/
cp = saved_sender_patterns = mystrdup(sender_patterns);
while (found_or_error == LSM_STAT_NOTFOUND
&& (sender_pattern = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
&& (sender_pattern = mystrtokdq(&cp, CHARS_COMMA_SP)) != 0) {
/* Special pattern: @domain. */
if (*sender_pattern == '@') {
if ((at_sender_domain = strrchr(sender_addr, '@')) != 0
&& strcasecmp_utf8(sender_pattern, at_sender_domain) == 0)
&& strcasecmp_utf8(sender_pattern, at_sender_domain) == 0)
found_or_error = LSM_STAT_FOUND;
}
/* Special pattern: wildcard. */
@ -218,13 +218,15 @@ int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name,
found_or_error = LSM_STAT_FOUND;
}
/* Literal pattern: match the stripped and externalized sender. */
if (ext_stripped_sender == 0)
ext_stripped_sender =
STR(strip_externalize_addr(lsm->ext_stripped_sender,
sender_addr,
lsm->ext_delimiters));
if (strcasecmp_utf8(sender_pattern, ext_stripped_sender) == 0)
found_or_error = LSM_STAT_FOUND;
else {
if (ext_stripped_sender == 0)
ext_stripped_sender =
STR(strip_externalize_addr(lsm->ext_stripped_sender,
sender_addr,
lsm->ext_delimiters));
if (strcasecmp_utf8(sender_pattern, ext_stripped_sender) == 0)
found_or_error = LSM_STAT_FOUND;
}
}
myfree(saved_sender_patterns);
} else {
@ -304,6 +306,18 @@ int main(int argc, char **argv)
"inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}",
"+-", "<>", "*", "uid:12345", "foo", LSM_STAT_FOUND
},
{"unknown \"other last\"",
"inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}",
"+-", "<>", "*", "foo", "other last", LSM_STAT_NOTFOUND
},
{"bare \"first last\"",
"inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}",
"+-", "<>", "*", "foo", "first last", LSM_STAT_FOUND
},
{"\"first last\"@domain",
"inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}",
"+-", "<>", "*", "foo", "first last@example.com", LSM_STAT_FOUND
},
};
struct testcase *tp;
int act_return;

View File

@ -27,3 +27,9 @@ unknown: RUN test case 12 unknown uid:number
unknown: PASS test 12
unknown: RUN test case 13 known uid:number
unknown: PASS test 13
unknown: RUN test case 14 unknown "other last"
unknown: PASS test 14
unknown: RUN test case 15 bare "first last"
unknown: PASS test 15
unknown: RUN test case 16 "first last"@domain
unknown: PASS test 16

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20201026"
#define MAIL_RELEASE_DATE "20201101"
#define MAIL_VERSION_NUMBER "3.6"
#ifdef SNAPSHOT

View File

@ -261,8 +261,10 @@ static int check_login_sender_acl(uid_t uid, VSTRING *sender_buf,
/*
* Optimization.
*/
#ifndef SNAPSHOT
if (strcmp(var_local_login_snd_maps, DEF_LOCAL_LOGIN_SND_MAPS) == 0)
return (CLEANUP_STAT_OK);
#endif
/*
* Get the username.

View File

@ -559,7 +559,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
miss_endif_pcre_test miss_endif_regexp_test split_qnameval_test \
vstring_test vstream_test dict_pcre_file_test dict_regexp_file_test \
dict_cidr_file_test dict_static_file_test dict_random_test \
dict_random_file_test dict_inline_file_test byte_mask_tests
dict_random_file_test dict_inline_file_test byte_mask_tests \
mystrtok_test
root_tests:
@ -957,6 +958,11 @@ vstream_test: vstream vstream_test.in vstream_test.ref
diff vstream_test.ref vstream_test.tmp
rm -f vstream_test.tmp
mystrtok_test: mystrtok mystrtok.ref
$(SHLIB_ENV) ${VALGRIND} ./mystrtok >mystrtok.tmp 2>&1
diff mystrtok.ref mystrtok.tmp
rm -f mystrtok.tmp
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \

View File

@ -14,6 +14,10 @@
/* char **bufp;
/* const char *delimiters;
/* const char *parens;
/*
/* char *mystrtokdq(bufp, delimiters)
/* char **bufp;
/* const char *delimiters;
/* DESCRIPTION
/* mystrtok() splits a buffer on the specified \fIdelimiters\fR.
/* Tokens are delimited by runs of delimiters, so this routine
@ -24,6 +28,11 @@
/* opening and closing parenthesis (one of each). The set of
/* \fIparens\fR must be distinct from the set of \fIdelimiters\fR.
/*
/* mystrtokdq() is like mystrtok() but will not split text
/* between double quotes. The backslash character may be used
/* to escape characters. The double quote and backslash
/* character must not appear in the set of \fIdelimiters\fR.
/*
/* The \fIbufp\fR argument specifies the start of the search; it
/* is updated with each call. The input is destroyed.
/*
@ -80,7 +89,7 @@ char *mystrtok(char **src, const char *sep)
char *mystrtokq(char **src, const char *sep, const char *parens)
{
char *start = *src;
static char *cp;
static char *cp;
int ch;
int level;
@ -99,7 +108,7 @@ char *mystrtokq(char **src, const char *sep, const char *parens)
for (level = 0, cp = start; (ch = *(unsigned char *) cp) != 0; cp++) {
if (ch == parens[0]) {
level++;
} else if (level > 0 && ch == parens[1]) {
} else if (level > 0 && ch == parens[1]) {
level--;
} else if (level == 0 && strchr(sep, ch) != 0) {
*cp++ = 0;
@ -110,34 +119,135 @@ char *mystrtokq(char **src, const char *sep, const char *parens)
return (start);
}
/* mystrtokdq - safe tokenizer, double quote and backslash support */
char *mystrtokdq(char **src, const char *sep)
{
char *cp = *src;
char *start;
/*
* Skip leading delimiters.
*/
cp += strspn(cp, sep);
/*
* Skip to next unquoted space or comma.
*/
if (*cp == 0) {
start = 0;
} else {
int in_quotes;
for (in_quotes = 0, start = cp; *cp; cp++) {
if (*cp == '\\') {
if (*++cp == 0)
break;
} else if (*cp == '"') {
in_quotes = !in_quotes;
} else if (!in_quotes && strchr(sep, *(unsigned char *) cp) != 0) {
*cp++ = 0;
break;
}
}
}
*src = cp;
return (start);
}
#ifdef TEST
/*
* Test program: read lines from stdin, split on whitespace.
* Test program.
*/
#include "vstring.h"
#include "vstream.h"
#include "vstring_vstream.h"
#include "msg.h"
#include "mymalloc.h"
/*
* The following needs to be large enough to include a null terminator in
* every testcase.expected field.
*/
#define EXPECT_SIZE 5
struct testcase {
const char *action;
const char *input;
const char *expected[EXPECT_SIZE];
};
static const struct testcase testcases[] = {
{"mystrtok", ""},
{"mystrtok", " foo ", {"foo"}},
{"mystrtok", " foo bar ", {"foo", "bar"}},
{"mystrtokq", ""},
{"mystrtokq", "foo bar", {"foo", "bar"}},
{"mystrtokq", "{ bar } ", {"{ bar }"}},
{"mystrtokq", "foo { bar } baz", {"foo", "{ bar }", "baz"}},
{"mystrtokq", "foo{ bar } baz", {"foo{ bar }", "baz"}},
{"mystrtokq", "foo { bar }baz", {"foo", "{ bar }baz"}},
{"mystrtokdq", ""},
{"mystrtokdq", " foo ", {"foo"}},
{"mystrtokdq", " foo bar ", {"foo", "bar"}},
{"mystrtokdq", " foo\\ bar ", {"foo\\ bar"}},
{"mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}},
{"mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}},
};
int main(void)
{
VSTRING *vp = vstring_alloc(100);
char *start;
char *str;
const struct testcase *tp;
char *actual;
int pass;
int fail;
int match;
int n;
while (vstring_fgets(vp, VSTREAM_IN) && VSTRING_LEN(vp) > 0) {
start = vstring_str(vp);
if (strchr(start, CHARS_BRACE[0]) == 0) {
while ((str = mystrtok(&start, CHARS_SPACE)) != 0)
vstream_printf(">%s<\n", str);
} else {
while ((str = mystrtokq(&start, CHARS_SPACE, CHARS_BRACE)) != 0)
vstream_printf(">%s<\n", str);
#define NUM_TESTS sizeof(testcases)/sizeof(testcases[0])
#define STR_OR_NULL(s) ((s) ? (s) : "null")
for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) {
char *saved_input = mystrdup(tp->input);
char *cp = saved_input;
msg_info("RUN test case %ld %s >%s<",
(long) (tp - testcases), tp->action, tp->input);
#if 0
msg_info("action=%s", tp->action);
msg_info("input=%s", tp->input);
for (n = 0; tp->expected[n]; tp++)
msg_info("expected[%d]=%s", n, tp->expected[n]);
#endif
for (n = 0; n < EXPECT_SIZE; n++) {
if (strcmp(tp->action, "mystrtok") == 0) {
actual = mystrtok(&cp, CHARS_SPACE);
} else if (strcmp(tp->action, "mystrtokq") == 0) {
actual = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE);
} else if (strcmp(tp->action, "mystrtokdq") == 0) {
actual = mystrtokdq(&cp, CHARS_SPACE);
} else {
msg_panic("invalid command: %s", tp->action);
}
if ((match = (actual && tp->expected[n]) ?
(strcmp(actual, tp->expected[n]) == 0) :
(actual == tp->expected[n])) != 0) {
if (actual == 0) {
msg_info("PASS test %ld", (long) (tp - testcases));
pass++;
break;
}
} else {
msg_warn("expected: >%s<, got: >%s<",
STR_OR_NULL(tp->expected[n]), STR_OR_NULL(actual));
msg_info("FAIL test %ld", (long) (tp - testcases));
fail++;
break;
}
}
vstream_fflush(VSTREAM_OUT);
if (n >= EXPECT_SIZE)
msg_panic("need to increase EXPECT_SIZE");
myfree(saved_input);
}
vstring_free(vp);
return (0);
return (fail > 0);
}
#endif

View File

@ -0,0 +1,30 @@
unknown: RUN test case 0 mystrtok ><
unknown: PASS test 0
unknown: RUN test case 1 mystrtok > foo <
unknown: PASS test 1
unknown: RUN test case 2 mystrtok > foo bar <
unknown: PASS test 2
unknown: RUN test case 3 mystrtokq ><
unknown: PASS test 3
unknown: RUN test case 4 mystrtokq >foo bar<
unknown: PASS test 4
unknown: RUN test case 5 mystrtokq >{ bar } <
unknown: PASS test 5
unknown: RUN test case 6 mystrtokq >foo { bar } baz<
unknown: PASS test 6
unknown: RUN test case 7 mystrtokq >foo{ bar } baz<
unknown: PASS test 7
unknown: RUN test case 8 mystrtokq >foo { bar }baz<
unknown: PASS test 8
unknown: RUN test case 9 mystrtokdq ><
unknown: PASS test 9
unknown: RUN test case 10 mystrtokdq > foo <
unknown: PASS test 10
unknown: RUN test case 11 mystrtokdq > foo bar <
unknown: PASS test 11
unknown: RUN test case 12 mystrtokdq > foo\ bar <
unknown: PASS test 12
unknown: RUN test case 13 mystrtokdq > foo \" bar<
unknown: PASS test 13
unknown: RUN test case 14 mystrtokdq > foo " bar baz" <
unknown: PASS test 14

View File

@ -30,6 +30,7 @@ extern char *trimblanks(char *, ssize_t);
extern char *concatenate(const char *,...);
extern char *mystrtok(char **, const char *);
extern char *mystrtokq(char **, const char *, const char *);
extern char *mystrtokdq(char **, const char *);
extern char *translit(char *, const char *, const char *);
#define printable(string, replacement) \