📄 map.c
字号:
/*
* Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1992, 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.
*
*/
#ifndef lint
static char id[] = "@(#)$Id: map.c,v 8.414.4.24 2000/09/27 04:11:29 gshapiro Exp $";
#endif /* ! lint */
#include <sendmail.h>
#ifdef NDBM
# include <ndbm.h>
# ifdef R_FIRST
ERROR README: You are running the Berkeley DB version of ndbm.h. See
ERROR README: the README file about tweaking Berkeley DB so it can
ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
ERROR README: and use -DNEWDB instead.
# endif /* R_FIRST */
#endif /* NDBM */
#ifdef NEWDB
# include <db.h>
# ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
# endif /* ! DB_VERSION_MAJOR */
#endif /* NEWDB */
#ifdef NIS
struct dom_binding; /* forward reference needed on IRIX */
# include <rpcsvc/ypclnt.h>
# ifdef NDBM
# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
# endif /* NDBM */
#endif /* NIS */
#ifdef NEWDB
# if DB_VERSION_MAJOR < 2
static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
# endif /* DB_VERSION_MAJOR < 2 */
# if DB_VERSION_MAJOR == 2
static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
# endif /* DB_VERSION_MAJOR == 2 */
# if DB_VERSION_MAJOR > 2
static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
# endif /* DB_VERSION_MAJOR > 2 */
#endif /* NEWDB */
static bool extract_canonname __P((char *, char *, char[], int));
#ifdef LDAPMAP
static void ldapmap_clear __P((LDAPMAP_STRUCT *));
static STAB *ldapmap_findconn __P((LDAPMAP_STRUCT *));
static int ldapmap_geterrno __P((LDAP *));
static void ldapmap_setopts __P((LDAP *, LDAPMAP_STRUCT *));
static bool ldapmap_start __P((MAP *));
static void ldaptimeout __P((int));
#endif /* LDAPMAP */
static void map_close __P((STAB *, int));
static void map_init __P((STAB *, int));
#ifdef NISPLUS
static bool nisplus_getcanonname __P((char *, int, int *));
#endif /* NISPLUS */
#ifdef NIS
static bool nis_getcanonname __P((char *, int, int *));
#endif /* NIS */
#if NETINFO
static bool ni_getcanonname __P((char *, int, int *));
#endif /* NETINFO */
static bool text_getcanonname __P((char *, int, int *));
/*
** MAP.C -- implementations for various map classes.
**
** Each map class implements a series of functions:
**
** bool map_parse(MAP *map, char *args)
** Parse the arguments from the config file. Return TRUE
** if they were ok, FALSE otherwise. Fill in map with the
** values.
**
** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
** Look up the key in the given map. If found, do any
** rewriting the map wants (including "args" if desired)
** and return the value. Set *pstat to the appropriate status
** on error and return NULL. Args will be NULL if called
** from the alias routines, although this should probably
** not be relied upon. It is suggested you call map_rewrite
** to return the results -- it takes care of null termination
** and uses a dynamically expanded buffer as needed.
**
** void map_store(MAP *map, char *key, char *value)
** Store the key:value pair in the map.
**
** bool map_open(MAP *map, int mode)
** Open the map for the indicated mode. Mode should
** be either O_RDONLY or O_RDWR. Return TRUE if it
** was opened successfully, FALSE otherwise. If the open
** failed an the MF_OPTIONAL flag is not set, it should
** also print an error. If the MF_ALIAS bit is set
** and this map class understands the @:@ convention, it
** should call aliaswait() before returning.
**
** void map_close(MAP *map)
** Close the map.
**
** This file also includes the implementation for getcanonname.
** It is currently implemented in a pretty ad-hoc manner; it ought
** to be more properly integrated into the map structure.
*/
#define DBMMODE 0644
#ifndef EX_NOTFOUND
# define EX_NOTFOUND EX_NOHOST
#endif /* ! EX_NOTFOUND */
#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
#ifndef O_ACCMODE
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
#endif /* ! O_ACCMODE */
/*
** MAP_PARSEARGS -- parse config line arguments for database lookup
**
** This is a generic version of the map_parse method.
**
** Parameters:
** map -- the map being initialized.
** ap -- a pointer to the args on the config line.
**
** Returns:
** TRUE -- if everything parsed OK.
** FALSE -- otherwise.
**
** Side Effects:
** null terminates the filename; stores it in map
*/
bool
map_parseargs(map, ap)
MAP *map;
char *ap;
{
register char *p = ap;
/*
** there is no check whether there is really an argument,
** but that's not important enough to warrant extra code
*/
map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
map->map_spacesub = SpaceSub; /* default value */
for (;;)
{
while (isascii(*p) && isspace(*p))
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'N':
map->map_mflags |= MF_INCLNULL;
map->map_mflags &= ~MF_TRY0NULL;
break;
case 'O':
map->map_mflags &= ~MF_TRY1NULL;
break;
case 'o':
map->map_mflags |= MF_OPTIONAL;
break;
case 'f':
map->map_mflags |= MF_NOFOLDCASE;
break;
case 'm':
map->map_mflags |= MF_MATCHONLY;
break;
case 'A':
map->map_mflags |= MF_APPEND;
break;
case 'q':
map->map_mflags |= MF_KEEPQUOTES;
break;
case 'a':
map->map_app = ++p;
break;
case 'T':
map->map_tapp = ++p;
break;
case 'k':
while (isascii(*++p) && isspace(*p))
continue;
map->map_keycolnm = p;
break;
case 'v':
while (isascii(*++p) && isspace(*p))
continue;
map->map_valcolnm = p;
break;
case 'z':
if (*++p != '\\')
map->map_coldelim = *p;
else
{
switch (*++p)
{
case 'n':
map->map_coldelim = '\n';
break;
case 't':
map->map_coldelim = '\t';
break;
default:
map->map_coldelim = '\\';
}
}
break;
case 't':
map->map_mflags |= MF_NODEFER;
break;
case 'S':
map->map_spacesub = *++p;
break;
case 'D':
map->map_mflags |= MF_DEFER;
break;
default:
syserr("Illegal option %c map %s", *p, map->map_mname);
break;
}
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
if (*p != '\0')
*p++ = '\0';
}
if (map->map_app != NULL)
map->map_app = newstr(map->map_app);
if (map->map_tapp != NULL)
map->map_tapp = newstr(map->map_tapp);
if (map->map_keycolnm != NULL)
map->map_keycolnm = newstr(map->map_keycolnm);
if (map->map_valcolnm != NULL)
map->map_valcolnm = newstr(map->map_valcolnm);
if (*p != '\0')
{
map->map_file = p;
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
if (*p != '\0')
*p++ = '\0';
map->map_file = newstr(map->map_file);
}
while (*p != '\0' && isascii(*p) && isspace(*p))
p++;
if (*p != '\0')
map->map_rebuild = newstr(p);
if (map->map_file == NULL &&
!bitset(MCF_OPTFILE, map->map_class->map_cflags))
{
syserr("No file name for %s map %s",
map->map_class->map_cname, map->map_mname);
return FALSE;
}
return TRUE;
}
/*
** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
**
** It also adds the map_app string. It can be used as a utility
** in the map_lookup method.
**
** Parameters:
** map -- the map that causes this.
** s -- the string to rewrite, NOT necessarily null terminated.
** slen -- the length of s.
** av -- arguments to interpolate into buf.
**
** Returns:
** Pointer to rewritten result. This is static data that
** should be copied if it is to be saved!
**
** Side Effects:
** none.
*/
char *
map_rewrite(map, s, slen, av)
register MAP *map;
register const char *s;
size_t slen;
char **av;
{
register char *bp;
register char c;
char **avp;
register char *ap;
size_t l;
size_t len;
static size_t buflen = 0;
static char *buf = NULL;
if (tTd(39, 1))
{
dprintf("map_rewrite(%.*s), av =", (int)slen, s);
if (av == NULL)
dprintf(" (nullv)");
else
{
for (avp = av; *avp != NULL; avp++)
dprintf("\n\t%s", *avp);
}
dprintf("\n");
}
/* count expected size of output (can safely overestimate) */
l = len = slen;
if (av != NULL)
{
const char *sp = s;
while (l-- > 0 && (c = *sp++) != '\0')
{
if (c != '%')
continue;
if (l-- <= 0)
break;
c = *sp++;
if (!(isascii(c) && isdigit(c)))
continue;
for (avp = av; --c >= '0' && *avp != NULL; avp++)
continue;
if (*avp == NULL)
continue;
len += strlen(*avp);
}
}
if (map->map_app != NULL)
len += strlen(map->map_app);
if (buflen < ++len)
{
/* need to malloc additional space */
buflen = len;
if (buf != NULL)
free(buf);
buf = xalloc(buflen);
}
bp = buf;
if (av == NULL)
{
memmove(bp, s, slen);
bp += slen;
/* assert(len > slen); */
len -= slen;
}
else
{
while (slen-- > 0 && (c = *s++) != '\0')
{
if (c != '%')
{
pushc:
if (--len <= 0)
break;
*bp++ = c;
continue;
}
if (slen-- <= 0 || (c = *s++) == '\0')
c = '%';
if (c == '%')
goto pushc;
if (!(isascii(c) && isdigit(c)))
{
*bp++ = '%';
--len;
goto pushc;
}
for (avp = av; --c >= '0' && *avp != NULL; avp++)
continue;
if (*avp == NULL)
continue;
/* transliterate argument into output string */
for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
*bp++ = c;
}
}
if (map->map_app != NULL && len > 0)
(void) strlcpy(bp, map->map_app, len);
else
*bp = '\0';
if (tTd(39, 1))
dprintf("map_rewrite => %s\n", buf);
return buf;
}
/*
** INITMAPS -- rebuild alias maps
**
** Parameters:
** none.
**
** Returns:
** none.
*/
void
initmaps()
{
#if XDEBUG
checkfd012("entering initmaps");
#endif /* XDEBUG */
stabapply(map_init, 0);
#if XDEBUG
checkfd012("exiting initmaps");
#endif /* XDEBUG */
}
/*
** MAP_INIT -- rebuild a map
**
** Parameters:
** s -- STAB entry: if map: try to rebuild
** unused -- unused variable
**
** Returns:
** none.
**
** Side Effects:
** will close already open rebuildable map.
*/
/* ARGSUSED1 */
static void
map_init(s, unused)
register STAB *s;
int unused;
{
register MAP *map;
/* has to be a map */
if (s->s_type != ST_MAP)
return;
map = &s->s_map;
if (!bitset(MF_VALID, map->map_mflags))
return;
if (tTd(38, 2))
dprintf("map_init(%s:%s, %s)\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" : map->map_mname,
map->map_file == NULL ? "NULL" : map->map_file);
if (!bitset(MF_ALIAS, map->map_mflags) ||
!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
{
if (tTd(38, 3))
dprintf("\tnot rebuildable\n");
return;
}
/* if already open, close it (for nested open) */
if (bitset(MF_OPEN, map->map_mflags))
{
map->map_class->map_close(map);
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
}
(void) rebuildaliases(map, FALSE);
return;
}
/*
** OPENMAP -- open a map
**
** Parameters:
** map -- map to open (it must not be open).
**
** Returns:
** whether open succeeded.
**
*/
bool
openmap(map)
MAP *map;
{
bool restore = FALSE;
bool savehold = HoldErrs;
bool savequick = QuickAbort;
int saveerrors = Errors;
if (!bitset(MF_VALID, map->map_mflags))
return FALSE;
/* better safe than sorry... */
if (bitset(MF_OPEN, map->map_mflags))
return TRUE;
/* Don't send a map open error out via SMTP */
if ((OnlyOneError || QuickAbort) &&
(OpMode == MD_SMTP || OpMode == MD_DAEMON))
{
restore = TRUE;
HoldErrs = TRUE;
QuickAbort = FALSE;
}
errno = 0;
if (map->map_class->map_open(map, O_RDONLY))
{
if (tTd(38, 4))
dprintf("openmap()\t%s:%s %s: valid\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" :
map->map_mname,
map->map_file == NULL ? "NULL" :
map->map_file);
map->map_mflags |= MF_OPEN;
map->map_pid = getpid();
}
else
{
if (tTd(38, 4))
dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" :
map->map_mname,
map->map_file == NULL ? "NULL" :
map->map_file,
errno == 0 ? "" : ": ",
errno == 0 ? "" : errstring(errno));
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
extern MAPCLASS BogusMapClass;
map->map_class = &BogusMapClass;
map->map_mflags |= MF_OPEN;
map->map_pid = getpid();
MapOpenErr = TRUE;
}
else
{
/* don't try again */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -