map.c revision 43730
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)map.c	8.261 (Berkeley) 2/2/1999";
15#endif /* not lint */
16
17#include "sendmail.h"
18
19#ifdef NDBM
20# include <ndbm.h>
21# ifdef R_FIRST
22  ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
23  ERROR README:	the README file about tweaking Berkeley DB so it can
24  ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
25  ERROR README: and use -DNEWDB instead.
26# endif
27#endif
28#ifdef NEWDB
29# include <db.h>
30# ifndef DB_VERSION_MAJOR
31#  define DB_VERSION_MAJOR 1
32# endif
33#endif
34#ifdef NIS
35  struct dom_binding;	/* forward reference needed on IRIX */
36# include <rpcsvc/ypclnt.h>
37# ifdef NDBM
38#  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
39# endif
40#endif
41
42/*
43**  MAP.C -- implementations for various map classes.
44**
45**	Each map class implements a series of functions:
46**
47**	bool map_parse(MAP *map, char *args)
48**		Parse the arguments from the config file.  Return TRUE
49**		if they were ok, FALSE otherwise.  Fill in map with the
50**		values.
51**
52**	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
53**		Look up the key in the given map.  If found, do any
54**		rewriting the map wants (including "args" if desired)
55**		and return the value.  Set *pstat to the appropriate status
56**		on error and return NULL.  Args will be NULL if called
57**		from the alias routines, although this should probably
58**		not be relied upon.  It is suggested you call map_rewrite
59**		to return the results -- it takes care of null termination
60**		and uses a dynamically expanded buffer as needed.
61**
62**	void map_store(MAP *map, char *key, char *value)
63**		Store the key:value pair in the map.
64**
65**	bool map_open(MAP *map, int mode)
66**		Open the map for the indicated mode.  Mode should
67**		be either O_RDONLY or O_RDWR.  Return TRUE if it
68**		was opened successfully, FALSE otherwise.  If the open
69**		failed an the MF_OPTIONAL flag is not set, it should
70**		also print an error.  If the MF_ALIAS bit is set
71**		and this map class understands the @:@ convention, it
72**		should call aliaswait() before returning.
73**
74**	void map_close(MAP *map)
75**		Close the map.
76**
77**	This file also includes the implementation for getcanonname.
78**	It is currently implemented in a pretty ad-hoc manner; it ought
79**	to be more properly integrated into the map structure.
80*/
81
82#define DBMMODE		0644
83
84#ifndef EX_NOTFOUND
85# define EX_NOTFOUND	EX_NOHOST
86#endif
87
88extern bool	aliaswait __P((MAP *, char *, int));
89extern bool	extract_canonname __P((char *, char *, char[], int));
90
91#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
92# define LOCK_ON_OPEN	1	/* we can open/create a locked file */
93#else
94# define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
95#endif
96
97#ifndef O_ACCMODE
98# define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
99#endif
100/*
101**  MAP_PARSEARGS -- parse config line arguments for database lookup
102**
103**	This is a generic version of the map_parse method.
104**
105**	Parameters:
106**		map -- the map being initialized.
107**		ap -- a pointer to the args on the config line.
108**
109**	Returns:
110**		TRUE -- if everything parsed OK.
111**		FALSE -- otherwise.
112**
113**	Side Effects:
114**		null terminates the filename; stores it in map
115*/
116
117bool
118map_parseargs(map, ap)
119	MAP *map;
120	char *ap;
121{
122	register char *p = ap;
123
124	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
125	for (;;)
126	{
127		while (isascii(*p) && isspace(*p))
128			p++;
129		if (*p != '-')
130			break;
131		switch (*++p)
132		{
133		  case 'N':
134			map->map_mflags |= MF_INCLNULL;
135			map->map_mflags &= ~MF_TRY0NULL;
136			break;
137
138		  case 'O':
139			map->map_mflags &= ~MF_TRY1NULL;
140			break;
141
142		  case 'o':
143			map->map_mflags |= MF_OPTIONAL;
144			break;
145
146		  case 'f':
147			map->map_mflags |= MF_NOFOLDCASE;
148			break;
149
150		  case 'm':
151			map->map_mflags |= MF_MATCHONLY;
152			break;
153
154		  case 'A':
155			map->map_mflags |= MF_APPEND;
156			break;
157
158		  case 'q':
159			map->map_mflags |= MF_KEEPQUOTES;
160			break;
161
162		  case 'a':
163			map->map_app = ++p;
164			break;
165
166		  case 'T':
167			map->map_tapp = ++p;
168			break;
169
170		  case 'k':
171			while (isascii(*++p) && isspace(*p))
172				continue;
173			map->map_keycolnm = p;
174			break;
175
176		  case 'v':
177			while (isascii(*++p) && isspace(*p))
178				continue;
179			map->map_valcolnm = p;
180			break;
181
182		  case 'z':
183			if (*++p != '\\')
184				map->map_coldelim = *p;
185			else
186			{
187				switch (*++p)
188				{
189				  case 'n':
190					map->map_coldelim = '\n';
191					break;
192
193				  case 't':
194					map->map_coldelim = '\t';
195					break;
196
197				  default:
198					map->map_coldelim = '\\';
199				}
200			}
201			break;
202
203		  case 't':
204			map->map_mflags |= MF_NODEFER;
205			break;
206
207#ifdef RESERVED_FOR_SUN
208		  case 'd':
209			map->map_mflags |= MF_DOMAIN_WIDE;
210			break;
211
212		  case 's':
213			/* info type */
214			break;
215#endif
216		}
217		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
218			p++;
219		if (*p != '\0')
220			*p++ = '\0';
221	}
222	if (map->map_app != NULL)
223		map->map_app = newstr(map->map_app);
224	if (map->map_tapp != NULL)
225		map->map_tapp = newstr(map->map_tapp);
226	if (map->map_keycolnm != NULL)
227		map->map_keycolnm = newstr(map->map_keycolnm);
228	if (map->map_valcolnm != NULL)
229		map->map_valcolnm = newstr(map->map_valcolnm);
230
231	if (*p != '\0')
232	{
233		map->map_file = p;
234		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
235			p++;
236		if (*p != '\0')
237			*p++ = '\0';
238		map->map_file = newstr(map->map_file);
239	}
240
241	while (*p != '\0' && isascii(*p) && isspace(*p))
242		p++;
243	if (*p != '\0')
244		map->map_rebuild = newstr(p);
245
246	if (map->map_file == NULL &&
247	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
248	{
249		syserr("No file name for %s map %s",
250			map->map_class->map_cname, map->map_mname);
251		return FALSE;
252	}
253	return TRUE;
254}
255/*
256**  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
257**
258**	It also adds the map_app string.  It can be used as a utility
259**	in the map_lookup method.
260**
261**	Parameters:
262**		map -- the map that causes this.
263**		s -- the string to rewrite, NOT necessarily null terminated.
264**		slen -- the length of s.
265**		av -- arguments to interpolate into buf.
266**
267**	Returns:
268**		Pointer to rewritten result.  This is static data that
269**		should be copied if it is to be saved!
270**
271**	Side Effects:
272**		none.
273*/
274
275char *
276map_rewrite(map, s, slen, av)
277	register MAP *map;
278	register const char *s;
279	size_t slen;
280	char **av;
281{
282	register char *bp;
283	register char c;
284	char **avp;
285	register char *ap;
286	size_t l;
287	size_t len;
288	static size_t buflen = 0;
289	static char *buf = NULL;
290
291	if (tTd(39, 1))
292	{
293		printf("map_rewrite(%.*s), av =", (int)slen, s);
294		if (av == NULL)
295			printf(" (nullv)");
296		else
297		{
298			for (avp = av; *avp != NULL; avp++)
299				printf("\n\t%s", *avp);
300		}
301		printf("\n");
302	}
303
304	/* count expected size of output (can safely overestimate) */
305	l = len = slen;
306	if (av != NULL)
307	{
308		const char *sp = s;
309
310		while (l-- > 0 && (c = *sp++) != '\0')
311		{
312			if (c != '%')
313				continue;
314			if (l-- <= 0)
315				break;
316			c = *sp++;
317			if (!(isascii(c) && isdigit(c)))
318				continue;
319			for (avp = av; --c >= '0' && *avp != NULL; avp++)
320				continue;
321			if (*avp == NULL)
322				continue;
323			len += strlen(*avp);
324		}
325	}
326	if (map->map_app != NULL)
327		len += strlen(map->map_app);
328	if (buflen < ++len)
329	{
330		/* need to malloc additional space */
331		buflen = len;
332		if (buf != NULL)
333			free(buf);
334		buf = xalloc(buflen);
335	}
336
337	bp = buf;
338	if (av == NULL)
339	{
340		bcopy(s, bp, slen);
341		bp += slen;
342	}
343	else
344	{
345		while (slen-- > 0 && (c = *s++) != '\0')
346		{
347			if (c != '%')
348			{
349  pushc:
350				*bp++ = c;
351				continue;
352			}
353			if (slen-- <= 0 || (c = *s++) == '\0')
354				c = '%';
355			if (c == '%')
356				goto pushc;
357			if (!(isascii(c) && isdigit(c)))
358			{
359				*bp++ = '%';
360				goto pushc;
361			}
362			for (avp = av; --c >= '0' && *avp != NULL; avp++)
363				continue;
364			if (*avp == NULL)
365				continue;
366
367			/* transliterate argument into output string */
368			for (ap = *avp; (c = *ap++) != '\0'; )
369				*bp++ = c;
370		}
371	}
372	if (map->map_app != NULL)
373		strcpy(bp, map->map_app);
374	else
375		*bp = '\0';
376	if (tTd(39, 1))
377		printf("map_rewrite => %s\n", buf);
378	return buf;
379}
380/*
381**  INITMAPS -- initialize for aliasing
382**
383**	Parameters:
384**		rebuild -- if TRUE, this rebuilds the cached versions.
385**		e -- current envelope.
386**
387**	Returns:
388**		none.
389**
390**	Side Effects:
391**		initializes aliases:
392**		if alias database:  opens the database.
393**		if no database available: reads aliases into the symbol table.
394*/
395
396void
397initmaps(rebuild, e)
398	bool rebuild;
399	register ENVELOPE *e;
400{
401	extern void map_init __P((STAB *, int));
402
403#if XDEBUG
404	checkfd012("entering initmaps");
405#endif
406	CurEnv = e;
407
408	stabapply(map_init, 0);
409	stabapply(map_init, rebuild ? 2 : 1);
410#if XDEBUG
411	checkfd012("exiting initmaps");
412#endif
413}
414
415void
416map_init(s, pass)
417	register STAB *s;
418	int pass;
419{
420	bool rebuildable;
421	register MAP *map;
422
423	/* has to be a map */
424	if (s->s_type != ST_MAP)
425		return;
426
427	map = &s->s_map;
428	if (!bitset(MF_VALID, map->map_mflags))
429		return;
430
431	if (tTd(38, 2))
432		printf("map_init(%s:%s, %s, %d)\n",
433			map->map_class->map_cname == NULL ? "NULL" :
434				map->map_class->map_cname,
435			map->map_mname == NULL ? "NULL" : map->map_mname,
436			map->map_file == NULL ? "NULL" : map->map_file,
437			pass);
438
439	/*
440	**  Pass 0 opens all non-rebuildable maps.
441	**  Pass 1 opens all rebuildable maps for read.
442	**  Pass 2 rebuilds all rebuildable maps.
443	*/
444
445	rebuildable = (bitset(MF_ALIAS, map->map_mflags) &&
446		       bitset(MCF_REBUILDABLE, map->map_class->map_cflags));
447
448	if ((pass == 0 && rebuildable) ||
449	    ((pass == 1 || pass == 2) && !rebuildable))
450	{
451		if (tTd(38, 3))
452			printf("\twrong pass (pass = %d, rebuildable = %d)\n",
453			       pass, rebuildable);
454		return;
455	}
456
457	/* if already open, close it (for nested open) */
458	if (bitset(MF_OPEN, map->map_mflags))
459	{
460		map->map_class->map_close(map);
461		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
462	}
463
464	if (pass == 2)
465	{
466		(void) rebuildaliases(map, FALSE);
467		return;
468	}
469
470	if (map->map_class->map_open(map, O_RDONLY))
471	{
472		if (tTd(38, 4))
473			printf("\t%s:%s %s: valid\n",
474				map->map_class->map_cname == NULL ? "NULL" :
475					map->map_class->map_cname,
476				map->map_mname == NULL ? "NULL" :
477					map->map_mname,
478				map->map_file == NULL ? "NULL" :
479					map->map_file);
480		map->map_mflags |= MF_OPEN;
481		map->map_pid = getpid();
482	}
483	else
484	{
485		if (tTd(38, 4))
486			printf("\t%s:%s %s: invalid: %s\n",
487				map->map_class->map_cname == NULL ? "NULL" :
488					map->map_class->map_cname,
489				map->map_mname == NULL ? "NULL" :
490					map->map_mname,
491				map->map_file == NULL ? "NULL" :
492					map->map_file,
493				errstring(errno));
494		if (!bitset(MF_OPTIONAL, map->map_mflags))
495		{
496			extern MAPCLASS BogusMapClass;
497
498			map->map_class = &BogusMapClass;
499			map->map_mflags |= MF_OPEN;
500			map->map_pid = getpid();
501		}
502	}
503}
504/*
505**  CLOSEMAPS -- close all open maps opened by the current pid.
506**
507**	Parameters:
508**		none
509**
510**	Returns:
511**		none.
512*/
513
514void
515closemaps()
516{
517	extern void map_close __P((STAB *, int));
518
519	stabapply(map_close, 0);
520}
521
522/* ARGSUSED1 */
523void
524map_close(s, unused)
525	register STAB *s;
526	int unused;
527{
528	MAP *map;
529
530	if (s->s_type != ST_MAP)
531		return;
532
533	map = &s->s_map;
534
535	if (!bitset(MF_VALID, map->map_mflags) ||
536	    !bitset(MF_OPEN, map->map_mflags) ||
537	    map->map_pid != getpid())
538		return;
539
540	if (tTd(38, 5))
541		printf("closemaps: closing %s (%s)\n",
542		       map->map_mname == NULL ? "NULL" : map->map_mname,
543		       map->map_file == NULL ? "NULL" : map->map_file);
544
545	map->map_class->map_close(map);
546	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
547}
548/*
549**  GETCANONNAME -- look up name using service switch
550**
551**	Parameters:
552**		host -- the host name to look up.
553**		hbsize -- the size of the host buffer.
554**		trymx -- if set, try MX records.
555**
556**	Returns:
557**		TRUE -- if the host was found.
558**		FALSE -- otherwise.
559*/
560
561bool
562getcanonname(host, hbsize, trymx)
563	char *host;
564	int hbsize;
565	bool trymx;
566{
567	int nmaps;
568	int mapno;
569	bool found = FALSE;
570	bool got_tempfail = FALSE;
571	auto int stat;
572	char *maptype[MAXMAPSTACK];
573	short mapreturn[MAXMAPACTIONS];
574
575	nmaps = switch_map_find("hosts", maptype, mapreturn);
576	for (mapno = 0; mapno < nmaps; mapno++)
577	{
578		int i;
579
580		if (tTd(38, 20))
581			printf("getcanonname(%s), trying %s\n",
582				host, maptype[mapno]);
583		if (strcmp("files", maptype[mapno]) == 0)
584		{
585			extern bool text_getcanonname __P((char *, int, int *));
586
587			found = text_getcanonname(host, hbsize, &stat);
588		}
589#ifdef NIS
590		else if (strcmp("nis", maptype[mapno]) == 0)
591		{
592			extern bool nis_getcanonname __P((char *, int, int *));
593
594			found = nis_getcanonname(host, hbsize, &stat);
595		}
596#endif
597#ifdef NISPLUS
598		else if (strcmp("nisplus", maptype[mapno]) == 0)
599		{
600			extern bool nisplus_getcanonname __P((char *, int, int *));
601
602			found = nisplus_getcanonname(host, hbsize, &stat);
603		}
604#endif
605#if NAMED_BIND
606		else if (strcmp("dns", maptype[mapno]) == 0)
607		{
608			extern bool dns_getcanonname __P((char *, int, bool, int *));
609
610			found = dns_getcanonname(host, hbsize, trymx, &stat);
611		}
612#endif
613#if NETINFO
614		else if (strcmp("netinfo", maptype[mapno]) == 0)
615		{
616			extern bool ni_getcanonname __P((char *, int, int *));
617
618			found = ni_getcanonname(host, hbsize, &stat);
619		}
620#endif
621		else
622		{
623			found = FALSE;
624			stat = EX_UNAVAILABLE;
625		}
626
627		/*
628		**  Heuristic: if $m is not set, we are running during system
629		**  startup.  In this case, when a name is apparently found
630		**  but has no dot, treat is as not found.  This avoids
631		**  problems if /etc/hosts has no FQDN but is listed first
632		**  in the service switch.
633		*/
634
635		if (found &&
636		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
637			break;
638
639		/* see if we should continue */
640		if (stat == EX_TEMPFAIL)
641		{
642			i = MA_TRYAGAIN;
643			got_tempfail = TRUE;
644		}
645		else if (stat == EX_NOTFOUND)
646			i = MA_NOTFOUND;
647		else
648			i = MA_UNAVAIL;
649		if (bitset(1 << mapno, mapreturn[i]))
650			break;
651	}
652
653	if (found)
654	{
655		char *d;
656
657		if (tTd(38, 20))
658			printf("getcanonname(%s), found\n", host);
659
660		/*
661		**  If returned name is still single token, compensate
662		**  by tagging on $m.  This is because some sites set
663		**  up their DNS or NIS databases wrong.
664		*/
665
666		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
667		{
668			d = macvalue('m', CurEnv);
669			if (d != NULL &&
670			    hbsize > (int) (strlen(host) + strlen(d) + 1))
671			{
672				if (host[strlen(host) - 1] != '.')
673					strcat(host, ".");
674				strcat(host, d);
675			}
676			else
677			{
678				return FALSE;
679			}
680		}
681		return TRUE;
682	}
683
684	if (tTd(38, 20))
685		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
686
687#if NAMED_BIND
688	if (got_tempfail)
689		h_errno = TRY_AGAIN;
690	else
691		h_errno = HOST_NOT_FOUND;
692#endif
693
694	return FALSE;
695}
696/*
697**  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
698**
699**	Parameters:
700**		name -- the name against which to match.
701**		line -- the /etc/hosts line.
702**		cbuf -- the location to store the result.
703**		cbuflen -- the size of cbuf.
704**
705**	Returns:
706**		TRUE -- if the line matched the desired name.
707**		FALSE -- otherwise.
708*/
709
710bool
711extract_canonname(name, line, cbuf, cbuflen)
712	char *name;
713	char *line;
714	char cbuf[];
715	int cbuflen;
716{
717	int i;
718	char *p;
719	bool found = FALSE;
720	extern char *get_column __P((char *, int, char, char *, int));
721
722	cbuf[0] = '\0';
723	if (line[0] == '#')
724		return FALSE;
725
726	for (i = 1; ; i++)
727	{
728		char nbuf[MAXNAME + 1];
729
730		p = get_column(line, i, '\0', nbuf, sizeof nbuf);
731		if (p == NULL)
732			break;
733		if (*p == '\0')
734			continue;
735		if (cbuf[0] == '\0' ||
736		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
737		{
738			snprintf(cbuf, cbuflen, "%s", p);
739		}
740		if (strcasecmp(name, p) == 0)
741			found = TRUE;
742	}
743	if (found && strchr(cbuf, '.') == NULL)
744	{
745		/* try to add a domain on the end of the name */
746		char *domain = macvalue('m', CurEnv);
747
748		if (domain != NULL &&
749		    strlen(domain) + strlen(cbuf) + 1 < cbuflen)
750		{
751			p = &cbuf[strlen(cbuf)];
752			*p++ = '.';
753			strcpy(p, domain);
754		}
755	}
756	return found;
757}
758/*
759**  NDBM modules
760*/
761
762#ifdef NDBM
763
764/*
765**  NDBM_MAP_OPEN -- DBM-style map open
766*/
767
768bool
769ndbm_map_open(map, mode)
770	MAP *map;
771	int mode;
772{
773	register DBM *dbm;
774	struct stat st;
775	int dfd;
776	int pfd;
777	int sff;
778	int ret;
779	int smode = S_IREAD;
780	char dirfile[MAXNAME + 1];
781	char pagfile[MAXNAME + 1];
782	struct stat std, stp;
783
784	if (tTd(38, 2))
785		printf("ndbm_map_open(%s, %s, %d)\n",
786			map->map_mname, map->map_file, mode);
787	map->map_lockfd = -1;
788	mode &= O_ACCMODE;
789
790	/* do initial file and directory checks */
791	snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file);
792	snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file);
793	sff = SFF_ROOTOK|SFF_REGONLY;
794	if (mode == O_RDWR)
795	{
796		sff |= SFF_CREAT;
797		if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
798			sff |= SFF_NOSLINK;
799		if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
800			sff |= SFF_NOHLINK;
801		smode = S_IWRITE;
802	}
803	else
804	{
805		if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
806			sff |= SFF_NOWLINK;
807	}
808	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
809		sff |= SFF_SAFEDIRPATH;
810	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
811			    sff, smode, &std);
812	if (ret == 0)
813		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
814			       sff, smode, &stp);
815	if (ret == ENOENT && AutoRebuild &&
816	    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
817	    (bitset(MF_IMPL_NDBM, map->map_mflags) ||
818	     bitset(MF_ALIAS, map->map_mflags)) &&
819	    mode == O_RDONLY)
820	{
821		bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
822		extern bool impl_map_open __P((MAP *, int));
823
824		/* may be able to rebuild */
825		map->map_mflags &= ~MF_IMPL_NDBM;
826		if (!rebuildaliases(map, TRUE))
827			return FALSE;
828		if (impl)
829			return impl_map_open(map, O_RDONLY);
830		else
831			return ndbm_map_open(map, O_RDONLY);
832	}
833	if (ret != 0)
834	{
835		char *prob = "unsafe";
836
837		/* cannot open this map */
838		if (ret == ENOENT)
839			prob = "missing";
840		if (tTd(38, 2))
841			printf("\t%s map file: %d\n", prob, ret);
842		if (!bitset(MF_OPTIONAL, map->map_mflags))
843			syserr("dbm map \"%s\": %s map file %s",
844				map->map_mname, prob, map->map_file);
845		return FALSE;
846	}
847	if (std.st_mode == ST_MODE_NOFILE)
848		mode |= O_CREAT|O_EXCL;
849
850#if LOCK_ON_OPEN
851	if (mode == O_RDONLY)
852		mode |= O_SHLOCK;
853	else
854		mode |= O_TRUNC|O_EXLOCK;
855#else
856	if ((mode & O_ACCMODE) == O_RDWR)
857	{
858# if NOFTRUNCATE
859		/*
860		**  Warning: race condition.  Try to lock the file as
861		**  quickly as possible after opening it.
862		**	This may also have security problems on some systems,
863		**	but there isn't anything we can do about it.
864		*/
865
866		mode |= O_TRUNC;
867# else
868		/*
869		**  This ugly code opens the map without truncating it,
870		**  locks the file, then truncates it.  Necessary to
871		**  avoid race conditions.
872		*/
873
874		int dirfd;
875		int pagfd;
876		int sff = SFF_CREAT|SFF_OPENASROOT;
877
878		if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
879			sff |= SFF_NOSLINK;
880		if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
881			sff |= SFF_NOHLINK;
882
883		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
884		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
885
886		if (dirfd < 0 || pagfd < 0)
887		{
888			int save_errno = errno;
889
890			if (dirfd >= 0)
891				(void) close(dirfd);
892			if (pagfd >= 0)
893				(void) close(pagfd);
894			errno = save_errno;
895			syserr("ndbm_map_open: cannot create database %s",
896				map->map_file);
897			return FALSE;
898		}
899		if (ftruncate(dirfd, (off_t) 0) < 0 ||
900		    ftruncate(pagfd, (off_t) 0) < 0)
901		{
902			int save_errno = errno;
903
904			(void) close(dirfd);
905			(void) close(pagfd);
906			errno = save_errno;
907			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
908				map->map_file);
909			return FALSE;
910		}
911
912		/* if new file, get "before" bits for later filechanged check */
913		if (std.st_mode == ST_MODE_NOFILE &&
914		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
915		{
916			int save_errno = errno;
917
918			(void) close(dirfd);
919			(void) close(pagfd);
920			errno = save_errno;
921			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
922				map->map_file);
923			return FALSE;
924		}
925
926		/* have to save the lock for the duration (bletch) */
927		map->map_lockfd = dirfd;
928		close(pagfd);
929
930		/* twiddle bits for dbm_open */
931		mode &= ~(O_CREAT|O_EXCL);
932# endif
933	}
934#endif
935
936	/* open the database */
937	dbm = dbm_open(map->map_file, mode, DBMMODE);
938	if (dbm == NULL)
939	{
940		int save_errno = errno;
941
942		if (bitset(MF_ALIAS, map->map_mflags) &&
943		    aliaswait(map, ".pag", FALSE))
944			return TRUE;
945#if !LOCK_ON_OPEN && !NOFTRUNCATE
946		if (map->map_lockfd >= 0)
947			close(map->map_lockfd);
948#endif
949		errno = save_errno;
950		if (!bitset(MF_OPTIONAL, map->map_mflags))
951			syserr("Cannot open DBM database %s", map->map_file);
952		return FALSE;
953	}
954	dfd = dbm_dirfno(dbm);
955	pfd = dbm_pagfno(dbm);
956	if (dfd == pfd)
957	{
958		/* heuristic: if files are linked, this is actually gdbm */
959		dbm_close(dbm);
960#if !LOCK_ON_OPEN && !NOFTRUNCATE
961		if (map->map_lockfd >= 0)
962			close(map->map_lockfd);
963#endif
964		errno = 0;
965		syserr("dbm map \"%s\": cannot support GDBM",
966			map->map_mname);
967		return FALSE;
968	}
969
970	if (filechanged(dirfile, dfd, &std) ||
971	    filechanged(pagfile, pfd, &stp))
972	{
973		int save_errno = errno;
974
975		dbm_close(dbm);
976#if !LOCK_ON_OPEN && !NOFTRUNCATE
977		if (map->map_lockfd >= 0)
978			close(map->map_lockfd);
979#endif
980		errno = save_errno;
981		syserr("ndbm_map_open(%s): file changed after open",
982			map->map_file);
983		return FALSE;
984	}
985
986	map->map_db1 = (ARBPTR_T) dbm;
987	if (mode == O_RDONLY)
988	{
989#if LOCK_ON_OPEN
990		if (dfd >= 0)
991			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
992		if (pfd >= 0)
993			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
994#endif
995		if (bitset(MF_ALIAS, map->map_mflags) &&
996		    !aliaswait(map, ".pag", TRUE))
997			return FALSE;
998	}
999	else
1000	{
1001		map->map_mflags |= MF_LOCKED;
1002#if _FFR_TRUSTED_USER
1003		if (geteuid() == 0 && TrustedUid != 0)
1004		{
1005			if (fchown(dfd, TrustedUid, -1) < 0 ||
1006			    fchown(pfd, TrustedUid, -1) < 0)
1007			{
1008				int err = errno;
1009
1010				sm_syslog(LOG_ALERT, NOQID,
1011					  "ownership change on %s failed: %s",
1012					  map->map_file, errstring(err));
1013				message("050 ownership change on %s failed: %s",
1014					map->map_file, errstring(err));
1015			}
1016		}
1017#endif
1018	}
1019	if (fstat(dfd, &st) >= 0)
1020		map->map_mtime = st.st_mtime;
1021	return TRUE;
1022}
1023
1024
1025/*
1026**  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1027*/
1028
1029char *
1030ndbm_map_lookup(map, name, av, statp)
1031	MAP *map;
1032	char *name;
1033	char **av;
1034	int *statp;
1035{
1036	datum key, val;
1037	int fd;
1038	char keybuf[MAXNAME + 1];
1039	struct stat stbuf;
1040
1041	if (tTd(38, 20))
1042		printf("ndbm_map_lookup(%s, %s)\n",
1043			map->map_mname, name);
1044
1045	key.dptr = name;
1046	key.dsize = strlen(name);
1047	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1048	{
1049		if (key.dsize > sizeof keybuf - 1)
1050			key.dsize = sizeof keybuf - 1;
1051		bcopy(key.dptr, keybuf, key.dsize);
1052		keybuf[key.dsize] = '\0';
1053		makelower(keybuf);
1054		key.dptr = keybuf;
1055	}
1056lockdbm:
1057	fd = dbm_dirfno((DBM *) map->map_db1);
1058	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1059		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
1060	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1061	{
1062		/* Reopen the database to sync the cache */
1063		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1064								 : O_RDONLY;
1065
1066		map->map_class->map_close(map);
1067		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1068		if (map->map_class->map_open(map, omode))
1069		{
1070			map->map_mflags |= MF_OPEN;
1071			map->map_pid = getpid();
1072			if ((omode && O_ACCMODE) == O_RDWR)
1073				map->map_mflags |= MF_WRITABLE;
1074			goto lockdbm;
1075		}
1076		else
1077		{
1078			if (!bitset(MF_OPTIONAL, map->map_mflags))
1079			{
1080				extern MAPCLASS BogusMapClass;
1081
1082				*statp = EX_TEMPFAIL;
1083				map->map_class = &BogusMapClass;
1084				map->map_mflags |= MF_OPEN;
1085				map->map_pid = getpid();
1086				syserr("Cannot reopen NDBM database %s",
1087					map->map_file);
1088			}
1089			return NULL;
1090		}
1091	}
1092	val.dptr = NULL;
1093	if (bitset(MF_TRY0NULL, map->map_mflags))
1094	{
1095		val = dbm_fetch((DBM *) map->map_db1, key);
1096		if (val.dptr != NULL)
1097			map->map_mflags &= ~MF_TRY1NULL;
1098	}
1099	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1100	{
1101		key.dsize++;
1102		val = dbm_fetch((DBM *) map->map_db1, key);
1103		if (val.dptr != NULL)
1104			map->map_mflags &= ~MF_TRY0NULL;
1105	}
1106	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1107		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
1108	if (val.dptr == NULL)
1109		return NULL;
1110	if (bitset(MF_MATCHONLY, map->map_mflags))
1111		return map_rewrite(map, name, strlen(name), NULL);
1112	else
1113		return map_rewrite(map, val.dptr, val.dsize, av);
1114}
1115
1116
1117/*
1118**  NDBM_MAP_STORE -- store a datum in the database
1119*/
1120
1121void
1122ndbm_map_store(map, lhs, rhs)
1123	register MAP *map;
1124	char *lhs;
1125	char *rhs;
1126{
1127	datum key;
1128	datum data;
1129	int stat;
1130	char keybuf[MAXNAME + 1];
1131
1132	if (tTd(38, 12))
1133		printf("ndbm_map_store(%s, %s, %s)\n",
1134			map->map_mname, lhs, rhs);
1135
1136	key.dsize = strlen(lhs);
1137	key.dptr = lhs;
1138	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1139	{
1140		if (key.dsize > sizeof keybuf - 1)
1141			key.dsize = sizeof keybuf - 1;
1142		bcopy(key.dptr, keybuf, key.dsize);
1143		keybuf[key.dsize] = '\0';
1144		makelower(keybuf);
1145		key.dptr = keybuf;
1146	}
1147
1148	data.dsize = strlen(rhs);
1149	data.dptr = rhs;
1150
1151	if (bitset(MF_INCLNULL, map->map_mflags))
1152	{
1153		key.dsize++;
1154		data.dsize++;
1155	}
1156
1157	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1158	if (stat > 0)
1159	{
1160		if (!bitset(MF_APPEND, map->map_mflags))
1161			message("050 Warning: duplicate alias name %s", lhs);
1162		else
1163		{
1164			static char *buf = NULL;
1165			static int bufsiz = 0;
1166			auto int xstat;
1167			datum old;
1168
1169			old.dptr = ndbm_map_lookup(map, key.dptr,
1170						   (char **)NULL, &xstat);
1171			if (old.dptr != NULL && *(char *) old.dptr != '\0')
1172			{
1173				old.dsize = strlen(old.dptr);
1174				if (data.dsize + old.dsize + 2 > bufsiz)
1175				{
1176					if (buf != NULL)
1177						(void) free(buf);
1178					bufsiz = data.dsize + old.dsize + 2;
1179					buf = xalloc(bufsiz);
1180				}
1181				snprintf(buf, bufsiz, "%s,%s",
1182					data.dptr, old.dptr);
1183				data.dsize = data.dsize + old.dsize + 1;
1184				data.dptr = buf;
1185				if (tTd(38, 9))
1186					printf("ndbm_map_store append=%s\n", data.dptr);
1187			}
1188		}
1189		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
1190	}
1191	if (stat != 0)
1192		syserr("readaliases: dbm put (%s)", lhs);
1193}
1194
1195
1196/*
1197**  NDBM_MAP_CLOSE -- close the database
1198*/
1199
1200void
1201ndbm_map_close(map)
1202	register MAP  *map;
1203{
1204	if (tTd(38, 9))
1205		printf("ndbm_map_close(%s, %s, %lx)\n",
1206			map->map_mname, map->map_file, map->map_mflags);
1207
1208	if (bitset(MF_WRITABLE, map->map_mflags))
1209	{
1210#ifdef NDBM_YP_COMPAT
1211		bool inclnull;
1212		char buf[MAXHOSTNAMELEN];
1213
1214		inclnull = bitset(MF_INCLNULL, map->map_mflags);
1215		map->map_mflags &= ~MF_INCLNULL;
1216
1217		if (strstr(map->map_file, "/yp/") != NULL)
1218		{
1219			long save_mflags = map->map_mflags;
1220
1221			map->map_mflags |= MF_NOFOLDCASE;
1222
1223			(void) snprintf(buf, sizeof buf, "%010ld", curtime());
1224			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1225
1226			(void) gethostname(buf, sizeof buf);
1227			ndbm_map_store(map, "YP_MASTER_NAME", buf);
1228
1229			map->map_mflags = save_mflags;
1230		}
1231
1232		if (inclnull)
1233			map->map_mflags |= MF_INCLNULL;
1234#endif
1235
1236		/* write out the distinguished alias */
1237		ndbm_map_store(map, "@", "@");
1238	}
1239	dbm_close((DBM *) map->map_db1);
1240
1241	/* release lock (if needed) */
1242#if !LOCK_ON_OPEN
1243	if (map->map_lockfd >= 0)
1244		(void) close(map->map_lockfd);
1245#endif
1246}
1247
1248#endif
1249/*
1250**  NEWDB (Hash and BTree) Modules
1251*/
1252
1253#ifdef NEWDB
1254
1255/*
1256**  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1257**
1258**	These do rather bizarre locking.  If you can lock on open,
1259**	do that to avoid the condition of opening a database that
1260**	is being rebuilt.  If you don't, we'll try to fake it, but
1261**	there will be a race condition.  If opening for read-only,
1262**	we immediately release the lock to avoid freezing things up.
1263**	We really ought to hold the lock, but guarantee that we won't
1264**	be pokey about it.  That's hard to do.
1265*/
1266
1267#if DB_VERSION_MAJOR < 2
1268extern bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
1269#else
1270extern bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
1271#endif
1272
1273/* these should be K line arguments */
1274#if DB_VERSION_MAJOR < 2
1275# define db_cachesize	cachesize
1276# define h_nelem	nelem
1277# ifndef DB_CACHE_SIZE
1278#  define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
1279# endif
1280# ifndef DB_HASH_NELEM
1281#  define DB_HASH_NELEM	4096		/* (starting) size of hash table */
1282# endif
1283#endif
1284
1285bool
1286bt_map_open(map, mode)
1287	MAP *map;
1288	int mode;
1289{
1290#if DB_VERSION_MAJOR < 2
1291	BTREEINFO btinfo;
1292#else
1293	DB_INFO btinfo;
1294#endif
1295
1296	if (tTd(38, 2))
1297		printf("bt_map_open(%s, %s, %d)\n",
1298			map->map_mname, map->map_file, mode);
1299
1300	bzero(&btinfo, sizeof btinfo);
1301#ifdef DB_CACHE_SIZE
1302	btinfo.db_cachesize = DB_CACHE_SIZE;
1303#endif
1304	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1305}
1306
1307bool
1308hash_map_open(map, mode)
1309	MAP *map;
1310	int mode;
1311{
1312#if DB_VERSION_MAJOR < 2
1313	HASHINFO hinfo;
1314#else
1315	DB_INFO hinfo;
1316#endif
1317
1318	if (tTd(38, 2))
1319		printf("hash_map_open(%s, %s, %d)\n",
1320			map->map_mname, map->map_file, mode);
1321
1322	bzero(&hinfo, sizeof hinfo);
1323#ifdef DB_HASH_NELEM
1324	hinfo.h_nelem = DB_HASH_NELEM;
1325#endif
1326#ifdef DB_CACHE_SIZE
1327	hinfo.db_cachesize = DB_CACHE_SIZE;
1328#endif
1329	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1330}
1331
1332bool
1333db_map_open(map, mode, mapclassname, dbtype, openinfo)
1334	MAP *map;
1335	int mode;
1336	char *mapclassname;
1337	DBTYPE dbtype;
1338#if DB_VERSION_MAJOR < 2
1339	const void *openinfo;
1340#else
1341	DB_INFO *openinfo;
1342#endif
1343{
1344	DB *db = NULL;
1345	int i;
1346	int omode;
1347	int smode = S_IREAD;
1348	int fd;
1349	int sff;
1350	int saveerrno;
1351	struct stat st;
1352	char buf[MAXNAME + 1];
1353
1354	/* do initial file and directory checks */
1355	snprintf(buf, sizeof buf - 3, "%s", map->map_file);
1356	i = strlen(buf);
1357	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1358		(void) strcat(buf, ".db");
1359
1360	mode &= O_ACCMODE;
1361	omode = mode;
1362
1363	sff = SFF_ROOTOK|SFF_REGONLY;
1364	if (mode == O_RDWR)
1365	{
1366		sff |= SFF_CREAT;
1367		if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1368			sff |= SFF_NOSLINK;
1369		if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1370			sff |= SFF_NOHLINK;
1371		smode = S_IWRITE;
1372	}
1373	else
1374	{
1375		if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1376			sff |= SFF_NOWLINK;
1377	}
1378	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1379		sff |= SFF_SAFEDIRPATH;
1380	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
1381	if (i == ENOENT && AutoRebuild &&
1382	    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
1383	    (bitset(MF_IMPL_HASH, map->map_mflags) ||
1384	     bitset(MF_ALIAS, map->map_mflags)) &&
1385	    mode == O_RDONLY)
1386	{
1387		bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
1388		extern bool impl_map_open __P((MAP *, int));
1389
1390		/* may be able to rebuild */
1391		map->map_mflags &= ~MF_IMPL_HASH;
1392		if (!rebuildaliases(map, TRUE))
1393			return FALSE;
1394		if (impl)
1395			return impl_map_open(map, O_RDONLY);
1396		else
1397			return db_map_open(map, O_RDONLY, mapclassname,
1398					   dbtype, openinfo);
1399	}
1400
1401	if (i != 0)
1402	{
1403		char *prob = "unsafe";
1404
1405		/* cannot open this map */
1406		if (i == ENOENT)
1407			prob = "missing";
1408		if (tTd(38, 2))
1409			printf("\t%s map file: %s\n", prob, errstring(i));
1410		errno = i;
1411		if (!bitset(MF_OPTIONAL, map->map_mflags))
1412			syserr("%s map \"%s\": %s map file %s",
1413				mapclassname, map->map_mname, prob, buf);
1414		return FALSE;
1415	}
1416	if (st.st_mode == ST_MODE_NOFILE)
1417		omode |= O_CREAT|O_EXCL;
1418
1419	map->map_lockfd = -1;
1420
1421#if LOCK_ON_OPEN
1422	if (mode == O_RDWR)
1423		omode |= O_TRUNC|O_EXLOCK;
1424	else
1425		omode |= O_SHLOCK;
1426#else
1427	/*
1428	**  Pre-lock the file to avoid race conditions.  In particular,
1429	**  since dbopen returns NULL if the file is zero length, we
1430	**  must have a locked instance around the dbopen.
1431	*/
1432
1433	fd = open(buf, omode, DBMMODE);
1434	if (fd < 0)
1435	{
1436		if (!bitset(MF_OPTIONAL, map->map_mflags))
1437			syserr("db_map_open: cannot pre-open database %s", buf);
1438		return FALSE;
1439	}
1440
1441	/* make sure no baddies slipped in just before the open... */
1442	if (filechanged(buf, fd, &st))
1443	{
1444		int save_errno = errno;
1445
1446		(void) close(fd);
1447		errno = save_errno;
1448		syserr("db_map_open(%s): file changed after pre-open", buf);
1449		return FALSE;
1450	}
1451
1452	/* if new file, get the "before" bits for later filechanged check */
1453	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
1454	{
1455		int save_errno = errno;
1456
1457		(void) close(fd);
1458		errno = save_errno;
1459		syserr("db_map_open(%s): cannot fstat pre-opened file",
1460			buf);
1461		return FALSE;
1462	}
1463
1464	/* actually lock the pre-opened file */
1465	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
1466		syserr("db_map_open: cannot lock %s", buf);
1467
1468	/* set up mode bits for dbopen */
1469	if (mode == O_RDWR)
1470		omode |= O_TRUNC;
1471	omode &= ~(O_EXCL|O_CREAT);
1472#endif
1473
1474#if DB_VERSION_MAJOR < 2
1475	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
1476#else
1477	{
1478		int flags = 0;
1479
1480		if (mode == O_RDONLY)
1481			flags |= DB_RDONLY;
1482		if (bitset(O_CREAT, omode))
1483			flags |= DB_CREATE;
1484		if (bitset(O_TRUNC, omode))
1485			flags |= DB_TRUNCATE;
1486
1487		errno = db_open(buf, dbtype, flags, DBMMODE,
1488				NULL, openinfo, &db);
1489	}
1490#endif
1491	saveerrno = errno;
1492
1493#if !LOCK_ON_OPEN
1494	if (mode == O_RDWR)
1495		map->map_lockfd = fd;
1496	else
1497		(void) close(fd);
1498#endif
1499
1500	if (db == NULL)
1501	{
1502		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1503		    aliaswait(map, ".db", FALSE))
1504			return TRUE;
1505#if !LOCK_ON_OPEN
1506		if (map->map_lockfd >= 0)
1507			(void) close(map->map_lockfd);
1508#endif
1509		errno = saveerrno;
1510		if (!bitset(MF_OPTIONAL, map->map_mflags))
1511			syserr("Cannot open %s database %s",
1512				mapclassname, buf);
1513		return FALSE;
1514	}
1515
1516#if DB_VERSION_MAJOR < 2
1517	fd = db->fd(db);
1518#else
1519	fd = -1;
1520	errno = db->fd(db, &fd);
1521#endif
1522	if (filechanged(buf, fd, &st))
1523	{
1524		int save_errno = errno;
1525
1526#if DB_VERSION_MAJOR < 2
1527		db->close(db);
1528#else
1529		errno = db->close(db, 0);
1530#endif
1531#if !LOCK_ON_OPEN
1532		if (map->map_lockfd >= 0)
1533			close(map->map_lockfd);
1534#endif
1535		errno = save_errno;
1536		syserr("db_map_open(%s): file changed after open", buf);
1537		return FALSE;
1538	}
1539
1540	if (mode == O_RDWR)
1541		map->map_mflags |= MF_LOCKED;
1542#if LOCK_ON_OPEN
1543	if (fd >= 0 && mode == O_RDONLY)
1544	{
1545		(void) lockfile(fd, buf, NULL, LOCK_UN);
1546	}
1547#endif
1548
1549	/* try to make sure that at least the database header is on disk */
1550	if (mode == O_RDWR)
1551	{
1552		(void) db->sync(db, 0);
1553#if _FFR_TRUSTED_USER
1554		if (geteuid() == 0 && TrustedUid != 0)
1555		{
1556			if (fchown(fd, TrustedUid, -1) < 0)
1557			{
1558				int err = errno;
1559
1560				sm_syslog(LOG_ALERT, NOQID,
1561					  "ownership change on %s failed: %s",
1562					  buf, errstring(err));
1563				message("050 ownership change on %s failed: %s",
1564					buf, errstring(err));
1565			}
1566		}
1567#endif
1568	}
1569
1570	if (fd >= 0 && fstat(fd, &st) >= 0)
1571		map->map_mtime = st.st_mtime;
1572
1573	map->map_db2 = (ARBPTR_T) db;
1574	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1575	    !aliaswait(map, ".db", TRUE))
1576		return FALSE;
1577	return TRUE;
1578}
1579
1580
1581/*
1582**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
1583*/
1584
1585char *
1586db_map_lookup(map, name, av, statp)
1587	MAP *map;
1588	char *name;
1589	char **av;
1590	int *statp;
1591{
1592	DBT key, val;
1593	register DB *db = (DB *) map->map_db2;
1594	int i;
1595	int st;
1596	int saveerrno;
1597	int fd;
1598	struct stat stbuf;
1599	char keybuf[MAXNAME + 1];
1600	char buf[MAXNAME + 1];
1601
1602	bzero(&key, sizeof key);
1603	bzero(&val, sizeof val);
1604
1605	if (tTd(38, 20))
1606		printf("db_map_lookup(%s, %s)\n",
1607			map->map_mname, name);
1608
1609	i = strlen(map->map_file);
1610	if (i > MAXNAME)
1611		i = MAXNAME;
1612	strncpy(buf, map->map_file, i);
1613	buf[i] = '\0';
1614	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
1615		buf[i - 3] = '\0';
1616
1617	key.size = strlen(name);
1618	if (key.size > sizeof keybuf - 1)
1619		key.size = sizeof keybuf - 1;
1620	key.data = keybuf;
1621	bcopy(name, keybuf, key.size);
1622	keybuf[key.size] = '\0';
1623	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1624		makelower(keybuf);
1625  lockdb:
1626#if DB_VERSION_MAJOR < 2
1627	fd = db->fd(db);
1628#else
1629	fd = -1;
1630	errno = db->fd(db, &fd);
1631#endif
1632	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1633		(void) lockfile(fd, buf, ".db", LOCK_SH);
1634	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1635	{
1636		/* Reopen the database to sync the cache */
1637		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1638								 : O_RDONLY;
1639
1640		map->map_class->map_close(map);
1641		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1642		if (map->map_class->map_open(map, omode))
1643		{
1644			map->map_mflags |= MF_OPEN;
1645			map->map_pid = getpid();
1646			if ((omode && O_ACCMODE) == O_RDWR)
1647				map->map_mflags |= MF_WRITABLE;
1648			db = (DB *) map->map_db2;
1649			goto lockdb;
1650		}
1651		else
1652		{
1653			if (!bitset(MF_OPTIONAL, map->map_mflags))
1654			{
1655				extern MAPCLASS BogusMapClass;
1656
1657				*statp = EX_TEMPFAIL;
1658				map->map_class = &BogusMapClass;
1659				map->map_mflags |= MF_OPEN;
1660				map->map_pid = getpid();
1661				syserr("Cannot reopen DB database %s",
1662					map->map_file);
1663			}
1664			return NULL;
1665		}
1666	}
1667
1668	st = 1;
1669	if (bitset(MF_TRY0NULL, map->map_mflags))
1670	{
1671#if DB_VERSION_MAJOR < 2
1672		st = db->get(db, &key, &val, 0);
1673#else
1674		errno = db->get(db, NULL, &key, &val, 0);
1675		switch (errno)
1676		{
1677		  case DB_NOTFOUND:
1678		  case DB_KEYEMPTY:
1679			st = 1;
1680			break;
1681
1682		  case 0:
1683			st = 0;
1684			break;
1685
1686		  default:
1687			st = -1;
1688			break;
1689		}
1690#endif
1691		if (st == 0)
1692			map->map_mflags &= ~MF_TRY1NULL;
1693	}
1694	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
1695	{
1696		key.size++;
1697#if DB_VERSION_MAJOR < 2
1698		st = db->get(db, &key, &val, 0);
1699#else
1700		errno = db->get(db, NULL, &key, &val, 0);
1701		switch (errno)
1702		{
1703		  case DB_NOTFOUND:
1704		  case DB_KEYEMPTY:
1705			st = 1;
1706			break;
1707
1708		  case 0:
1709			st = 0;
1710			break;
1711
1712		  default:
1713			st = -1;
1714			break;
1715		}
1716#endif
1717		if (st == 0)
1718			map->map_mflags &= ~MF_TRY0NULL;
1719	}
1720	saveerrno = errno;
1721	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1722		(void) lockfile(fd, buf, ".db", LOCK_UN);
1723	if (st != 0)
1724	{
1725		errno = saveerrno;
1726		if (st < 0)
1727			syserr("db_map_lookup: get (%s)", name);
1728		return NULL;
1729	}
1730	if (bitset(MF_MATCHONLY, map->map_mflags))
1731		return map_rewrite(map, name, strlen(name), NULL);
1732	else
1733		return map_rewrite(map, val.data, val.size, av);
1734}
1735
1736
1737/*
1738**  DB_MAP_STORE -- store a datum in the NEWDB database
1739*/
1740
1741void
1742db_map_store(map, lhs, rhs)
1743	register MAP *map;
1744	char *lhs;
1745	char *rhs;
1746{
1747	int stat;
1748	DBT key;
1749	DBT data;
1750	register DB *db = map->map_db2;
1751	char keybuf[MAXNAME + 1];
1752
1753	bzero(&key, sizeof key);
1754	bzero(&data, sizeof data);
1755
1756	if (tTd(38, 12))
1757		printf("db_map_store(%s, %s, %s)\n",
1758			map->map_mname, lhs, rhs);
1759
1760	key.size = strlen(lhs);
1761	key.data = lhs;
1762	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1763	{
1764		if (key.size > sizeof keybuf - 1)
1765			key.size = sizeof keybuf - 1;
1766		bcopy(key.data, keybuf, key.size);
1767		keybuf[key.size] = '\0';
1768		makelower(keybuf);
1769		key.data = keybuf;
1770	}
1771
1772	data.size = strlen(rhs);
1773	data.data = rhs;
1774
1775	if (bitset(MF_INCLNULL, map->map_mflags))
1776	{
1777		key.size++;
1778		data.size++;
1779	}
1780
1781#if DB_VERSION_MAJOR < 2
1782	stat = db->put(db, &key, &data, R_NOOVERWRITE);
1783#else
1784	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
1785	switch (errno)
1786	{
1787	  case DB_KEYEXIST:
1788		stat = 1;
1789		break;
1790
1791	  case 0:
1792		stat = 0;
1793		break;
1794
1795	  default:
1796		stat = -1;
1797		break;
1798	}
1799#endif
1800	if (stat > 0)
1801	{
1802		if (!bitset(MF_APPEND, map->map_mflags))
1803			message("050 Warning: duplicate alias name %s", lhs);
1804		else
1805		{
1806			static char *buf = NULL;
1807			static int bufsiz = 0;
1808			DBT old;
1809
1810			bzero(&old, sizeof old);
1811
1812			old.data = db_map_lookup(map, key.data,
1813						 (char **)NULL, &stat);
1814			if (old.data != NULL)
1815			{
1816				old.size = strlen(old.data);
1817				if (data.size + old.size + 2 > bufsiz)
1818				{
1819					if (buf != NULL)
1820						(void) free(buf);
1821					bufsiz = data.size + old.size + 2;
1822					buf = xalloc(bufsiz);
1823				}
1824				snprintf(buf, bufsiz, "%s,%s",
1825					(char *) data.data, (char *) old.data);
1826				data.size = data.size + old.size + 1;
1827				data.data = buf;
1828				if (tTd(38, 9))
1829					printf("db_map_store append=%s\n",
1830					       (char *) data.data);
1831			}
1832		}
1833#if DB_VERSION_MAJOR < 2
1834		stat = db->put(db, &key, &data, 0);
1835#else
1836		stat = errno = db->put(db, NULL, &key, &data, 0);
1837#endif
1838	}
1839	if (stat != 0)
1840		syserr("readaliases: db put (%s)", lhs);
1841}
1842
1843
1844/*
1845**  DB_MAP_CLOSE -- add distinguished entries and close the database
1846*/
1847
1848void
1849db_map_close(map)
1850	MAP *map;
1851{
1852	register DB *db = map->map_db2;
1853
1854	if (tTd(38, 9))
1855		printf("db_map_close(%s, %s, %lx)\n",
1856			map->map_mname, map->map_file, map->map_mflags);
1857
1858	if (bitset(MF_WRITABLE, map->map_mflags))
1859	{
1860		/* write out the distinguished alias */
1861		db_map_store(map, "@", "@");
1862	}
1863
1864	(void) db->sync(db, 0);
1865
1866#if !LOCK_ON_OPEN
1867	if (map->map_lockfd >= 0)
1868		(void) close(map->map_lockfd);
1869#endif
1870
1871#if DB_VERSION_MAJOR < 2
1872	if (db->close(db) != 0)
1873#else
1874	/*
1875	**  Berkeley DB can use internal shared memory
1876	**  locking for its memory pool.  Closing a map
1877	**  opened by another process will interfere
1878	**  with the shared memory and locks of the parent
1879	**  process leaving things in a bad state.
1880	*/
1881
1882	/*
1883	**  If this map was not opened by the current
1884	**  process, do not close the map but recover
1885	**  the file descriptor.
1886	*/
1887	if (map->map_pid != getpid())
1888	{
1889		int fd = -1;
1890
1891		errno = db->fd(db, &fd);
1892		if (fd >= 0)
1893			(void) close(fd);
1894		return;
1895	}
1896
1897	if ((errno = db->close(db, 0)) != 0)
1898#endif
1899		syserr("db_map_close(%s, %s, %lx): db close failure",
1900			map->map_mname, map->map_file, map->map_mflags);
1901}
1902
1903#endif
1904/*
1905**  NIS Modules
1906*/
1907
1908# ifdef NIS
1909
1910# ifndef YPERR_BUSY
1911#  define YPERR_BUSY	16
1912# endif
1913
1914/*
1915**  NIS_MAP_OPEN -- open DBM map
1916*/
1917
1918bool
1919nis_map_open(map, mode)
1920	MAP *map;
1921	int mode;
1922{
1923	int yperr;
1924	register char *p;
1925	auto char *vp;
1926	auto int vsize;
1927
1928	if (tTd(38, 2))
1929		printf("nis_map_open(%s, %s, %d)\n",
1930			map->map_mname, map->map_file, mode);
1931
1932	mode &= O_ACCMODE;
1933	if (mode != O_RDONLY)
1934	{
1935		/* issue a pseudo-error message */
1936#ifdef ENOSYS
1937		errno = ENOSYS;
1938#else
1939# ifdef EFTYPE
1940		errno = EFTYPE;
1941# else
1942		errno = ENXIO;
1943# endif
1944#endif
1945		return FALSE;
1946	}
1947
1948	p = strchr(map->map_file, '@');
1949	if (p != NULL)
1950	{
1951		*p++ = '\0';
1952		if (*p != '\0')
1953			map->map_domain = p;
1954	}
1955
1956	if (*map->map_file == '\0')
1957		map->map_file = "mail.aliases";
1958
1959	if (map->map_domain == NULL)
1960	{
1961		yperr = yp_get_default_domain(&map->map_domain);
1962		if (yperr != 0)
1963		{
1964			if (!bitset(MF_OPTIONAL, map->map_mflags))
1965				syserr("421 NIS map %s specified, but NIS not running",
1966					map->map_file);
1967			return FALSE;
1968		}
1969	}
1970
1971	/* check to see if this map actually exists */
1972	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1973			&vp, &vsize);
1974	if (tTd(38, 10))
1975		printf("nis_map_open: yp_match(@, %s, %s) => %s\n",
1976			map->map_domain, map->map_file, yperr_string(yperr));
1977	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1978	{
1979		/*
1980		**  We ought to be calling aliaswait() here if this is an
1981		**  alias file, but powerful HP-UX NIS servers  apparently
1982		**  don't insert the @:@ token into the alias map when it
1983		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
1984		*/
1985
1986#if 0
1987		if (!bitset(MF_ALIAS, map->map_mflags) ||
1988		    aliaswait(map, NULL, TRUE))
1989#endif
1990			return TRUE;
1991	}
1992
1993	if (!bitset(MF_OPTIONAL, map->map_mflags))
1994	{
1995		syserr("421 Cannot bind to map %s in domain %s: %s",
1996			map->map_file, map->map_domain, yperr_string(yperr));
1997	}
1998
1999	return FALSE;
2000}
2001
2002
2003/*
2004**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2005*/
2006
2007/* ARGSUSED3 */
2008char *
2009nis_map_lookup(map, name, av, statp)
2010	MAP *map;
2011	char *name;
2012	char **av;
2013	int *statp;
2014{
2015	char *vp;
2016	auto int vsize;
2017	int buflen;
2018	int yperr;
2019	char keybuf[MAXNAME + 1];
2020
2021	if (tTd(38, 20))
2022		printf("nis_map_lookup(%s, %s)\n",
2023			map->map_mname, name);
2024
2025	buflen = strlen(name);
2026	if (buflen > sizeof keybuf - 1)
2027		buflen = sizeof keybuf - 1;
2028	bcopy(name, keybuf, buflen);
2029	keybuf[buflen] = '\0';
2030	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2031		makelower(keybuf);
2032	yperr = YPERR_KEY;
2033	if (bitset(MF_TRY0NULL, map->map_mflags))
2034	{
2035		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2036			     &vp, &vsize);
2037		if (yperr == 0)
2038			map->map_mflags &= ~MF_TRY1NULL;
2039	}
2040	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2041	{
2042		buflen++;
2043		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2044			     &vp, &vsize);
2045		if (yperr == 0)
2046			map->map_mflags &= ~MF_TRY0NULL;
2047	}
2048	if (yperr != 0)
2049	{
2050		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2051			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2052		return NULL;
2053	}
2054	if (bitset(MF_MATCHONLY, map->map_mflags))
2055		return map_rewrite(map, name, strlen(name), NULL);
2056	else
2057		return map_rewrite(map, vp, vsize, av);
2058}
2059
2060
2061/*
2062**  NIS_GETCANONNAME -- look up canonical name in NIS
2063*/
2064
2065bool
2066nis_getcanonname(name, hbsize, statp)
2067	char *name;
2068	int hbsize;
2069	int *statp;
2070{
2071	char *vp;
2072	auto int vsize;
2073	int keylen;
2074	int yperr;
2075	static bool try0null = TRUE;
2076	static bool try1null = TRUE;
2077	static char *yp_domain = NULL;
2078	char host_record[MAXLINE];
2079	char cbuf[MAXNAME];
2080	char nbuf[MAXNAME + 1];
2081
2082	if (tTd(38, 20))
2083		printf("nis_getcanonname(%s)\n", name);
2084
2085	if (strlen(name) >= sizeof nbuf)
2086	{
2087		*statp = EX_UNAVAILABLE;
2088		return FALSE;
2089	}
2090	(void) strcpy(nbuf, name);
2091	shorten_hostname(nbuf);
2092	keylen = strlen(nbuf);
2093
2094	if (yp_domain == NULL)
2095		yp_get_default_domain(&yp_domain);
2096	makelower(nbuf);
2097	yperr = YPERR_KEY;
2098	if (try0null)
2099	{
2100		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2101			     &vp, &vsize);
2102		if (yperr == 0)
2103			try1null = FALSE;
2104	}
2105	if (yperr == YPERR_KEY && try1null)
2106	{
2107		keylen++;
2108		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2109			     &vp, &vsize);
2110		if (yperr == 0)
2111			try0null = FALSE;
2112	}
2113	if (yperr != 0)
2114	{
2115		if (yperr == YPERR_KEY)
2116			*statp = EX_NOHOST;
2117		else if (yperr == YPERR_BUSY)
2118			*statp = EX_TEMPFAIL;
2119		else
2120			*statp = EX_UNAVAILABLE;
2121		return FALSE;
2122	}
2123	if (vsize >= sizeof host_record)
2124		vsize = sizeof host_record - 1;
2125	strncpy(host_record, vp, vsize);
2126	host_record[vsize] = '\0';
2127	if (tTd(38, 44))
2128		printf("got record `%s'\n", host_record);
2129	if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
2130	{
2131		/* this should not happen, but.... */
2132		*statp = EX_NOHOST;
2133		return FALSE;
2134	}
2135	if (hbsize < strlen(cbuf))
2136	{
2137		*statp = EX_UNAVAILABLE;
2138		return FALSE;
2139	}
2140	strcpy(name, cbuf);
2141	*statp = EX_OK;
2142	return TRUE;
2143}
2144
2145#endif
2146/*
2147**  NISPLUS Modules
2148**
2149**	This code donated by Sun Microsystems.
2150*/
2151
2152#ifdef NISPLUS
2153
2154#undef NIS		/* symbol conflict in nis.h */
2155#undef T_UNSPEC		/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2156#include <rpcsvc/nis.h>
2157#include <rpcsvc/nislib.h>
2158
2159#define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2160#define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2161#define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2162#define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
2163
2164/*
2165**  NISPLUS_MAP_OPEN -- open nisplus table
2166*/
2167
2168bool
2169nisplus_map_open(map, mode)
2170	MAP *map;
2171	int mode;
2172{
2173	nis_result *res = NULL;
2174	int retry_cnt, max_col, i;
2175	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2176
2177	if (tTd(38, 2))
2178		printf("nisplus_map_open(%s, %s, %d)\n",
2179			map->map_mname, map->map_file, mode);
2180
2181	mode &= O_ACCMODE;
2182	if (mode != O_RDONLY)
2183	{
2184		errno = EPERM;
2185		return FALSE;
2186	}
2187
2188	if (*map->map_file == '\0')
2189		map->map_file = "mail_aliases.org_dir";
2190
2191	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2192	{
2193		/* set default NISPLUS Domain to $m */
2194		extern char *nisplus_default_domain __P((void));
2195
2196		map->map_domain = newstr(nisplus_default_domain());
2197		if (tTd(38, 2))
2198			printf("nisplus_map_open(%s): using domain %s\n",
2199				 map->map_file, map->map_domain);
2200	}
2201	if (!PARTIAL_NAME(map->map_file))
2202	{
2203		map->map_domain = newstr("");
2204		snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
2205	}
2206	else
2207	{
2208		/* check to see if this map actually exists */
2209		snprintf(qbuf, sizeof qbuf, "%s.%s",
2210			map->map_file, map->map_domain);
2211	}
2212
2213	retry_cnt = 0;
2214	while (res == NULL || res->status != NIS_SUCCESS)
2215	{
2216		res = nis_lookup(qbuf, FOLLOW_LINKS);
2217		switch (res->status)
2218		{
2219		  case NIS_SUCCESS:
2220			break;
2221
2222		  case NIS_TRYAGAIN:
2223		  case NIS_RPCERROR:
2224		  case NIS_NAMEUNREACHABLE:
2225			if (retry_cnt++ > 4)
2226			{
2227				errno = EAGAIN;
2228				return FALSE;
2229			}
2230			/* try not to overwhelm hosed server */
2231			sleep(2);
2232			break;
2233
2234		  default:		/* all other nisplus errors */
2235#if 0
2236			if (!bitset(MF_OPTIONAL, map->map_mflags))
2237				syserr("421 Cannot find table %s.%s: %s",
2238					map->map_file, map->map_domain,
2239					nis_sperrno(res->status));
2240#endif
2241			errno = EAGAIN;
2242			return FALSE;
2243		}
2244	}
2245
2246	if (NIS_RES_NUMOBJ(res) != 1 ||
2247	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2248	{
2249		if (tTd(38, 10))
2250			printf("nisplus_map_open: %s is not a table\n", qbuf);
2251#if 0
2252		if (!bitset(MF_OPTIONAL, map->map_mflags))
2253			syserr("421 %s.%s: %s is not a table",
2254				map->map_file, map->map_domain,
2255				nis_sperrno(res->status));
2256#endif
2257		errno = EBADF;
2258		return FALSE;
2259	}
2260	/* default key column is column 0 */
2261	if (map->map_keycolnm == NULL)
2262		map->map_keycolnm = newstr(COL_NAME(res,0));
2263
2264	max_col = COL_MAX(res);
2265
2266	/* verify the key column exist */
2267	for (i=0; i< max_col; i++)
2268	{
2269		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
2270			break;
2271	}
2272	if (i == max_col)
2273	{
2274		if (tTd(38, 2))
2275			printf("nisplus_map_open(%s): can not find key column %s\n",
2276				map->map_file, map->map_keycolnm);
2277		errno = ENOENT;
2278		return FALSE;
2279	}
2280
2281	/* default value column is the last column */
2282	if (map->map_valcolnm == NULL)
2283	{
2284		map->map_valcolno = max_col - 1;
2285		return TRUE;
2286	}
2287
2288	for (i=0; i< max_col; i++)
2289	{
2290		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2291		{
2292			map->map_valcolno = i;
2293			return TRUE;
2294		}
2295	}
2296
2297	if (tTd(38, 2))
2298		printf("nisplus_map_open(%s): can not find column %s\n",
2299			 map->map_file, map->map_keycolnm);
2300	errno = ENOENT;
2301	return FALSE;
2302}
2303
2304
2305/*
2306**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2307*/
2308
2309char *
2310nisplus_map_lookup(map, name, av, statp)
2311	MAP *map;
2312	char *name;
2313	char **av;
2314	int *statp;
2315{
2316	char *p;
2317	auto int vsize;
2318	char *skp;
2319	int skleft;
2320	char search_key[MAXNAME + 4];
2321	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2322	nis_result *result;
2323
2324	if (tTd(38, 20))
2325		printf("nisplus_map_lookup(%s, %s)\n",
2326			map->map_mname, name);
2327
2328	if (!bitset(MF_OPEN, map->map_mflags))
2329	{
2330		if (nisplus_map_open(map, O_RDONLY))
2331		{
2332			map->map_mflags |= MF_OPEN;
2333			map->map_pid = getpid();
2334		}
2335		else
2336		{
2337			*statp = EX_UNAVAILABLE;
2338			return NULL;
2339		}
2340	}
2341
2342	/*
2343	**  Copy the name to the key buffer, escaping double quote characters
2344	**  by doubling them and quoting "]" and "," to avoid having the
2345	**  NIS+ parser choke on them.
2346	*/
2347
2348	skleft = sizeof search_key - 4;
2349	skp = search_key;
2350	for (p = name; *p != '\0' && skleft > 0; p++)
2351	{
2352		switch (*p)
2353		{
2354		  case ']':
2355		  case ',':
2356			/* quote the character */
2357			*skp++ = '"';
2358			*skp++ = *p;
2359			*skp++ = '"';
2360			skleft -= 3;
2361			break;
2362
2363		  case '"':
2364			/* double the quote */
2365			*skp++ = '"';
2366			skleft--;
2367			/* fall through... */
2368
2369		  default:
2370			*skp++ = *p;
2371			skleft--;
2372			break;
2373		}
2374	}
2375	*skp = '\0';
2376	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2377		makelower(search_key);
2378
2379	/* construct the query */
2380	if (PARTIAL_NAME(map->map_file))
2381		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
2382			map->map_keycolnm, search_key, map->map_file,
2383			map->map_domain);
2384	else
2385		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
2386			map->map_keycolnm, search_key, map->map_file);
2387
2388	if (tTd(38, 20))
2389		printf("qbuf=%s\n", qbuf);
2390	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
2391	if (result->status == NIS_SUCCESS)
2392	{
2393		int count;
2394		char *str;
2395
2396		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2397		{
2398			if (LogLevel > 10)
2399				sm_syslog(LOG_WARNING, CurEnv->e_id,
2400				  "%s: lookup error, expected 1 entry, got %d",
2401				    map->map_file, count);
2402
2403			/* ignore second entry */
2404			if (tTd(38, 20))
2405				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
2406					name, count);
2407		}
2408
2409		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
2410		/* set the length of the result */
2411		if (p == NULL)
2412			p = "";
2413		vsize = strlen(p);
2414		if (tTd(38, 20))
2415			printf("nisplus_map_lookup(%s), found %s\n",
2416				name, p);
2417		if (bitset(MF_MATCHONLY, map->map_mflags))
2418			str = map_rewrite(map, name, strlen(name), NULL);
2419		else
2420			str = map_rewrite(map, p, vsize, av);
2421		nis_freeresult(result);
2422		*statp = EX_OK;
2423		return str;
2424	}
2425	else
2426	{
2427		if (result->status == NIS_NOTFOUND)
2428			*statp = EX_NOTFOUND;
2429		else if (result->status == NIS_TRYAGAIN)
2430			*statp = EX_TEMPFAIL;
2431		else
2432		{
2433			*statp = EX_UNAVAILABLE;
2434			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2435		}
2436	}
2437	if (tTd(38, 20))
2438		printf("nisplus_map_lookup(%s), failed\n", name);
2439	nis_freeresult(result);
2440	return NULL;
2441}
2442
2443
2444
2445/*
2446**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
2447*/
2448
2449bool
2450nisplus_getcanonname(name, hbsize, statp)
2451	char *name;
2452	int hbsize;
2453	int *statp;
2454{
2455	char *vp;
2456	auto int vsize;
2457	nis_result *result;
2458	char *p;
2459	char nbuf[MAXNAME + 1];
2460	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2461
2462	if (strlen(name) >= sizeof nbuf)
2463	{
2464		*statp = EX_UNAVAILABLE;
2465		return FALSE;
2466	}
2467	(void) strcpy(nbuf, name);
2468	shorten_hostname(nbuf);
2469
2470	p = strchr(nbuf, '.');
2471	if (p == NULL)
2472	{
2473		/* single token */
2474		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
2475	}
2476	else if (p[1] != '\0')
2477	{
2478		/* multi token -- take only first token in nbuf */
2479		*p = '\0';
2480		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
2481			nbuf, &p[1]);
2482	}
2483	else
2484	{
2485		*statp = EX_NOHOST;
2486		return FALSE;
2487	}
2488
2489	if (tTd(38, 20))
2490		printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
2491			 name, qbuf);
2492
2493	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
2494		NULL, NULL);
2495
2496	if (result->status == NIS_SUCCESS)
2497	{
2498		int count;
2499		char *domain;
2500
2501		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2502		{
2503			if (LogLevel > 10)
2504				sm_syslog(LOG_WARNING, CurEnv->e_id,
2505				       "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
2506				       count);
2507
2508			/* ignore second entry */
2509			if (tTd(38, 20))
2510				printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
2511					name, count);
2512		}
2513
2514		if (tTd(38, 20))
2515			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
2516			       name, (NIS_RES_OBJECT(result))->zo_domain);
2517
2518
2519		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
2520		vsize = strlen(vp);
2521		if (tTd(38, 20))
2522			printf("nisplus_getcanonname(%s), found %s\n",
2523				name, vp);
2524		if (strchr(vp, '.') != NULL)
2525		{
2526			domain = "";
2527		}
2528		else
2529		{
2530			domain = macvalue('m', CurEnv);
2531			if (domain == NULL)
2532				domain = "";
2533		}
2534		if (hbsize > vsize + (int) strlen(domain) + 1)
2535		{
2536			if (domain[0] == '\0')
2537				strcpy(name, vp);
2538			else
2539				snprintf(name, hbsize, "%s.%s", vp, domain);
2540			*statp = EX_OK;
2541		}
2542		else
2543			*statp = EX_NOHOST;
2544		nis_freeresult(result);
2545		return TRUE;
2546	}
2547	else
2548	{
2549		if (result->status == NIS_NOTFOUND)
2550			*statp = EX_NOHOST;
2551		else if (result->status == NIS_TRYAGAIN)
2552			*statp = EX_TEMPFAIL;
2553		else
2554			*statp = EX_UNAVAILABLE;
2555	}
2556	if (tTd(38, 20))
2557		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
2558			name, result->status, *statp);
2559	nis_freeresult(result);
2560	return FALSE;
2561}
2562
2563
2564char *
2565nisplus_default_domain()
2566{
2567	static char default_domain[MAXNAME + 1] = "";
2568	char *p;
2569
2570	if (default_domain[0] != '\0')
2571		return(default_domain);
2572
2573	p = nis_local_directory();
2574	snprintf(default_domain, sizeof default_domain, "%s", p);
2575	return default_domain;
2576}
2577
2578#endif /* NISPLUS */
2579/*
2580**  LDAP Modules
2581**
2582**	Contributed by Booker C. Bense <bbense@networking.stanford.edu>.
2583**	Get your support from him.
2584*/
2585
2586#ifdef LDAPMAP
2587
2588# undef NEEDGETOPT		/* used for something else in LDAP */
2589
2590# include <lber.h>
2591# include <ldap.h>
2592# include "ldap_map.h"
2593
2594/*
2595**  LDAP_MAP_OPEN -- open LDAP map
2596**
2597**	Since LDAP is TCP-based there is not much we can or should do
2598**	here.  It might be a good idea to attempt an open/close here.
2599*/
2600
2601bool
2602ldap_map_open(map, mode)
2603	MAP *map;
2604	int mode;
2605{
2606	if (tTd(38, 2))
2607		printf("ldap_map_open(%s, %d)\n", map->map_mname, mode);
2608
2609	mode &= O_ACCMODE;
2610	if (mode != O_RDONLY)
2611	{
2612		/* issue a pseudo-error message */
2613#ifdef ENOSYS
2614		errno = ENOSYS;
2615#else
2616# ifdef EFTYPE
2617		errno = EFTYPE;
2618# else
2619		errno = ENXIO;
2620# endif
2621#endif
2622		return FALSE;
2623	}
2624	return TRUE;
2625}
2626
2627
2628/*
2629**  LDAP_MAP_START -- actually open LDAP map
2630**
2631**	Caching should be investigated.
2632*/
2633
2634static jmp_buf	LDAPTimeout;
2635
2636static void
2637ldaptimeout(sig_no)
2638	int sig_no;
2639{
2640	longjmp(LDAPTimeout, 1);
2641}
2642
2643bool
2644ldap_map_start(map)
2645	MAP *map;
2646{
2647	LDAP_MAP_STRUCT *lmap;
2648	LDAP *ld;
2649	register EVENT *ev = NULL;
2650
2651	if (tTd(38, 2))
2652		printf("ldap_map_start(%s)\n", map->map_mname);
2653
2654	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2655
2656	if (tTd(38,9))
2657		printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport);
2658
2659	/* Need to set an alarm here, ldap_open is hopelessly broken. */
2660
2661	/* set the timeout */
2662	if (lmap->timeout.tv_sec != 0)
2663	{
2664		if (setjmp(LDAPTimeout) != 0)
2665		{
2666			if (LogLevel > 1)
2667				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2668				       "timeout waiting for ldap_open to %.100s",
2669				       lmap->ldaphost);
2670			return (FALSE);
2671		}
2672		ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0);
2673	}
2674
2675#ifdef USE_LDAP_INIT
2676	ld = ldap_init(lmap->ldaphost,lmap->ldapport);
2677#else
2678	ld = ldap_open(lmap->ldaphost,lmap->ldapport);
2679#endif
2680
2681 	/* clear the event if it has not sprung */
2682	if (lmap->timeout.tv_sec != 0)
2683		clrevent(ev);
2684
2685	if (ld == NULL)
2686	{
2687		if (!bitset(MF_OPTIONAL, map->map_mflags))
2688		{
2689			syserr("%sldapopen failed to %s in map %s",
2690				bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2691				lmap->ldaphost, map->map_mname);
2692		}
2693		return FALSE;
2694	}
2695
2696#ifdef USE_LDAP_SET_OPTION
2697	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref);
2698	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit);
2699	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit);
2700	ldap_set_option(ld, LDAP_OPT_REFERRALS,
2701			bitset(LDAP_OPT_REFERRALS, lmap->ldap_options) ?
2702			LDAP_OPT_ON : LDAP_OPT_OFF);
2703#else
2704	/* From here on in we can use ldap internal timelimits */
2705	ld->ld_deref = lmap->deref;
2706	ld->ld_timelimit = lmap->timelimit;
2707	ld->ld_sizelimit = lmap->sizelimit;
2708	ld->ld_options = lmap->ldap_options;
2709#endif
2710
2711#ifdef USE_LDAP_INIT
2712	/* ld needs to be cast into the map struct */
2713	lmap->ld = ld;
2714	return TRUE;
2715#else
2716	if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS)
2717	{
2718		if (!bitset(MF_OPTIONAL, map->map_mflags))
2719		{
2720			syserr("421 Cannot bind to map %s in ldap server %s",
2721				map->map_mname, lmap->ldaphost);
2722		}
2723	}
2724	else
2725	{
2726		/* We need to cast ld into the map structure */
2727		lmap->ld = ld;
2728		return TRUE;
2729	}
2730
2731	return FALSE;
2732#endif
2733}
2734
2735
2736/*
2737**  LDAP_MAP_STOP -- close the ldap connection
2738*/
2739
2740void
2741ldap_map_stop(map)
2742	MAP *map;
2743{
2744	LDAP_MAP_STRUCT *lmap;
2745
2746	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2747	if (lmap->ld != NULL)
2748	{
2749		ldap_unbind(lmap->ld);
2750		lmap->ld = NULL;
2751	}
2752}
2753
2754/*
2755**  LDAP_MAP_CLOSE -- close ldap map
2756*/
2757
2758void
2759ldap_map_close(map)
2760	MAP *map;
2761{
2762	ldap_map_stop(map);
2763}
2764
2765#ifdef SUNET_ID
2766/*
2767**  SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
2768**  This only makes sense at Stanford University.
2769*/
2770
2771char *
2772sunet_id_hash(str)
2773	char *str;
2774{
2775	char *p, *p_last;
2776
2777	p = str;
2778	p_last = p;
2779	while (*p != '\0')
2780	{
2781		if (islower(*p) || isdigit(*p))
2782		{
2783			*p_last = *p;
2784			p_last++;
2785		}
2786		else if (isupper(*p))
2787		{
2788			*p_last = tolower(*p);
2789			p_last++;
2790		}
2791		++p;
2792	}
2793	if (*p_last != '\0')
2794		*p_last = '\0';
2795	return (str);
2796}
2797
2798
2799
2800#endif /* SUNET_ID */
2801/*
2802**  LDAP_MAP_LOOKUP -- look up a datum in a LDAP map
2803*/
2804
2805char *
2806ldap_map_lookup(map, name, av, statp)
2807	MAP *map;
2808	char *name;
2809	char **av;
2810	int *statp;
2811{
2812	LDAP_MAP_STRUCT *lmap = NULL;
2813	LDAPMessage *entry;
2814	char *vp;
2815	auto int vsize;
2816	char keybuf[MAXNAME + 1];
2817	char filter[LDAP_MAP_MAX_FILTER + 1];
2818	char **attr_values = NULL;
2819	char *result;
2820	int name_len;
2821	char *fp, *p, *q;
2822
2823	if (tTd(38, 20))
2824		printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name);
2825
2826	/* actually open the map */
2827	if (!ldap_map_start(map))
2828	{
2829		result = NULL;
2830		*statp = EX_TEMPFAIL;
2831		goto quick_exit;
2832	}
2833
2834	/* Get ldap struct pointer from map */
2835	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2836
2837	name_len = strlen(name);
2838	if (name_len > MAXNAME)
2839		name_len = MAXNAME;
2840	strncpy(keybuf, name, name_len);
2841	keybuf[name_len] = '\0';
2842
2843	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2844#ifdef SUNET_ID
2845		sunet_id_hash(keybuf);
2846#else
2847		makelower(keybuf);
2848#endif /*SUNET_ID */
2849
2850	/* substitute keybuf into filter, perhaps multiple times */
2851	fp = filter;
2852	p = lmap->filter;
2853	while ((q = strchr(p, '%')) != NULL)
2854	{
2855		if (q[1] == 's')
2856		{
2857			snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
2858				 q - p, p, keybuf);
2859			p = q + 2;
2860		}
2861		else
2862		{
2863			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
2864				 q - p + 1, p);
2865			p = q + (q[1] == '%' ? 2 : 1);
2866		}
2867		fp += strlen(fp);
2868	}
2869	snprintf(fp, SPACELEFT(filter, fp), "%s", p);
2870	if (tTd(38, 20))
2871		printf("ldap search filter=%s\n", filter);
2872
2873	if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter,
2874			   lmap->attr, lmap->attrsonly, &(lmap->timeout),
2875			   &(lmap->res)) != LDAP_SUCCESS)
2876	{
2877		/* try stopping/starting map */
2878		ldap_map_stop(map);
2879		if (!ldap_map_start(map))
2880		{
2881			result = NULL;
2882			*statp = EX_TEMPFAIL;
2883			goto quick_exit;
2884		}
2885		if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter,
2886				   lmap->attr, lmap->attrsonly,
2887				   &(lmap->timeout), &(lmap->res))
2888			!= LDAP_SUCCESS)
2889		{
2890			if (!bitset(MF_OPTIONAL, map->map_mflags))
2891			{
2892				syserr("%sError in ldap_search_st using %s in map %s",
2893					bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2894					filter, map->map_mname);
2895			}
2896			result = NULL;
2897			*statp = EX_TEMPFAIL;
2898			goto quick_exit;
2899		}
2900	}
2901
2902	entry = ldap_first_entry(lmap->ld,lmap->res);
2903	if (entry == NULL)
2904	{
2905	        result = NULL;
2906		*statp = EX_NOTFOUND;
2907		goto quick_exit;
2908	}
2909
2910	/* Need to build the args for map_rewrite here */
2911	attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]);
2912	if (attr_values == NULL)
2913	{
2914		/* bad things happened */
2915		result = NULL;
2916		*statp = EX_NOTFOUND;
2917		goto quick_exit;
2918	}
2919
2920	*statp = EX_OK;
2921
2922	/* If there is more that one use the first */
2923	vp = attr_values[0];
2924	vsize = strlen(vp);
2925
2926	if (LogLevel > 9)
2927		sm_syslog(LOG_INFO, CurEnv->e_id,
2928			"ldap %.100s => %s",
2929			name, vp);
2930	if (bitset(MF_MATCHONLY, map->map_mflags))
2931		result = map_rewrite(map, name, strlen(name), NULL);
2932	else
2933		result = map_rewrite(map, vp, vsize, av);
2934
2935  quick_exit:
2936	if (attr_values != NULL)
2937		ldap_value_free(attr_values);
2938	if (lmap != NULL)
2939		ldap_msgfree(lmap->res);
2940	ldap_map_stop(map);
2941	return result ;
2942}
2943
2944
2945/*
2946**  LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs
2947*/
2948
2949char *
2950ldap_map_dequote(str)
2951	char *str;
2952{
2953	char *p;
2954	char *start;
2955	p = str;
2956
2957	if (*p == '"')
2958	{
2959		start = ++p;
2960		/* Should probably swallow initial whitespace here */
2961	}
2962	else
2963	{
2964		return(str);
2965	}
2966	while (*p != '"' && *p != '\0')
2967	{
2968		p++;
2969	}
2970	if (*p != '\0')
2971		*p = '\0';
2972	return start;
2973}
2974
2975/*
2976**  LDAP_MAP_PARSEARGS -- parse ldap map definition args.
2977*/
2978
2979bool
2980ldap_map_parseargs(map,args)
2981	MAP *map;
2982	char *args;
2983{
2984	register char *p = args;
2985	register int done;
2986	LDAP_MAP_STRUCT *lmap;
2987
2988	/* We need to alloc an LDAP_MAP_STRUCT struct */
2989	lmap  = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT));
2990
2991	/* Set default int's here , default strings below */
2992	lmap->ldapport =  DEFAULT_LDAP_MAP_PORT;
2993	lmap->deref = DEFAULT_LDAP_MAP_DEREF;
2994	lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT;
2995	lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT;
2996	lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS;
2997	lmap->method = DEFAULT_LDAP_MAP_METHOD;
2998	lmap->scope = DEFAULT_LDAP_MAP_SCOPE;
2999	lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY;
3000	lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT;
3001	lmap->timeout.tv_usec = 0;
3002
3003	/* Default char ptrs to NULL */
3004	lmap->binddn = NULL;
3005	lmap->passwd = NULL;
3006	lmap->base   = NULL;
3007	lmap->ldaphost = NULL;
3008
3009	/* Default general ptrs to NULL */
3010	lmap->ld = NULL;
3011	lmap->res = NULL;
3012
3013	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
3014	for (;;)
3015	{
3016		while (isascii(*p) && isspace(*p))
3017			p++;
3018		if (*p != '-')
3019			break;
3020		switch (*++p)
3021		{
3022		  case 'N':
3023			map->map_mflags |= MF_INCLNULL;
3024			map->map_mflags &= ~MF_TRY0NULL;
3025			break;
3026
3027		  case 'O':
3028			map->map_mflags &= ~MF_TRY1NULL;
3029			break;
3030
3031		  case 'o':
3032			map->map_mflags |= MF_OPTIONAL;
3033			break;
3034
3035		  case 'f':
3036			map->map_mflags |= MF_NOFOLDCASE;
3037			break;
3038
3039		  case 'm':
3040			map->map_mflags |= MF_MATCHONLY;
3041			break;
3042
3043		  case 'A':
3044			map->map_mflags |= MF_APPEND;
3045			break;
3046
3047		  case 'q':
3048			map->map_mflags |= MF_KEEPQUOTES;
3049			break;
3050
3051		  case 't':
3052			map->map_mflags |= MF_NODEFER;
3053			break;
3054
3055		  case 'a':
3056			map->map_app = ++p;
3057			break;
3058
3059		  case 'T':
3060			map->map_tapp = ++p;
3061			break;
3062
3063			/* Start of ldap_map specific args */
3064		  case 'k':		/* search field */
3065			while (isascii(*++p) && isspace(*p))
3066				continue;
3067			lmap->filter = p;
3068			break;
3069
3070		  case 'v':		/* attr to return */
3071			while (isascii(*++p) && isspace(*p))
3072				continue;
3073			lmap->attr[0] = p;
3074			lmap->attr[1] = NULL;
3075			break;
3076
3077			/* args stolen from ldapsearch.c */
3078		  case 'R':		/* don't auto chase referrals */
3079#ifdef LDAP_REFERRALS
3080			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3081#else  /* LDAP_REFERRALS */
3082			syserr("compile with -DLDAP_REFERRALS for referral support\n");
3083#endif /* LDAP_REFERRALS */
3084			break;
3085
3086		  case 'n':		/* retrieve attribute names only -- no values */
3087			lmap->attrsonly += 1;
3088			break;
3089
3090		  case 's':		/* search scope */
3091			if (strncasecmp(++p, "base", 4) == 0)
3092			{
3093				lmap->scope = LDAP_SCOPE_BASE;
3094			}
3095			else if (strncasecmp(p, "one", 3) == 0)
3096			{
3097				lmap->scope = LDAP_SCOPE_ONELEVEL;
3098			}
3099			else if (strncasecmp(p, "sub", 3) == 0)
3100			{
3101				lmap->scope = LDAP_SCOPE_SUBTREE;
3102			}
3103			else
3104			{		/* bad config line */
3105				if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3106				{
3107					char *ptr;
3108
3109					if ((ptr = strchr(p, ' ')) != NULL)
3110						*ptr = '\0';
3111					syserr("Scope must be [base|one|sub] not %s in map %s",
3112						p, map->map_mname);
3113					if (ptr != NULL)
3114						*ptr = ' ';
3115					return FALSE;
3116				}
3117			}
3118			break;
3119
3120		  case 'h':		/* ldap host */
3121			while (isascii(*++p) && isspace(*p))
3122				continue;
3123			map->map_domain = p;
3124			lmap->ldaphost = p;
3125			break;
3126
3127		  case 'b':		/* search base */
3128			while (isascii(*++p) && isspace(*p))
3129				continue;
3130			lmap->base = p;
3131			break;
3132
3133		  case 'p':		/* ldap port */
3134			while (isascii(*++p) && isspace(*p))
3135				continue;
3136			lmap->ldapport = atoi(p);
3137			break;
3138
3139		  case 'l':		/* time limit */
3140			while (isascii(*++p) && isspace(*p))
3141				continue;
3142			lmap->timelimit = atoi(p);
3143			lmap->timeout.tv_sec = lmap->timelimit;
3144			break;
3145
3146		}
3147
3148		/* need to account for quoted strings here arggg... */
3149		done =  isascii(*p) && isspace(*p);
3150		while (*p != '\0' && !done)
3151		{
3152			if (*p == '"')
3153			{
3154				while (*++p != '"' && *p != '\0')
3155				{
3156					continue;
3157				}
3158				if (*p != '\0')
3159					p++;
3160			}
3161			else
3162			{
3163				p++;
3164			}
3165			done = isascii(*p) && isspace(*p);
3166		}
3167
3168		if (*p != '\0')
3169			*p++ = '\0';
3170	}
3171
3172	if (map->map_app != NULL)
3173		map->map_app = newstr(ldap_map_dequote(map->map_app));
3174	if (map->map_tapp != NULL)
3175		map->map_tapp = newstr(ldap_map_dequote(map->map_tapp));
3176	if (map->map_domain != NULL)
3177		map->map_domain = newstr(ldap_map_dequote(map->map_domain));
3178
3179	/*
3180	**  We need to swallow up all the stuff into a struct
3181	**  and dump it into map->map_dbptr1
3182	*/
3183
3184	if (lmap->ldaphost != NULL)
3185		lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost));
3186	else
3187	{
3188		syserr("LDAP map: -h flag is required");
3189		return FALSE;
3190	}
3191
3192	if (lmap->binddn != NULL)
3193		lmap->binddn = newstr(ldap_map_dequote(lmap->binddn));
3194	else
3195		lmap->binddn = DEFAULT_LDAP_MAP_BINDDN;
3196
3197
3198	if (lmap->passwd != NULL)
3199		lmap->passwd = newstr(ldap_map_dequote(lmap->passwd));
3200	else
3201		lmap->passwd = DEFAULT_LDAP_MAP_PASSWD;
3202
3203	if (lmap->base != NULL)
3204		lmap->base = newstr(ldap_map_dequote(lmap->base));
3205	else
3206	{
3207		syserr("LDAP map: -b flag is required");
3208		return FALSE;
3209	}
3210
3211
3212	if (lmap->filter != NULL)
3213		lmap->filter = newstr(ldap_map_dequote(lmap->filter));
3214	else
3215	{
3216		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3217		{
3218			syserr("No filter given in map %s", map->map_mname);
3219			return FALSE;
3220		}
3221	}
3222	if (lmap->attr[0] != NULL)
3223		lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0]));
3224	else
3225	{
3226		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3227		{
3228			syserr("No return attribute in %s", map->map_mname);
3229			return FALSE;
3230		}
3231	}
3232
3233	map->map_db1 = (ARBPTR_T) lmap;
3234	return TRUE;
3235}
3236
3237#endif /* LDAP Modules */
3238/*
3239**  syslog map
3240*/
3241
3242#if _FFR_MAP_SYSLOG
3243
3244#define map_prio	map_lockfd	/* overload field */
3245
3246/*
3247**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
3248*/
3249
3250bool
3251syslog_map_parseargs(map, args)
3252	MAP *map;
3253	char *args;
3254{
3255	char *p = args;
3256	char *priority = NULL;
3257
3258	for (;;)
3259	{
3260		while (isascii(*p) && isspace(*p))
3261			p++;
3262		if (*p != '-')
3263			break;
3264		if (*++p == 'L')
3265			priority = ++p;
3266		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3267			p++;
3268		if (*p != '\0')
3269			*p++ = '\0';
3270	}
3271
3272	if (priority == NULL)
3273		map->map_prio = LOG_INFO;
3274	else
3275	{
3276		if (strncasecmp("LOG_", priority, 4) == 0)
3277			priority += 4;
3278
3279#ifdef LOG_EMERG
3280		if (strcasecmp("EMERG", priority) == 0)
3281			map->map_prio = LOG_EMERG;
3282		else
3283#endif
3284#ifdef LOG_ALERT
3285		if (strcasecmp("ALERT", priority) == 0)
3286			map->map_prio = LOG_ALERT;
3287		else
3288#endif
3289#ifdef LOG_CRIT
3290		if (strcasecmp("CRIT", priority) == 0)
3291			map->map_prio = LOG_CRIT;
3292		else
3293#endif
3294#ifdef LOG_ERR
3295		if (strcasecmp("ERR", priority) == 0)
3296			map->map_prio = LOG_ERR;
3297		else
3298#endif
3299#ifdef LOG_WARNING
3300		if (strcasecmp("WARNING", priority) == 0)
3301			map->map_prio = LOG_WARNING;
3302		else
3303#endif
3304#ifdef LOG_NOTICE
3305		if (strcasecmp("NOTICE", priority) == 0)
3306			map->map_prio = LOG_NOTICE;
3307		else
3308#endif
3309#ifdef LOG_INFO
3310		if (strcasecmp("INFO", priority) == 0)
3311			map->map_prio = LOG_INFO;
3312		else
3313#endif
3314#ifdef LOG_DEBUG
3315		if (strcasecmp("DEBUG", priority) == 0)
3316			map->map_prio = LOG_DEBUG;
3317		else
3318#endif
3319		{
3320			syserr("syslog_map_parseargs: Unknown priority %s\n",
3321			       priority);
3322			return FALSE;
3323		}
3324	}
3325	return TRUE;
3326}
3327
3328/*
3329**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
3330*/
3331
3332char *
3333syslog_map_lookup(map, string, args, statp)
3334	MAP *map;
3335	char *string;
3336	char **args;
3337	int *statp;
3338{
3339	char *ptr = map_rewrite(map, string, strlen(string), args);
3340
3341	if (ptr != NULL)
3342	{
3343		if (tTd(38, 20))
3344			printf("syslog_map_lookup(%s (priority %d): %s\n",
3345			       map->map_mname, map->map_prio, ptr);
3346
3347		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
3348	}
3349
3350	*statp = EX_OK;
3351	return "";
3352}
3353
3354#endif /* _FFR_MAP_SYSLOG */
3355/*
3356**  HESIOD Modules
3357*/
3358
3359#ifdef HESIOD
3360
3361bool
3362hes_map_open(map, mode)
3363	MAP *map;
3364	int mode;
3365{
3366	if (tTd(38, 2))
3367		printf("hes_map_open(%s, %s, %d)\n",
3368			map->map_mname, map->map_file, mode);
3369
3370	if (mode != O_RDONLY)
3371	{
3372		/* issue a pseudo-error message */
3373#ifdef ENOSYS
3374		errno = ENOSYS;
3375#else
3376# ifdef EFTYPE
3377		errno = EFTYPE;
3378# else
3379		errno = ENXIO;
3380# endif
3381#endif
3382		return FALSE;
3383	}
3384
3385#ifdef HESIOD_INIT
3386	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
3387		return TRUE;
3388
3389	if (!bitset(MF_OPTIONAL, map->map_mflags))
3390		syserr("421 cannot initialize Hesiod map (%s)",
3391			errstring(errno));
3392	return FALSE;
3393#else
3394	if (hes_error() == HES_ER_UNINIT)
3395		hes_init();
3396	switch (hes_error())
3397	{
3398	  case HES_ER_OK:
3399	  case HES_ER_NOTFOUND:
3400		return TRUE;
3401	}
3402
3403	if (!bitset(MF_OPTIONAL, map->map_mflags))
3404		syserr("421 cannot initialize Hesiod map (%d)", hes_error());
3405
3406	return FALSE;
3407#endif /* HESIOD_INIT */
3408}
3409
3410char *
3411hes_map_lookup(map, name, av, statp)
3412	MAP *map;
3413	char *name;
3414	char **av;
3415	int *statp;
3416{
3417	char **hp;
3418
3419	if (tTd(38, 20))
3420		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
3421
3422	if (name[0] == '\\')
3423	{
3424		char *np;
3425		int nl;
3426		char nbuf[MAXNAME];
3427
3428		nl = strlen(name);
3429		if (nl < sizeof nbuf - 1)
3430			np = nbuf;
3431		else
3432			np = xalloc(strlen(name) + 2);
3433		np[0] = '\\';
3434		strcpy(&np[1], name);
3435#ifdef HESIOD_INIT
3436		hp = hesiod_resolve(HesiodContext, np, map->map_file);
3437#else
3438		hp = hes_resolve(np, map->map_file);
3439#endif /* HESIOD_INIT */
3440		if (np != nbuf)
3441			free(np);
3442	}
3443	else
3444	{
3445#ifdef HESIOD_INIT
3446		hp = hesiod_resolve(HesiodContext, name, map->map_file);
3447#else
3448		hp = hes_resolve(name, map->map_file);
3449#endif /* HESIOD_INIT */
3450	}
3451#ifdef HESIOD_INIT
3452	if (hp == NULL)
3453		return NULL;
3454	if (*hp == NULL)
3455	{
3456		hesiod_free_list(HesiodContext, hp);
3457		switch (errno)
3458		{
3459		  case ENOENT:
3460			  *statp = EX_NOTFOUND;
3461			  break;
3462		  case ECONNREFUSED:
3463		  case EMSGSIZE:
3464			  *statp = EX_TEMPFAIL;
3465			  break;
3466		  case ENOMEM:
3467		  default:
3468			  *statp = EX_UNAVAILABLE;
3469			  break;
3470		}
3471		return NULL;
3472	}
3473#else
3474	if (hp == NULL || hp[0] == NULL)
3475	{
3476		switch (hes_error())
3477		{
3478		  case HES_ER_OK:
3479			*statp = EX_OK;
3480			break;
3481
3482		  case HES_ER_NOTFOUND:
3483			*statp = EX_NOTFOUND;
3484			break;
3485
3486		  case HES_ER_CONFIG:
3487			*statp = EX_UNAVAILABLE;
3488			break;
3489
3490		  case HES_ER_NET:
3491			*statp = EX_TEMPFAIL;
3492			break;
3493		}
3494		return NULL;
3495	}
3496#endif /* HESIOD_INIT */
3497
3498	if (bitset(MF_MATCHONLY, map->map_mflags))
3499		return map_rewrite(map, name, strlen(name), NULL);
3500	else
3501		return map_rewrite(map, hp[0], strlen(hp[0]), av);
3502}
3503
3504#endif
3505/*
3506**  NeXT NETINFO Modules
3507*/
3508
3509#if NETINFO
3510
3511# define NETINFO_DEFAULT_DIR		"/aliases"
3512# define NETINFO_DEFAULT_PROPERTY	"members"
3513
3514extern char	*ni_propval __P((char *, char *, char *, char *, int));
3515
3516
3517/*
3518**  NI_MAP_OPEN -- open NetInfo Aliases
3519*/
3520
3521bool
3522ni_map_open(map, mode)
3523	MAP *map;
3524	int mode;
3525{
3526	if (tTd(38, 2))
3527		printf("ni_map_open(%s, %s, %d)\n",
3528			map->map_mname, map->map_file, mode);
3529	mode &= O_ACCMODE;
3530
3531	if (*map->map_file == '\0')
3532		map->map_file = NETINFO_DEFAULT_DIR;
3533
3534	if (map->map_valcolnm == NULL)
3535		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
3536
3537	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
3538		map->map_coldelim = ',';
3539
3540	return TRUE;
3541}
3542
3543
3544/*
3545**  NI_MAP_LOOKUP -- look up a datum in NetInfo
3546*/
3547
3548char *
3549ni_map_lookup(map, name, av, statp)
3550	MAP *map;
3551	char *name;
3552	char **av;
3553	int *statp;
3554{
3555	char *res;
3556	char *propval;
3557
3558	if (tTd(38, 20))
3559		printf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
3560
3561	propval = ni_propval(map->map_file, map->map_keycolnm, name,
3562			     map->map_valcolnm, map->map_coldelim);
3563
3564	if (propval == NULL)
3565		return NULL;
3566
3567	if (bitset(MF_MATCHONLY, map->map_mflags))
3568		res = map_rewrite(map, name, strlen(name), NULL);
3569	else
3570		res = map_rewrite(map, propval, strlen(propval), av);
3571	free(propval);
3572	return res;
3573}
3574
3575
3576bool
3577ni_getcanonname(name, hbsize, statp)
3578	char *name;
3579	int hbsize;
3580	int *statp;
3581{
3582	char *vptr;
3583	char *ptr;
3584	char nbuf[MAXNAME + 1];
3585
3586	if (tTd(38, 20))
3587		printf("ni_getcanonname(%s)\n", name);
3588
3589	if (strlen(name) >= sizeof nbuf)
3590	{
3591		*statp = EX_UNAVAILABLE;
3592		return FALSE;
3593	}
3594	(void) strcpy(nbuf, name);
3595	shorten_hostname(nbuf);
3596
3597	/* we only accept single token search key */
3598	if (strchr(nbuf, '.'))
3599	{
3600		*statp = EX_NOHOST;
3601		return FALSE;
3602	}
3603
3604	/* Do the search */
3605	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
3606
3607	if (vptr == NULL)
3608	{
3609		*statp = EX_NOHOST;
3610		return FALSE;
3611	}
3612
3613	/* Only want the first machine name */
3614	if ((ptr = strchr(vptr, '\n')) != NULL)
3615		*ptr = '\0';
3616
3617	if (hbsize >= strlen(vptr))
3618	{
3619		strcpy(name, vptr);
3620		*statp = EX_OK;
3621		return TRUE;
3622	}
3623	*statp = EX_UNAVAILABLE;
3624	free(vptr);
3625	return FALSE;
3626}
3627
3628
3629/*
3630**  NI_PROPVAL -- NetInfo property value lookup routine
3631**
3632**	Parameters:
3633**		keydir -- the NetInfo directory name in which to search
3634**			for the key.
3635**		keyprop -- the name of the property in which to find the
3636**			property we are interested.  Defaults to "name".
3637**		keyval -- the value for which we are really searching.
3638**		valprop -- the property name for the value in which we
3639**			are interested.
3640**		sepchar -- if non-nil, this can be multiple-valued, and
3641**			we should return a string separated by this
3642**			character.
3643**
3644**	Returns:
3645**		NULL -- if:
3646**			1. the directory is not found
3647**			2. the property name is not found
3648**			3. the property contains multiple values
3649**			4. some error occured
3650**		else -- the value of the lookup.
3651**
3652**	Example:
3653**		To search for an alias value, use:
3654**		  ni_propval("/aliases", "name", aliasname, "members", ',')
3655**
3656**	Notes:
3657**      	Caller should free the return value of ni_proval
3658*/
3659
3660# include <netinfo/ni.h>
3661
3662# define LOCAL_NETINFO_DOMAIN    "."
3663# define PARENT_NETINFO_DOMAIN   ".."
3664# define MAX_NI_LEVELS           256
3665
3666char *
3667ni_propval(keydir, keyprop, keyval, valprop, sepchar)
3668	char *keydir;
3669	char *keyprop;
3670	char *keyval;
3671	char *valprop;
3672	int sepchar;
3673{
3674	char *propval = NULL;
3675	int i;
3676	int j, alen;
3677	void *ni = NULL;
3678	void *lastni = NULL;
3679	ni_status nis;
3680	ni_id nid;
3681	ni_namelist ninl;
3682	register char *p;
3683	char keybuf[1024];
3684
3685	/*
3686	**  Create the full key from the two parts.
3687	**
3688	**	Note that directory can end with, e.g., "name=" to specify
3689	**	an alternate search property.
3690	*/
3691
3692	i = strlen(keydir) + strlen(keyval) + 2;
3693	if (keyprop != NULL)
3694		i += strlen(keyprop) + 1;
3695	if (i > sizeof keybuf)
3696		return NULL;
3697	strcpy(keybuf, keydir);
3698	strcat(keybuf, "/");
3699	if (keyprop != NULL)
3700	{
3701		strcat(keybuf, keyprop);
3702		strcat(keybuf, "=");
3703	}
3704	strcat(keybuf, keyval);
3705
3706	if (tTd(38, 21))
3707		printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
3708			keydir, keyprop, keyval, valprop, sepchar, keybuf);
3709	/*
3710	**  If the passed directory and property name are found
3711	**  in one of netinfo domains we need to search (starting
3712	**  from the local domain moving all the way back to the
3713	**  root domain) set propval to the property's value
3714	**  and return it.
3715	*/
3716
3717	for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
3718	{
3719		if (i == 0)
3720		{
3721			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
3722			if (tTd(38, 20))
3723				printf("ni_open(LOCAL) = %d\n", nis);
3724		}
3725		else
3726		{
3727			if (lastni != NULL)
3728				ni_free(lastni);
3729			lastni = ni;
3730			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
3731			if (tTd(38, 20))
3732				printf("ni_open(PARENT) = %d\n", nis);
3733		}
3734
3735		/*
3736		**  Don't bother if we didn't get a handle on a
3737		**  proper domain.  This is not necessarily an error.
3738		**  We would get a positive ni_status if, for instance
3739		**  we never found the directory or property and tried
3740		**  to open the parent of the root domain!
3741		*/
3742
3743		if (nis != 0)
3744			break;
3745
3746		/*
3747		**  Find the path to the server information.
3748		*/
3749
3750		if (ni_pathsearch(ni, &nid, keybuf) != 0)
3751			continue;
3752
3753		/*
3754		**  Find associated value information.
3755		*/
3756
3757		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
3758			continue;
3759
3760		if (tTd(38, 20))
3761			printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len);
3762		/*
3763		**  See if we have an acceptable number of values.
3764		*/
3765
3766		if (ninl.ni_namelist_len <= 0)
3767			continue;
3768
3769		if (sepchar == '\0' && ninl.ni_namelist_len > 1)
3770		{
3771			ni_namelist_free(&ninl);
3772			continue;
3773		}
3774
3775		/*
3776		**  Calculate number of bytes needed and build result
3777		*/
3778
3779		alen = 1;
3780		for (j = 0; j < ninl.ni_namelist_len; j++)
3781			alen += strlen(ninl.ni_namelist_val[j]) + 1;
3782		propval = p = xalloc(alen);
3783		for (j = 0; j < ninl.ni_namelist_len; j++)
3784		{
3785			strcpy(p, ninl.ni_namelist_val[j]);
3786			p += strlen(p);
3787			*p++ = sepchar;
3788		}
3789		*--p = '\0';
3790
3791		ni_namelist_free(&ninl);
3792	}
3793
3794	/*
3795	**  Clean up.
3796	*/
3797
3798	if (ni != NULL)
3799		ni_free(ni);
3800	if (lastni != NULL && ni != lastni)
3801		ni_free(lastni);
3802	if (tTd(38, 20))
3803		printf("ni_propval returns: '%s'\n", propval);
3804
3805	return propval;
3806}
3807
3808#endif /* NETINFO */
3809/*
3810**  TEXT (unindexed text file) Modules
3811**
3812**	This code donated by Sun Microsystems.
3813*/
3814
3815#define map_sff		map_lockfd	/* overload field */
3816
3817
3818/*
3819**  TEXT_MAP_OPEN -- open text table
3820*/
3821
3822bool
3823text_map_open(map, mode)
3824	MAP *map;
3825	int mode;
3826{
3827	int sff;
3828	int i;
3829
3830	if (tTd(38, 2))
3831		printf("text_map_open(%s, %s, %d)\n",
3832			map->map_mname, map->map_file, mode);
3833
3834	mode &= O_ACCMODE;
3835	if (mode != O_RDONLY)
3836	{
3837		errno = EPERM;
3838		return FALSE;
3839	}
3840
3841	if (*map->map_file == '\0')
3842	{
3843		syserr("text map \"%s\": file name required",
3844			map->map_mname);
3845		return FALSE;
3846	}
3847
3848	if (map->map_file[0] != '/')
3849	{
3850		syserr("text map \"%s\": file name must be fully qualified",
3851			map->map_mname);
3852		return FALSE;
3853	}
3854
3855	sff = SFF_ROOTOK|SFF_REGONLY;
3856	if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
3857		sff |= SFF_NOWLINK;
3858	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
3859		sff |= SFF_SAFEDIRPATH;
3860	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
3861			  sff, S_IRUSR, NULL)) != 0)
3862	{
3863		/* cannot open this map */
3864		if (tTd(38, 2))
3865			printf("\tunsafe map file: %d\n", i);
3866		if (!bitset(MF_OPTIONAL, map->map_mflags))
3867			syserr("text map \"%s\": unsafe map file %s",
3868				map->map_mname, map->map_file);
3869		return FALSE;
3870	}
3871
3872	if (map->map_keycolnm == NULL)
3873		map->map_keycolno = 0;
3874	else
3875	{
3876		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
3877		{
3878			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
3879				map->map_mname, map->map_file,
3880				map->map_keycolnm);
3881			return FALSE;
3882		}
3883		map->map_keycolno = atoi(map->map_keycolnm);
3884	}
3885
3886	if (map->map_valcolnm == NULL)
3887		map->map_valcolno = 0;
3888	else
3889	{
3890		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
3891		{
3892			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
3893					map->map_mname, map->map_file,
3894					map->map_valcolnm);
3895			return FALSE;
3896		}
3897		map->map_valcolno = atoi(map->map_valcolnm);
3898	}
3899
3900	if (tTd(38, 2))
3901	{
3902		printf("text_map_open(%s, %s): delimiter = ",
3903			map->map_mname, map->map_file);
3904		if (map->map_coldelim == '\0')
3905			printf("(white space)\n");
3906		else
3907			printf("%c\n", map->map_coldelim);
3908	}
3909
3910	map->map_sff = sff;
3911	return TRUE;
3912}
3913
3914
3915/*
3916**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
3917*/
3918
3919char *
3920text_map_lookup(map, name, av, statp)
3921	MAP *map;
3922	char *name;
3923	char **av;
3924	int *statp;
3925{
3926	char *vp;
3927	auto int vsize;
3928	int buflen;
3929	FILE *f;
3930	char delim;
3931	int key_idx;
3932	bool found_it;
3933	int sff = map->map_sff;
3934	char search_key[MAXNAME + 1];
3935	char linebuf[MAXLINE];
3936	char buf[MAXNAME + 1];
3937	extern char *get_column __P((char *, int, char, char *, int));
3938
3939	found_it = FALSE;
3940	if (tTd(38, 20))
3941		printf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
3942
3943	buflen = strlen(name);
3944	if (buflen > sizeof search_key - 1)
3945		buflen = sizeof search_key - 1;
3946	bcopy(name, search_key, buflen);
3947	search_key[buflen] = '\0';
3948	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3949		makelower(search_key);
3950
3951	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
3952	if (f == NULL)
3953	{
3954		map->map_mflags &= ~(MF_VALID|MF_OPEN);
3955		*statp = EX_UNAVAILABLE;
3956		return NULL;
3957	}
3958	key_idx = map->map_keycolno;
3959	delim = map->map_coldelim;
3960	while (fgets(linebuf, MAXLINE, f) != NULL)
3961	{
3962		char *p;
3963
3964		/* skip comment line */
3965		if (linebuf[0] == '#')
3966			continue;
3967		p = strchr(linebuf, '\n');
3968		if (p != NULL)
3969			*p = '\0';
3970		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
3971		if (p != NULL && strcasecmp(search_key, p) == 0)
3972		{
3973			found_it = TRUE;
3974			break;
3975		}
3976	}
3977	fclose(f);
3978	if (!found_it)
3979	{
3980		*statp = EX_NOTFOUND;
3981		return NULL;
3982	}
3983	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
3984	if (vp == NULL)
3985	{
3986		*statp = EX_NOTFOUND;
3987		return NULL;
3988	}
3989	vsize = strlen(vp);
3990	*statp = EX_OK;
3991	if (bitset(MF_MATCHONLY, map->map_mflags))
3992		return map_rewrite(map, name, strlen(name), NULL);
3993	else
3994		return map_rewrite(map, vp, vsize, av);
3995}
3996
3997
3998/*
3999**  TEXT_GETCANONNAME -- look up canonical name in hosts file
4000*/
4001
4002bool
4003text_getcanonname(name, hbsize, statp)
4004	char *name;
4005	int hbsize;
4006	int *statp;
4007{
4008	bool found;
4009	FILE *f;
4010	char linebuf[MAXLINE];
4011	char cbuf[MAXNAME + 1];
4012	char nbuf[MAXNAME + 1];
4013
4014	if (tTd(38, 20))
4015		printf("text_getcanonname(%s)\n", name);
4016
4017	if (strlen(name) >= (SIZE_T) sizeof nbuf)
4018	{
4019		*statp = EX_UNAVAILABLE;
4020		return FALSE;
4021	}
4022	(void) strcpy(nbuf, name);
4023	shorten_hostname(nbuf);
4024
4025	f = fopen(HostsFile, "r");
4026	if (f == NULL)
4027	{
4028		*statp = EX_UNAVAILABLE;
4029		return FALSE;
4030	}
4031	found = FALSE;
4032	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
4033	{
4034		char *p = strpbrk(linebuf, "#\n");
4035
4036		if (p != NULL)
4037			*p = '\0';
4038		if (linebuf[0] != '\0')
4039			found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
4040	}
4041	fclose(f);
4042	if (!found)
4043	{
4044		*statp = EX_NOHOST;
4045		return FALSE;
4046	}
4047
4048	if ((SIZE_T) hbsize >= strlen(cbuf))
4049	{
4050		strcpy(name, cbuf);
4051		*statp = EX_OK;
4052		return TRUE;
4053	}
4054	*statp = EX_UNAVAILABLE;
4055	return FALSE;
4056}
4057/*
4058**  STAB (Symbol Table) Modules
4059*/
4060
4061
4062/*
4063**  STAB_MAP_LOOKUP -- look up alias in symbol table
4064*/
4065
4066/* ARGSUSED2 */
4067char *
4068stab_map_lookup(map, name, av, pstat)
4069	register MAP *map;
4070	char *name;
4071	char **av;
4072	int *pstat;
4073{
4074	register STAB *s;
4075
4076	if (tTd(38, 20))
4077		printf("stab_lookup(%s, %s)\n",
4078			map->map_mname, name);
4079
4080	s = stab(name, ST_ALIAS, ST_FIND);
4081	if (s != NULL)
4082		return (s->s_alias);
4083	return (NULL);
4084}
4085
4086
4087/*
4088**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
4089*/
4090
4091void
4092stab_map_store(map, lhs, rhs)
4093	register MAP *map;
4094	char *lhs;
4095	char *rhs;
4096{
4097	register STAB *s;
4098
4099	s = stab(lhs, ST_ALIAS, ST_ENTER);
4100	s->s_alias = newstr(rhs);
4101}
4102
4103
4104/*
4105**  STAB_MAP_OPEN -- initialize (reads data file)
4106**
4107**	This is a wierd case -- it is only intended as a fallback for
4108**	aliases.  For this reason, opens for write (only during a
4109**	"newaliases") always fails, and opens for read open the
4110**	actual underlying text file instead of the database.
4111*/
4112
4113bool
4114stab_map_open(map, mode)
4115	register MAP *map;
4116	int mode;
4117{
4118	FILE *af;
4119	int sff;
4120	struct stat st;
4121
4122	if (tTd(38, 2))
4123		printf("stab_map_open(%s, %s, %d)\n",
4124			map->map_mname, map->map_file, mode);
4125
4126	mode &= O_ACCMODE;
4127	if (mode != O_RDONLY)
4128	{
4129		errno = EPERM;
4130		return FALSE;
4131	}
4132
4133	sff = SFF_ROOTOK|SFF_REGONLY;
4134	if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
4135		sff |= SFF_NOWLINK;
4136	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
4137		sff |= SFF_SAFEDIRPATH;
4138	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
4139	if (af == NULL)
4140		return FALSE;
4141	readaliases(map, af, FALSE, FALSE);
4142
4143	if (fstat(fileno(af), &st) >= 0)
4144		map->map_mtime = st.st_mtime;
4145	fclose(af);
4146
4147	return TRUE;
4148}
4149/*
4150**  Implicit Modules
4151**
4152**	Tries several types.  For back compatibility of aliases.
4153*/
4154
4155
4156/*
4157**  IMPL_MAP_LOOKUP -- lookup in best open database
4158*/
4159
4160char *
4161impl_map_lookup(map, name, av, pstat)
4162	MAP *map;
4163	char *name;
4164	char **av;
4165	int *pstat;
4166{
4167	if (tTd(38, 20))
4168		printf("impl_map_lookup(%s, %s)\n",
4169			map->map_mname, name);
4170
4171#ifdef NEWDB
4172	if (bitset(MF_IMPL_HASH, map->map_mflags))
4173		return db_map_lookup(map, name, av, pstat);
4174#endif
4175#ifdef NDBM
4176	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4177		return ndbm_map_lookup(map, name, av, pstat);
4178#endif
4179	return stab_map_lookup(map, name, av, pstat);
4180}
4181
4182/*
4183**  IMPL_MAP_STORE -- store in open databases
4184*/
4185
4186void
4187impl_map_store(map, lhs, rhs)
4188	MAP *map;
4189	char *lhs;
4190	char *rhs;
4191{
4192	if (tTd(38, 12))
4193		printf("impl_map_store(%s, %s, %s)\n",
4194			map->map_mname, lhs, rhs);
4195#ifdef NEWDB
4196	if (bitset(MF_IMPL_HASH, map->map_mflags))
4197		db_map_store(map, lhs, rhs);
4198#endif
4199#ifdef NDBM
4200	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4201		ndbm_map_store(map, lhs, rhs);
4202#endif
4203	stab_map_store(map, lhs, rhs);
4204}
4205
4206/*
4207**  IMPL_MAP_OPEN -- implicit database open
4208*/
4209
4210bool
4211impl_map_open(map, mode)
4212	MAP *map;
4213	int mode;
4214{
4215	if (tTd(38, 2))
4216		printf("impl_map_open(%s, %s, %d)\n",
4217			map->map_mname, map->map_file, mode);
4218
4219	mode &= O_ACCMODE;
4220#ifdef NEWDB
4221	map->map_mflags |= MF_IMPL_HASH;
4222	if (hash_map_open(map, mode))
4223	{
4224# ifdef NDBM_YP_COMPAT
4225		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
4226# endif
4227			return TRUE;
4228	}
4229	else
4230		map->map_mflags &= ~MF_IMPL_HASH;
4231#endif
4232#ifdef NDBM
4233	map->map_mflags |= MF_IMPL_NDBM;
4234	if (ndbm_map_open(map, mode))
4235	{
4236		return TRUE;
4237	}
4238	else
4239		map->map_mflags &= ~MF_IMPL_NDBM;
4240#endif
4241
4242#if defined(NEWDB) || defined(NDBM)
4243	if (Verbose)
4244		message("WARNING: cannot open alias database %s%s",
4245			map->map_file,
4246			mode == O_RDONLY ? "; reading text version" : "");
4247#else
4248	if (mode != O_RDONLY)
4249		usrerr("Cannot rebuild aliases: no database format defined");
4250#endif
4251
4252	if (mode == O_RDONLY)
4253		return stab_map_open(map, mode);
4254	else
4255		return FALSE;
4256}
4257
4258
4259/*
4260**  IMPL_MAP_CLOSE -- close any open database(s)
4261*/
4262
4263void
4264impl_map_close(map)
4265	MAP *map;
4266{
4267	if (tTd(38, 9))
4268		printf("impl_map_close(%s, %s, %lx)\n",
4269			map->map_mname, map->map_file, map->map_mflags);
4270#ifdef NEWDB
4271	if (bitset(MF_IMPL_HASH, map->map_mflags))
4272	{
4273		db_map_close(map);
4274		map->map_mflags &= ~MF_IMPL_HASH;
4275	}
4276#endif
4277
4278#ifdef NDBM
4279	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4280	{
4281		ndbm_map_close(map);
4282		map->map_mflags &= ~MF_IMPL_NDBM;
4283	}
4284#endif
4285}
4286/*
4287**  User map class.
4288**
4289**	Provides access to the system password file.
4290*/
4291
4292/*
4293**  USER_MAP_OPEN -- open user map
4294**
4295**	Really just binds field names to field numbers.
4296*/
4297
4298bool
4299user_map_open(map, mode)
4300	MAP *map;
4301	int mode;
4302{
4303	if (tTd(38, 2))
4304		printf("user_map_open(%s, %d)\n",
4305			map->map_mname, mode);
4306
4307	mode &= O_ACCMODE;
4308	if (mode != O_RDONLY)
4309	{
4310		/* issue a pseudo-error message */
4311#ifdef ENOSYS
4312		errno = ENOSYS;
4313#else
4314# ifdef EFTYPE
4315		errno = EFTYPE;
4316# else
4317		errno = ENXIO;
4318# endif
4319#endif
4320		return FALSE;
4321	}
4322	if (map->map_valcolnm == NULL)
4323		/* nothing */ ;
4324	else if (strcasecmp(map->map_valcolnm, "name") == 0)
4325		map->map_valcolno = 1;
4326	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
4327		map->map_valcolno = 2;
4328	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
4329		map->map_valcolno = 3;
4330	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
4331		map->map_valcolno = 4;
4332	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
4333		map->map_valcolno = 5;
4334	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
4335		map->map_valcolno = 6;
4336	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
4337		map->map_valcolno = 7;
4338	else
4339	{
4340		syserr("User map %s: unknown column name %s",
4341			map->map_mname, map->map_valcolnm);
4342		return FALSE;
4343	}
4344	return TRUE;
4345}
4346
4347
4348/*
4349**  USER_MAP_LOOKUP -- look up a user in the passwd file.
4350*/
4351
4352/* ARGSUSED3 */
4353char *
4354user_map_lookup(map, key, av, statp)
4355	MAP *map;
4356	char *key;
4357	char **av;
4358	int *statp;
4359{
4360	struct passwd *pw;
4361	auto bool fuzzy;
4362
4363	if (tTd(38, 20))
4364		printf("user_map_lookup(%s, %s)\n",
4365			map->map_mname, key);
4366
4367	pw = finduser(key, &fuzzy);
4368	if (pw == NULL)
4369		return NULL;
4370	if (bitset(MF_MATCHONLY, map->map_mflags))
4371		return map_rewrite(map, key, strlen(key), NULL);
4372	else
4373	{
4374		char *rwval = NULL;
4375		char buf[30];
4376
4377		switch (map->map_valcolno)
4378		{
4379		  case 0:
4380		  case 1:
4381			rwval = pw->pw_name;
4382			break;
4383
4384		  case 2:
4385			rwval = pw->pw_passwd;
4386			break;
4387
4388		  case 3:
4389			snprintf(buf, sizeof buf, "%d", pw->pw_uid);
4390			rwval = buf;
4391			break;
4392
4393		  case 4:
4394			snprintf(buf, sizeof buf, "%d", pw->pw_gid);
4395			rwval = buf;
4396			break;
4397
4398		  case 5:
4399			rwval = pw->pw_gecos;
4400			break;
4401
4402		  case 6:
4403			rwval = pw->pw_dir;
4404			break;
4405
4406		  case 7:
4407			rwval = pw->pw_shell;
4408			break;
4409		}
4410		return map_rewrite(map, rwval, strlen(rwval), av);
4411	}
4412}
4413/*
4414**  Program map type.
4415**
4416**	This provides access to arbitrary programs.  It should be used
4417**	only very sparingly, since there is no way to bound the cost
4418**	of invoking an arbitrary program.
4419*/
4420
4421char *
4422prog_map_lookup(map, name, av, statp)
4423	MAP *map;
4424	char *name;
4425	char **av;
4426	int *statp;
4427{
4428	int i;
4429	register char *p;
4430	int fd;
4431	auto pid_t pid;
4432	char *rval;
4433	int stat;
4434	char *argv[MAXPV + 1];
4435	char buf[MAXLINE];
4436
4437	if (tTd(38, 20))
4438		printf("prog_map_lookup(%s, %s) %s\n",
4439			map->map_mname, name, map->map_file);
4440
4441	i = 0;
4442	argv[i++] = map->map_file;
4443	if (map->map_rebuild != NULL)
4444	{
4445		snprintf(buf, sizeof buf, "%s", map->map_rebuild);
4446		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
4447		{
4448			if (i >= MAXPV - 1)
4449				break;
4450			argv[i++] = p;
4451		}
4452	}
4453	argv[i++] = name;
4454	argv[i] = NULL;
4455	if (tTd(38, 21))
4456	{
4457		printf("prog_open:");
4458		for (i = 0; argv[i] != NULL; i++)
4459			printf(" %s", argv[i]);
4460		printf("\n");
4461	}
4462	(void) blocksignal(SIGCHLD);
4463	pid = prog_open(argv, &fd, CurEnv);
4464	if (pid < 0)
4465	{
4466		if (!bitset(MF_OPTIONAL, map->map_mflags))
4467			syserr("prog_map_lookup(%s) failed (%s) -- closing",
4468				map->map_mname, errstring(errno));
4469		else if (tTd(38, 9))
4470			printf("prog_map_lookup(%s) failed (%s) -- closing",
4471				map->map_mname, errstring(errno));
4472		map->map_mflags &= ~(MF_VALID|MF_OPEN);
4473		*statp = EX_OSFILE;
4474		return NULL;
4475	}
4476	i = read(fd, buf, sizeof buf - 1);
4477	if (i < 0)
4478	{
4479		syserr("prog_map_lookup(%s): read error %s\n",
4480			map->map_mname, errstring(errno));
4481		rval = NULL;
4482	}
4483	else if (i == 0)
4484	{
4485		if (tTd(38, 20))
4486			printf("prog_map_lookup(%s): empty answer\n",
4487				map->map_mname);
4488		rval = NULL;
4489	}
4490	else
4491	{
4492		buf[i] = '\0';
4493		p = strchr(buf, '\n');
4494		if (p != NULL)
4495			*p = '\0';
4496
4497		/* collect the return value */
4498		if (bitset(MF_MATCHONLY, map->map_mflags))
4499			rval = map_rewrite(map, name, strlen(name), NULL);
4500		else
4501			rval = map_rewrite(map, buf, strlen(buf), NULL);
4502
4503		/* now flush any additional output */
4504		while ((i = read(fd, buf, sizeof buf)) > 0)
4505			continue;
4506	}
4507
4508	/* wait for the process to terminate */
4509	close(fd);
4510	stat = waitfor(pid);
4511	(void) releasesignal(SIGCHLD);
4512
4513	if (stat == -1)
4514	{
4515		syserr("prog_map_lookup(%s): wait error %s\n",
4516			map->map_mname, errstring(errno));
4517		*statp = EX_SOFTWARE;
4518		rval = NULL;
4519	}
4520	else if (WIFEXITED(stat))
4521	{
4522		if ((*statp = WEXITSTATUS(stat)) != EX_OK)
4523			rval = NULL;
4524	}
4525	else
4526	{
4527		syserr("prog_map_lookup(%s): child died on signal %d",
4528			map->map_mname, stat);
4529		*statp = EX_UNAVAILABLE;
4530		rval = NULL;
4531	}
4532	return rval;
4533}
4534/*
4535**  Sequenced map type.
4536**
4537**	Tries each map in order until something matches, much like
4538**	implicit.  Stores go to the first map in the list that can
4539**	support storing.
4540**
4541**	This is slightly unusual in that there are two interfaces.
4542**	The "sequence" interface lets you stack maps arbitrarily.
4543**	The "switch" interface builds a sequence map by looking
4544**	at a system-dependent configuration file such as
4545**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
4546**
4547**	We don't need an explicit open, since all maps are
4548**	opened during startup, including underlying maps.
4549*/
4550
4551/*
4552**  SEQ_MAP_PARSE -- Sequenced map parsing
4553*/
4554
4555bool
4556seq_map_parse(map, ap)
4557	MAP *map;
4558	char *ap;
4559{
4560	int maxmap;
4561
4562	if (tTd(38, 2))
4563		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
4564	maxmap = 0;
4565	while (*ap != '\0')
4566	{
4567		register char *p;
4568		STAB *s;
4569
4570		/* find beginning of map name */
4571		while (isascii(*ap) && isspace(*ap))
4572			ap++;
4573		for (p = ap; isascii(*p) && isalnum(*p); p++)
4574			continue;
4575		if (*p != '\0')
4576			*p++ = '\0';
4577		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
4578			p++;
4579		if (*ap == '\0')
4580		{
4581			ap = p;
4582			continue;
4583		}
4584		s = stab(ap, ST_MAP, ST_FIND);
4585		if (s == NULL)
4586		{
4587			syserr("Sequence map %s: unknown member map %s",
4588				map->map_mname, ap);
4589		}
4590		else if (maxmap == MAXMAPSTACK)
4591		{
4592			syserr("Sequence map %s: too many member maps (%d max)",
4593				map->map_mname, MAXMAPSTACK);
4594			maxmap++;
4595		}
4596		else if (maxmap < MAXMAPSTACK)
4597		{
4598			map->map_stack[maxmap++] = &s->s_map;
4599		}
4600		ap = p;
4601	}
4602	return TRUE;
4603}
4604
4605
4606/*
4607**  SWITCH_MAP_OPEN -- open a switched map
4608**
4609**	This looks at the system-dependent configuration and builds
4610**	a sequence map that does the same thing.
4611**
4612**	Every system must define a switch_map_find routine in conf.c
4613**	that will return the list of service types associated with a
4614**	given service class.
4615*/
4616
4617bool
4618switch_map_open(map, mode)
4619	MAP *map;
4620	int mode;
4621{
4622	int mapno;
4623	int nmaps;
4624	char *maptype[MAXMAPSTACK];
4625
4626	if (tTd(38, 2))
4627		printf("switch_map_open(%s, %s, %d)\n",
4628			map->map_mname, map->map_file, mode);
4629
4630	mode &= O_ACCMODE;
4631	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
4632	if (tTd(38, 19))
4633	{
4634		printf("\tswitch_map_find => %d\n", nmaps);
4635		for (mapno = 0; mapno < nmaps; mapno++)
4636			printf("\t\t%s\n", maptype[mapno]);
4637	}
4638	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
4639		return FALSE;
4640
4641	for (mapno = 0; mapno < nmaps; mapno++)
4642	{
4643		register STAB *s;
4644		char nbuf[MAXNAME + 1];
4645
4646		if (maptype[mapno] == NULL)
4647			continue;
4648		(void) snprintf(nbuf, sizeof nbuf, "%s.%s",
4649			map->map_mname, maptype[mapno]);
4650		s = stab(nbuf, ST_MAP, ST_FIND);
4651		if (s == NULL)
4652		{
4653			syserr("Switch map %s: unknown member map %s",
4654				map->map_mname, nbuf);
4655		}
4656		else
4657		{
4658			map->map_stack[mapno] = &s->s_map;
4659			if (tTd(38, 4))
4660				printf("\tmap_stack[%d] = %s:%s\n",
4661					mapno, s->s_map.map_class->map_cname,
4662					nbuf);
4663		}
4664	}
4665	return TRUE;
4666}
4667
4668
4669/*
4670**  SEQ_MAP_CLOSE -- close all underlying maps
4671*/
4672
4673void
4674seq_map_close(map)
4675	MAP *map;
4676{
4677	int mapno;
4678
4679	if (tTd(38, 9))
4680		printf("seq_map_close(%s)\n", map->map_mname);
4681
4682	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4683	{
4684		MAP *mm = map->map_stack[mapno];
4685
4686		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
4687			continue;
4688		mm->map_class->map_close(mm);
4689		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4690	}
4691}
4692
4693
4694/*
4695**  SEQ_MAP_LOOKUP -- sequenced map lookup
4696*/
4697
4698char *
4699seq_map_lookup(map, key, args, pstat)
4700	MAP *map;
4701	char *key;
4702	char **args;
4703	int *pstat;
4704{
4705	int mapno;
4706	int mapbit = 0x01;
4707	bool tempfail = FALSE;
4708
4709	if (tTd(38, 20))
4710		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
4711
4712	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
4713	{
4714		MAP *mm = map->map_stack[mapno];
4715		char *rv;
4716
4717		if (mm == NULL)
4718			continue;
4719		if (!bitset(MF_OPEN, mm->map_mflags))
4720		{
4721			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
4722			{
4723				*pstat = EX_UNAVAILABLE;
4724				return NULL;
4725			}
4726			continue;
4727		}
4728		*pstat = EX_OK;
4729		rv = mm->map_class->map_lookup(mm, key, args, pstat);
4730		if (rv != NULL)
4731			return rv;
4732		if (*pstat == EX_TEMPFAIL)
4733		{
4734			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
4735				return NULL;
4736			tempfail = TRUE;
4737		}
4738		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
4739			break;
4740	}
4741	if (tempfail)
4742		*pstat = EX_TEMPFAIL;
4743	else if (*pstat == EX_OK)
4744		*pstat = EX_NOTFOUND;
4745	return NULL;
4746}
4747
4748
4749/*
4750**  SEQ_MAP_STORE -- sequenced map store
4751*/
4752
4753void
4754seq_map_store(map, key, val)
4755	MAP *map;
4756	char *key;
4757	char *val;
4758{
4759	int mapno;
4760
4761	if (tTd(38, 12))
4762		printf("seq_map_store(%s, %s, %s)\n",
4763			map->map_mname, key, val);
4764
4765	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4766	{
4767		MAP *mm = map->map_stack[mapno];
4768
4769		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
4770			continue;
4771
4772		mm->map_class->map_store(mm, key, val);
4773		return;
4774	}
4775	syserr("seq_map_store(%s, %s, %s): no writable map",
4776		map->map_mname, key, val);
4777}
4778/*
4779**  NULL stubs
4780*/
4781
4782/* ARGSUSED */
4783bool
4784null_map_open(map, mode)
4785	MAP *map;
4786	int mode;
4787{
4788	return TRUE;
4789}
4790
4791/* ARGSUSED */
4792void
4793null_map_close(map)
4794	MAP *map;
4795{
4796	return;
4797}
4798
4799char *
4800null_map_lookup(map, key, args, pstat)
4801	MAP *map;
4802	char *key;
4803	char **args;
4804	int *pstat;
4805{
4806	*pstat = EX_NOTFOUND;
4807	return NULL;
4808}
4809
4810/* ARGSUSED */
4811void
4812null_map_store(map, key, val)
4813	MAP *map;
4814	char *key;
4815	char *val;
4816{
4817	return;
4818}
4819
4820
4821/*
4822**  BOGUS stubs
4823*/
4824
4825char *
4826bogus_map_lookup(map, key, args, pstat)
4827	MAP *map;
4828	char *key;
4829	char **args;
4830	int *pstat;
4831{
4832	*pstat = EX_TEMPFAIL;
4833	return NULL;
4834}
4835
4836MAPCLASS	BogusMapClass =
4837{
4838	"bogus-map",		NULL,		0,
4839	NULL,		bogus_map_lookup,	null_map_store,
4840	null_map_open,	null_map_close,
4841};
4842/*
4843**  REGEX modules
4844*/
4845
4846#ifdef MAP_REGEX
4847
4848# include <regex.h>
4849
4850# define DEFAULT_DELIM	CONDELSE
4851
4852# define END_OF_FIELDS	-1
4853
4854# define ERRBUF_SIZE	80
4855# define MAX_MATCH	32
4856
4857# define xnalloc(s)	memset(xalloc(s), 0, s);
4858
4859struct regex_map
4860{
4861	regex_t	pattern_buf;		/* xalloc it */
4862	int	*regex_subfields;	/* move to type MAP */
4863	char	*delim;			/* move to type MAP */
4864};
4865
4866static int
4867parse_fields(s, ibuf, blen, nr_substrings)
4868	char *s;
4869	int *ibuf;		/* array */
4870	int blen;		/* number of elements in ibuf */
4871	int nr_substrings;	/* number of substrings in the pattern */
4872{
4873	register char *cp;
4874	int i = 0;
4875	bool lastone = FALSE;
4876
4877	blen--;		/* for terminating END_OF_FIELDS */
4878	cp = s;
4879	do
4880	{
4881		for (;; cp++)
4882		{
4883			if (*cp == ',')
4884			{
4885				*cp = '\0';
4886				break;
4887			}
4888			if (*cp == '\0')
4889			{
4890				lastone = TRUE;
4891				break;
4892			}
4893		}
4894		if (i < blen)
4895		{
4896			int val = atoi(s);
4897
4898			if (val < 0 || val >= nr_substrings)
4899			{
4900				syserr("field (%d) out of range, only %d substrings in pattern",
4901				       val, nr_substrings);
4902				return -1;
4903			}
4904			ibuf[i++] = val;
4905		}
4906		else
4907		{
4908			syserr("too many fields, %d max\n", blen);
4909			return -1;
4910		}
4911		s = ++cp;
4912	} while (!lastone);
4913	ibuf[i] = END_OF_FIELDS;
4914	return i;
4915}
4916
4917bool
4918regex_map_init(map, ap)
4919	MAP *map;
4920	char *ap;
4921{
4922	int regerr;
4923	struct regex_map *map_p;
4924	register char *p;
4925	char *sub_param = NULL;
4926	int pflags;
4927	static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
4928
4929	if (tTd(38, 2))
4930		printf("regex_map_init: mapname '%s', args '%s'\n",
4931				map->map_mname, ap);
4932
4933	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
4934
4935	p = ap;
4936
4937	map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map));
4938
4939	for (;;)
4940        {
4941		while (isascii(*p) && isspace(*p))
4942			p++;
4943		if (*p != '-')
4944			break;
4945		switch (*++p)
4946		{
4947		  case 'n':	/* not */
4948			map->map_mflags |= MF_REGEX_NOT;
4949			break;
4950
4951		  case 'f':	/* case sensitive */
4952			map->map_mflags |= MF_NOFOLDCASE;
4953			pflags &= ~REG_ICASE;
4954			break;
4955
4956		  case 'b':	/* basic regular expressions */
4957			pflags &= ~REG_EXTENDED;
4958			break;
4959
4960		  case 's':	/* substring match () syntax */
4961			sub_param = ++p;
4962			pflags &= ~REG_NOSUB;
4963			break;
4964
4965		  case 'd':	/* delimiter */
4966			map_p->delim = ++p;
4967			break;
4968
4969		  case 'a':	/* map append */
4970			map->map_app = ++p;
4971			break;
4972
4973		  case 'm':	/* matchonly */
4974			map->map_mflags |= MF_MATCHONLY;
4975			break;
4976
4977		}
4978                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4979                        p++;
4980                if (*p != '\0')
4981                        *p++ = '\0';
4982	}
4983	if (tTd(38, 3))
4984		printf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
4985
4986	if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0)
4987	{
4988		/* Errorhandling */
4989		char errbuf[ERRBUF_SIZE];
4990
4991		regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE);
4992		syserr("pattern-compile-error: %s\n", errbuf);
4993		free(map_p);
4994		return FALSE;
4995	}
4996
4997	if (map->map_app != NULL)
4998		map->map_app = newstr(map->map_app);
4999	if (map_p->delim != NULL)
5000		map_p->delim = newstr(map_p->delim);
5001	else
5002		map_p->delim = defdstr;
5003
5004	if (!bitset(REG_NOSUB, pflags))
5005	{
5006		/* substring matching */
5007		int substrings;
5008		int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1));
5009
5010		substrings = map_p->pattern_buf.re_nsub + 1;
5011
5012		if (tTd(38, 3))
5013			printf("regex_map_init: nr of substrings %d\n", substrings);
5014
5015		if (substrings >= MAX_MATCH)
5016		{
5017			syserr("too many substrings, %d max\n", MAX_MATCH);
5018			free(map_p);
5019			return FALSE;
5020		}
5021		if (sub_param != NULL && sub_param[0] != '\0')
5022		{
5023			/* optional parameter -sfields */
5024			if (parse_fields(sub_param, fields,
5025					 MAX_MATCH + 1, substrings) == -1)
5026				return FALSE;
5027		}
5028		else
5029		{
5030			/* set default fields  */
5031			int i;
5032
5033			for (i = 0; i < substrings; i++)
5034				fields[i] = i;
5035			fields[i] = END_OF_FIELDS;
5036		}
5037		map_p->regex_subfields = fields;
5038		if (tTd(38, 3))
5039		{
5040			int *ip;
5041
5042			printf("regex_map_init: subfields");
5043			for (ip = fields; *ip != END_OF_FIELDS; ip++)
5044				printf(" %d", *ip);
5045			printf("\n");
5046		}
5047	}
5048	map->map_db1 = (ARBPTR_T)map_p;	/* dirty hack */
5049
5050	return TRUE;
5051}
5052
5053static char *
5054regex_map_rewrite(map, s, slen, av)
5055	MAP *map;
5056	const char *s;
5057	size_t slen;
5058	char **av;
5059{
5060	if (bitset(MF_MATCHONLY, map->map_mflags))
5061		return map_rewrite(map, av[0], strlen(av[0]), NULL);
5062	else
5063		return map_rewrite(map, s, slen, NULL);
5064}
5065
5066char *
5067regex_map_lookup(map, name, av, statp)
5068	MAP *map;
5069	char *name;
5070	char **av;
5071	int *statp;
5072{
5073	int reg_res;
5074	struct regex_map *map_p;
5075	regmatch_t pmatch[MAX_MATCH];
5076
5077	if (tTd(38, 20))
5078	{
5079		char **cpp;
5080
5081		printf("regex_map_lookup: key '%s'\n", name);
5082		for (cpp = av; cpp && *cpp; cpp++)
5083			printf("regex_map_lookup: arg '%s'\n", *cpp);
5084	}
5085
5086	map_p = (struct regex_map *)(map->map_db1);
5087	reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0);
5088
5089	if (bitset(MF_REGEX_NOT, map->map_mflags))
5090	{
5091		/* option -n */
5092		if (reg_res == REG_NOMATCH)
5093			return regex_map_rewrite(map, "", (size_t)0, av);
5094		else
5095			return NULL;
5096	}
5097	if (reg_res == REG_NOMATCH)
5098		return NULL;
5099
5100	if (map_p->regex_subfields != NULL)
5101	{
5102		/* option -s */
5103		static char retbuf[MAXNAME];
5104		int fields[MAX_MATCH + 1];
5105		bool first = TRUE;
5106		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
5107		bool quotemode = FALSE, bslashmode = FALSE;
5108		register char *dp, *sp;
5109		char *endp, *ldp;
5110		int *ip;
5111
5112		dp = retbuf;
5113		ldp = retbuf + sizeof(retbuf) - 1;
5114
5115		if (av[1] != NULL)
5116		{
5117			if (parse_fields(av[1], fields, MAX_MATCH + 1,
5118				 (int) map_p->pattern_buf.re_nsub + 1) == -1)
5119			{
5120				*statp = EX_CONFIG;
5121				return NULL;
5122			}
5123			ip = fields;
5124		}
5125		else
5126			ip = map_p->regex_subfields;
5127
5128		for ( ; *ip != END_OF_FIELDS; ip++)
5129		{
5130			if (!first)
5131			{
5132				for (sp = map_p->delim; *sp; sp++)
5133				{
5134					if (dp < ldp)
5135						*dp++ = *sp;
5136				}
5137			}
5138			else
5139				first = FALSE;
5140
5141
5142			if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
5143				continue;
5144
5145			sp = name + pmatch[*ip].rm_so;
5146			endp = name + pmatch[*ip].rm_eo;
5147			for (; endp > sp; sp++)
5148			{
5149				if (dp < ldp)
5150				{
5151					if(bslashmode)
5152					{
5153						*dp++ = *sp;
5154						bslashmode = FALSE;
5155					}
5156					else if(quotemode && *sp != '"' &&
5157						*sp != '\\')
5158					{
5159						*dp++ = *sp;
5160					}
5161					else switch(*dp++ = *sp)
5162					{
5163						case '\\':
5164						bslashmode = TRUE;
5165						break;
5166
5167						case '(':
5168						cmntcnt++;
5169						break;
5170
5171						case ')':
5172						cmntcnt--;
5173						break;
5174
5175						case '<':
5176						anglecnt++;
5177						break;
5178
5179						case '>':
5180						anglecnt--;
5181						break;
5182
5183						case ' ':
5184						spacecnt++;
5185						break;
5186
5187						case '"':
5188						quotemode = !quotemode;
5189						break;
5190					}
5191				}
5192			}
5193		}
5194		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
5195		    bslashmode || spacecnt != 0)
5196		{
5197			sm_syslog(LOG_WARNING, NOQID,
5198				 "Warning: regex may cause prescan() failure map=%s lookup=%s",
5199				 map->map_mname, name);
5200			return NULL;
5201		}
5202
5203		*dp = '\0';
5204
5205		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
5206	}
5207	return regex_map_rewrite(map, "", (size_t)0, av);
5208}
5209#endif /* MAP_REGEX */
5210