From: Wietse Venema
What notifications are sent: success, failure, delay, or -none.
+none. Normally, Postfix informs the sender only mail when delivery +is delayed or when delivery fails.What content is returned in case of failure: only the message headers, or the full message.
@@ -68,10 +69,12 @@ internal infrastructure than desirable. Unfortunately, disallowing requests as well. The RFCs do not offer the option to negotiate feature subsets. -This is not as bad as it sounds. Remote senders with DSN support -will still be informed that their mail reached your Postfix gateway -successfully; they just will not get successful delivery notices -from your internal systems.
+This is not as bad as it sounds. When you turn off DSN for +remote inbound mail, remote senders with DSN support will still be +informed that their mail reached your Postfix gateway successfully; +they just will not get successful delivery notices from your internal +systems. Remote senders lose very little: they can no longer specify +how Postfix should report delayed or failed delivery.
Use the smtpd_discard_ehlo_keyword_address_maps feature if you wish to allow DSN requests from trusted clients but not from random diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index 3890e73a9..bb7a9bcfc 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -227,33 +227,27 @@ ACCESS(5) ACCESS(5) transport(5) table to direct mail to the discard(8) service. - Note: this action does not work in Postfix 2.2 - smtpd_end_of_data_restrictions. - This feature is available in Postfix 2.0 and later. - DUNNO Pretend that the lookup key was not found. This - prevents Postfix from trying substrings of the - lookup key (such as a subdomain name, or a network + DUNNO Pretend that the lookup key was not found. This + prevents Postfix from trying substrings of the + lookup key (such as a subdomain name, or a network address subnetwork). This feature is available in Postfix 2.0 and later. FILTER transport:destination - After the message is queued, send the entire mes- + After the message is queued, send the entire mes- sage through the specified external content filter. - The transport:destination syntax is described in - the transport(5) manual page. More information - about external content filters is in the Postfix + The transport:destination syntax is described in + the transport(5) manual page. More information + about external content filters is in the Postfix FILTER_README file. - Note: this action overrides the main.cf con- + Note: this action overrides the main.cf con- tent_filter setting, and currently affects all recipients of the message. - Note: this action does not work in Postfix 2.2 - smtpd_end_of_data_restrictions. - This feature is available in Postfix 2.0 and later. HOLD optional text... @@ -274,37 +268,31 @@ ACCESS(5) ACCESS(5) Note: this action currently affects all recipients of the message. - Note: this action does not work in Postfix 2.2 - smtpd_end_of_data_restrictions. - This feature is available in Postfix 2.0 and later. PREPEND headername: headervalue - Prepend the specified message header to the mes- + Prepend the specified message header to the mes- sage. When this action is used multiple times, the - first prepended header appears before the second + first prepended header appears before the second etc. prepended header. - Note: this action does not support multi-line mes- + Note: this action does not support multi-line mes- sage headers. - Note: this action must be used before the message - content is received; it cannot be used in + Note: this action must be used before the message + content is received; it cannot be used in smtpd_end_of_data_restrictions. This feature is available in Postfix 2.1 and later. REDIRECT user@domain - After the message is queued, send the message to + After the message is queued, send the message to the specified address instead of the intended recipient(s). - Note: this action overrides the FILTER action, and + Note: this action overrides the FILTER action, and currently affects all recipients of the message. - Note: this action does not work in Postfix 2.2 - smtpd_end_of_data_restrictions. - This feature is available in Postfix 2.1 and later. WARN optional text... diff --git a/postfix/html/pcre_table.5.html b/postfix/html/pcre_table.5.html index 238c1c206..8698d02a8 100644 --- a/postfix/html/pcre_table.5.html +++ b/postfix/html/pcre_table.5.html @@ -50,112 +50,120 @@ PCRE_TABLE(5) PCRE_TABLE(5) Note: do not prepend whitespace to patterns inside if..endif. + This feature is available in Postfix 2.1 and later. + if !/pattern/flags endif Match the input string against the patterns between - if and endif, if and only if the input string does + if and endif, if and only if the input string does not match pattern. The if..endif can nest. + Note: do not prepend whitespace to patterns inside + if..endif. + + This feature is available in Postfix 2.1 and later. + blank lines and comments - Empty lines and whitespace-only lines are ignored, - as are lines whose first non-whitespace character + Empty lines and whitespace-only lines are ignored, + as are lines whose first non-whitespace character is a `#'. multi-line text - A logical line starts with non-whitespace text. A - line that starts with whitespace continues a logi- + A logical line starts with non-whitespace text. A + line that starts with whitespace continues a logi- cal line. Each pattern is a perl-like regular expression. The - expression delimiter can be any character, except white- - space or characters that have special meaning (tradition- - ally the forward slash is used). The regular expression + expression delimiter can be any character, except white- + space or characters that have special meaning (tradition- + ally the forward slash is used). The regular expression can contain whitespace. By default, matching is case-insensitive, and newlines are - not treated as special characters. The behavior is con- - trolled by flags, which are toggled by appending one or + not treated as special characters. The behavior is con- + trolled by flags, which are toggled by appending one or more of the following characters after the pattern: i (default: on) - Toggles the case sensitivity flag. By default, + Toggles the case sensitivity flag. By default, matching is case insensitive. m (default: off) - Toggles the PCRE_MULTILINE flag. When this flag is - on, the ^ and $ metacharacters match immediately - after and immediately before a newline character, - respectively, in addition to matching at the start + Toggles the PCRE_MULTILINE flag. When this flag is + on, the ^ and $ metacharacters match immediately + after and immediately before a newline character, + respectively, in addition to matching at the start and end of the subject string. s (default: on) Toggles the PCRE_DOTALL flag. When this flag is on, the . metacharacter matches the newline character. With Postfix versions prior to 2.0, The flag is off - by default, which is inconvenient for multi-line + by default, which is inconvenient for multi-line message header matching. x (default: off) - Toggles the pcre extended flag. When this flag is - on, whitespace in the pattern (other than in a + Toggles the pcre extended flag. When this flag is + on, whitespace in the pattern (other than in a character class) and characters between a # outside - a character class and the next newline character - are ignored. An escaping backslash can be used to - include a whitespace or # character as part of the + a character class and the next newline character + are ignored. An escaping backslash can be used to + include a whitespace or # character as part of the pattern. A (default: off) - Toggles the PCRE_ANCHORED flag. When this flag is - on, the pattern is forced to be "anchored", that + Toggles the PCRE_ANCHORED flag. When this flag is + on, the pattern is forced to be "anchored", that is, it is constrained to match only at the start of - the string which is being searched (the "subject - string"). This effect can also be achieved by + the string which is being searched (the "subject + string"). This effect can also be achieved by appropriate constructs in the pattern itself. E (default: off) - Toggles the PCRE_DOLLAR_ENDONLY flag. When this - flag is on, a $ metacharacter in the pattern - matches only at the end of the subject string. - Without this flag, a dollar also matches immedi- + Toggles the PCRE_DOLLAR_ENDONLY flag. When this + flag is on, a $ metacharacter in the pattern + matches only at the end of the subject string. + Without this flag, a dollar also matches immedi- ately before the final character if it is a newline character (but not before any other newline charac- - ters). This flag is ignored if PCRE_MULTILINE flag + ters). This flag is ignored if PCRE_MULTILINE flag is set. U (default: off) Toggles the ungreedy matching flag. When this flag - is on, the pattern matching engine inverts the - "greediness" of the quantifiers so that they are - not greedy by default, but become greedy if fol- - lowed by "?". This flag can also set by a (?U) + is on, the pattern matching engine inverts the + "greediness" of the quantifiers so that they are + not greedy by default, but become greedy if fol- + lowed by "?". This flag can also set by a (?U) modifier within the pattern. X (default: off) Toggles the PCRE_EXTRA flag. When this flag is on, - any backslash in a pattern that is followed by a + any backslash in a pattern that is followed by a letter that has no special meaning causes an error, thus reserving these combinations for future expan- sion. SEARCH ORDER - Patterns are applied in the order as specified in the ta- - ble, until a pattern is found that matches the input + Patterns are applied in the order as specified in the ta- + ble, until a pattern is found that matches the input string. - Each pattern is applied to the entire input string. - Depending on the application, that string is an entire + Each pattern is applied to the entire input string. + Depending on the application, that string is an entire client hostname, an entire client IP address, or an entire - mail address. Thus, no parent domain or parent network - search is done, and user@domain mail addresses are not - broken up into their user and domain constituent parts, + mail address. Thus, no parent domain or parent network + search is done, and user@domain mail addresses are not + broken up into their user and domain constituent parts, nor is user+foo broken up into user and foo. TEXT SUBSTITUTION - Substitution of substrings from the matched expression - into the result string is possible using the conventional - perl syntax ($1, $2, etc.). The macros in the result - string may need to be written as ${n} or $(n) if they - aren't followed by whitespace. + Substitution of substrings from the matched expression + into the result string is possible using the conventional + perl syntax ($1, $2, etc.); specify $$ to produce a $ + character as output. The macros in the result string may + need to be written as ${n} or $(n) if they aren't followed + by whitespace. Note: since negated patterns (those preceded by !) return a result when the expression does not match, substitutions diff --git a/postfix/html/regexp_table.5.html b/postfix/html/regexp_table.5.html index aa543964b..d42fa4483 100644 --- a/postfix/html/regexp_table.5.html +++ b/postfix/html/regexp_table.5.html @@ -50,11 +50,19 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) Note: do not prepend whitespace to patterns inside if..endif. + This feature is available in Postfix 2.1 and later. + if !/pattern/flags endif Match the input string against the patterns between if and endif, if and only if that same input string - does not match pattern. The if..endif can nest. + does not match pattern. The if..endif can nest. + matches pattern. The if..endif can nest. + + Note: do not prepend whitespace to patterns inside + if..endif. + + This feature is available in Postfix 2.1 and later. blank lines and comments Empty lines and whitespace-only lines are ignored, @@ -113,11 +121,12 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) TEXT SUBSTITUTION Substitution of substrings from the matched expression - into the result string is possible using $1, $2, etc.. The - macros in the result string may need to be written as ${n} - or $(n) if they aren't followed by whitespace. + into the result string is possible using $1, $2, etc.; + specify $$ to produce a $ character as output. The macros + in the result string may need to be written as ${n} or + $(n) if they aren't followed by whitespace. - Note: since negated patterns (those preceded by !) return + Note: since negated patterns (those preceded by !) return a result when the expression does not match, substitutions are not available for negated patterns. diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 8f7df2a13..761e5d99e 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -212,9 +212,6 @@ Note: this action currently affects all recipients of the message. To discard only one recipient without discarding the entire message, use the transport(5) table to direct mail to the discard(8) service. .sp -Note: this action does not work in Postfix 2.2 -\fBsmtpd_end_of_data_restrictions\fR. -.sp This feature is available in Postfix 2.0 and later. .IP \fBDUNNO\fR Pretend that the lookup key was not found. This @@ -232,9 +229,6 @@ about external content filters is in the Postfix FILTER_README file. Note: this action overrides the \fBmain.cf content_filter\fR setting, and currently affects all recipients of the message. .sp -Note: this action does not work in Postfix 2.2 -\fBsmtpd_end_of_data_restrictions\fR. -.sp This feature is available in Postfix 2.0 and later. .IP "\fBHOLD \fIoptional text...\fR" Place the message on the \fBhold\fR queue, where it will sit @@ -252,9 +246,6 @@ or \fB$bounce_queue_lifetime\fR, or longer. .sp Note: this action currently affects all recipients of the message. .sp -Note: this action does not work in Postfix 2.2 -\fBsmtpd_end_of_data_restrictions\fR. -.sp This feature is available in Postfix 2.0 and later. .IP "\fBPREPEND \fIheadername: headervalue\fR" Prepend the specified message header to the message. @@ -274,9 +265,6 @@ address instead of the intended recipient(s). Note: this action overrides the FILTER action, and currently affects all recipients of the message. .sp -Note: this action does not work in Postfix 2.2 -\fBsmtpd_end_of_data_restrictions\fR. -.sp This feature is available in Postfix 2.1 and later. .IP "\fBWARN \fIoptional text...\fR Log a warning with the optional text, together with client information diff --git a/postfix/man/man5/pcre_table.5 b/postfix/man/man5/pcre_table.5 index 90e72e99f..dc2cb3dfa 100644 --- a/postfix/man/man5/pcre_table.5 +++ b/postfix/man/man5/pcre_table.5 @@ -48,11 +48,18 @@ and \fBendif\fR, if and only if the input string also matches .sp Note: do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR. +.sp +This feature is available in Postfix 2.1 and later. .IP "\fBif !/\fIpattern\fB/\fIflags\fR" .IP "\fBendif\fR" Match the input string against the patterns between \fBif\fR and \fBendif\fR, if and only if the input string does \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +.sp +Note: do not prepend whitespace to patterns inside +\fBif\fR..\fBendif\fR. +.sp +This feature is available in Postfix 2.1 and later. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. @@ -137,7 +144,8 @@ broken up into \fIuser\fR and \fIfoo\fR. .ad .fi Substitution of substrings from the matched expression into the result -string is possible using the conventional perl syntax ($1, $2, etc.). +string is possible using the conventional perl syntax ($1, $2, etc.); +specify $$ to produce a $ character as output. The macros in the result string may need to be written as ${n} or $(n) if they aren't followed by whitespace. diff --git a/postfix/man/man5/regexp_table.5 b/postfix/man/man5/regexp_table.5 index 432f4e977..43af5f38b 100644 --- a/postfix/man/man5/regexp_table.5 +++ b/postfix/man/man5/regexp_table.5 @@ -48,11 +48,19 @@ matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. .sp Note: do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR. +.sp +This feature is available in Postfix 2.1 and later. .IP "\fBif !/\fIpattern\fB/\fIflags\fR" .IP "\fBendif\fR" Match the input string against the patterns between \fBif\fR and \fBendif\fR, if and only if that same input string does \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +.sp +Note: do not prepend whitespace to patterns inside +\fBif\fR..\fBendif\fR. +.sp +This feature is available in Postfix 2.1 and later. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. @@ -105,7 +113,9 @@ broken up into \fIuser\fR and \fIfoo\fR. .ad .fi Substitution of substrings from the matched expression into the result -string is possible using $1, $2, etc.. The macros in the result string +string is possible using $1, $2, etc.; +specify $$ to produce a $ character as output. +The macros in the result string may need to be written as ${n} or $(n) if they aren't followed by whitespace. diff --git a/postfix/proto/DSN_README.html b/postfix/proto/DSN_README.html index adcd4a642..ec219ca45 100644 --- a/postfix/proto/DSN_README.html +++ b/postfix/proto/DSN_README.html @@ -30,7 +30,8 @@ specify:
What notifications are sent: success, failure, delay, or -none.
+none. Normally, Postfix informs the sender only mail when delivery +is delayed or when delivery fails.What content is returned in case of failure: only the message headers, or the full message.
@@ -68,10 +69,12 @@ internal infrastructure than desirable. Unfortunately, disallowing requests as well. The RFCs do not offer the option to negotiate feature subsets. -This is not as bad as it sounds. Remote senders with DSN support -will still be informed that their mail reached your Postfix gateway -successfully; they just will not get successful delivery notices -from your internal systems.
+This is not as bad as it sounds. When you turn off DSN for +remote inbound mail, remote senders with DSN support will still be +informed that their mail reached your Postfix gateway successfully; +they just will not get successful delivery notices from your internal +systems. Remote senders lose very little: they can no longer specify +how Postfix should report delayed or failed delivery.
Use the smtpd_discard_ehlo_keyword_address_maps feature if you wish to allow DSN requests from trusted clients but not from random diff --git a/postfix/proto/access b/postfix/proto/access index e164c21e9..d4530eb08 100644 --- a/postfix/proto/access +++ b/postfix/proto/access @@ -219,9 +219,6 @@ # To discard only one recipient without discarding the entire message, # use the transport(5) table to direct mail to the discard(8) service. # .sp -# Note: this action does not work in Postfix 2.2 -# \fBsmtpd_end_of_data_restrictions\fR. -# .sp # This feature is available in Postfix 2.0 and later. # .IP \fBDUNNO\fR # Pretend that the lookup key was not found. This @@ -239,9 +236,6 @@ # Note: this action overrides the \fBmain.cf content_filter\fR setting, # and currently affects all recipients of the message. # .sp -# Note: this action does not work in Postfix 2.2 -# \fBsmtpd_end_of_data_restrictions\fR. -# .sp # This feature is available in Postfix 2.0 and later. # .IP "\fBHOLD \fIoptional text...\fR" # Place the message on the \fBhold\fR queue, where it will sit @@ -259,9 +253,6 @@ # .sp # Note: this action currently affects all recipients of the message. # .sp -# Note: this action does not work in Postfix 2.2 -# \fBsmtpd_end_of_data_restrictions\fR. -# .sp # This feature is available in Postfix 2.0 and later. # .IP "\fBPREPEND \fIheadername: headervalue\fR" # Prepend the specified message header to the message. @@ -281,9 +272,6 @@ # Note: this action overrides the FILTER action, and currently affects # all recipients of the message. # .sp -# Note: this action does not work in Postfix 2.2 -# \fBsmtpd_end_of_data_restrictions\fR. -# .sp # This feature is available in Postfix 2.1 and later. # .IP "\fBWARN \fIoptional text...\fR # Log a warning with the optional text, together with client information diff --git a/postfix/proto/pcre_table b/postfix/proto/pcre_table index 55eaa2b59..a2620d2ca 100644 --- a/postfix/proto/pcre_table +++ b/postfix/proto/pcre_table @@ -40,11 +40,18 @@ # .sp # Note: do not prepend whitespace to patterns inside # \fBif\fR..\fBendif\fR. +# .sp +# This feature is available in Postfix 2.1 and later. # .IP "\fBif !/\fIpattern\fB/\fIflags\fR" # .IP "\fBendif\fR" # Match the input string against the patterns between \fBif\fR # and \fBendif\fR, if and only if the input string does \fBnot\fR # match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +# .sp +# Note: do not prepend whitespace to patterns inside +# \fBif\fR..\fBendif\fR. +# .sp +# This feature is available in Postfix 2.1 and later. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. @@ -125,7 +132,8 @@ # .ad # .fi # Substitution of substrings from the matched expression into the result -# string is possible using the conventional perl syntax ($1, $2, etc.). +# string is possible using the conventional perl syntax ($1, $2, etc.); +# specify $$ to produce a $ character as output. # The macros in the result string may need to be written as ${n} # or $(n) if they aren't followed by whitespace. # diff --git a/postfix/proto/regexp_table b/postfix/proto/regexp_table index 0785fd285..5662d4603 100644 --- a/postfix/proto/regexp_table +++ b/postfix/proto/regexp_table @@ -40,11 +40,19 @@ # .sp # Note: do not prepend whitespace to patterns inside # \fBif\fR..\fBendif\fR. +# .sp +# This feature is available in Postfix 2.1 and later. # .IP "\fBif !/\fIpattern\fB/\fIflags\fR" # .IP "\fBendif\fR" # Match the input string against the patterns between \fBif\fR # and \fBendif\fR, if and only if that same input string does # \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +# matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest. +# .sp +# Note: do not prepend whitespace to patterns inside +# \fBif\fR..\fBendif\fR. +# .sp +# This feature is available in Postfix 2.1 and later. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. @@ -93,7 +101,9 @@ # .ad # .fi # Substitution of substrings from the matched expression into the result -# string is possible using $1, $2, etc.. The macros in the result string +# string is possible using $1, $2, etc.; +# specify $$ to produce a $ character as output. +# The macros in the result string # may need to be written as ${n} or $(n) if they aren't followed # by whitespace. # diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 361b4bff1..4572e1b3b 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 "20051223" +#define MAIL_RELEASE_DATE "20051226" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 1465124fe..9532fec0b 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -40,7 +40,7 @@ typedef struct DICT { time_t mtime; /* mod time at open */ } DICT; -extern DICT *dict_alloc(const char *, const char *, int); +extern DICT *dict_alloc(const char *, const char *, ssize_t); extern void dict_free(DICT *); extern DICT *dict_debug(DICT *); diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c index 3ac5be42f..5d9dc83a6 100644 --- a/postfix/src/util/dict_alloc.c +++ b/postfix/src/util/dict_alloc.c @@ -9,7 +9,7 @@ /* DICT *dict_alloc(dict_type, dict_name, size) /* const char *dict_type; /* const char *dict_name; -/* int size; +/* ssize_t size; /* /* void dict_free(dict) /* DICT *ptr; @@ -105,7 +105,7 @@ static void dict_default_close(DICT *dict) /* dict_alloc - allocate dictionary object, initialize super-class */ -DICT *dict_alloc(const char *dict_type, const char *dict_name, int size) +DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size) { DICT *dict = (DICT *) mymalloc(size); diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c index cc7b24bcf..c706f522f 100644 --- a/postfix/src/util/dict_pcre.c +++ b/postfix/src/util/dict_pcre.c @@ -101,6 +101,7 @@ typedef struct { pcre_extra *hints; /* hints to speed pattern execution */ char *replacement; /* replacement string */ int match; /* positive or negative match */ + size_t max_sub; /* largest $number in replacement */ } DICT_PCRE_MATCH_RULE; typedef struct { @@ -116,6 +117,7 @@ typedef struct { typedef struct { DICT dict; /* generic members */ DICT_PCRE_RULE *head; + VSTRING *expansion_buf; /* lookup result */ } DICT_PCRE; static int dict_pcre_init = 0; /* flag need to init pcre library */ @@ -124,9 +126,8 @@ static int dict_pcre_init = 0; /* flag need to init pcre library */ * Context for $number expansion callback. */ typedef struct { - const char *mapname; /* source dict name */ - int lineno; /* source file line number */ - VSTRING *expansion_buf; /* target string buffer */ + DICT_PCRE *dict_pcre; /* the dictionary handle */ + DICT_PCRE_MATCH_RULE *match_rule; /* the rule we matched */ const char *lookup_string; /* string against which we match */ int offsets[PCRE_MAX_CAPTURE * 3]; /* Cut substrings */ int matches; /* Count of cuts */ @@ -138,8 +139,8 @@ typedef struct { typedef struct { const char *mapname; /* name of regexp map */ int lineno; /* where in file */ - int flags; /* dict_flags */ size_t max_sub; /* Largest $n seen */ + char *literal; /* constant result, $$ -> $ */ } DICT_PCRE_PRESCAN_CONTEXT; /* @@ -162,6 +163,8 @@ typedef struct { static int dict_pcre_expand(int type, VSTRING *buf, char *ptr) { DICT_PCRE_EXPAND_CONTEXT *ctxt = (DICT_PCRE_EXPAND_CONTEXT *) ptr; + DICT_PCRE_MATCH_RULE *match_rule = ctxt->match_rule; + DICT_PCRE *dict_pcre = ctxt->dict_pcre; const char *pp; int n; int ret; @@ -176,16 +179,16 @@ static int dict_pcre_expand(int type, VSTRING *buf, char *ptr) if (ret < 0) { if (ret == PCRE_ERROR_NOSUBSTRING) msg_fatal("regexp %s, line %d: replace index out of range", - ctxt->mapname, ctxt->lineno); + dict_pcre->dict.name, match_rule->rule.lineno); else msg_fatal("regexp %s, line %d: pcre_get_substring error: %d", - ctxt->mapname, ctxt->lineno, ret); + dict_pcre->dict.name, match_rule->rule.lineno, ret); } if (*pp == 0) { myfree((char *) pp); return (MAC_PARSE_UNDEF); } - vstring_strcat(ctxt->expansion_buf, pp); + vstring_strcat(dict_pcre->expansion_buf, pp); myfree((char *) pp); return (MAC_PARSE_OK); } @@ -194,7 +197,7 @@ static int dict_pcre_expand(int type, VSTRING *buf, char *ptr) * Straight text - duplicate with no substitution. */ else { - vstring_strcat(ctxt->expansion_buf, vstring_str(buf)); + vstring_strcat(dict_pcre->expansion_buf, vstring_str(buf)); return (MAC_PARSE_OK); } } @@ -252,7 +255,6 @@ static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string) DICT_PCRE_MATCH_RULE *match_rule; int lookup_len = strlen(lookup_string); DICT_PCRE_EXPAND_CONTEXT ctxt; - static VSTRING *expansion_buf; int nesting = 0; dict_errno = 0; @@ -292,28 +294,31 @@ static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string) continue; /* pcre_exec failed */ } - /* Negative rules can't have any substitutions */ - if (!match_rule->match) + /* + * Skip $number substitutions when the replacement text contains + * no $number strings, as learned during the compile time + * pre-scan. The pre-scan already replaced $$ by $. + */ + if (match_rule->max_sub == 0) return match_rule->replacement; /* * We've got a match. Perform substitution on replacement string. */ - if (expansion_buf == 0) - expansion_buf = vstring_alloc(10); - VSTRING_RESET(expansion_buf); - ctxt.expansion_buf = expansion_buf; + if (dict_pcre->expansion_buf == 0) + dict_pcre->expansion_buf = vstring_alloc(10); + VSTRING_RESET(dict_pcre->expansion_buf); + ctxt.dict_pcre = dict_pcre; + ctxt.match_rule = match_rule; ctxt.lookup_string = lookup_string; - ctxt.mapname = dict->name; - ctxt.lineno = rule->lineno; if (mac_parse(match_rule->replacement, dict_pcre_expand, (char *) &ctxt) & MAC_PARSE_ERROR) msg_fatal("pcre map %s, line %d: bad replacement syntax", dict->name, rule->lineno); - VSTRING_TERMINATE(expansion_buf); - return (vstring_str(expansion_buf)); + VSTRING_TERMINATE(dict_pcre->expansion_buf); + return (vstring_str(dict_pcre->expansion_buf)); /* * Conditional. XXX We provide space for matched substring info @@ -392,6 +397,8 @@ static void dict_pcre_close(DICT *dict) } myfree((char *) rule); } + if (dict_pcre->expansion_buf) + vstring_free(dict_pcre->expansion_buf); dict_free(dict); } @@ -493,12 +500,15 @@ static int dict_pcre_prescan(int type, VSTRING *buf, char *context) DICT_PCRE_PRESCAN_CONTEXT *ctxt = (DICT_PCRE_PRESCAN_CONTEXT *) context; size_t n; + /* + * Keep a copy of literal text (with $$ already replaced by $) if and + * only if the replacement text contains no $number expression. This way + * we can avoid having to scan the replacement text at lookup time. + */ if (type == MAC_PARSE_VARNAME) { - if (ctxt->flags & DICT_FLAG_NO_REGSUB) { - msg_warn("pcre map %s, line %d: " - "regular expression substitution is not allowed", - ctxt->mapname, ctxt->lineno); - return (MAC_PARSE_ERROR); + if (ctxt->literal) { + myfree(ctxt->literal); + ctxt->literal = 0; } if (!alldig(vstring_str(buf))) { msg_warn("pcre map %s, line %d: non-numeric replacement index \"%s\"", @@ -513,6 +523,11 @@ static int dict_pcre_prescan(int type, VSTRING *buf, char *context) } if (n > ctxt->max_sub) ctxt->max_sub = n; + } else if (type == MAC_PARSE_LITERAL && ctxt->max_sub == 0) { + if (ctxt->literal) + msg_panic("pcre map %s, line %d: multiple literals but no $number", + ctxt->mapname, ctxt->lineno); + ctxt->literal = mystrdup(vstring_str(buf)); } return (MAC_PARSE_OK); } @@ -599,14 +614,23 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno, */ prescan_context.mapname = mapname; prescan_context.lineno = lineno; - prescan_context.flags = dict_flags; prescan_context.max_sub = 0; + prescan_context.literal = 0; + + /* + * The optimizer will eliminate code duplication and/or dead code. + */ +#define CREATE_MATCHOP_ERROR_RETURN(rval) do { \ + if (prescan_context.literal) \ + myfree(prescan_context.literal); \ + return (rval); \ + } while (0) if (mac_parse(p, dict_pcre_prescan, (char *) &prescan_context) & MAC_PARSE_ERROR) { msg_warn("pcre map %s, line %d: bad replacement syntax: " "skipping this rule", mapname, lineno); - return (0); + CREATE_MATCHOP_ERROR_RETURN(0); } /* @@ -615,14 +639,20 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno, if (prescan_context.max_sub > 0 && regexp.match == 0) { msg_warn("pcre map %s, line %d: $number found in negative match " "replacement text: skipping this rule", mapname, lineno); - return (0); + CREATE_MATCHOP_ERROR_RETURN(0); + } + if (prescan_context.max_sub > 0 && (dict_flags & DICT_FLAG_NO_REGSUB)) { + msg_warn("pcre map %s, line %d: " + "regular expression substitution is not allowed: " + "skipping this rule", mapname, lineno); + CREATE_MATCHOP_ERROR_RETURN(0); } /* * Compile the pattern. */ if (dict_pcre_compile(mapname, lineno, ®exp, &engine) == 0) - return (0); + CREATE_MATCHOP_ERROR_RETURN(0); /* * Save the result. @@ -631,7 +661,11 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno, dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno, sizeof(DICT_PCRE_MATCH_RULE)); match_rule->match = regexp.match; - match_rule->replacement = mystrdup(p); + match_rule->max_sub = prescan_context.max_sub; + if (prescan_context.literal) + match_rule->replacement = prescan_context.literal; + else + match_rule->replacement = mystrdup(p); match_rule->pattern = engine.pattern; match_rule->hints = engine.hints; return ((DICT_PCRE_RULE *) match_rule); @@ -747,6 +781,7 @@ DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags) dict_pcre->dict.close = dict_pcre_close; dict_pcre->dict.flags = dict_flags | DICT_FLAG_PATTERN; dict_pcre->head = 0; + dict_pcre->expansion_buf = 0; if (dict_pcre_init == 0) { pcre_malloc = (void *(*) (size_t)) mymalloc; diff --git a/postfix/src/util/dict_regexp.c b/postfix/src/util/dict_regexp.c index 8bb73bc74..f56a7ea59 100644 --- a/postfix/src/util/dict_regexp.c +++ b/postfix/src/util/dict_regexp.c @@ -110,6 +110,7 @@ typedef struct { DICT dict; /* generic members */ regmatch_t *pmatch; /* matched substring info */ DICT_REGEXP_RULE *head; /* first rule */ + VSTRING *expansion_buf; /* lookup result */ } DICT_REGEXP; /* @@ -122,10 +123,9 @@ typedef struct { * Context for $number expansion callback. */ typedef struct { - DICT_REGEXP *dict_regexp; /* the dictionary entry */ + DICT_REGEXP *dict_regexp; /* the dictionary handle */ DICT_REGEXP_MATCH_RULE *match_rule; /* the rule we matched */ const char *lookup_string; /* matched text */ - VSTRING *expansion_buf; /* buffer for $number expansion */ } DICT_REGEXP_EXPAND_CONTEXT; /* @@ -135,6 +135,7 @@ typedef struct { const char *mapname; /* name of regexp map */ int lineno; /* where in file */ size_t max_sub; /* largest $number seen */ + char *literal; /* constant result, $$ -> $ */ } DICT_REGEXP_PRESCAN_CONTEXT; /* @@ -168,7 +169,7 @@ static int dict_regexp_expand(int type, VSTRING *buf, char *ptr) pmatch = dict_regexp->pmatch + n; if (pmatch->rm_so < 0 || pmatch->rm_so == pmatch->rm_eo) return (MAC_PARSE_UNDEF); /* empty or not matched */ - vstring_strncat(ctxt->expansion_buf, + vstring_strncat(dict_regexp->expansion_buf, ctxt->lookup_string + pmatch->rm_so, pmatch->rm_eo - pmatch->rm_so); return (MAC_PARSE_OK); @@ -178,7 +179,7 @@ static int dict_regexp_expand(int type, VSTRING *buf, char *ptr) * Straight text - duplicate with no substitution. */ else { - vstring_strcat(ctxt->expansion_buf, vstring_str(buf)); + vstring_strcat(dict_regexp->expansion_buf, vstring_str(buf)); return (MAC_PARSE_OK); } } @@ -212,7 +213,6 @@ static const char *dict_regexp_lookup(DICT *dict, const char *lookup_string) DICT_REGEXP_IF_RULE *if_rule; DICT_REGEXP_MATCH_RULE *match_rule; DICT_REGEXP_EXPAND_CONTEXT expand_context; - static VSTRING *expansion_buf; int error; int nesting = 0; @@ -256,7 +256,8 @@ static const char *dict_regexp_lookup(DICT *dict, const char *lookup_string) /* * Skip $number substitutions when the replacement text contains - * no $number strings (as learned during the pre-scan). + * no $number strings, as learned during the compile time + * pre-scan. The pre-scan already replaced $$ by $. */ if (match_rule->max_sub == 0) return (match_rule->replacement); @@ -267,10 +268,9 @@ static const char *dict_regexp_lookup(DICT *dict, const char *lookup_string) * expansion errors at this point mean something impossible has * happened. */ - if (!expansion_buf) - expansion_buf = vstring_alloc(10); - VSTRING_RESET(expansion_buf); - expand_context.expansion_buf = expansion_buf; + if (!dict_regexp->expansion_buf) + dict_regexp->expansion_buf = vstring_alloc(10); + VSTRING_RESET(dict_regexp->expansion_buf); expand_context.lookup_string = lookup_string; expand_context.match_rule = match_rule; expand_context.dict_regexp = dict_regexp; @@ -279,8 +279,8 @@ static const char *dict_regexp_lookup(DICT *dict, const char *lookup_string) (char *) &expand_context) & MAC_PARSE_ERROR) msg_panic("regexp map %s, line %d: bad replacement syntax", dict->name, rule->lineno); - VSTRING_TERMINATE(expansion_buf); - return (vstring_str(expansion_buf)); + VSTRING_TERMINATE(dict_regexp->expansion_buf); + return (vstring_str(dict_regexp->expansion_buf)); /* * Conditional. @@ -349,6 +349,8 @@ static void dict_regexp_close(DICT *dict) } if (dict_regexp->pmatch) myfree((char *) dict_regexp->pmatch); + if (dict_regexp->expansion_buf) + vstring_free(dict_regexp->expansion_buf); dict_free(dict); } @@ -467,15 +469,34 @@ static int dict_regexp_prescan(int type, VSTRING *buf, char *context) DICT_REGEXP_PRESCAN_CONTEXT *ctxt = (DICT_REGEXP_PRESCAN_CONTEXT *) context; size_t n; + /* + * Keep a copy of literal text (with $$ already replaced by $) if and + * only if the replacement text contains no $number expression. This way + * we can avoid having to scan the replacement text at lookup time. + */ if (type == MAC_PARSE_VARNAME) { + if (ctxt->literal) { + myfree(ctxt->literal); + ctxt->literal = 0; + } if (!alldig(vstring_str(buf))) { msg_warn("regexp map %s, line %d: non-numeric replacement index \"%s\"", ctxt->mapname, ctxt->lineno, vstring_str(buf)); return (MAC_PARSE_ERROR); } n = atoi(vstring_str(buf)); + if (n < 1) { + msg_warn("regexp map %s, line %d: out-of-range replacement index \"%s\"", + ctxt->mapname, ctxt->lineno, vstring_str(buf)); + return (MAC_PARSE_ERROR); + } if (n > ctxt->max_sub) ctxt->max_sub = n; + } else if (type == MAC_PARSE_LITERAL && ctxt->max_sub == 0) { + if (ctxt->literal) + msg_panic("regexp map %s, line %d: multiple literals but no $number", + ctxt->mapname, ctxt->lineno); + ctxt->literal = mystrdup(vstring_str(buf)); } return (MAC_PARSE_OK); } @@ -532,7 +553,7 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno, DICT_REGEXP_PATTERN first_pat; DICT_REGEXP_PATTERN second_pat; DICT_REGEXP_PRESCAN_CONTEXT prescan_context; - regex_t *first_exp; + regex_t *first_exp = 0; regex_t *second_exp; DICT_REGEXP_MATCH_RULE *match_rule; @@ -563,12 +584,26 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno, prescan_context.mapname = mapname; prescan_context.lineno = lineno; prescan_context.max_sub = 0; + prescan_context.literal = 0; + + /* + * The optimizer will eliminate code duplication and/or dead code. + */ +#define CREATE_MATCHOP_ERROR_RETURN(rval) do { \ + if (first_exp) { \ + regfree(first_exp); \ + myfree((char *) first_exp); \ + } \ + if (prescan_context.literal) \ + myfree(prescan_context.literal); \ + return (rval); \ + } while (0) if (mac_parse(p, dict_regexp_prescan, (char *) &prescan_context) & MAC_PARSE_ERROR) { msg_warn("regexp map %s, line %d: bad replacement syntax: " "skipping this rule", mapname, lineno); - return (0); + CREATE_MATCHOP_ERROR_RETURN(0); } /* @@ -577,36 +612,33 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno, * result string, or when the highest numbered substring is less than * the total number of () subpatterns. */ -#define FREE_EXPR_AND_RETURN(expr, rval) \ - { regfree(expr); myfree((char *) (expr)); return (rval); } - - if (prescan_context.max_sub == 0 || first_pat.match == 0) { + if (prescan_context.max_sub == 0) first_pat.options |= REG_NOSUB; - } else if (dict_flags & DICT_FLAG_NO_REGSUB) { + if (prescan_context.max_sub > 0 && first_pat.match == 0) { + msg_warn("regexp map %s, line %d: $number found in negative match " + "replacement text: skipping this rule", mapname, lineno); + CREATE_MATCHOP_ERROR_RETURN(0); + } + if (prescan_context.max_sub > 0 && (dict_flags & DICT_FLAG_NO_REGSUB)) { msg_warn("regexp map %s, line %d: " "regular expression substitution is not allowed: " "skipping this rule", mapname, lineno); - return (0); + CREATE_MATCHOP_ERROR_RETURN(0); } if ((first_exp = dict_regexp_compile_pat(mapname, lineno, &first_pat)) == 0) - return (0); - if (prescan_context.max_sub > 0 && first_pat.match == 0) { - msg_warn("regexp map %s, line %d: $number found in negative match replacement text: " - "skipping this rule", mapname, lineno); - FREE_EXPR_AND_RETURN(first_exp, 0); - } + CREATE_MATCHOP_ERROR_RETURN(0); if (prescan_context.max_sub > first_exp->re_nsub) { msg_warn("regexp map %s, line %d: out of range replacement index \"%d\": " "skipping this rule", mapname, lineno, (int) prescan_context.max_sub); - FREE_EXPR_AND_RETURN(first_exp, 0); + CREATE_MATCHOP_ERROR_RETURN(0); } if (second_pat.regexp != 0) { second_pat.options |= REG_NOSUB; if ((second_exp = dict_regexp_compile_pat(mapname, lineno, &second_pat)) == 0) - FREE_EXPR_AND_RETURN(first_exp, 0); + CREATE_MATCHOP_ERROR_RETURN(0); } else { second_exp = 0; } @@ -615,11 +647,13 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno, sizeof(DICT_REGEXP_MATCH_RULE)); match_rule->first_exp = first_exp; match_rule->first_match = first_pat.match; - match_rule->max_sub = - (prescan_context.max_sub > 0 ? prescan_context.max_sub + 1 : 0); + match_rule->max_sub = prescan_context.max_sub; match_rule->second_exp = second_exp; match_rule->second_match = second_pat.match; - match_rule->replacement = mystrdup(p); + if (prescan_context.literal) + match_rule->replacement = prescan_context.literal; + else + match_rule->replacement = mystrdup(p); return ((DICT_REGEXP_RULE *) match_rule); } @@ -706,6 +740,7 @@ DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags) dict_regexp->dict.flags = dict_flags | DICT_FLAG_PATTERN; dict_regexp->head = 0; dict_regexp->pmatch = 0; + dict_regexp->expansion_buf = 0; /* * Parse the regexp table. diff --git a/postfix/src/util/dict_regexp.ref b/postfix/src/util/dict_regexp.ref index 4e9e0660e..62d05f8d6 100644 --- a/postfix/src/util/dict_regexp.ref +++ b/postfix/src/util/dict_regexp.ref @@ -4,20 +4,37 @@ ./dict_open: warning: regexp map dict_regexp.map, line 10: out of range replacement index "5": skipping this rule ./dict_open: warning: regexp map dict_regexp.map, line 17: $number found in negative match replacement text: skipping this rule ./dict_open: warning: regexp map dict_regexp.map, line 22: no regexp: skipping this rule +> get true true: not found +> get true1 true1=1 +> get true2 true2: not found +> get truefalse2 truefalse2=2 +> get 3 3: not found +> get true3 true3=3 +> get c c= +> get d d: not found +> get ab ab: not found +> get aa aa=a!b +> get 1235 1235=(1)(2)(3) +> get 1234 1234=(1)(2)(3)(4) +> get 123 123=(1)(2)(3) +> get bar/find bar/find: not found +> get bar/whynot bar/whynot=Don't have a liquor license +> get bar/elbereth bar/elbereth=(elbereth) +> get say/elbereth say/elbereth: not found diff --git a/postfix/src/util/myaddrinfo.c b/postfix/src/util/myaddrinfo.c index 941d58b94..0cf8ab2b2 100644 --- a/postfix/src/util/myaddrinfo.c +++ b/postfix/src/util/myaddrinfo.c @@ -550,6 +550,7 @@ int sockaddr_to_hostaddr(const struct sockaddr * sa, SOCKADDR_SIZE salen, if (hostaddr != 0) { if (inet_ntop(AF_INET, (void *) &(SOCK_ADDR_IN_ADDR(sa)), hostaddr->buf, sizeof(hostaddr->buf)) == 0) + errno = ENOSPC; return (EAI_SYSTEM); } if (portnum != 0) { diff --git a/postfix/src/xsasl/xsasl_cyrus_client.c b/postfix/src/xsasl/xsasl_cyrus_client.c index 5e9e85b66..b42065e87 100644 --- a/postfix/src/xsasl/xsasl_cyrus_client.c +++ b/postfix/src/xsasl/xsasl_cyrus_client.c @@ -300,7 +300,7 @@ XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *unused_impl, int sasl_status; /* - * The optimizer will eliminate duplication and/or dead code. + * The optimizer will eliminate code duplication and/or dead code. */ #define XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(x) \ do { \ diff --git a/postfix/src/xsasl/xsasl_cyrus_server.c b/postfix/src/xsasl/xsasl_cyrus_server.c index f9ecbe1b6..182213f32 100644 --- a/postfix/src/xsasl/xsasl_cyrus_server.c +++ b/postfix/src/xsasl/xsasl_cyrus_server.c @@ -262,7 +262,7 @@ static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *unused_impl, myname, service, realm ? realm : "(null)"); /* - * The optimizer will eliminate duplication and/or dead code. + * The optimizer will eliminate code duplication and/or dead code. */ #define XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(x) \ do { \