summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2022-06-26 15:27:32 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2022-06-26 15:27:32 +0100
commitc6887a05b9c56d373086e9a79e20c26bebd300b2 (patch)
treeaa9c86912e02d23b0c61af892c93525218b3890f
parentd05685413efd3262b4a5622717f90bba351f1074 (diff)
Variable setting in -be
-rw-r--r--doc/doc-docbook/spec.xfpt6
-rw-r--r--doc/doc-txt/NewStuff5
-rw-r--r--src/src/acl.c176
-rw-r--r--src/src/exim.c2
-rw-r--r--src/src/functions.h3
-rw-r--r--src/src/readconf.c844
6 files changed, 540 insertions, 496 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 3b9082031..deee95a9b 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -2913,6 +2913,12 @@ defined and macros will be expanded.
Because macros in the config file are often used for secrets, those are only
available to admin users.
+.new
+The word &"set"& at the start of a line, followed by a single space,
+is recognised specially as defining a value for a variable.
+The syntax is otherwise the same as the ACL modifier &"set ="&.
+.wen
+
.vitem &%-bem%&&~<&'filename'&>
.oindex "&%-bem%&"
.cindex "testing" "string expansion"
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index 2986b2cdd..807d9e434 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -6,6 +6,11 @@ Before a formal release, there may be quite a lot of detail so that people can
test from the snapshots or the Git before the documentation is updated. Once
the documentation is updated, this file is reduced to a short list.
+Version 4.97
+------------
+
+ 1. The expansion-test faciility (exim -be) can set variables.
+
Version 4.96
------------
diff --git a/src/src/acl.c b/src/src/acl.c
index 0078aca7d..3af3a4eee 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -734,6 +734,78 @@ return -1;
}
+static BOOL
+acl_varname_to_cond(const uschar ** sp, acl_condition_block * cond, uschar ** error)
+{
+const uschar * s = *sp, * endptr;
+
+#ifndef DISABLE_DKIM
+if ( Ustrncmp(s, "dkim_verify_status", 18) == 0
+ || Ustrncmp(s, "dkim_verify_reason", 18) == 0)
+ {
+ endptr = s+18;
+ if (isalnum(*endptr))
+ {
+ *error = string_sprintf("invalid variable name after \"set\" in ACL "
+ "modifier \"set %s\" "
+ "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)",
+ s);
+ return FALSE;
+ }
+ cond->u.varname = string_copyn(s, 18);
+ }
+else
+#endif
+ {
+ if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0)
+ {
+ *error = string_sprintf("invalid variable name after \"set\" in ACL "
+ "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
+ return FALSE;
+ }
+
+ endptr = s + 5;
+ if (!isdigit(*endptr) && *endptr != '_')
+ {
+ *error = string_sprintf("invalid variable name after \"set\" in ACL "
+ "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
+ s);
+ return FALSE;
+ }
+
+ for ( ; *endptr && *endptr != '=' && !isspace(*endptr); endptr++)
+ if (!isalnum(*endptr) && *endptr != '_')
+ {
+ *error = string_sprintf("invalid character \"%c\" in variable name "
+ "in ACL modifier \"set %s\"", *endptr, s);
+ return FALSE;
+ }
+
+ cond->u.varname = string_copyn(s + 4, endptr - s - 4);
+ }
+s = endptr;
+Uskip_whitespace(&s);
+*sp = s;
+return TRUE;
+}
+
+
+static BOOL
+acl_data_to_cond(const uschar * s, acl_condition_block * cond,
+ const uschar * name, uschar ** error)
+{
+if (*s++ != '=')
+ {
+ *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name,
+ conditions[cond->type].is_modifier ? US"modifier" : US"condition");
+ return FALSE;;
+ }
+Uskip_whitespace(&s);
+cond->arg = string_copy(s);
+return TRUE;
+}
+
+
/*************************************************
* Read and parse one ACL *
*************************************************/
@@ -760,7 +832,7 @@ acl_block **lastp = &yield;
acl_block *this = NULL;
acl_condition_block *cond;
acl_condition_block **condp = NULL;
-uschar * s;
+const uschar * s;
*error = NULL;
@@ -768,7 +840,7 @@ while ((s = (*func)()))
{
int v, c;
BOOL negated = FALSE;
- uschar *saveline = s;
+ const uschar * saveline = s;
uschar name[EXIM_DRIVERNAME_MAX];
/* Conditions (but not verbs) are allowed to be negated by an initial
@@ -808,16 +880,15 @@ while ((s = (*func)()))
*error = string_sprintf("malformed ACL line \"%s\"", saveline);
return NULL;
}
- this = store_get(sizeof(acl_block), GET_UNTAINTED);
- *lastp = this;
- lastp = &(this->next);
+ *lastp = this = store_get(sizeof(acl_block), GET_UNTAINTED);
+ lastp = &this->next;
this->next = NULL;
this->condition = NULL;
this->verb = v;
this->srcline = config_lineno; /* for debug output */
this->srcfile = config_filename; /**/
- condp = &(this->condition);
- if (*s == 0) continue; /* No condition on this line */
+ condp = &this->condition;
+ if (!*s) continue; /* No condition on this line */
if (*s == '!')
{
negated = TRUE;
@@ -861,7 +932,7 @@ while ((s = (*func)()))
cond->u.negated = negated;
*condp = cond;
- condp = &(cond->next);
+ condp = &cond->next;
/* The "set" modifier is different in that its argument is "name=value"
rather than just a value, and we can check the validity of the name, which
@@ -874,75 +945,13 @@ while ((s = (*func)()))
compatibility. */
if (c == ACLC_SET)
-#ifndef DISABLE_DKIM
- if ( Ustrncmp(s, "dkim_verify_status", 18) == 0
- || Ustrncmp(s, "dkim_verify_reason", 18) == 0)
- {
- uschar * endptr = s+18;
-
- if (isalnum(*endptr))
- {
- *error = string_sprintf("invalid variable name after \"set\" in ACL "
- "modifier \"set %s\" "
- "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)",
- s);
- return NULL;
- }
- cond->u.varname = string_copyn(s, 18);
- s = endptr;
- Uskip_whitespace(&s);
- }
- else
-#endif
- {
- uschar *endptr;
-
- if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0)
- {
- *error = string_sprintf("invalid variable name after \"set\" in ACL "
- "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
- return NULL;
- }
-
- endptr = s + 5;
- if (!isdigit(*endptr) && *endptr != '_')
- {
- *error = string_sprintf("invalid variable name after \"set\" in ACL "
- "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
- s);
- return NULL;
- }
-
- while (*endptr && *endptr != '=' && !isspace(*endptr))
- {
- if (!isalnum(*endptr) && *endptr != '_')
- {
- *error = string_sprintf("invalid character \"%c\" in variable name "
- "in ACL modifier \"set %s\"", *endptr, s);
- return NULL;
- }
- endptr++;
- }
-
- cond->u.varname = string_copyn(s + 4, endptr - s - 4);
- s = endptr;
- Uskip_whitespace(&s);
- }
+ if (!acl_varname_to_cond(&s, cond, error)) return NULL;
/* For "set", we are now positioned for the data. For the others, only
"endpass" has no data */
if (c != ACLC_ENDPASS)
- {
- if (*s++ != '=')
- {
- *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name,
- conditions[c].is_modifier ? US"modifier" : US"condition");
- return NULL;
- }
- Uskip_whitespace(&s);
- cond->arg = string_copy(s);
- }
+ if (!acl_data_to_cond(s, cond, name, error)) return NULL;
}
return yield;
@@ -4894,6 +4903,29 @@ if (is_tainted(value))
fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
}
+
+
+
+uschar *
+acl_standalone_setvar(const uschar * s)
+{
+acl_condition_block * cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED);
+uschar * errstr = NULL, * log_msg = NULL;
+BOOL endpass_seen;
+int e;
+
+cond->next = NULL;
+cond->type = ACLC_SET;
+if (!acl_varname_to_cond(&s, cond, &errstr)) return errstr;
+if (!acl_data_to_cond(s, cond, US"'-be'", &errstr)) return errstr;
+
+if (acl_check_condition(ACL_WARN, cond, ACL_WHERE_UNKNOWN,
+ NULL, 0, &endpass_seen, &errstr, &log_msg, &e) != OK)
+ return string_sprintf("oops: %s", errstr);
+return string_sprintf("variable %s set", cond->u.varname);
+}
+
+
#endif /* !MACRO_PREDEF */
/* vi: aw ai sw=2
*/
diff --git a/src/src/exim.c b/src/src/exim.c
index dec8de4b4..23e206d2a 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -1670,6 +1670,8 @@ if (isupper(big_buffer[0]))
if (macro_read_assignment(big_buffer))
printf("Defined macro '%s'\n", mlast->name);
}
+else if (Ustrncmp(big_buffer, "set ", 4) == 0)
+ printf("%s\n", acl_standalone_setvar(big_buffer+4));
else
if ((s = expand_string(big_buffer))) printf("%s\n", CS s);
else printf("Failed: %s\n", expand_string_message);
diff --git a/src/src/functions.h b/src/src/functions.h
index 4caae346d..e71823410 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -100,6 +100,7 @@ extern acl_block *acl_read(uschar *(*)(void), uschar **);
extern int acl_check(int, uschar *, uschar *, uschar **, uschar **);
extern uschar *acl_current_verb(void);
extern int acl_eval(int, uschar *, uschar **, uschar **);
+extern uschar *acl_standalone_setvar(const uschar *);
extern tree_node *acl_var_create(uschar *);
extern void acl_var_write(uschar *, uschar *, void *);
@@ -425,7 +426,7 @@ extern void readconf_main(BOOL);
extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *);
extern BOOL readconf_print(const uschar *, uschar *, BOOL);
extern uschar *readconf_printtime(int);
-extern uschar *readconf_readname(uschar *, int, uschar *);
+extern const uschar *readconf_readname(uschar *, int, const uschar *);
extern int readconf_readtime(const uschar *, int, BOOL);
extern void readconf_rest(void);
extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *);
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 5068dc60e..83ee51b65 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -1172,8 +1172,8 @@ Arguments:
Returns: new input pointer
*/
-uschar *
-readconf_readname(uschar *name, int len, uschar *s)
+const uschar *
+readconf_readname(uschar * name, int len, const uschar * s)
{
int p = 0;
BOOL broken = FALSE;
@@ -1632,7 +1632,7 @@ rmark reset_point;
int intbase = 0;
uschar *inttype = US"";
uschar *sptr;
-uschar *s = buffer;
+const uschar * s = buffer;
uschar **str_target;
uschar name[EXIM_DRIVERNAME_MAX];
uschar name2[EXIM_DRIVERNAME_MAX];
@@ -1752,337 +1752,337 @@ switch (type)
case opt_gidlist:
case opt_rewrite:
- reset_point = store_mark();
- sptr = read_string(s, name);
+ reset_point = store_mark();
+ sptr = read_string(s, name);
- /* Having read a string, we now have several different ways of using it,
- depending on the data type, so do another switch. If keeping the actual
- string is not required (because it is interpreted), freesptr is set TRUE,
- and at the end we reset the pool. */
+ /* Having read a string, we now have several different ways of using it,
+ depending on the data type, so do another switch. If keeping the actual
+ string is not required (because it is interpreted), freesptr is set TRUE,
+ and at the end we reset the pool. */
- switch (type)
- {
- /* If this was a string, set the variable to point to the new string,
- and set the flag so its store isn't reclaimed. If it was a list of rewrite
- rules, we still keep the string (for printing), and parse the rules into a
- control block and flags word. */
-
- case opt_stringptr:
- str_target = data_block ? USS (US data_block + ol->v.offset)
- : USS ol->v.value;
- if (ol->type & opt_rep_con)
- {
- uschar * saved_condition;
- /* We already have a condition, we're conducting a crude hack to let
- multiple condition rules be chained together, despite storing them in
- text form. */
- *str_target = string_copy_perm( (saved_condition = *str_target)
- ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
- saved_condition, sptr)
- : sptr,
- FALSE);
- /* TODO(pdp): there is a memory leak here and just below
- when we set 3 or more conditions; I still don't
- understand the store mechanism enough to know
- what's the safe way to free content from an earlier store.
- AFAICT, stores stack, so freeing an early stored item also stores
- all data alloc'd after it. If we knew conditions were adjacent,
- we could survive that, but we don't. So I *think* we need to take
- another bit from opt_type to indicate "malloced"; this seems like
- quite a hack, especially for this one case. It also means that
- we can't ever reclaim the store from the *first* condition.
-
- Because we only do this once, near process start-up, I'm prepared to
- let this slide for the time being, even though it rankles. */
- }
- else if (ol->type & opt_rep_str)
+ switch (type)
{
- uschar sep_o =
- Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
- : Ustrncmp(name, "set", 3) == 0 ? ';'
- : ':';
- int sep_i = -(int)sep_o;
- const uschar * list = sptr;
- uschar * s;
- gstring * list_o = NULL;
-
- if (*str_target)
- {
- list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
- list_o = string_cat(list_o, *str_target);
- }
-
- while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
- list_o = string_append_listele(list_o, sep_o, s);
+ /* If this was a string, set the variable to point to the new string,
+ and set the flag so its store isn't reclaimed. If it was a list of rewrite
+ rules, we still keep the string (for printing), and parse the rules into a
+ control block and flags word. */
+
+ case opt_stringptr:
+ str_target = data_block ? USS (US data_block + ol->v.offset)
+ : USS ol->v.value;
+ if (ol->type & opt_rep_con)
+ {
+ uschar * saved_condition;
+ /* We already have a condition, we're conducting a crude hack to let
+ multiple condition rules be chained together, despite storing them in
+ text form. */
+ *str_target = string_copy_perm( (saved_condition = *str_target)
+ ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
+ saved_condition, sptr)
+ : sptr,
+ FALSE);
+ /* TODO(pdp): there is a memory leak here and just below
+ when we set 3 or more conditions; I still don't
+ understand the store mechanism enough to know
+ what's the safe way to free content from an earlier store.
+ AFAICT, stores stack, so freeing an early stored item also stores
+ all data alloc'd after it. If we knew conditions were adjacent,
+ we could survive that, but we don't. So I *think* we need to take
+ another bit from opt_type to indicate "malloced"; this seems like
+ quite a hack, especially for this one case. It also means that
+ we can't ever reclaim the store from the *first* condition.
+
+ Because we only do this once, near process start-up, I'm prepared to
+ let this slide for the time being, even though it rankles. */
+ }
+ else if (ol->type & opt_rep_str)
+ {
+ uschar sep_o =
+ Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
+ : Ustrncmp(name, "set", 3) == 0 ? ';'
+ : ':';
+ int sep_i = -(int)sep_o;
+ const uschar * list = sptr;
+ uschar * s;
+ gstring * list_o = NULL;
+
+ if (*str_target)
+ {
+ list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
+ list_o = string_cat(list_o, *str_target);
+ }
- if (list_o)
- *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
- }
- else
- {
- *str_target = sptr;
- freesptr = FALSE;
- }
- break;
+ while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
+ list_o = string_append_listele(list_o, sep_o, s);
- case opt_rewrite:
- if (data_block)
- *USS (US data_block + ol->v.offset) = sptr;
- else
- *USS ol->v.value = sptr;
- freesptr = FALSE;
- if (type == opt_rewrite)
- {
- int sep = 0;
- int *flagptr;
- uschar *p = sptr;
- rewrite_rule **chain;
- optionlist *ol3;
-
- sprintf(CS name2, "*%.50s_rules", name);
- ol2 = find_option(name2, oltop, last);
- sprintf(CS name2, "*%.50s_flags", name);
- ol3 = find_option(name2, oltop, last);
-
- if (!ol2 || !ol3)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "rewrite rules not available for driver");
+ if (list_o)
+ *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
+ }
+ else
+ {
+ *str_target = sptr;
+ freesptr = FALSE;
+ }
+ break;
- if (data_block)
- {
- chain = (rewrite_rule **)(US data_block + ol2->v.offset);
- flagptr = (int *)(US data_block + ol3->v.offset);
- }
- else
- {
- chain = (rewrite_rule **)ol2->v.value;
- flagptr = (int *)ol3->v.value;
- }
+ case opt_rewrite:
+ if (data_block)
+ *USS (US data_block + ol->v.offset) = sptr;
+ else
+ *USS ol->v.value = sptr;
+ freesptr = FALSE;
+ if (type == opt_rewrite)
+ {
+ int sep = 0;
+ int *flagptr;
+ uschar *p = sptr;
+ rewrite_rule **chain;
+ optionlist *ol3;
+
+ sprintf(CS name2, "*%.50s_rules", name);
+ ol2 = find_option(name2, oltop, last);
+ sprintf(CS name2, "*%.50s_flags", name);
+ ol3 = find_option(name2, oltop, last);
+
+ if (!ol2 || !ol3)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "rewrite rules not available for driver");
+
+ if (data_block)
+ {
+ chain = (rewrite_rule **)(US data_block + ol2->v.offset);
+ flagptr = (int *)(US data_block + ol3->v.offset);
+ }
+ else
+ {
+ chain = (rewrite_rule **)ol2->v.value;
+ flagptr = (int *)ol3->v.value;
+ }
- /* This will trap if sptr is tainted. Not sure if that can happen */
- while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
- {
- rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
- *chain = next;
- chain = &(next->next);
- }
+ /* This will trap if sptr is tainted. Not sure if that can happen */
+ while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
+ {
+ rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
+ *chain = next;
+ chain = &(next->next);
+ }
- if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
- "non-header rewrite - not allowed at transport time -");
- }
- break;
+ if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
+ "non-header rewrite - not allowed at transport time -");
+ }
+ break;
- /* If it was an expanded uid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
- of data. */
+ /* If it was an expanded uid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
+ of data. */
- case opt_expand_uid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ case opt_expand_uid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Look up a fixed uid, and also make use of the corresponding gid
- if a passwd entry is returned and the gid has not been set. */
+ /* Look up a fixed uid, and also make use of the corresponding gid
+ if a passwd entry is returned and the gid has not been set. */
- case opt_uid:
- if (!route_finduser(sptr, &pw, &uid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
- if (data_block)
- *(uid_t *)(US data_block + ol->v.offset) = uid;
- else
- *(uid_t *)ol->v.value = uid;
+ case opt_uid:
+ if (!route_finduser(sptr, &pw, &uid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
+ if (data_block)
+ *(uid_t *)(US data_block + ol->v.offset) = uid;
+ else
+ *(uid_t *)ol->v.value = uid;
- /* Set the flag indicating a fixed value is set */
+ /* Set the flag indicating a fixed value is set */
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- /* Handle matching gid if we have a passwd entry: done by finding the
- same name with terminating "user" changed to "group"; if not found,
- ignore. Also ignore if the value is already set. */
+ /* Handle matching gid if we have a passwd entry: done by finding the
+ same name with terminating "user" changed to "group"; if not found,
+ ignore. Also ignore if the value is already set. */
- if (pw == NULL) break;
- Ustrcpy(name+Ustrlen(name)-4, US"group");
- ol2 = find_option(name, oltop, last);
- if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
- (ol2->type & opt_mask) == opt_expand_gid))
- {
- BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
- if (!*set_flag)
- {
- if (data_block)
- *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
- else
- *((gid_t *)ol2->v.value) = pw->pw_gid;
- *set_flag = TRUE;
- }
- }
- break;
+ if (pw == NULL) break;
+ Ustrcpy(name+Ustrlen(name)-4, US"group");
+ ol2 = find_option(name, oltop, last);
+ if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
+ (ol2->type & opt_mask) == opt_expand_gid))
+ {
+ BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
+ if (!*set_flag)
+ {
+ if (data_block)
+ *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
+ else
+ *((gid_t *)ol2->v.value) = pw->pw_gid;
+ *set_flag = TRUE;
+ }
+ }
+ break;
- /* If it was an expanded gid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
- of data. */
+ /* If it was an expanded gid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
+ of data. */
- case opt_expand_gid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ case opt_expand_gid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Handle freestanding gid */
+ /* Handle freestanding gid */
- case opt_gid:
- if (!route_findgroup(sptr, &gid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
- if (data_block)
- *((gid_t *)(US data_block + ol->v.offset)) = gid;
- else
- *((gid_t *)ol->v.value) = gid;
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- break;
+ case opt_gid:
+ if (!route_findgroup(sptr, &gid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
+ if (data_block)
+ *((gid_t *)(US data_block + ol->v.offset)) = gid;
+ else
+ *((gid_t *)ol->v.value) = gid;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ break;
- /* If it was a uid list, look up each individual entry, and build
- a vector of uids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into
- permanent store already.) */
+ /* If it was a uid list, look up each individual entry, and build
+ a vector of uids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into
+ permanent store already.) */
- case opt_uidlist:
- {
- int count = 1;
- uid_t *list;
- int ptr = 0;
- const uschar *p;
- const uschar *op = expand_string (sptr);
-
- if (op == NULL)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
- name, expand_string_message);
-
- p = op;
- if (*p != 0) count++;
- while (*p != 0) if (*p++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(uid_t));
- list[ptr++] = (uid_t)(count - 1);
-
- if (data_block)
- *((uid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((uid_t **)ol->v.value) = list;
+ case opt_uidlist:
+ {
+ int count = 1;
+ uid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const uschar *op = expand_string (sptr);
+
+ if (op == NULL)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+ name, expand_string_message);
+
+ p = op;
+ if (*p != 0) count++;
+ while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(uid_t));
+ list[ptr++] = (uid_t)(count - 1);
+
+ if (data_block)
+ *((uid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((uid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- /* If p is tainted we trap. Not sure that can happen */
- (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
- if (!route_finduser(big_buffer, NULL, &uid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
- big_buffer);
- list[ptr++] = uid;
- }
- }
- break;
+ p = op;
+ while (count-- > 1)
+ {
+ int sep = 0;
+ /* If p is tainted we trap. Not sure that can happen */
+ (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+ if (!route_finduser(big_buffer, NULL, &uid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
+ big_buffer);
+ list[ptr++] = uid;
+ }
+ break;
+ }
- /* If it was a gid list, look up each individual entry, and build
- a vector of gids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into permanent
- store already.) */
+ /* If it was a gid list, look up each individual entry, and build
+ a vector of gids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into permanent
+ store already.) */
- case opt_gidlist:
- {
- int count = 1;
- gid_t *list;
- int ptr = 0;
- const uschar *p;
- const uschar *op = expand_string (sptr);
-
- if (!op)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
- name, expand_string_message);
-
- p = op;
- if (*p != 0) count++;
- while (*p != 0) if (*p++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(gid_t));
- list[ptr++] = (gid_t)(count - 1);
-
- if (data_block)
- *((gid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((gid_t **)ol->v.value) = list;
+ case opt_gidlist:
+ {
+ int count = 1;
+ gid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const uschar *op = expand_string (sptr);
+
+ if (!op)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+ name, expand_string_message);
+
+ p = op;
+ if (*p != 0) count++;
+ while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(gid_t));
+ list[ptr++] = (gid_t)(count - 1);
+
+ if (data_block)
+ *((gid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((gid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- /* If p is tainted we trap. Not sure that can happen */
- (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
- if (!route_findgroup(big_buffer, &gid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
- big_buffer);
- list[ptr++] = gid;
- }
+ p = op;
+ while (count-- > 1)
+ {
+ int sep = 0;
+ /* If p is tainted we trap. Not sure that can happen */
+ (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+ if (!route_findgroup(big_buffer, &gid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
+ big_buffer);
+ list[ptr++] = gid;
+ }
+ break;
+ }
}
- break;
- }
- /* Release store if the value of the string doesn't need to be kept. */
+ /* Release store if the value of the string doesn't need to be kept. */
- if (freesptr) reset_point = store_reset(reset_point);
- break;
+ if (freesptr) reset_point = store_reset(reset_point);
+ break;
/* Expanded boolean: if no characters follow, or if there are no dollar
characters, this is a fixed-valued boolean, and we fall through. Otherwise,
save the string for later expansion in the alternate place. */
case opt_expand_bool:
- if (*s && Ustrchr(s, '$') != 0)
- {
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
+ if (*s && Ustrchr(s, '$') != 0)
{
- reset_point = store_mark();
- sptr = read_string(s, name);
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = sptr;
- else
- *(USS ol2->v.value) = sptr;
- freesptr = FALSE;
- break;
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ reset_point = store_mark();
+ sptr = read_string(s, name);
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = sptr;
+ else
+ *(USS ol2->v.value) = sptr;
+ freesptr = FALSE;
+ break;
+ }
}
- }
- /* Fall through */
+ /* Fall through */
/* Boolean: if no characters follow, the value is boolvalue. Otherwise
look for yes/not/true/false. Some booleans are stored in a single bit in
@@ -2095,121 +2095,121 @@ switch (type)
case opt_bit:
case opt_bool_verify:
case opt_bool_set:
- if (*s != 0)
- {
- s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
- if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
- boolvalue = TRUE;
- else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
- boolvalue = FALSE;
- else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "\"%s\" is not a valid value for the \"%s\" option", name2, name);
- if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
- US"for boolean option ", name);
- }
+ if (*s)
+ {
+ s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
+ if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
+ boolvalue = TRUE;
+ else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
+ boolvalue = FALSE;
+ else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "\"%s\" is not a valid value for the \"%s\" option", name2, name);
+ if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
+ US"for boolean option ", name);
+ }
- /* Handle single-bit type. */
+ /* Handle single-bit type. */
- if (type == opt_bit)
- {
- int bit = 1 << ((ol->type >> 16) & 31);
- int * ptr = data_block
- ? (int *)(US data_block + ol->v.offset)
- : (int *)ol->v.value;
- if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
- break;
- }
+ if (type == opt_bit)
+ {
+ int bit = 1 << ((ol->type >> 16) & 31);
+ int * ptr = data_block
+ ? (int *)(US data_block + ol->v.offset)
+ : (int *)ol->v.value;
+ if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
+ break;
+ }
- /* Handle full BOOL types */
+ /* Handle full BOOL types */
- if (data_block)
- *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
- else
- *((BOOL *)ol->v.value) = boolvalue;
+ if (data_block)
+ *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol->v.value) = boolvalue;
- /* Verify fudge */
+ /* Verify fudge */
- if (type == opt_bool_verify)
- {
- sprintf(CS name2, "%.50s_recipient", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
- else
- *((BOOL *)ol2->v.value) = boolvalue;
- }
+ if (type == opt_bool_verify)
+ {
+ sprintf(CS name2, "%.50s_recipient", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol2->v.value) = boolvalue;
+ }
- /* Note that opt_bool_set type is set, if there is somewhere to do so */
+ /* Note that opt_bool_set type is set, if there is somewhere to do so */
- else if (type == opt_bool_set)
- {
- sprintf(CS name2, "*set_%.50s", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
- else
- *((BOOL *)ol2->v.value) = TRUE;
- }
- break;
+ else if (type == opt_bool_set)
+ {
+ sprintf(CS name2, "*set_%.50s", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
+ else
+ *((BOOL *)ol2->v.value) = TRUE;
+ }
+ break;
/* Octal integer */
case opt_octint:
- intbase = 8;
- inttype = US"octal ";
+ intbase = 8;
+ inttype = US"octal ";
/* Integer: a simple(ish) case; allow octal and hex formats, and
suffixes K, M, G, and T. The different types affect output, not input. */
case opt_mkint:
case opt_int:
- {
- uschar *endptr;
- long int lvalue;
+ {
+ uschar *endptr;
+ long int lvalue;
- errno = 0;
- lvalue = strtol(CS s, CSS &endptr, intbase);
+ errno = 0;
+ lvalue = strtol(CS s, CSS &endptr, intbase);
- if (endptr == s)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
- inttype, name);
-
- if (errno != ERANGE && *endptr)
- {
- uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
+ if (endptr == s)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
+ inttype, name);
- if ((mp = Ustrchr(mp, *endptr)))
+ if (errno != ERANGE && *endptr)
{
- endptr++;
- do
+ uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
+
+ if ((mp = Ustrchr(mp, *endptr)))
{
- if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ endptr++;
+ do
{
- errno = ERANGE;
- break;
+ if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ {
+ errno = ERANGE;
+ break;
+ }
+ lvalue *= 1024;
}
- lvalue *= 1024;
+ while (*(mp += 2));
}
- while (*(mp += 2));
}
- }
- if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "absolute value of integer \"%s\" is too large (overflow)", s);
+ if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "absolute value of integer \"%s\" is too large (overflow)", s);
- while (isspace(*endptr)) endptr++;
- if (*endptr)
- extra_chars_error(endptr, inttype, US"integer value for ", name);
+ while (isspace(*endptr)) endptr++;
+ if (*endptr)
+ extra_chars_error(endptr, inttype, US"integer value for ", name);
- value = (int)lvalue;
- }
+ value = (int)lvalue;
+ }
- if (data_block)
- *(int *)(US data_block + ol->v.offset) = value;
- else
- *(int *)ol->v.value = value;
- break;
+ if (data_block)
+ *(int *)(US data_block + ol->v.offset) = value;
+ else
+ *(int *)ol->v.value = value;
+ break;
/* Integer held in K: again, allow formats and suffixes as above. */
@@ -2261,56 +2261,56 @@ switch (type)
/* Fixed-point number: held to 3 decimal places. */
case opt_fixed:
- if (sscanf(CS s, "%d%n", &value, &count) != 1)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "fixed-point number expected for %s", name);
+ if (sscanf(CS s, "%d%n", &value, &count) != 1)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "fixed-point number expected for %s", name);
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- value *= 1000;
+ value *= 1000;
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- /* We get a coverity error here for using count, as it derived
- from the tainted buffer pointed to by s, as parsed by sscanf().
- By the definition of sscanf we must be accessing between start
- and end of s (assuming it is nul-terminated...) so ignore the error. */
- /* coverity[tainted_data] */
- if (s[count] == '.')
- {
- int d = 100;
- while (isdigit(s[++count]))
+ /* We get a coverity error here for using count, as it derived
+ from the tainted buffer pointed to by s, as parsed by sscanf().
+ By the definition of sscanf we must be accessing between start
+ and end of s (assuming it is nul-terminated...) so ignore the error. */
+ /* coverity[tainted_data] */
+ if (s[count] == '.')
{
- value += (s[count] - '0') * d;
- d /= 10;
+ int d = 100;
+ while (isdigit(s[++count]))
+ {
+ value += (s[count] - '0') * d;
+ d /= 10;
+ }
}
- }
- while (isspace(s[count])) count++;
+ while (isspace(s[count])) count++;
- if (s[count] != 0)
- extra_chars_error(s+count, US"fixed-point value for ", name, US"");
+ if (s[count] != 0)
+ extra_chars_error(s+count, US"fixed-point value for ", name, US"");
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* There's a special routine to read time values. */
case opt_time:
- value = readconf_readtime(s, 0, FALSE);
- if (value < 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
- name);
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ value = readconf_readtime(s, 0, FALSE);
+ if (value < 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
+ name);
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* A time list is a list of colon-separated times, with the first
element holding the size of the list and the second the number of
@@ -3714,14 +3714,14 @@ readconf_driver_init(
optionlist *driver_optionlist,
int driver_optionlist_count)
{
-driver_instance **p = anchor;
-driver_instance *d = NULL;
-uschar *buffer;
+driver_instance ** p = anchor;
+driver_instance * d = NULL;
+uschar * buffer;
while ((buffer = get_config_line()))
{
uschar name[EXIM_DRIVERNAME_MAX];
- uschar *s;
+ const uschar * s;
/* Read the first name on the line and test for the start of a new driver. A
macro definition indicates the end of the previous driver. If this isn't the
@@ -4241,8 +4241,6 @@ Returns: nothing
static void
readconf_acl(void)
{
-uschar *p;
-
/* Read each ACL and add it into the tree. Macro (re)definitions are allowed
between ACLs. */
@@ -4251,10 +4249,10 @@ acl_line = get_config_line();
while(acl_line)
{
uschar name[EXIM_DRIVERNAME_MAX];
- tree_node *node;
- uschar *error;
+ tree_node * node;
+ uschar * error;
+ const uschar * p = readconf_readname(name, sizeof(name), acl_line);
- p = readconf_readname(name, sizeof(name), acl_line);
if (isupper(*name) && *p == '=')
{
if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE);