{
if (*s != '{')
{
- if (i < m) return 1;
+ if (i < m)
+ {
+ expand_string_message = string_sprintf("Not enough arguments for '%s' "
+ "(min is %d)", name, m);
+ return 1;
+ }
sub[i] = NULL;
break;
}
- sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok);
- if (sub[i] == NULL) return 3;
+ if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok)))
+ return 3;
if (*s++ != '}') return 1;
while (isspace(*s)) s++;
}
{
if (s[-1] == '{')
{
- expand_string_message = string_sprintf("Too many arguments for \"%s\" "
+ expand_string_message = string_sprintf("Too many arguments for '%s' "
"(max is %d)", name, n);
return 2;
}
+ expand_string_message = string_sprintf("missing '}' after '%s'", name);
return 1;
}
int rc = 0;
const uschar *s = *sptr; /* Local value */
uschar *sub1, *sub2;
+const uschar * errwhere;
/* If there are no following strings, we substitute the contents of $value for
lookups and for extractions in the success case. For the ${if item, the string
/* The first following string must be braced. */
-if (*s++ != '{') goto FAILED_CURLY;
+if (*s++ != '{')
+ {
+ errwhere = US"'yes' part did not start with '{'";
+ goto FAILED_CURLY;
+ }
/* Expand the first substring. Forced failures are noticed only if we actually
want this string. Set skipping in the call in the fail case (this will always
sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok);
if (sub1 == NULL && (yes || !expand_string_forcedfail)) goto FAILED;
expand_string_forcedfail = FALSE;
-if (*s++ != '}') goto FAILED_CURLY;
+if (*s++ != '}')
+ {
+ errwhere = US"'yes' part did not end with '}'";
+ goto FAILED_CURLY;
+ }
/* If we want the first string, add it to the output */
sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok);
if (sub2 == NULL && (!yes || !expand_string_forcedfail)) goto FAILED;
expand_string_forcedfail = FALSE;
- if (*s++ != '}') goto FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ errwhere = US"'no' part did not start with '{'";
+ goto FAILED_CURLY;
+ }
/* If we want the second string, add it to the output */
if (!yes && !skipping)
{
while (isspace(*s)) s++;
- if (*s++ != '}') goto FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ errwhere = US"did not close with '}' after forcedfail";
+ goto FAILED_CURLY;
+ }
expand_string_message =
string_sprintf("\"%s\" failed and \"fail\" requested", type);
expand_string_forcedfail = TRUE;
/* All we have to do now is to check on the final closing brace. */
while (isspace(*s)) s++;
-if (*s++ == '}') goto RETURN;
-
-/* Get here if there is a bracketing failure */
-
-FAILED_CURLY:
-rc++;
-
-/* Get here for other failures */
-
-FAILED:
-rc++;
+if (*s++ != '}')
+ {
+ errwhere = US"did not close with '}'";
+ goto FAILED_CURLY;
+ }
-/* Update the input pointer value before returning */
RETURN:
+/* Update the input pointer value before returning */
*sptr = s;
return rc;
+
+FAILED_CURLY:
+ /* Get here if there is a bracketing failure */
+ expand_string_message = string_sprintf(
+ "curly-bracket problem in conditional yes/no parsing: %s\n"
+ " remaining string is '%s'", errwhere, --s);
+ rc = 2;
+ goto RETURN;
+
+FAILED:
+ /* Get here for other failures */
+ rc = 1;
+ goto RETURN;
}
{
int ptr = 0;
int size = Ustrlen(string)+ 64;
-int item_type;
uschar *yield = store_get(size);
+int item_type;
const uschar *s = string;
uschar *save_expand_nstring[EXPAND_MAXN+1];
int save_expand_nlength[EXPAND_MAXN+1];
BOOL resetok = TRUE;
+DEBUG(D_expand)
+ debug_printf("%s: %s\n", skipping ? " scanning" : "considering", string);
+
expand_string_forcedfail = FALSE;
expand_string_message = US"";
if (*s == '{') /*}*/
{
key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
- if (key == NULL) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (!key) goto EXPAND_FAILED; /*{{*/
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' after lookup key";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
}
else key = NULL;
queries that also require a file name (e.g. sqlite), the file name comes
first. */
- if (*s != '{') goto EXPAND_FAILED_CURLY;
+ if (*s != '{')
+ {
+ expand_string_message = US"missing '{' for lookup file-or-query arg";
+ goto EXPAND_FAILED_CURLY;
+ }
filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (filename == NULL) goto EXPAND_FAILED;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing lookup file-or-query arg";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
/* If this isn't a single-key+file lookup, re-arrange the variables
there is just a "key", and no file name. For the special query-style +
file types, the query (i.e. "key") starts with a file name. */
- if (key == NULL)
+ if (!key)
{
while (isspace(*filename)) filename++;
key = filename;
if (mac_islookup(stype, lookup_querystyle))
- {
filename = NULL;
- }
else
{
if (*filename != '/')
{
if (expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok) == NULL)
goto EXPAND_FAILED;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing failstring for readsocket";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
}
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+ readsock_done:
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing readsocket";
+ goto EXPAND_FAILED_CURLY;
+ }
continue;
/* Come here on failure to create socket, connect socket, write to the
if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok)))
goto EXPAND_FAILED;
yield = string_cat(yield, &size, &ptr, arg);
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing failstring for readsocket";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
- continue;
+ goto readsock_done;
}
/* Handle "run" to execute a program. */
}
while (isspace(*s)) s++;
- if (*s != '{') goto EXPAND_FAILED_CURLY;
+ if (*s != '{')
+ {
+ expand_string_message = US"missing '{' for command arg of run";
+ goto EXPAND_FAILED_CURLY;
+ }
arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (arg == NULL) goto EXPAND_FAILED;
while (isspace(*s)) s++;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing command arg of run";
+ goto EXPAND_FAILED_CURLY;
+ }
if (skipping) /* Just pretend it worked when we're skipping */
- {
runrc = 0;
- }
else
{
if (!transport_set_up_command(&argv, /* anchor for arg list */
case EITEM_EXTRACT:
{
int i;
- int j = 2;
+ int j;
int field_number = 1;
BOOL field_number_set = FALSE;
uschar *save_lookup_value = lookup_value;
/* While skipping we cannot rely on the data for expansions being
available (eg. $item) hence cannot decide on numeric vs. keyed.
- Just read as many arguments as there are. */
+ Read a maximum of 5 arguments (inclding the yes/no) */
if (skipping)
{
while (isspace(*s)) s++;
- while (*s == '{')
+ for (j = 5; j > 0 && *s == '{'; j--)
{
if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '{' for arg of extract";
+ goto EXPAND_FAILED_CURLY;
+ }
+ while (isspace(*s)) s++;
+ }
+ if ( Ustrncmp(s, "fail", 4) == 0
+ && (s[4] == '}' || s[4] == ' ' || s[4] == '\t' || !s[4])
+ )
+ {
+ s += 4;
while (isspace(*s)) s++;
}
if (*s != '}')
+ {
+ expand_string_message = US"missing '}' closing extract";
goto EXPAND_FAILED_CURLY;
+ }
}
- else for (i = 0; i < j; i++) /* Read the proper number of arguments */
+ else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
{
while (isspace(*s)) s++;
if (*s == '{') /*}*/
{
sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (sub[i] == NULL) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = string_sprintf(
+ "missing '}' closing arg %d of extract", i+1);
+ goto EXPAND_FAILED_CURLY;
+ }
/* After removal of leading and trailing white space, the first
argument must not be empty; if it consists entirely of digits
}
}
}
- else goto EXPAND_FAILED_CURLY;
+ else
+ {
+ expand_string_message = string_sprintf(
+ "missing '{' for arg %d of extract", i+1);
+ goto EXPAND_FAILED_CURLY;
+ }
}
/* Extract either the numbered or the keyed substring into $value. If
{
while (isspace(*s)) s++;
if (*s != '{') /*}*/
+ {
+ expand_string_message = string_sprintf(
+ "missing '{' for arg %d of listextract", i+1);
goto EXPAND_FAILED_CURLY;
+ }
sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (!sub[i]) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = string_sprintf(
+ "missing '}' closing arg %d of listextract", i+1);
+ goto EXPAND_FAILED_CURLY;
+ }
/* After removal of leading and trailing white space, the first
argument must be numeric and nonempty. */
/* Read the field argument */
while (isspace(*s)) s++;
if (*s != '{') /*}*/
+ {
+ expand_string_message = US"missing '{' for field arg of certextract";
goto EXPAND_FAILED_CURLY;
+ }
sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (!sub[0]) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing field arg of certextract";
+ goto EXPAND_FAILED_CURLY;
+ }
/* strip spaces fore & aft */
{
int len;
/* inspect the cert argument */
while (isspace(*s)) s++;
if (*s != '{') /*}*/
+ {
+ expand_string_message = US"missing '{' for cert variable arg of certextract";
goto EXPAND_FAILED_CURLY;
+ }
if (*++s != '$')
{
expand_string_message = US"second argument of \"certextract\" must "
}
sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
if (!sub[1]) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing cert variable arg of certextract";
+ goto EXPAND_FAILED_CURLY;
+ }
if (skipping)
lookup_value = NULL;
uschar *save_lookup_value = lookup_value;
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message =
+ string_sprintf("missing '{' for first arg of %s", name);
+ goto EXPAND_FAILED_CURLY;
+ }
list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (list == NULL) goto EXPAND_FAILED;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message =
+ string_sprintf("missing '}' closing first arg of %s", name);
+ goto EXPAND_FAILED_CURLY;
+ }
if (item_type == EITEM_REDUCE)
{
uschar * t;
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message = US"missing '{' for second arg of reduce";
+ goto EXPAND_FAILED_CURLY;
+ }
t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (!t) goto EXPAND_FAILED;
lookup_value = t;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing second arg of reduce";
+ goto EXPAND_FAILED_CURLY;
+ }
}
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message =
+ string_sprintf("missing '{' for last arg of %s", name);
+ goto EXPAND_FAILED_CURLY;
+ }
expr = s;
uschar *save_iterate_item = iterate_item;
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message = US"missing '{' for list arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
if (!srclist) goto EXPAND_FAILED;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing list arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message = US"missing '{' for comparator arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
if (!cmp) goto EXPAND_FAILED;
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing comparator arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
while (isspace(*s)) s++;
- if (*s++ != '{') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '{')
+ {
+ expand_string_message = US"missing '{' for extractor arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
xtract = s;
tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
if (!tmp) goto EXPAND_FAILED;
xtract = string_copyn(xtract, s - xtract);
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '}' closing extractor arg of sort";
+ goto EXPAND_FAILED_CURLY;
+ }
/*{*/
if (*s++ != '}')
{ /*{*/
key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
if (!key) goto EXPAND_FAILED; /*{*/
- if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+ if (*s++ != '}')
+ {
+ expand_string_message = US"missing '{' for name arg of env";
+ goto EXPAND_FAILED_CURLY;
+ }
lookup_value = US getenv(CS key);
sub = expand_string_internal(s+2, TRUE, &s1, skipping,
FALSE, &resetok);
if (!sub) goto EXPAND_FAILED; /*{*/
- if (*s1 != '}') goto EXPAND_FAILED_CURLY;
+ if (*s1 != '}')
+ {
+ expand_string_message =
+ string_sprintf("missing '}' closing cert arg of %s", name);
+ goto EXPAND_FAILED_CURLY;
+ }
if ((vp = find_var_ent(sub)) && vp->type == vtype_cert)
{
s = s1+1;
DEBUG(D_expand)
{
- debug_printf("expanding: %.*s\n result: %s\n", (int)(s - string), string,
+ debug_printf(" expanding: %.*s\n result: %s\n", (int)(s - string), string,
yield);
- if (skipping) debug_printf("skipping: result is not used\n");
+ if (skipping) debug_printf(" skipping: result is not used\n");
}
return yield;
*/
EXPAND_FAILED_CURLY:
-expand_string_message = malformed_header?
- US"missing or misplaced { or } - could be header name not terminated by colon"
- :
- US"missing or misplaced { or }";
+if (malformed_header)
+ expand_string_message =
+ US"missing or misplaced { or } - could be header name not terminated by colon";
+
+else if (!expand_string_message || !*expand_string_message)
+ expand_string_message = US"missing or misplaced { or }";
/* At one point, Exim reset the store to yield (if yield was not NULL), but
that is a bad idea, because expand_string_message is in dynamic store. */
+/* Avoid potentially exposing a password in a string about to be logged */
+
+uschar *
+expand_hide_passwords(uschar * s)
+{
+return ( ( Ustrstr(s, "failed to expand") != NULL
+ || Ustrstr(s, "expansion of ") != NULL
+ )
+ && ( Ustrstr(s, "mysql") != NULL
+ || Ustrstr(s, "pgsql") != NULL
+ || Ustrstr(s, "redis") != NULL
+ || Ustrstr(s, "sqlite") != NULL
+ || Ustrstr(s, "ldap:") != NULL
+ || Ustrstr(s, "ldaps:") != NULL
+ || Ustrstr(s, "ldapi:") != NULL
+ || Ustrstr(s, "ldapdn:") != NULL
+ || Ustrstr(s, "ldapm:") != NULL
+ ) )
+ ? US"Temporary internal error" : s;
+}
+
+
+
/*************************************************
**************************************************