rewrite with the "h" flag, by using the "-F" command-line option, or
by using a "name=" option on a control=submission ACL modifier.
+JH/21 Bug 2630: Fix eol-replacement string for the ${readsocket } expansion.
+ Previously when a whitespace character was specified it was not inserted
+ after removing the newline.
+
Exim version 4.94
-----------------
{
expand_string_message =
string_sprintf("lookup of \"%s\" gave DEFER: %s",
- string_printing2(key, FALSE), search_error_message);
+ string_printing2(key, SP_TAB), search_error_message);
goto EXPAND_FAILED;
}
if (expand_setup > 0) expand_nmax = expand_setup;
while ((item = string_nextinlist(&list, &sep, NULL, 0)))
g = string_append_listele(g, ',', item);
- /* possibly plus an EOL string */
+ /* possibly plus an EOL string. Process with escapes, to protect
+ from list-processing. The only current user of eol= in search
+ options is the readsock expansion. */
+
if (sub_arg[3] && *sub_arg[3])
g = string_append_listele(g, ',',
- string_sprintf("eol=%s", sub_arg[3]));
-
+ string_sprintf("eol=%s",
+ string_printing2(sub_arg[3], SP_TAB|SP_SPACE)));
}
/* Gat a (possibly cached) handle for the connection */
#ifdef SUPPORT_I18N
extern BOOL string_is_utf8(const uschar *);
#endif
-extern const uschar *string_printing2(const uschar *, BOOL);
+extern const uschar *string_printing2(const uschar *, int);
extern uschar *string_split_message(uschar *);
extern uschar *string_unprinting(uschar *);
#ifdef SUPPORT_I18N
gstring * yield;
int ret = DEFER;
-DEBUG(D_lookup) debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", filename, keystring, length, opts);
+DEBUG(D_lookup)
+ debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
+ filename, keystring, length, opts);
/* Parse options */
lf.do_tls = TRUE;
#endif
else if (Ustrncmp(s, "eol=", 4) == 0)
- eol = s + 4;
+ eol = string_unprinting(s + 4);
else if (Ustrcmp(s, "cache=yes") == 0)
lf.cache = TRUE;
else if (Ustrcmp(s, "send=no") == 0)
/* For almost all calls to convert things to printing characters, we want to
-allow tabs. A macro just makes life a bit easier. */
+allow tabs & spaces. A macro just makes life a bit easier. */
-#define string_printing(s) string_printing2((s), TRUE)
+#define string_printing(s) string_printing2((s), 0)
+#define SP_TAB BIT(0)
+#define SP_SPACE BIT(1)
/* We need a special return code for "no recipients and failed to send an error
{
if (flags & opt_fn_print_label) printf("%s = ", name);
printf("%s\n", smtp_receive_timeout_s
- ? string_printing2(smtp_receive_timeout_s, FALSE)
+ ? string_printing2(smtp_receive_timeout_s, SP_TAB)
: readconf_printtime(smtp_receive_timeout));
}
else if (*str == '$')
case opt_rewrite: /* Show the text value */
s = *(USS value);
if (!no_labels) printf("%s = ", name);
- printf("%s\n", s ? string_printing2(s, FALSE) : US"");
+ printf("%s\n", s ? string_printing2(s, SP_TAB) : US"");
break;
case opt_int:
/* This function is called for critical strings. It checks for any
non-printing characters, and if any are found, it makes a new copy
of the string with suitable escape sequences. It is most often called by the
-macro string_printing(), which sets allow_tab TRUE.
+macro string_printing(), which sets flags to 0.
Arguments:
s the input string
- allow_tab TRUE to allow tab as a printing character
+ flags Bit 0: convert tabs. Bit 1: convert spaces.
Returns: string with non-printers encoded as printing sequences
*/
const uschar *
-string_printing2(const uschar *s, BOOL allow_tab)
+string_printing2(const uschar *s, int flags)
{
int nonprintcount = 0;
int length = 0;
while (*t != 0)
{
int c = *t++;
- if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++;
+ if ( !mac_isprint(c)
+ || flags & SP_TAB && c == '\t'
+ || flags & SP_SPACE && c == ' '
+ ) nonprintcount++;
length++;
}
/* Get a new block of store guaranteed big enough to hold the
expanded string. */
-ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s));
+tt = ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s));
/* Copy everything, escaping non printers. */
-t = s;
-tt = ss;
-
-while (*t != 0)
+for (t = s; *t; )
{
int c = *t;
- if (mac_isprint(c) && (allow_tab || c != '\t')) *tt++ = *t++; else
+ if ( mac_isprint(c)
+ && (!(flags & SP_TAB) || c != '\t')
+ && (!(flags & SP_SPACE) || c != ' ')
+ )
+ *tt++ = *t++;
+ else
{
*tt++ = '\\';
switch (*t)
s = ss;
if (!*s || *++s != sep || sep_is_special) break;
}
- while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--;
+ /* while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; */
+ while ( g->ptr > 0 && isspace(g->s[g->ptr-1])
+ && (g->ptr == 1 || g->s[g->ptr-2] != '\\') )
+ g->ptr--;
buffer = string_from_gstring(g);
gstring_release_unused(g);
}
#
# Tests of IPv4 sockets
#
-server PORT_S 11
+server PORT_S 17
QUERY-1
>LF>ANSWER-1
>*eof
>*eof
>LF>ANSWER-11
>*eof
+QUERY-12
+>>ANSWER-12\x0d\x0aANSWER-12\x0d\x0a
+>*eof
+QUERY-13
+>>ANSWER-13\x0d\x0aANSWER-13\x0d\x0a
+>*eof
+QUERY-14
+>>ANSWER-14\x0d\x0aANSWER-14\x0d\x0a
+>*eof
+QUERY-15
+>>ANSWER-15\x0d\x0aANSWER-15\x0d\x0a
+>*eof
+QUERY-16
+>>ANSWER-16\x0d\x0aANSWER-16\x0d\x0a
+>*eof
+QUERY-17
+>>ANSWER-17\x0d\x0aANSWER-17\x0d\x0a
+>*eof
****
millisleep 500
exim -be
9 sock error >>${readsocket{inet:127.0.0.1:PORT_S}{QUERY-9\n}{1s}{}{sock error}}<<
10 ANSWER-10\\n >>${readsocket{inet:badloop:PORT_S}{QUERY-10\n}}<<
11 ANSWER-11 >>${readsocket{inet:thisloop:PORT_S}{QUERY-11\n}{2s:shutdown=no}}<<
+
+eol-replacement arg
+12 ANSWER-12x2 (no arg) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-12\n}{2s}}}<<
+13 ANSWER-13x2 (empty arg) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-13\n}{2s}{}}}<<
+14 ANSWER-14x2 X >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-14\n}{2s}{X}}}<<
+15 ANSWER-15x2 XYZZY >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-15\n}{2s}{XYZZY}}}<<
+16 ANSWER-16x2 (space) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-16\n}{2s}{ }}}<<
+17 ANSWER-17x2 (newline) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-17\n}{2s}{\n}}}<<
****
#
exim -be
<<
> 11 ANSWER-11 >><<
>
+> eol-replacement arg
+> 12 ANSWER-12x2 (no arg) >>ANSWER-12\r\nANSWER-12\r\n<<
+> 13 ANSWER-13x2 (empty arg) >>ANSWER-13\r\nANSWER-13\r\n<<
+> 14 ANSWER-14x2 X >>ANSWER-14\rXANSWER-14\rX<<
+> 15 ANSWER-15x2 XYZZY >>ANSWER-15\rXYZZYANSWER-15\rXYZZY<<
+> 16 ANSWER-16x2 (space) >>ANSWER-16\r ANSWER-16\r <<
+> 17 ANSWER-17x2 (newline) >>ANSWER-17\r\nANSWER-17\r\n<<
+>
> Failed: failed to connect to any address for 127.0.0.1: Connection refused
>
> caching of response value
Connection request from [ip4.ip4.ip4.ip4]
>LF>ANSWER-11
>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-12
+>>ANSWER-12\x0d\x0aANSWER-12\x0d\x0a
+>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-13
+>>ANSWER-13\x0d\x0aANSWER-13\x0d\x0a
+>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-14
+>>ANSWER-14\x0d\x0aANSWER-14\x0d\x0a
+>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-15
+>>ANSWER-15\x0d\x0aANSWER-15\x0d\x0a
+>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-16
+>>ANSWER-16\x0d\x0aANSWER-16\x0d\x0a
+>*eof
+Listening on port 1224 ...
+Connection request from [127.0.0.1]
+QUERY-17
+>>ANSWER-17\x0d\x0aANSWER-17\x0d\x0a
+>*eof
End of script
Listening on TESTSUITE/test-socket ...
Connection request