From: Philip Hazel Date: Tue, 5 Apr 2005 13:58:34 +0000 (+0000) Subject: Kjetil Homme's patch for extended macro features (redefinition, X-Git-Tag: exim-4_51~30 X-Git-Url: https://git.exim.org/users/jgh/exim.git/commitdiff_plain/cf00dad6749f2b3c2ba9b1e02034b23944b7ee0c?hp=54cdb463ab15d0a064cfe0a276b3e3974767c8c7 Kjetil Homme's patch for extended macro features (redefinition, definition between driver and ACL definitions). --- diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 87e001f27..ff8c2835d 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.107 2005/04/04 10:33:49 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.108 2005/04/05 13:58:34 ph10 Exp $ Change log file for Exim from version 4.21 ------------------------------------------- @@ -119,6 +119,9 @@ PH/20 When checking for unexpected SMTP input at connect time (before writing PH/21 Added acl_not_smtp_mime to allow for MIME scanning for non-SMTP messages. +PH/22 Added support for macro redefinition, and (re)definition in between + driver and ACL definitions. + A note about Exim versions 4.44 and 4.50 ---------------------------------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 6f3ac86d1..b2563f30c 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/NewStuff,v 1.32 2005/04/04 10:33:49 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/NewStuff,v 1.33 2005/04/05 13:58:34 ph10 Exp $ New Features in Exim -------------------- @@ -100,6 +100,36 @@ PH/04 There is a new option called acl_not_smtp_mime that allows you to scan MIME parts in non-SMTP messages. It operates in exactly the same way as acl_smtp_mime +PH/05 It is now possible to redefine a macro within the configuration file. + The macro must have been previously defined within the configuration (or + an included file). A definition on the command line using the -D option + causes all definitions and redefinitions within the file to be ignored. + In other words, -D overrides any values that are set in the file. + Redefinition is specified by using '==' instead of '='. For example: + + MAC1 = initial value + ... + MAC1 == updated value + + Redefinition does not alter the order in which the macros are applied to + the subsequent lines of the configuration file. It is still the same + order in which the macros were originally defined. All that changes is + the macro's value. Redefinition makes it possible to accumulate values. + For example: + + MAC1 = initial value + ... + MAC1 == MAC1 and something added + + This can be helpful in situations where the configuration file is built + from a number of other files. + +PH/06 Macros may now be defined or redefined between router, transport, + authenticator, or ACL definitions, as well as in the main part of the + configuration. They may not, however, be changed within an individual + driver or ACL, or in the local_scan, retry, or rewrite sections of the + configuration. + Version 4.50 ------------ diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index 2b5426b9d..ce5402eb8 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -1,4 +1,4 @@ -$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.18 2005/03/29 14:53:09 ph10 Exp $ +$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.19 2005/04/05 13:58:35 ph10 Exp $ EXIM ACKNOWLEDGEMENTS @@ -20,7 +20,7 @@ relatively small patches. Philip Hazel Lists created: 20 November 2002 -Last updated: 29 March 2005 +Last updated: 05 April 2005 THE OLD LIST @@ -41,6 +41,7 @@ Yann Golanski Numerical hash function Jason Gunthorpe IPv6 support (Linux) Michael Haardt LDAP support enhancement Steve Haslam First code for TLS +Kjetil Torgrim Homme Suggested patch for macro extensions John Horne Proof-reading documentation (repeatedly) Pierre Humblet Cygwin support Paul Kelly MySQL interface diff --git a/src/src/acl.c b/src/src/acl.c index 704e9cb5e..1d20ff7c7 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.26 2005/03/29 10:56:48 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.27 2005/04/05 13:58:35 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -589,18 +589,11 @@ while ((s = (*func)()) != NULL) s++; } - /* Read the name of a verb or a condition, or the start of a new ACL */ + /* Read the name of a verb or a condition, or the start of a new ACL, which + can be started by a name, or by a macro definition. */ s = readconf_readname(name, sizeof(name), s); - if (*s == ':') - { - if (negated || name[0] == 0) - { - *error = string_sprintf("malformed ACL name in \"%s\"", saveline); - return NULL; - } - break; - } + if (*s == ':' || isupper(name[0] && *s == '=')) return yield; /* If a verb is unrecognized, it may be another condition or modifier that continues the previous verb. */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 0fe00cf6b..cf4048aae 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.6 2005/04/04 10:33:49 ph10 Exp $ */ +/* $Cambridge: exim/src/src/readconf.c,v 1.7 2005/04/05 13:58:35 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -435,6 +435,122 @@ return US""; +/************************************************* +* Deal with an assignment to a macro * +*************************************************/ + +/* This function is called when a line that starts with an upper case letter is +encountered. The argument "line" should contain a complete logical line, and +start with the first letter of the macro name. The macro name and the +replacement text are extracted and stored. Redefinition of existing, +non-command line, macros is permitted using '==' instead of '='. + +Arguments: + s points to the start of the logical line + +Returns: nothing +*/ + +static void +read_macro_assignment(uschar *s) +{ +uschar name[64]; +int namelen = 0; +BOOL redef = FALSE; +macro_item *m; +macro_item *mlast = NULL; + +while (isalnum(*s) || *s == '_') + { + if (namelen >= sizeof(name) - 1) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "macro name too long (maximum is %d characters)", sizeof(name) - 1); + name[namelen++] = *s++; + } +name[namelen] = 0; + +while (isspace(*s)) s++; +if (*s++ != '=') + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "malformed macro definition"); + +if (*s == '=') + { + redef = TRUE; + s++; + } +while (isspace(*s)) s++; + +/* If an existing macro of the same name was defined on the command line, we +just skip this definition. It's an error to attempt to redefine a macro without +redef set to TRUE, or to redefine a macro when it hasn't been defined earlier. +It is also an error to define a macro whose name begins with the name of a +previously defined macro. Note: it is documented that the other way round +works. */ + +for (m = macros; m != NULL; m = m->next) + { + int len = Ustrlen(m->name); + + if (Ustrcmp(m->name, name) == 0) + { + if (!m->command_line && !redef) + log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already " + "defined (use \"==\" if you want to redefine it", name); + break; + } + + if (len < namelen && Ustrstr(name, m->name) != NULL) + log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as " + "a macro because previously defined macro \"%s\" is a substring", + name, m->name); + + /* We cannot have this test, because it is documented that a substring + macro is permitted (there is even an example). + * + * if (len > namelen && Ustrstr(m->name, name) != NULL) + * log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as " + * "a macro because it is a substring of previously defined macro \"%s\"", + * name, m->name); + */ + + mlast = m; + } + +/* Check for an overriding command-line definition. */ + +if (m != NULL && m->command_line) return; + +/* Redefinition must refer to an existing macro. */ + +if (redef) + { + if (m == NULL) + log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "can't redefine an undefined macro " + "\"%s\"", name); + } + +/* We have a new definition. The macro_item structure includes a final vector +called "name" which is one byte long. Thus, adding "namelen" gives us enough +room to store the "name" string. */ + +else + { + m = store_get(sizeof(macro_item) + namelen); + if (macros == NULL) macros = m; else mlast->next = m; + Ustrncpy(m->name, name, namelen); + m->name[namelen] = 0; + m->next = NULL; + m->command_line = FALSE; + } + +/* Set the value of the new or redefined macro */ + +m->replacement = string_copy(s); +} + + + + /************************************************* * Read configuration line * @@ -2631,65 +2747,7 @@ a macro definition. */ while ((s = get_config_line()) != NULL) { - if (isupper(s[0])) - { - macro_item *m; - macro_item *mlast = NULL; - uschar name[64]; - int namelen = 0; - - while (isalnum(*s) || *s == '_') - { - if (namelen >= sizeof(name) - 1) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "macro name too long (maximum is %d characters)", sizeof(name) - 1); - name[namelen++] = *s++; - } - name[namelen] = 0; - while (isspace(*s)) s++; - if (*s++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "malformed macro definition"); - while (isspace(*s)) s++; - - /* If an existing macro of the same name was defined on the command line, - we just skip this definition. Otherwise it's an error to attempt to - redefine a macro. It is also an error to define a macro whose name begins - with the name of a previously-defined macro. */ - - for (m = macros; m != NULL; m = m->next) - { - int len = Ustrlen(m->name); - - if (Ustrcmp(m->name, name) == 0) - { - if (m->command_line) break; - log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already " - "defined", name); - } - - if (len < namelen && Ustrstr(name, m->name) != NULL) - log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as " - "a macro because previously defined macro \"%s\" is a substring", - name, m->name); - - mlast = m; - } - if (m != NULL) continue; /* Found an overriding command-line definition */ - - m = store_get(sizeof(macro_item) + namelen); - m->next = NULL; - m->command_line = FALSE; - if (mlast == NULL) macros = m; else mlast->next = m; - - /* This use of strcpy() is OK because we have obtained a block of store - whose size is based on the length of "name". The definition of the - macro_item structure includes a final vector called "name" which is one - byte long. Thus, adding "namelen" gives us enough room to store the "name" - string. */ - - Ustrcpy(m->name, name); - m->replacement = string_copy(s); - } + if (isupper(s[0])) read_macro_assignment(s); else if (Ustrncmp(s, "domainlist", 10) == 0) read_named_list(&domainlist_anchor, &domainlist_count, @@ -3071,16 +3129,33 @@ driver_instance **p = anchor; driver_instance *d = NULL; uschar *buffer; -/* Now process the configuration lines */ - while ((buffer = get_config_line()) != NULL) { uschar name[64]; + uschar *s; - /* Read the first name on the line and test for the start of a new driver. - If this isn't the start of a new driver, the line will be re-read. */ + /* 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 + start of a new driver, the line will be re-read. */ - uschar *s = readconf_readname(name, sizeof(name), buffer); + s = readconf_readname(name, sizeof(name), buffer); + + /* Handle macro definition, first finishing off the initialization of the + previous driver, if any. */ + + if (isupper(*name) && *s == '=') + { + if (d != NULL) + { + if (d->driver_name == NULL) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "no driver defined for %s \"%s\"", class, d->name); + (d->info->init)(d); + d = NULL; + } + read_macro_assignment(buffer); + continue; + } /* If the line starts with a name terminated by a colon, we are at the start of the definition of a new driver. The rest of the line must be @@ -3128,7 +3203,8 @@ while ((buffer = get_config_line()) != NULL) continue; } - /* Give an error if we have not set up a current driver yet. */ + /* Not the start of a new driver. Give an error if we have not set up a + current driver yet. */ if (d == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s name missing", class); @@ -3582,7 +3658,8 @@ if (skip) return; } -/* Read each ACL and add it into the tree */ +/* Read each ACL and add it into the tree. Macro (re)definitions are allowed +between ACLs. */ acl_line = get_config_line(); @@ -3593,8 +3670,15 @@ while(acl_line != NULL) uschar *error; p = readconf_readname(name, sizeof(name), acl_line); - if (*p != ':') - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing ACL name"); + if (isupper(*name) && *p == '=') + { + read_macro_assignment(acl_line); + acl_line = get_config_line(); + continue; + } + + if (*p != ':' || name[0] == 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name"); node = store_get(sizeof(tree_node) + Ustrlen(name)); Ustrcpy(node->name, name);