diff options
Diffstat (limited to 'sendmail/src/macro.c')
-rw-r--r-- | sendmail/src/macro.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/sendmail/src/macro.c b/sendmail/src/macro.c new file mode 100644 index 0000000..cdde4d2 --- /dev/null +++ b/sendmail/src/macro.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 1998-2001, 2003, 2006, 2007 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#include <sendmail.h> + +SM_RCSID("@(#)$Id: macro.c,v 8.107 2007/08/06 22:29:02 ca Exp $") + +#include <sm/sendmail.h> +#if MAXMACROID != (BITMAPBITS - 1) + ERROR Read the comment in conf.h +#endif /* MAXMACROID != (BITMAPBITS - 1) */ + +static char *MacroName[MAXMACROID + 1]; /* macro id to name table */ + +/* +** Codes for long named macros. +** See also macname(): + * if not ASCII printable, look up the name * + if (n <= 0x20 || n > 0x7f) +** First use 1 to NEXTMACROID_L, then use NEXTMACROID_H to MAXMACROID. +*/ + +#define NEXTMACROID_L 037 +#define NEXTMACROID_H 0240 + +#if _FFR_MORE_MACROS +/* table for next id in non-printable ASCII range: disallow some value */ +static int NextMIdTable[] = +{ + /* 0 nul */ 1, + /* 1 soh */ 2, + /* 2 stx */ 3, + /* 3 etx */ 4, + /* 4 eot */ 5, + /* 5 enq */ 6, + /* 6 ack */ 7, + /* 7 bel */ 8, + /* 8 bs */ 14, + /* 9 ht */ -1, + /* 10 nl */ -1, + /* 11 vt */ -1, + /* 12 np */ -1, + /* 13 cr */ -1, + /* 14 so */ 15, + /* 15 si */ 16, + /* 16 dle */ 17, + /* 17 dc1 */ 18, + /* 18 dc2 */ 19, + /* 19 dc3 */ 20, + /* 20 dc4 */ 21, + /* 21 nak */ 22, + /* 22 syn */ 23, + /* 23 etb */ 24, + /* 24 can */ 25, + /* 25 em */ 26, + /* 26 sub */ 27, + /* 27 esc */ 28, + /* 28 fs */ 29, + /* 29 gs */ 30, + /* 30 rs */ 31, + /* 31 us */ 32, + /* 32 sp */ -1, +}; + +#define NEXTMACROID(mid) ( \ + (mid < NEXTMACROID_L) ? (NextMIdTable[mid]) : \ + ((mid < NEXTMACROID_H) ? NEXTMACROID_H : (mid + 1))) + +int NextMacroId = 1; /* codes for long named macros */ +/* see sendmail.h: Special characters in rewriting rules. */ +#else /* _FFR_MORE_MACROS */ +int NextMacroId = 0240; /* codes for long named macros */ +#define NEXTMACROID(mid) ((mid) + 1) +#endif /* _FFR_MORE_MACROS */ + + +/* +** INITMACROS -- initialize the macro system +** +** This just involves defining some macros that are actually +** used internally as metasymbols to be themselves. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** initializes several macros to be themselves. +*/ + +struct metamac MetaMacros[] = +{ + /* LHS pattern matching characters */ + { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, + { '=', MATCHCLASS }, { '~', MATCHNCLASS }, + + /* these are RHS metasymbols */ + { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, + { '>', CALLSUBR }, + + /* the conditional operations */ + { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, + + /* the hostname lookup characters */ + { '[', HOSTBEGIN }, { ']', HOSTEND }, + { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, + + /* miscellaneous control characters */ + { '&', MACRODEXPAND }, + + { '\0', '\0' } +}; + +#define MACBINDING(name, mid) \ + stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ + MacroName[mid] = name; + +void +initmacros(e) + ENVELOPE *e; +{ + struct metamac *m; + int c; + char buf[5]; + + for (m = MetaMacros; m->metaname != '\0'; m++) + { + buf[0] = m->metaval; + buf[1] = '\0'; + macdefine(&e->e_macro, A_TEMP, m->metaname, buf); + } + buf[0] = MATCHREPL; + buf[2] = '\0'; + for (c = '0'; c <= '9'; c++) + { + buf[1] = c; + macdefine(&e->e_macro, A_TEMP, c, buf); + } + + /* set defaults for some macros sendmail will use later */ + macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON"); + + /* set up external names for some internal macros */ + MACBINDING("opMode", MID_OPMODE); + /*XXX should probably add equivalents for all short macros here XXX*/ +} + +/* +** EXPAND/DOEXPAND -- macro expand a string using $x escapes. +** +** After expansion, the expansion will be in external form (that is, +** there will be no sendmail metacharacters and METAQUOTEs will have +** been stripped out). +** +** Parameters: +** s -- the string to expand. +** buf -- the place to put the expansion. +** bufsize -- the size of the buffer. +** explevel -- the depth of expansion (doexpand only) +** e -- envelope in which to work. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +static void doexpand __P(( char *, char *, size_t, int, ENVELOPE *)); + +static void +doexpand(s, buf, bufsize, explevel, e) + char *s; + char *buf; + size_t bufsize; + int explevel; + ENVELOPE *e; +{ + char *xp; + char *q; + bool skipping; /* set if conditionally skipping output */ + bool recurse; /* set if recursion required */ + size_t i; + int skiplev; /* skipping nesting level */ + int iflev; /* if nesting level */ + bool quotenext; /* quote the following character */ + char xbuf[MACBUFSIZE]; + + if (tTd(35, 24)) + { + sm_dprintf("expand("); + xputs(sm_debug_file(), s); + sm_dprintf(")\n"); + } + + recurse = false; + skipping = false; + skiplev = 0; + iflev = 0; + quotenext = false; + if (s == NULL) + s = ""; + for (xp = xbuf; *s != '\0'; s++) + { + int c; + + /* + ** Check for non-ordinary (special?) character. + ** 'q' will be the interpolated quantity. + */ + + q = NULL; + c = *s & 0377; + + if (quotenext) + { + quotenext = false; + goto simpleinterpolate; + } + + switch (c) + { + case CONDIF: /* see if var set */ + iflev++; + c = *++s & 0377; + if (skipping) + skiplev++; + else + { + char *mv; + + mv = macvalue(c, e); + skipping = (mv == NULL || *mv == '\0'); + } + continue; + + case CONDELSE: /* change state of skipping */ + if (iflev == 0) + break; /* XXX: error */ + if (skiplev == 0) + skipping = !skipping; + continue; + + case CONDFI: /* stop skipping */ + if (iflev == 0) + break; /* XXX: error */ + iflev--; + if (skiplev == 0) + skipping = false; + if (skipping) + skiplev--; + continue; + + case MACROEXPAND: /* macro interpolation */ + c = bitidx(*++s); + if (c != '\0') + q = macvalue(c, e); + else + { + s--; + q = NULL; + } + if (q == NULL) + continue; + break; + + case METAQUOTE: + /* next octet completely quoted */ + quotenext = true; + break; + } + + /* + ** Interpolate q or output one character + */ + + simpleinterpolate: + if (skipping || xp >= &xbuf[sizeof(xbuf) - 1]) + continue; + if (q == NULL) + *xp++ = c; + else + { + /* copy to end of q or max space remaining in buf */ + bool hiderecurse = false; + + while ((c = *q++) != '\0' && + xp < &xbuf[sizeof(xbuf) - 1]) + { + /* check for any sendmail metacharacters */ + if (!hiderecurse && (c & 0340) == 0200) + recurse = true; + *xp++ = c; + + /* give quoted characters a free ride */ + hiderecurse = (c & 0377) == METAQUOTE; + } + } + } + *xp = '\0'; + + if (tTd(35, 28)) + { + sm_dprintf("expand(%d) ==> ", explevel); + xputs(sm_debug_file(), xbuf); + sm_dprintf("\n"); + } + + /* recurse as appropriate */ + if (recurse) + { + if (explevel < MaxMacroRecursion) + { + doexpand(xbuf, buf, bufsize, explevel + 1, e); + return; + } + syserr("expand: recursion too deep (%d max)", + MaxMacroRecursion); + } + + /* copy results out */ + if (explevel == 0) + (void) sm_strlcpy(buf, xbuf, bufsize); + else + { + /* leave in internal form */ + i = xp - xbuf; + if (i >= bufsize) + i = bufsize - 1; + memmove(buf, xbuf, i); + buf[i] = '\0'; + } + + if (tTd(35, 24)) + { + sm_dprintf("expand ==> "); + xputs(sm_debug_file(), buf); + sm_dprintf("\n"); + } +} + +void +expand(s, buf, bufsize, e) + char *s; + char *buf; + size_t bufsize; + ENVELOPE *e; +{ + doexpand(s, buf, bufsize, 0, e); +} + +/* +** MACDEFINE -- bind a macro name to a value +** +** Set a macro to a value, with fancy storage management. +** macdefine will make a copy of the value, if required, +** and will ensure that the storage for the previous value +** is not leaked. +** +** Parameters: +** mac -- Macro table. +** vclass -- storage class of 'value', ignored if value==NULL. +** A_HEAP means that the value was allocated by +** malloc, and that macdefine owns the storage. +** A_TEMP means that value points to temporary storage, +** and thus macdefine needs to make a copy. +** A_PERM means that value points to storage that +** will remain allocated and unchanged for +** at least the lifetime of mac. Use A_PERM if: +** -- value == NULL, +** -- value points to a string literal, +** -- value was allocated from mac->mac_rpool +** or (in the case of an envelope macro) +** from e->e_rpool, +** -- in the case of an envelope macro, +** value is a string member of the envelope +** such as e->e_sender. +** id -- Macro id. This is a single character macro name +** such as 'g', or a value returned by macid(). +** value -- Macro value: either NULL, or a string. +*/ + +void +#if SM_HEAP_CHECK +macdefine_tagged(mac, vclass, id, value, file, line, grp) +#else /* SM_HEAP_CHECK */ +macdefine(mac, vclass, id, value) +#endif /* SM_HEAP_CHECK */ + MACROS_T *mac; + ARGCLASS_T vclass; + int id; + char *value; +#if SM_HEAP_CHECK + char *file; + int line; + int grp; +#endif /* SM_HEAP_CHECK */ +{ + char *newvalue; + + if (id < 0 || id > MAXMACROID) + return; + + if (tTd(35, 9)) + { + sm_dprintf("%sdefine(%s as ", + mac->mac_table[id] == NULL ? "" : "re", macname(id)); + xputs(sm_debug_file(), value); + sm_dprintf(")\n"); + } + + if (mac->mac_rpool == NULL) + { + char *freeit = NULL; + + if (mac->mac_table[id] != NULL && + bitnset(id, mac->mac_allocated)) + freeit = mac->mac_table[id]; + + if (value == NULL || vclass == A_HEAP) + { + sm_heap_checkptr_tagged(value, file, line); + newvalue = value; + clrbitn(id, mac->mac_allocated); + } + else + { +#if SM_HEAP_CHECK + newvalue = sm_strdup_tagged_x(value, file, line, 0); +#else /* SM_HEAP_CHECK */ + newvalue = sm_strdup_x(value); +#endif /* SM_HEAP_CHECK */ + setbitn(id, mac->mac_allocated); + } + mac->mac_table[id] = newvalue; + if (freeit != NULL) + sm_free(freeit); + } + else + { + if (value == NULL || vclass == A_PERM) + newvalue = value; + else + newvalue = sm_rpool_strdup_x(mac->mac_rpool, value); + mac->mac_table[id] = newvalue; + if (vclass == A_HEAP) + sm_free(value); + } + +#if _FFR_RESET_MACRO_GLOBALS + switch (id) + { + case 'j': + PSTRSET(MyHostName, value); + break; + } +#endif /* _FFR_RESET_MACRO_GLOBALS */ +} + +/* +** MACSET -- set a named macro to a value (low level) +** +** No fancy storage management; the caller takes full responsibility. +** Often used with macget; see also macdefine. +** +** Parameters: +** mac -- Macro table. +** i -- Macro name, specified as an integer offset. +** value -- Macro value: either NULL, or a string. +*/ + +void +macset(mac, i, value) + MACROS_T *mac; + int i; + char *value; +{ + if (i < 0 || i > MAXMACROID) + return; + + if (tTd(35, 9)) + { + sm_dprintf("macset(%s as ", macname(i)); + xputs(sm_debug_file(), value); + sm_dprintf(")\n"); + } + mac->mac_table[i] = value; +} + +/* +** MACVALUE -- return uninterpreted value of a macro. +** +** Does fancy path searching. +** The low level counterpart is macget. +** +** Parameters: +** n -- the name of the macro. +** e -- envelope in which to start looking for the macro. +** +** Returns: +** The value of n. +** +** Side Effects: +** none. +*/ + +char * +macvalue(n, e) + int n; + ENVELOPE *e; +{ + n = bitidx(n); + if (e != NULL && e->e_mci != NULL) + { + char *p = e->e_mci->mci_macro.mac_table[n]; + + if (p != NULL) + return p; + } + while (e != NULL) + { + char *p = e->e_macro.mac_table[n]; + + if (p != NULL) + return p; + if (e == e->e_parent) + break; + e = e->e_parent; + } + return GlobalMacros.mac_table[n]; +} + +/* +** MACNAME -- return the name of a macro given its internal id +** +** Parameter: +** n -- the id of the macro +** +** Returns: +** The name of n. +** +** Side Effects: +** none. +** +** WARNING: +** Not thread-safe. +*/ + +char * +macname(n) + int n; +{ + static char mbuf[2]; + + n = (int)(unsigned char)n; + if (n > MAXMACROID) + return "***OUT OF RANGE MACRO***"; + + /* if not ASCII printable, look up the name */ + if (n <= 0x20 || n > 0x7f) + { + char *p = MacroName[n]; + + if (p != NULL) + return p; + return "***UNDEFINED MACRO***"; + } + + /* if in the ASCII graphic range, just return the id directly */ + mbuf[0] = n; + mbuf[1] = '\0'; + return mbuf; +} + +/* +** MACID_PARSE -- return id of macro identified by its name +** +** Parameters: +** p -- pointer to name string -- either a single +** character or {name}. +** ep -- filled in with the pointer to the byte +** after the name. +** +** Returns: +** 0 -- An error was detected. +** 1..MAXMACROID -- The internal id code for this macro. +** +** Side Effects: +** If this is a new macro name, a new id is allocated. +** On error, syserr is called. +*/ + +int +macid_parse(p, ep) + char *p; + char **ep; +{ + int mid; + char *bp; + char mbuf[MAXMACNAMELEN + 1]; + + if (tTd(35, 14)) + { + sm_dprintf("macid("); + xputs(sm_debug_file(), p); + sm_dprintf(") => "); + } + + if (*p == '\0' || (p[0] == '{' && p[1] == '}')) + { + syserr("Name required for macro/class"); + if (ep != NULL) + *ep = p; + if (tTd(35, 14)) + sm_dprintf("NULL\n"); + return 0; + } + if (*p != '{') + { + /* the macro is its own code */ + if (ep != NULL) + *ep = p + 1; + if (tTd(35, 14)) + { + char buf[2]; + + buf[0] = *p; + buf[1] = '\0'; + xputs(sm_debug_file(), buf); + sm_dprintf("\n"); + } + return bitidx(*p); + } + bp = mbuf; + while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof(mbuf) - 1]) + { + if (isascii(*p) && (isalnum(*p) || *p == '_')) + *bp++ = *p; + else + syserr("Invalid macro/class character %c", *p); + } + *bp = '\0'; + mid = -1; + if (*p == '\0') + { + syserr("Unbalanced { on %s", mbuf); /* missing } */ + } + else if (*p != '}') + { + syserr("Macro/class name ({%s}) too long (%d chars max)", + mbuf, (int) (sizeof(mbuf) - 1)); + } + else if (mbuf[1] == '\0' && mbuf[0] >= 0x20) + { + /* ${x} == $x */ + mid = bitidx(mbuf[0]); + p++; + } + else + { + STAB *s; + + s = stab(mbuf, ST_MACRO, ST_ENTER); + if (s->s_macro != 0) + mid = s->s_macro; + else + { + if (NextMacroId > MAXMACROID) + { + syserr("Macro/class {%s}: too many long names", + mbuf); + s->s_macro = -1; + } + else + { + MacroName[NextMacroId] = s->s_name; + s->s_macro = mid = NextMacroId; + NextMacroId = NEXTMACROID(NextMacroId); + } + } + p++; + } + if (ep != NULL) + *ep = p; + if (mid < 0 || mid > MAXMACROID) + { + syserr("Unable to assign macro/class ID (mid = 0x%x)", mid); + if (tTd(35, 14)) + sm_dprintf("NULL\n"); + return 0; + } + if (tTd(35, 14)) + sm_dprintf("0x%x\n", mid); + return mid; +} + +/* +** WORDINCLASS -- tell if a word is in a specific class +** +** Parameters: +** str -- the name of the word to look up. +** cl -- the class name. +** +** Returns: +** true if str can be found in cl. +** false otherwise. +*/ + +bool +wordinclass(str, cl) + char *str; + int cl; +{ + STAB *s; + + s = stab(str, ST_CLASS, ST_FIND); + return s != NULL && bitnset(bitidx(cl), s->s_class); +} |