458 lines
9.6 KiB
C
458 lines
9.6 KiB
C
/*
|
||
* Copyright (c) 1983, 1995-1997 Eric P. Allman
|
||
* Copyright (c) 1988, 1993
|
||
* The Regents of the University of California. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* 3. All advertising materials mentioning features or use of this software
|
||
* must display the following acknowledgement:
|
||
* This product includes software developed by the University of
|
||
* California, Berkeley and its contributors.
|
||
* 4. Neither the name of the University nor the names of its contributors
|
||
* may be used to endorse or promote products derived from this software
|
||
* without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
* SUCH DAMAGE.
|
||
*/
|
||
|
||
#ifndef lint
|
||
static char sccsid[] = "@(#)macro.c 8.18 (Berkeley) 2/1/97";
|
||
#endif /* not lint */
|
||
|
||
# include "sendmail.h"
|
||
|
||
char *MacroName[256]; /* macro id to name table */
|
||
int NextMacroId = 0240; /* codes for long named macros */
|
||
|
||
|
||
/*
|
||
** EXPAND -- macro expand a string using $x escapes.
|
||
**
|
||
** Parameters:
|
||
** s -- the string to expand.
|
||
** buf -- the place to put the expansion.
|
||
** bufsize -- the size of the buffer.
|
||
** e -- envelope in which to work.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
*/
|
||
|
||
void
|
||
expand(s, buf, bufsize, e)
|
||
register char *s;
|
||
register char *buf;
|
||
size_t bufsize;
|
||
register ENVELOPE *e;
|
||
{
|
||
register char *xp;
|
||
register char *q;
|
||
bool skipping; /* set if conditionally skipping output */
|
||
bool recurse = FALSE; /* set if recursion required */
|
||
int i;
|
||
int skiplev; /* skipping nesting level */
|
||
int iflev; /* if nesting level */
|
||
char xbuf[BUFSIZ];
|
||
static int explevel = 0;
|
||
|
||
if (tTd(35, 24))
|
||
{
|
||
printf("expand(");
|
||
xputs(s);
|
||
printf(")\n");
|
||
}
|
||
|
||
skipping = FALSE;
|
||
skiplev = 0;
|
||
iflev = 0;
|
||
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;
|
||
switch (c & 0377)
|
||
{
|
||
case CONDIF: /* see if var set */
|
||
iflev++;
|
||
c = *++s;
|
||
if (skipping)
|
||
skiplev++;
|
||
else
|
||
skipping = macvalue(c, e) == NULL;
|
||
continue;
|
||
|
||
case CONDELSE: /* change state of skipping */
|
||
if (iflev == 0)
|
||
break;
|
||
if (skiplev == 0)
|
||
skipping = !skipping;
|
||
continue;
|
||
|
||
case CONDFI: /* stop skipping */
|
||
if (iflev == 0)
|
||
break;
|
||
iflev--;
|
||
if (skiplev == 0)
|
||
skipping = FALSE;
|
||
if (skipping)
|
||
skiplev--;
|
||
continue;
|
||
|
||
case MACROEXPAND: /* macro interpolation */
|
||
c = *++s & 0377;
|
||
if (c != '\0')
|
||
q = macvalue(c, e);
|
||
else
|
||
{
|
||
s--;
|
||
q = NULL;
|
||
}
|
||
if (q == NULL)
|
||
continue;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
** Interpolate q or output one character
|
||
*/
|
||
|
||
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 */
|
||
while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
|
||
{
|
||
/* check for any sendmail metacharacters */
|
||
if ((c & 0340) == 0200)
|
||
recurse = TRUE;
|
||
*xp++ = c;
|
||
}
|
||
}
|
||
}
|
||
*xp = '\0';
|
||
|
||
if (tTd(35, 24))
|
||
{
|
||
printf("expand ==> ");
|
||
xputs(xbuf);
|
||
printf("\n");
|
||
}
|
||
|
||
/* recurse as appropriate */
|
||
if (recurse)
|
||
{
|
||
if (explevel < MaxMacroRecursion)
|
||
{
|
||
explevel++;
|
||
expand(xbuf, buf, bufsize, e);
|
||
explevel--;
|
||
return;
|
||
}
|
||
syserr("expand: recursion too deep (%d max)",
|
||
MaxMacroRecursion);
|
||
}
|
||
|
||
/* copy results out */
|
||
i = xp - xbuf;
|
||
if (i >= bufsize)
|
||
i = bufsize - 1;
|
||
bcopy(xbuf, buf, i);
|
||
buf[i] = '\0';
|
||
}
|
||
/*
|
||
** DEFINE -- define a macro.
|
||
**
|
||
** this would be better done using a #define macro.
|
||
**
|
||
** Parameters:
|
||
** n -- the macro name.
|
||
** v -- the macro value.
|
||
** e -- the envelope to store the definition in.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** e->e_macro[n] is defined.
|
||
**
|
||
** Notes:
|
||
** There is one macro for each ASCII character,
|
||
** although they are not all used. The currently
|
||
** defined macros are:
|
||
**
|
||
** $a date in ARPANET format (preferring the Date: line
|
||
** of the message)
|
||
** $b the current date (as opposed to the date as found
|
||
** the message) in ARPANET format
|
||
** $c hop count
|
||
** $d (current) date in UNIX (ctime) format
|
||
** $e the SMTP entry message+
|
||
** $f raw from address
|
||
** $g translated from address
|
||
** $h to host
|
||
** $i queue id
|
||
** $j official SMTP hostname, used in messages+
|
||
** $k UUCP node name
|
||
** $l UNIX-style from line+
|
||
** $m The domain part of our full name.
|
||
** $n name of sendmail ("MAILER-DAEMON" on local
|
||
** net typically)+
|
||
** $o delimiters ("operators") for address tokens+
|
||
** $p my process id in decimal
|
||
** $q the string that becomes an address -- this is
|
||
** normally used to combine $g & $x.
|
||
** $r protocol used to talk to sender
|
||
** $s sender's host name
|
||
** $t the current time in seconds since 1/1/1970
|
||
** $u to user
|
||
** $v version number of sendmail
|
||
** $w our host name (if it can be determined)
|
||
** $x signature (full name) of from person
|
||
** $y the tty id of our terminal
|
||
** $z home directory of to person
|
||
** $_ RFC1413 authenticated sender address
|
||
**
|
||
** Macros marked with + must be defined in the
|
||
** configuration file and are used internally, but
|
||
** are not set.
|
||
**
|
||
** There are also some macros that can be used
|
||
** arbitrarily to make the configuration file
|
||
** cleaner. In general all upper-case letters
|
||
** are available.
|
||
*/
|
||
|
||
void
|
||
define(n, v, e)
|
||
int n;
|
||
char *v;
|
||
register ENVELOPE *e;
|
||
{
|
||
if (tTd(35, 9))
|
||
{
|
||
printf("%sdefine(%s as ",
|
||
(e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n));
|
||
xputs(v);
|
||
printf(")\n");
|
||
}
|
||
e->e_macro[n & 0377] = v;
|
||
}
|
||
/*
|
||
** MACVALUE -- return uninterpreted value of a macro.
|
||
**
|
||
** Parameters:
|
||
** n -- the name of the macro.
|
||
**
|
||
** Returns:
|
||
** The value of n.
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
*/
|
||
|
||
char *
|
||
macvalue(n, e)
|
||
int n;
|
||
register ENVELOPE *e;
|
||
{
|
||
n &= 0377;
|
||
while (e != NULL)
|
||
{
|
||
register char *p = e->e_macro[n];
|
||
|
||
if (p != NULL)
|
||
return (p);
|
||
e = e->e_parent;
|
||
}
|
||
return (NULL);
|
||
}
|
||
/*
|
||
** 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.
|
||
*/
|
||
|
||
char *
|
||
macname(n)
|
||
int n;
|
||
{
|
||
static char mbuf[2];
|
||
|
||
n &= 0377;
|
||
if (bitset(0200, n))
|
||
{
|
||
char *p = MacroName[n];
|
||
|
||
if (p != NULL)
|
||
return p;
|
||
return "***UNDEFINED MACRO***";
|
||
}
|
||
mbuf[0] = n;
|
||
mbuf[1] = '\0';
|
||
return mbuf;
|
||
}
|
||
/*
|
||
** MACID -- 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:
|
||
** The internal id code for this macro. This will
|
||
** fit into a single byte.
|
||
**
|
||
** Side Effects:
|
||
** If this is a new macro name, a new id is allocated.
|
||
*/
|
||
|
||
int
|
||
macid(p, ep)
|
||
register char *p;
|
||
char **ep;
|
||
{
|
||
int mid;
|
||
register char *bp;
|
||
char mbuf[21];
|
||
|
||
if (tTd(35, 14))
|
||
{
|
||
printf("macid(");
|
||
xputs(p);
|
||
printf(") => ");
|
||
}
|
||
|
||
if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
|
||
{
|
||
syserr("Name required for macro/class");
|
||
if (ep != NULL)
|
||
*ep = p;
|
||
if (tTd(35, 14))
|
||
printf("NULL\n");
|
||
return '\0';
|
||
}
|
||
if (*p != '{')
|
||
{
|
||
/* the macro is its own code */
|
||
if (ep != NULL)
|
||
*ep = p + 1;
|
||
if (tTd(35, 14))
|
||
printf("%c\n", *p);
|
||
return *p;
|
||
}
|
||
bp = mbuf;
|
||
while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof mbuf])
|
||
{
|
||
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, sizeof mbuf - 1);
|
||
}
|
||
else if (mbuf[1] == '\0')
|
||
{
|
||
/* ${x} == $x */
|
||
mid = mbuf[0];
|
||
p++;
|
||
}
|
||
else
|
||
{
|
||
register STAB *s;
|
||
|
||
s = stab(mbuf, ST_MACRO, ST_ENTER);
|
||
if (s->s_macro != 0)
|
||
mid = s->s_macro;
|
||
else
|
||
{
|
||
if (NextMacroId > 0377)
|
||
{
|
||
syserr("Macro/class {%s}: too many long names", mbuf);
|
||
s->s_macro = -1;
|
||
}
|
||
else
|
||
{
|
||
MacroName[NextMacroId] = s->s_name;
|
||
s->s_macro = mid = NextMacroId++;
|
||
}
|
||
}
|
||
p++;
|
||
}
|
||
if (ep != NULL)
|
||
*ep = p;
|
||
if (tTd(35, 14))
|
||
printf("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;
|
||
{
|
||
register STAB *s;
|
||
|
||
s = stab(str, ST_CLASS, ST_FIND);
|
||
return s != NULL && bitnset(cl & 0xff, s->s_class);
|
||
}
|