map.c revision 42575
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.256 (Berkeley) 11/15/1998";
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	**  If this map was not opened by the current
1882	**  process, do not close it here but recover
1883	**  the file descriptor.
1884	*/
1885	if (map->map_pid != getpid())
1886	{
1887		int fd = -1;
1888
1889		errno = db->fd(db, &fd);
1890		if (fd >= 0)
1891			(void) close(fd);
1892		return;
1893	}
1894
1895	if ((errno = db->close(db, 0)) != 0)
1896#endif
1897		syserr("db_map_close(%s, %s, %lx): db close failure",
1898			map->map_mname, map->map_file, map->map_mflags);
1899}
1900
1901#endif
1902/*
1903**  NIS Modules
1904*/
1905
1906# ifdef NIS
1907
1908# ifndef YPERR_BUSY
1909#  define YPERR_BUSY	16
1910# endif
1911
1912/*
1913**  NIS_MAP_OPEN -- open DBM map
1914*/
1915
1916bool
1917nis_map_open(map, mode)
1918	MAP *map;
1919	int mode;
1920{
1921	int yperr;
1922	register char *p;
1923	auto char *vp;
1924	auto int vsize;
1925
1926	if (tTd(38, 2))
1927		printf("nis_map_open(%s, %s, %d)\n",
1928			map->map_mname, map->map_file, mode);
1929
1930	mode &= O_ACCMODE;
1931	if (mode != O_RDONLY)
1932	{
1933		/* issue a pseudo-error message */
1934#ifdef ENOSYS
1935		errno = ENOSYS;
1936#else
1937# ifdef EFTYPE
1938		errno = EFTYPE;
1939# else
1940		errno = ENXIO;
1941# endif
1942#endif
1943		return FALSE;
1944	}
1945
1946	p = strchr(map->map_file, '@');
1947	if (p != NULL)
1948	{
1949		*p++ = '\0';
1950		if (*p != '\0')
1951			map->map_domain = p;
1952	}
1953
1954	if (*map->map_file == '\0')
1955		map->map_file = "mail.aliases";
1956
1957	if (map->map_domain == NULL)
1958	{
1959		yperr = yp_get_default_domain(&map->map_domain);
1960		if (yperr != 0)
1961		{
1962			if (!bitset(MF_OPTIONAL, map->map_mflags))
1963				syserr("421 NIS map %s specified, but NIS not running",
1964					map->map_file);
1965			return FALSE;
1966		}
1967	}
1968
1969	/* check to see if this map actually exists */
1970	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1971			&vp, &vsize);
1972	if (tTd(38, 10))
1973		printf("nis_map_open: yp_match(@, %s, %s) => %s\n",
1974			map->map_domain, map->map_file, yperr_string(yperr));
1975	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1976	{
1977		/*
1978		**  We ought to be calling aliaswait() here if this is an
1979		**  alias file, but powerful HP-UX NIS servers  apparently
1980		**  don't insert the @:@ token into the alias map when it
1981		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
1982		*/
1983
1984#if 0
1985		if (!bitset(MF_ALIAS, map->map_mflags) ||
1986		    aliaswait(map, NULL, TRUE))
1987#endif
1988			return TRUE;
1989	}
1990
1991	if (!bitset(MF_OPTIONAL, map->map_mflags))
1992	{
1993		syserr("421 Cannot bind to map %s in domain %s: %s",
1994			map->map_file, map->map_domain, yperr_string(yperr));
1995	}
1996
1997	return FALSE;
1998}
1999
2000
2001/*
2002**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2003*/
2004
2005/* ARGSUSED3 */
2006char *
2007nis_map_lookup(map, name, av, statp)
2008	MAP *map;
2009	char *name;
2010	char **av;
2011	int *statp;
2012{
2013	char *vp;
2014	auto int vsize;
2015	int buflen;
2016	int yperr;
2017	char keybuf[MAXNAME + 1];
2018
2019	if (tTd(38, 20))
2020		printf("nis_map_lookup(%s, %s)\n",
2021			map->map_mname, name);
2022
2023	buflen = strlen(name);
2024	if (buflen > sizeof keybuf - 1)
2025		buflen = sizeof keybuf - 1;
2026	bcopy(name, keybuf, buflen);
2027	keybuf[buflen] = '\0';
2028	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2029		makelower(keybuf);
2030	yperr = YPERR_KEY;
2031	if (bitset(MF_TRY0NULL, map->map_mflags))
2032	{
2033		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2034			     &vp, &vsize);
2035		if (yperr == 0)
2036			map->map_mflags &= ~MF_TRY1NULL;
2037	}
2038	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2039	{
2040		buflen++;
2041		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2042			     &vp, &vsize);
2043		if (yperr == 0)
2044			map->map_mflags &= ~MF_TRY0NULL;
2045	}
2046	if (yperr != 0)
2047	{
2048		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2049			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2050		return NULL;
2051	}
2052	if (bitset(MF_MATCHONLY, map->map_mflags))
2053		return map_rewrite(map, name, strlen(name), NULL);
2054	else
2055		return map_rewrite(map, vp, vsize, av);
2056}
2057
2058
2059/*
2060**  NIS_GETCANONNAME -- look up canonical name in NIS
2061*/
2062
2063bool
2064nis_getcanonname(name, hbsize, statp)
2065	char *name;
2066	int hbsize;
2067	int *statp;
2068{
2069	char *vp;
2070	auto int vsize;
2071	int keylen;
2072	int yperr;
2073	static bool try0null = TRUE;
2074	static bool try1null = TRUE;
2075	static char *yp_domain = NULL;
2076	char host_record[MAXLINE];
2077	char cbuf[MAXNAME];
2078	char nbuf[MAXNAME + 1];
2079
2080	if (tTd(38, 20))
2081		printf("nis_getcanonname(%s)\n", name);
2082
2083	if (strlen(name) >= sizeof nbuf)
2084	{
2085		*statp = EX_UNAVAILABLE;
2086		return FALSE;
2087	}
2088	(void) strcpy(nbuf, name);
2089	shorten_hostname(nbuf);
2090	keylen = strlen(nbuf);
2091
2092	if (yp_domain == NULL)
2093		yp_get_default_domain(&yp_domain);
2094	makelower(nbuf);
2095	yperr = YPERR_KEY;
2096	if (try0null)
2097	{
2098		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2099			     &vp, &vsize);
2100		if (yperr == 0)
2101			try1null = FALSE;
2102	}
2103	if (yperr == YPERR_KEY && try1null)
2104	{
2105		keylen++;
2106		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2107			     &vp, &vsize);
2108		if (yperr == 0)
2109			try0null = FALSE;
2110	}
2111	if (yperr != 0)
2112	{
2113		if (yperr == YPERR_KEY)
2114			*statp = EX_NOHOST;
2115		else if (yperr == YPERR_BUSY)
2116			*statp = EX_TEMPFAIL;
2117		else
2118			*statp = EX_UNAVAILABLE;
2119		return FALSE;
2120	}
2121	if (vsize >= sizeof host_record)
2122		vsize = sizeof host_record - 1;
2123	strncpy(host_record, vp, vsize);
2124	host_record[vsize] = '\0';
2125	if (tTd(38, 44))
2126		printf("got record `%s'\n", host_record);
2127	if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
2128	{
2129		/* this should not happen, but.... */
2130		*statp = EX_NOHOST;
2131		return FALSE;
2132	}
2133	if (hbsize < strlen(cbuf))
2134	{
2135		*statp = EX_UNAVAILABLE;
2136		return FALSE;
2137	}
2138	strcpy(name, cbuf);
2139	*statp = EX_OK;
2140	return TRUE;
2141}
2142
2143#endif
2144/*
2145**  NISPLUS Modules
2146**
2147**	This code donated by Sun Microsystems.
2148*/
2149
2150#ifdef NISPLUS
2151
2152#undef NIS		/* symbol conflict in nis.h */
2153#undef T_UNSPEC		/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2154#include <rpcsvc/nis.h>
2155#include <rpcsvc/nislib.h>
2156
2157#define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2158#define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2159#define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2160#define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
2161
2162/*
2163**  NISPLUS_MAP_OPEN -- open nisplus table
2164*/
2165
2166bool
2167nisplus_map_open(map, mode)
2168	MAP *map;
2169	int mode;
2170{
2171	nis_result *res = NULL;
2172	int retry_cnt, max_col, i;
2173	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2174
2175	if (tTd(38, 2))
2176		printf("nisplus_map_open(%s, %s, %d)\n",
2177			map->map_mname, map->map_file, mode);
2178
2179	mode &= O_ACCMODE;
2180	if (mode != O_RDONLY)
2181	{
2182		errno = EPERM;
2183		return FALSE;
2184	}
2185
2186	if (*map->map_file == '\0')
2187		map->map_file = "mail_aliases.org_dir";
2188
2189	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2190	{
2191		/* set default NISPLUS Domain to $m */
2192		extern char *nisplus_default_domain __P((void));
2193
2194		map->map_domain = newstr(nisplus_default_domain());
2195		if (tTd(38, 2))
2196			printf("nisplus_map_open(%s): using domain %s\n",
2197				 map->map_file, map->map_domain);
2198	}
2199	if (!PARTIAL_NAME(map->map_file))
2200	{
2201		map->map_domain = newstr("");
2202		snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
2203	}
2204	else
2205	{
2206		/* check to see if this map actually exists */
2207		snprintf(qbuf, sizeof qbuf, "%s.%s",
2208			map->map_file, map->map_domain);
2209	}
2210
2211	retry_cnt = 0;
2212	while (res == NULL || res->status != NIS_SUCCESS)
2213	{
2214		res = nis_lookup(qbuf, FOLLOW_LINKS);
2215		switch (res->status)
2216		{
2217		  case NIS_SUCCESS:
2218			break;
2219
2220		  case NIS_TRYAGAIN:
2221		  case NIS_RPCERROR:
2222		  case NIS_NAMEUNREACHABLE:
2223			if (retry_cnt++ > 4)
2224			{
2225				errno = EAGAIN;
2226				return FALSE;
2227			}
2228			/* try not to overwhelm hosed server */
2229			sleep(2);
2230			break;
2231
2232		  default:		/* all other nisplus errors */
2233#if 0
2234			if (!bitset(MF_OPTIONAL, map->map_mflags))
2235				syserr("421 Cannot find table %s.%s: %s",
2236					map->map_file, map->map_domain,
2237					nis_sperrno(res->status));
2238#endif
2239			errno = EAGAIN;
2240			return FALSE;
2241		}
2242	}
2243
2244	if (NIS_RES_NUMOBJ(res) != 1 ||
2245	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2246	{
2247		if (tTd(38, 10))
2248			printf("nisplus_map_open: %s is not a table\n", qbuf);
2249#if 0
2250		if (!bitset(MF_OPTIONAL, map->map_mflags))
2251			syserr("421 %s.%s: %s is not a table",
2252				map->map_file, map->map_domain,
2253				nis_sperrno(res->status));
2254#endif
2255		errno = EBADF;
2256		return FALSE;
2257	}
2258	/* default key column is column 0 */
2259	if (map->map_keycolnm == NULL)
2260		map->map_keycolnm = newstr(COL_NAME(res,0));
2261
2262	max_col = COL_MAX(res);
2263
2264	/* verify the key column exist */
2265	for (i=0; i< max_col; i++)
2266	{
2267		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
2268			break;
2269	}
2270	if (i == max_col)
2271	{
2272		if (tTd(38, 2))
2273			printf("nisplus_map_open(%s): can not find key column %s\n",
2274				map->map_file, map->map_keycolnm);
2275		errno = ENOENT;
2276		return FALSE;
2277	}
2278
2279	/* default value column is the last column */
2280	if (map->map_valcolnm == NULL)
2281	{
2282		map->map_valcolno = max_col - 1;
2283		return TRUE;
2284	}
2285
2286	for (i=0; i< max_col; i++)
2287	{
2288		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2289		{
2290			map->map_valcolno = i;
2291			return TRUE;
2292		}
2293	}
2294
2295	if (tTd(38, 2))
2296		printf("nisplus_map_open(%s): can not find column %s\n",
2297			 map->map_file, map->map_keycolnm);
2298	errno = ENOENT;
2299	return FALSE;
2300}
2301
2302
2303/*
2304**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2305*/
2306
2307char *
2308nisplus_map_lookup(map, name, av, statp)
2309	MAP *map;
2310	char *name;
2311	char **av;
2312	int *statp;
2313{
2314	char *p;
2315	auto int vsize;
2316	char *skp;
2317	int skleft;
2318	char search_key[MAXNAME + 4];
2319	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2320	nis_result *result;
2321
2322	if (tTd(38, 20))
2323		printf("nisplus_map_lookup(%s, %s)\n",
2324			map->map_mname, name);
2325
2326	if (!bitset(MF_OPEN, map->map_mflags))
2327	{
2328		if (nisplus_map_open(map, O_RDONLY))
2329		{
2330			map->map_mflags |= MF_OPEN;
2331			map->map_pid = getpid();
2332		}
2333		else
2334		{
2335			*statp = EX_UNAVAILABLE;
2336			return NULL;
2337		}
2338	}
2339
2340	/*
2341	**  Copy the name to the key buffer, escaping double quote characters
2342	**  by doubling them and quoting "]" and "," to avoid having the
2343	**  NIS+ parser choke on them.
2344	*/
2345
2346	skleft = sizeof search_key - 4;
2347	skp = search_key;
2348	for (p = name; *p != '\0' && skleft > 0; p++)
2349	{
2350		switch (*p)
2351		{
2352		  case ']':
2353		  case ',':
2354			/* quote the character */
2355			*skp++ = '"';
2356			*skp++ = *p;
2357			*skp++ = '"';
2358			skleft -= 3;
2359			break;
2360
2361		  case '"':
2362			/* double the quote */
2363			*skp++ = '"';
2364			skleft--;
2365			/* fall through... */
2366
2367		  default:
2368			*skp++ = *p;
2369			skleft--;
2370			break;
2371		}
2372	}
2373	*skp = '\0';
2374	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2375		makelower(search_key);
2376
2377	/* construct the query */
2378	if (PARTIAL_NAME(map->map_file))
2379		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
2380			map->map_keycolnm, search_key, map->map_file,
2381			map->map_domain);
2382	else
2383		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
2384			map->map_keycolnm, search_key, map->map_file);
2385
2386	if (tTd(38, 20))
2387		printf("qbuf=%s\n", qbuf);
2388	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
2389	if (result->status == NIS_SUCCESS)
2390	{
2391		int count;
2392		char *str;
2393
2394		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2395		{
2396			if (LogLevel > 10)
2397				sm_syslog(LOG_WARNING, CurEnv->e_id,
2398				  "%s: lookup error, expected 1 entry, got %d",
2399				    map->map_file, count);
2400
2401			/* ignore second entry */
2402			if (tTd(38, 20))
2403				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
2404					name, count);
2405		}
2406
2407		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
2408		/* set the length of the result */
2409		if (p == NULL)
2410			p = "";
2411		vsize = strlen(p);
2412		if (tTd(38, 20))
2413			printf("nisplus_map_lookup(%s), found %s\n",
2414				name, p);
2415		if (bitset(MF_MATCHONLY, map->map_mflags))
2416			str = map_rewrite(map, name, strlen(name), NULL);
2417		else
2418			str = map_rewrite(map, p, vsize, av);
2419		nis_freeresult(result);
2420		*statp = EX_OK;
2421		return str;
2422	}
2423	else
2424	{
2425		if (result->status == NIS_NOTFOUND)
2426			*statp = EX_NOTFOUND;
2427		else if (result->status == NIS_TRYAGAIN)
2428			*statp = EX_TEMPFAIL;
2429		else
2430		{
2431			*statp = EX_UNAVAILABLE;
2432			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2433		}
2434	}
2435	if (tTd(38, 20))
2436		printf("nisplus_map_lookup(%s), failed\n", name);
2437	nis_freeresult(result);
2438	return NULL;
2439}
2440
2441
2442
2443/*
2444**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
2445*/
2446
2447bool
2448nisplus_getcanonname(name, hbsize, statp)
2449	char *name;
2450	int hbsize;
2451	int *statp;
2452{
2453	char *vp;
2454	auto int vsize;
2455	nis_result *result;
2456	char *p;
2457	char nbuf[MAXNAME + 1];
2458	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2459
2460	if (strlen(name) >= sizeof nbuf)
2461	{
2462		*statp = EX_UNAVAILABLE;
2463		return FALSE;
2464	}
2465	(void) strcpy(nbuf, name);
2466	shorten_hostname(nbuf);
2467
2468	p = strchr(nbuf, '.');
2469	if (p == NULL)
2470	{
2471		/* single token */
2472		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
2473	}
2474	else if (p[1] != '\0')
2475	{
2476		/* multi token -- take only first token in nbuf */
2477		*p = '\0';
2478		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
2479			nbuf, &p[1]);
2480	}
2481	else
2482	{
2483		*statp = EX_NOHOST;
2484		return FALSE;
2485	}
2486
2487	if (tTd(38, 20))
2488		printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
2489			 name, qbuf);
2490
2491	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
2492		NULL, NULL);
2493
2494	if (result->status == NIS_SUCCESS)
2495	{
2496		int count;
2497		char *domain;
2498
2499		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2500		{
2501			if (LogLevel > 10)
2502				sm_syslog(LOG_WARNING, CurEnv->e_id,
2503				       "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
2504				       count);
2505
2506			/* ignore second entry */
2507			if (tTd(38, 20))
2508				printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
2509					name, count);
2510		}
2511
2512		if (tTd(38, 20))
2513			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
2514			       name, (NIS_RES_OBJECT(result))->zo_domain);
2515
2516
2517		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
2518		vsize = strlen(vp);
2519		if (tTd(38, 20))
2520			printf("nisplus_getcanonname(%s), found %s\n",
2521				name, vp);
2522		if (strchr(vp, '.') != NULL)
2523		{
2524			domain = "";
2525		}
2526		else
2527		{
2528			domain = macvalue('m', CurEnv);
2529			if (domain == NULL)
2530				domain = "";
2531		}
2532		if (hbsize > vsize + (int) strlen(domain) + 1)
2533		{
2534			if (domain[0] == '\0')
2535				strcpy(name, vp);
2536			else
2537				snprintf(name, hbsize, "%s.%s", vp, domain);
2538			*statp = EX_OK;
2539		}
2540		else
2541			*statp = EX_NOHOST;
2542		nis_freeresult(result);
2543		return TRUE;
2544	}
2545	else
2546	{
2547		if (result->status == NIS_NOTFOUND)
2548			*statp = EX_NOHOST;
2549		else if (result->status == NIS_TRYAGAIN)
2550			*statp = EX_TEMPFAIL;
2551		else
2552			*statp = EX_UNAVAILABLE;
2553	}
2554	if (tTd(38, 20))
2555		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
2556			name, result->status, *statp);
2557	nis_freeresult(result);
2558	return FALSE;
2559}
2560
2561
2562char *
2563nisplus_default_domain()
2564{
2565	static char default_domain[MAXNAME + 1] = "";
2566	char *p;
2567
2568	if (default_domain[0] != '\0')
2569		return(default_domain);
2570
2571	p = nis_local_directory();
2572	snprintf(default_domain, sizeof default_domain, "%s", p);
2573	return default_domain;
2574}
2575
2576#endif /* NISPLUS */
2577/*
2578**  LDAP Modules
2579**
2580**	Contributed by Booker C. Bense <bbense@networking.stanford.edu>.
2581**	Get your support from him.
2582*/
2583
2584#ifdef LDAPMAP
2585
2586# undef NEEDGETOPT		/* used for something else in LDAP */
2587
2588# include <lber.h>
2589# include <ldap.h>
2590# include "ldap_map.h"
2591
2592/*
2593**  LDAP_MAP_OPEN -- open LDAP map
2594**
2595**	Since LDAP is TCP-based there is not much we can or should do
2596**	here.  It might be a good idea to attempt an open/close here.
2597*/
2598
2599bool
2600ldap_map_open(map, mode)
2601	MAP *map;
2602	int mode;
2603{
2604	if (tTd(38, 2))
2605		printf("ldap_map_open(%s, %d)\n", map->map_mname, mode);
2606
2607	mode &= O_ACCMODE;
2608	if (mode != O_RDONLY)
2609	{
2610		/* issue a pseudo-error message */
2611#ifdef ENOSYS
2612		errno = ENOSYS;
2613#else
2614# ifdef EFTYPE
2615		errno = EFTYPE;
2616# else
2617		errno = ENXIO;
2618# endif
2619#endif
2620		return FALSE;
2621	}
2622	return TRUE;
2623}
2624
2625
2626/*
2627**  LDAP_MAP_START -- actually open LDAP map
2628**
2629**	Caching should be investigated.
2630*/
2631
2632static jmp_buf	LDAPTimeout;
2633
2634static void
2635ldaptimeout(sig_no)
2636	int sig_no;
2637{
2638	longjmp(LDAPTimeout, 1);
2639}
2640
2641bool
2642ldap_map_start(map)
2643	MAP *map;
2644{
2645	LDAP_MAP_STRUCT *lmap;
2646	LDAP *ld;
2647	register EVENT *ev = NULL;
2648
2649	if (tTd(38, 2))
2650		printf("ldap_map_start(%s)\n", map->map_mname);
2651
2652	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2653
2654	if (tTd(38,9))
2655		printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport);
2656
2657	/* Need to set an alarm here, ldap_open is hopelessly broken. */
2658
2659	/* set the timeout */
2660	if (lmap->timeout.tv_sec != 0)
2661	{
2662		if (setjmp(LDAPTimeout) != 0)
2663		{
2664			if (LogLevel > 1)
2665				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2666				       "timeout waiting for ldap_open to %.100s",
2667				       lmap->ldaphost);
2668			return (FALSE);
2669		}
2670		ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0);
2671	}
2672
2673#ifdef LDAP_VERSION3
2674	ld = ldap_init(lmap->ldaphost,lmap->ldapport);
2675#else
2676	ld = ldap_open(lmap->ldaphost,lmap->ldapport);
2677#endif
2678
2679 	/* clear the event if it has not sprung */
2680	if (lmap->timeout.tv_sec != 0)
2681		clrevent(ev);
2682
2683	if (ld == NULL)
2684	{
2685		if (!bitset(MF_OPTIONAL, map->map_mflags))
2686		{
2687			syserr("%sldapopen failed to %s in map %s",
2688				bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2689				lmap->ldaphost, map->map_mname);
2690		}
2691		return FALSE;
2692	}
2693
2694#ifdef LDAP_VERSION3
2695	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref);
2696	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit);
2697	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit);
2698	ldap_set_option(ld, LDAP_OPT_REFERRALS, &lmap->ldap_options);
2699
2700	/* ld needs to be cast into the map struct */
2701	lmap->ld = ld;
2702	return TRUE;
2703#else
2704
2705	/* From here on in we can use ldap internal timelimits */
2706	ld->ld_deref = lmap->deref;
2707	ld->ld_timelimit = lmap->timelimit;
2708	ld->ld_sizelimit = lmap->sizelimit;
2709	ld->ld_options = lmap->ldap_options;
2710
2711	if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS)
2712	{
2713		if (!bitset(MF_OPTIONAL, map->map_mflags))
2714		{
2715			syserr("421 Cannot bind to map %s in ldap server %s",
2716				map->map_mname, lmap->ldaphost);
2717		}
2718	}
2719	else
2720	{
2721		/* We need to cast ld into the map structure */
2722		lmap->ld = ld;
2723		return TRUE;
2724	}
2725
2726	return FALSE;
2727#endif
2728}
2729
2730
2731/*
2732**  LDAP_MAP_CLOSE -- close ldap map
2733*/
2734
2735void
2736ldap_map_close(map)
2737	MAP *map;
2738{
2739	LDAP_MAP_STRUCT *lmap ;
2740	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2741	if (lmap->ld != NULL)
2742		ldap_unbind(lmap->ld);
2743}
2744
2745
2746#ifdef SUNET_ID
2747/*
2748**  SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
2749**  This only makes sense at Stanford University.
2750*/
2751
2752char *
2753sunet_id_hash(str)
2754	char *str;
2755{
2756	char *p, *p_last;
2757
2758	p = str;
2759	p_last = p;
2760	while (*p != '\0')
2761	{
2762		if (islower(*p) || isdigit(*p))
2763		{
2764			*p_last = *p;
2765			p_last++;
2766		}
2767		else if (isupper(*p))
2768		{
2769			*p_last = tolower(*p);
2770			p_last++;
2771		}
2772		++p;
2773	}
2774	if (*p_last != '\0')
2775		*p_last = '\0';
2776	return (str);
2777}
2778
2779
2780
2781#endif /* SUNET_ID */
2782/*
2783**  LDAP_MAP_LOOKUP -- look up a datum in a LDAP map
2784*/
2785
2786char *
2787ldap_map_lookup(map, name, av, statp)
2788	MAP *map;
2789	char *name;
2790	char **av;
2791	int *statp;
2792{
2793	LDAP_MAP_STRUCT *lmap = NULL;
2794	LDAPMessage *entry;
2795	char *vp;
2796	auto int vsize;
2797	char keybuf[MAXNAME + 1];
2798	char filter[LDAP_MAP_MAX_FILTER + 1];
2799	char **attr_values = NULL;
2800	char *result;
2801	int name_len;
2802	char *fp, *p, *q;
2803
2804	if (tTd(38, 20))
2805		printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name);
2806
2807	/* actually open the map */
2808	if (!ldap_map_start(map))
2809	{
2810		result = NULL;
2811		*statp = EX_TEMPFAIL;
2812		goto quick_exit;
2813	}
2814
2815	/* Get ldap struct pointer from map */
2816	lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2817
2818	name_len = strlen(name);
2819	if (name_len > MAXNAME)
2820		name_len = MAXNAME;
2821	strncpy(keybuf, name, name_len);
2822	keybuf[name_len] = '\0';
2823
2824	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2825#ifdef SUNET_ID
2826		sunet_id_hash(keybuf);
2827#else
2828		makelower(keybuf);
2829#endif /*SUNET_ID */
2830
2831	/* substitute keybuf into filter, perhaps multiple times */
2832	fp = filter;
2833	p = lmap->filter;
2834	while ((q = strchr(p, '%')) != NULL)
2835	{
2836		if (q[1] == 's')
2837		{
2838			snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
2839				 q - p, p, keybuf);
2840			p = q + 2;
2841		}
2842		else
2843		{
2844			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
2845				 q - p + 1, p);
2846			p = q + (q[1] == '%' ? 2 : 1);
2847		}
2848		fp += strlen(fp);
2849	}
2850	snprintf(fp, SPACELEFT(filter, fp), "%s", p);
2851	if (tTd(38, 20))
2852		printf("ldap search filter=%s\n", filter);
2853
2854	if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter,
2855			   lmap->attr, lmap->attrsonly, &(lmap->timeout),
2856			   &(lmap->res)) != LDAP_SUCCESS)
2857	{
2858		/* try close/opening map */
2859		ldap_map_close(map);
2860		if (!ldap_map_start(map))
2861		{
2862			result = NULL;
2863			*statp = EX_TEMPFAIL;
2864			goto quick_exit;
2865		}
2866		if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter,
2867				   lmap->attr, lmap->attrsonly,
2868				   &(lmap->timeout), &(lmap->res))
2869			!= LDAP_SUCCESS)
2870		{
2871			if (!bitset(MF_OPTIONAL, map->map_mflags))
2872			{
2873				syserr("%sError in ldap_search_st using %s in map %s",
2874					bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2875					filter, map->map_mname);
2876			}
2877			result = NULL;
2878			*statp = EX_TEMPFAIL;
2879			goto quick_exit;
2880		}
2881	}
2882
2883	entry = ldap_first_entry(lmap->ld,lmap->res);
2884	if (entry == NULL)
2885	{
2886	        result = NULL;
2887		*statp = EX_NOTFOUND;
2888		goto quick_exit;
2889	}
2890
2891	/* Need to build the args for map_rewrite here */
2892	attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]);
2893	if (attr_values == NULL)
2894	{
2895		/* bad things happened */
2896		result = NULL;
2897		*statp = EX_NOTFOUND;
2898		goto quick_exit;
2899	}
2900
2901	*statp = EX_OK;
2902
2903	/* If there is more that one use the first */
2904	vp = attr_values[0];
2905	vsize = strlen(vp);
2906
2907	if (LogLevel > 9)
2908		sm_syslog(LOG_INFO, CurEnv->e_id,
2909			"ldap %.100s => %s",
2910			name, vp);
2911	if (bitset(MF_MATCHONLY, map->map_mflags))
2912		result = map_rewrite(map, name, strlen(name), NULL);
2913	else
2914		result = map_rewrite(map, vp, vsize, av);
2915
2916  quick_exit:
2917	if (attr_values != NULL)
2918		ldap_value_free(attr_values);
2919	if (lmap != NULL)
2920		ldap_msgfree(lmap->res);
2921	ldap_map_close(map);
2922	return result ;
2923}
2924
2925
2926/*
2927**  LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs
2928*/
2929
2930char *
2931ldap_map_dequote(str)
2932	char *str;
2933{
2934	char *p;
2935	char *start;
2936	p = str;
2937
2938	if (*p == '"')
2939	{
2940		start = ++p;
2941		/* Should probably swallow initial whitespace here */
2942	}
2943	else
2944	{
2945		return(str);
2946	}
2947	while (*p != '"' && *p != '\0')
2948	{
2949		p++;
2950	}
2951	if (*p != '\0')
2952		*p = '\0';
2953	return start;
2954}
2955
2956/*
2957**  LDAP_MAP_PARSEARGS -- parse ldap map definition args.
2958*/
2959
2960bool
2961ldap_map_parseargs(map,args)
2962	MAP *map;
2963	char *args;
2964{
2965	register char *p = args;
2966	register int done;
2967	LDAP_MAP_STRUCT *lmap;
2968
2969	/* We need to alloc an LDAP_MAP_STRUCT struct */
2970	lmap  = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT));
2971
2972	/* Set default int's here , default strings below */
2973	lmap->ldapport =  DEFAULT_LDAP_MAP_PORT;
2974	lmap->deref = DEFAULT_LDAP_MAP_DEREF;
2975	lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT;
2976	lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT;
2977	lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS;
2978	lmap->method = DEFAULT_LDAP_MAP_METHOD;
2979	lmap->scope = DEFAULT_LDAP_MAP_SCOPE;
2980	lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY;
2981	lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT;
2982	lmap->timeout.tv_usec = 0;
2983
2984	/* Default char ptrs to NULL */
2985	lmap->binddn = NULL;
2986	lmap->passwd = NULL;
2987	lmap->base   = NULL;
2988	lmap->ldaphost = NULL;
2989
2990	/* Default general ptrs to NULL */
2991	lmap->ld = NULL;
2992	lmap->res = NULL;
2993
2994	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
2995	for (;;)
2996	{
2997		while (isascii(*p) && isspace(*p))
2998			p++;
2999		if (*p != '-')
3000			break;
3001		switch (*++p)
3002		{
3003		  case 'N':
3004			map->map_mflags |= MF_INCLNULL;
3005			map->map_mflags &= ~MF_TRY0NULL;
3006			break;
3007
3008		  case 'O':
3009			map->map_mflags &= ~MF_TRY1NULL;
3010			break;
3011
3012		  case 'o':
3013			map->map_mflags |= MF_OPTIONAL;
3014			break;
3015
3016		  case 'f':
3017			map->map_mflags |= MF_NOFOLDCASE;
3018			break;
3019
3020		  case 'm':
3021			map->map_mflags |= MF_MATCHONLY;
3022			break;
3023
3024		  case 'A':
3025			map->map_mflags |= MF_APPEND;
3026			break;
3027
3028		  case 'q':
3029			map->map_mflags |= MF_KEEPQUOTES;
3030			break;
3031
3032		  case 't':
3033			map->map_mflags |= MF_NODEFER;
3034			break;
3035
3036		  case 'a':
3037			map->map_app = ++p;
3038			break;
3039
3040		  case 'T':
3041			map->map_tapp = ++p;
3042			break;
3043
3044			/* Start of ldap_map specific args */
3045		  case 'k':		/* search field */
3046			while (isascii(*++p) && isspace(*p))
3047				continue;
3048			lmap->filter = p;
3049			break;
3050
3051		  case 'v':		/* attr to return */
3052			while (isascii(*++p) && isspace(*p))
3053				continue;
3054			lmap->attr[0] = p;
3055			lmap->attr[1] = NULL;
3056			break;
3057
3058			/* args stolen from ldapsearch.c */
3059		  case 'R':		/* don't auto chase referrals */
3060#ifdef LDAP_REFERRALS
3061			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3062#else  /* LDAP_REFERRALS */
3063			syserr("compile with -DLDAP_REFERRALS for referral support\n");
3064#endif /* LDAP_REFERRALS */
3065			break;
3066
3067		  case 'n':		/* retrieve attribute names only -- no values */
3068			lmap->attrsonly += 1;
3069			break;
3070
3071		  case 's':		/* search scope */
3072			if (strncasecmp(++p, "base", 4) == 0)
3073			{
3074				lmap->scope = LDAP_SCOPE_BASE;
3075			}
3076			else if (strncasecmp(p, "one", 3) == 0)
3077			{
3078				lmap->scope = LDAP_SCOPE_ONELEVEL;
3079			}
3080			else if (strncasecmp(p, "sub", 3) == 0)
3081			{
3082				lmap->scope = LDAP_SCOPE_SUBTREE;
3083			}
3084			else
3085			{		/* bad config line */
3086				if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3087				{
3088					char *ptr;
3089
3090					if ((ptr = strchr(p, ' ')) != NULL)
3091						*ptr = '\0';
3092					syserr("Scope must be [base|one|sub] not %s in map %s",
3093						p, map->map_mname);
3094					if (ptr != NULL)
3095						*ptr = ' ';
3096					return FALSE;
3097				}
3098			}
3099			break;
3100
3101		  case 'h':		/* ldap host */
3102			while (isascii(*++p) && isspace(*p))
3103				continue;
3104			map->map_domain = p;
3105			lmap->ldaphost = p;
3106			break;
3107
3108		  case 'b':		/* search base */
3109			while (isascii(*++p) && isspace(*p))
3110				continue;
3111			lmap->base = p;
3112			break;
3113
3114		  case 'p':		/* ldap port */
3115			while (isascii(*++p) && isspace(*p))
3116				continue;
3117			lmap->ldapport = atoi(p);
3118			break;
3119
3120		  case 'l':		/* time limit */
3121			while (isascii(*++p) && isspace(*p))
3122				continue;
3123			lmap->timelimit = atoi(p);
3124			lmap->timeout.tv_sec = lmap->timelimit;
3125			break;
3126
3127		}
3128
3129		/* need to account for quoted strings here arggg... */
3130		done =  isascii(*p) && isspace(*p);
3131		while (*p != '\0' && !done)
3132		{
3133			if (*p == '"')
3134			{
3135				while (*++p != '"' && *p != '\0')
3136				{
3137					continue;
3138				}
3139				if (*p != '\0')
3140					p++;
3141			}
3142			else
3143			{
3144				p++;
3145			}
3146			done = isascii(*p) && isspace(*p);
3147		}
3148
3149		if (*p != '\0')
3150			*p++ = '\0';
3151	}
3152
3153	if (map->map_app != NULL)
3154		map->map_app = newstr(ldap_map_dequote(map->map_app));
3155	if (map->map_tapp != NULL)
3156		map->map_tapp = newstr(ldap_map_dequote(map->map_tapp));
3157	if (map->map_domain != NULL)
3158		map->map_domain = newstr(ldap_map_dequote(map->map_domain));
3159
3160	/*
3161	**  We need to swallow up all the stuff into a struct
3162	**  and dump it into map->map_dbptr1
3163	*/
3164
3165	if (lmap->ldaphost != NULL)
3166		lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost));
3167	else
3168	{
3169		syserr("LDAP map: -h flag is required");
3170		return FALSE;
3171	}
3172
3173	if (lmap->binddn != NULL)
3174		lmap->binddn = newstr(ldap_map_dequote(lmap->binddn));
3175	else
3176		lmap->binddn = DEFAULT_LDAP_MAP_BINDDN;
3177
3178
3179	if (lmap->passwd != NULL)
3180		lmap->passwd = newstr(ldap_map_dequote(lmap->passwd));
3181	else
3182		lmap->passwd = DEFAULT_LDAP_MAP_PASSWD;
3183
3184	if (lmap->base != NULL)
3185		lmap->base = newstr(ldap_map_dequote(lmap->base));
3186	else
3187	{
3188		syserr("LDAP map: -b flag is required");
3189		return FALSE;
3190	}
3191
3192
3193	if (lmap->filter != NULL)
3194		lmap->filter = newstr(ldap_map_dequote(lmap->filter));
3195	else
3196	{
3197		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3198		{
3199			syserr("No filter given in map %s", map->map_mname);
3200			return FALSE;
3201		}
3202	}
3203	if (lmap->attr[0] != NULL)
3204		lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0]));
3205	else
3206	{
3207		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3208		{
3209			syserr("No return attribute in %s", map->map_mname);
3210			return FALSE;
3211		}
3212	}
3213
3214	map->map_db1 = (ARBPTR_T) lmap;
3215	return TRUE;
3216}
3217
3218#endif /* LDAP Modules */
3219/*
3220**  syslog map
3221*/
3222
3223#if _FFR_MAP_SYSLOG
3224
3225#define map_prio	map_lockfd	/* overload field */
3226
3227/*
3228**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
3229*/
3230
3231bool
3232syslog_map_parseargs(map, args)
3233	MAP *map;
3234	char *args;
3235{
3236	char *p = args;
3237	char *priority = NULL;
3238
3239	for (;;)
3240	{
3241		while (isascii(*p) && isspace(*p))
3242			p++;
3243		if (*p != '-')
3244			break;
3245		if (*++p == 'L')
3246			priority = ++p;
3247		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3248			p++;
3249		if (*p != '\0')
3250			*p++ = '\0';
3251	}
3252
3253	if (priority == NULL)
3254		map->map_prio = LOG_INFO;
3255	else
3256	{
3257		if (strncasecmp("LOG_", priority, 4) == 0)
3258			priority += 4;
3259
3260#ifdef LOG_EMERG
3261		if (strcasecmp("EMERG", priority) == 0)
3262			map->map_prio = LOG_EMERG;
3263		else
3264#endif
3265#ifdef LOG_ALERT
3266		if (strcasecmp("ALERT", priority) == 0)
3267			map->map_prio = LOG_ALERT;
3268		else
3269#endif
3270#ifdef LOG_CRIT
3271		if (strcasecmp("CRIT", priority) == 0)
3272			map->map_prio = LOG_CRIT;
3273		else
3274#endif
3275#ifdef LOG_ERR
3276		if (strcasecmp("ERR", priority) == 0)
3277			map->map_prio = LOG_ERR;
3278		else
3279#endif
3280#ifdef LOG_WARNING
3281		if (strcasecmp("WARNING", priority) == 0)
3282			map->map_prio = LOG_WARNING;
3283		else
3284#endif
3285#ifdef LOG_NOTICE
3286		if (strcasecmp("NOTICE", priority) == 0)
3287			map->map_prio = LOG_NOTICE;
3288		else
3289#endif
3290#ifdef LOG_INFO
3291		if (strcasecmp("INFO", priority) == 0)
3292			map->map_prio = LOG_INFO;
3293		else
3294#endif
3295#ifdef LOG_DEBUG
3296		if (strcasecmp("DEBUG", priority) == 0)
3297			map->map_prio = LOG_DEBUG;
3298		else
3299#endif
3300		{
3301			syserr("syslog_map_parseargs: Unknown priority %s\n",
3302			       priority);
3303			return FALSE;
3304		}
3305	}
3306	return TRUE;
3307}
3308
3309/*
3310**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
3311*/
3312
3313char *
3314syslog_map_lookup(map, string, args, statp)
3315	MAP *map;
3316	char *string;
3317	char **args;
3318	int *statp;
3319{
3320	char *ptr = map_rewrite(map, string, strlen(string), args);
3321
3322	if (ptr != NULL)
3323	{
3324		if (tTd(38, 20))
3325			printf("syslog_map_lookup(%s (priority %d): %s\n",
3326			       map->map_mname, map->map_prio, ptr);
3327
3328		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
3329	}
3330
3331	*statp = EX_OK;
3332	return "";
3333}
3334
3335#endif /* _FFR_MAP_SYSLOG */
3336/*
3337**  HESIOD Modules
3338*/
3339
3340#ifdef HESIOD
3341
3342bool
3343hes_map_open(map, mode)
3344	MAP *map;
3345	int mode;
3346{
3347	if (tTd(38, 2))
3348		printf("hes_map_open(%s, %s, %d)\n",
3349			map->map_mname, map->map_file, mode);
3350
3351	if (mode != O_RDONLY)
3352	{
3353		/* issue a pseudo-error message */
3354#ifdef ENOSYS
3355		errno = ENOSYS;
3356#else
3357# ifdef EFTYPE
3358		errno = EFTYPE;
3359# else
3360		errno = ENXIO;
3361# endif
3362#endif
3363		return FALSE;
3364	}
3365
3366#ifdef HESIOD_INIT
3367	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
3368		return TRUE;
3369
3370	if (!bitset(MF_OPTIONAL, map->map_mflags))
3371		syserr("421 cannot initialize Hesiod map (%s)",
3372			errstring(errno));
3373	return FALSE;
3374#else
3375	if (hes_error() == HES_ER_UNINIT)
3376		hes_init();
3377	switch (hes_error())
3378	{
3379	  case HES_ER_OK:
3380	  case HES_ER_NOTFOUND:
3381		return TRUE;
3382	}
3383
3384	if (!bitset(MF_OPTIONAL, map->map_mflags))
3385		syserr("421 cannot initialize Hesiod map (%d)", hes_error());
3386
3387	return FALSE;
3388#endif /* HESIOD_INIT */
3389}
3390
3391char *
3392hes_map_lookup(map, name, av, statp)
3393	MAP *map;
3394	char *name;
3395	char **av;
3396	int *statp;
3397{
3398	char **hp;
3399
3400	if (tTd(38, 20))
3401		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
3402
3403	if (name[0] == '\\')
3404	{
3405		char *np;
3406		int nl;
3407		char nbuf[MAXNAME];
3408
3409		nl = strlen(name);
3410		if (nl < sizeof nbuf - 1)
3411			np = nbuf;
3412		else
3413			np = xalloc(strlen(name) + 2);
3414		np[0] = '\\';
3415		strcpy(&np[1], name);
3416#ifdef HESIOD_INIT
3417		hp = hesiod_resolve(HesiodContext, np, map->map_file);
3418#else
3419		hp = hes_resolve(np, map->map_file);
3420#endif /* HESIOD_INIT */
3421		if (np != nbuf)
3422			free(np);
3423	}
3424	else
3425	{
3426#ifdef HESIOD_INIT
3427		hp = hesiod_resolve(HesiodContext, name, map->map_file);
3428#else
3429		hp = hes_resolve(name, map->map_file);
3430#endif /* HESIOD_INIT */
3431	}
3432#ifdef HESIOD_INIT
3433	if (hp == NULL)
3434		return NULL;
3435	if (*hp == NULL)
3436	{
3437		hesiod_free_list(HesiodContext, hp);
3438		switch (errno)
3439		{
3440		  case ENOENT:
3441			  *statp = EX_NOTFOUND;
3442			  break;
3443		  case ECONNREFUSED:
3444		  case EMSGSIZE:
3445			  *statp = EX_TEMPFAIL;
3446			  break;
3447		  case ENOMEM:
3448		  default:
3449			  *statp = EX_UNAVAILABLE;
3450			  break;
3451		}
3452		return NULL;
3453	}
3454#else
3455	if (hp == NULL || hp[0] == NULL)
3456	{
3457		switch (hes_error())
3458		{
3459		  case HES_ER_OK:
3460			*statp = EX_OK;
3461			break;
3462
3463		  case HES_ER_NOTFOUND:
3464			*statp = EX_NOTFOUND;
3465			break;
3466
3467		  case HES_ER_CONFIG:
3468			*statp = EX_UNAVAILABLE;
3469			break;
3470
3471		  case HES_ER_NET:
3472			*statp = EX_TEMPFAIL;
3473			break;
3474		}
3475		return NULL;
3476	}
3477#endif /* HESIOD_INIT */
3478
3479	if (bitset(MF_MATCHONLY, map->map_mflags))
3480		return map_rewrite(map, name, strlen(name), NULL);
3481	else
3482		return map_rewrite(map, hp[0], strlen(hp[0]), av);
3483}
3484
3485#endif
3486/*
3487**  NeXT NETINFO Modules
3488*/
3489
3490#if NETINFO
3491
3492# define NETINFO_DEFAULT_DIR		"/aliases"
3493# define NETINFO_DEFAULT_PROPERTY	"members"
3494
3495extern char	*ni_propval __P((char *, char *, char *, char *, int));
3496
3497
3498/*
3499**  NI_MAP_OPEN -- open NetInfo Aliases
3500*/
3501
3502bool
3503ni_map_open(map, mode)
3504	MAP *map;
3505	int mode;
3506{
3507	if (tTd(38, 2))
3508		printf("ni_map_open(%s, %s, %d)\n",
3509			map->map_mname, map->map_file, mode);
3510	mode &= O_ACCMODE;
3511
3512	if (*map->map_file == '\0')
3513		map->map_file = NETINFO_DEFAULT_DIR;
3514
3515	if (map->map_valcolnm == NULL)
3516		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
3517
3518	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
3519		map->map_coldelim = ',';
3520
3521	return TRUE;
3522}
3523
3524
3525/*
3526**  NI_MAP_LOOKUP -- look up a datum in NetInfo
3527*/
3528
3529char *
3530ni_map_lookup(map, name, av, statp)
3531	MAP *map;
3532	char *name;
3533	char **av;
3534	int *statp;
3535{
3536	char *res;
3537	char *propval;
3538
3539	if (tTd(38, 20))
3540		printf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
3541
3542	propval = ni_propval(map->map_file, map->map_keycolnm, name,
3543			     map->map_valcolnm, map->map_coldelim);
3544
3545	if (propval == NULL)
3546		return NULL;
3547
3548	if (bitset(MF_MATCHONLY, map->map_mflags))
3549		res = map_rewrite(map, name, strlen(name), NULL);
3550	else
3551		res = map_rewrite(map, propval, strlen(propval), av);
3552	free(propval);
3553	return res;
3554}
3555
3556
3557bool
3558ni_getcanonname(name, hbsize, statp)
3559	char *name;
3560	int hbsize;
3561	int *statp;
3562{
3563	char *vptr;
3564	char *ptr;
3565	char nbuf[MAXNAME + 1];
3566
3567	if (tTd(38, 20))
3568		printf("ni_getcanonname(%s)\n", name);
3569
3570	if (strlen(name) >= sizeof nbuf)
3571	{
3572		*statp = EX_UNAVAILABLE;
3573		return FALSE;
3574	}
3575	(void) strcpy(nbuf, name);
3576	shorten_hostname(nbuf);
3577
3578	/* we only accept single token search key */
3579	if (strchr(nbuf, '.'))
3580	{
3581		*statp = EX_NOHOST;
3582		return FALSE;
3583	}
3584
3585	/* Do the search */
3586	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
3587
3588	if (vptr == NULL)
3589	{
3590		*statp = EX_NOHOST;
3591		return FALSE;
3592	}
3593
3594	/* Only want the first machine name */
3595	if ((ptr = strchr(vptr, '\n')) != NULL)
3596		*ptr = '\0';
3597
3598	if (hbsize >= strlen(vptr))
3599	{
3600		strcpy(name, vptr);
3601		*statp = EX_OK;
3602		return TRUE;
3603	}
3604	*statp = EX_UNAVAILABLE;
3605	free(vptr);
3606	return FALSE;
3607}
3608
3609
3610/*
3611**  NI_PROPVAL -- NetInfo property value lookup routine
3612**
3613**	Parameters:
3614**		keydir -- the NetInfo directory name in which to search
3615**			for the key.
3616**		keyprop -- the name of the property in which to find the
3617**			property we are interested.  Defaults to "name".
3618**		keyval -- the value for which we are really searching.
3619**		valprop -- the property name for the value in which we
3620**			are interested.
3621**		sepchar -- if non-nil, this can be multiple-valued, and
3622**			we should return a string separated by this
3623**			character.
3624**
3625**	Returns:
3626**		NULL -- if:
3627**			1. the directory is not found
3628**			2. the property name is not found
3629**			3. the property contains multiple values
3630**			4. some error occured
3631**		else -- the value of the lookup.
3632**
3633**	Example:
3634**		To search for an alias value, use:
3635**		  ni_propval("/aliases", "name", aliasname, "members", ',')
3636**
3637**	Notes:
3638**      	Caller should free the return value of ni_proval
3639*/
3640
3641# include <netinfo/ni.h>
3642
3643# define LOCAL_NETINFO_DOMAIN    "."
3644# define PARENT_NETINFO_DOMAIN   ".."
3645# define MAX_NI_LEVELS           256
3646
3647char *
3648ni_propval(keydir, keyprop, keyval, valprop, sepchar)
3649	char *keydir;
3650	char *keyprop;
3651	char *keyval;
3652	char *valprop;
3653	int sepchar;
3654{
3655	char *propval = NULL;
3656	int i;
3657	int j, alen;
3658	void *ni = NULL;
3659	void *lastni = NULL;
3660	ni_status nis;
3661	ni_id nid;
3662	ni_namelist ninl;
3663	register char *p;
3664	char keybuf[1024];
3665
3666	/*
3667	**  Create the full key from the two parts.
3668	**
3669	**	Note that directory can end with, e.g., "name=" to specify
3670	**	an alternate search property.
3671	*/
3672
3673	i = strlen(keydir) + strlen(keyval) + 2;
3674	if (keyprop != NULL)
3675		i += strlen(keyprop) + 1;
3676	if (i > sizeof keybuf)
3677		return NULL;
3678	strcpy(keybuf, keydir);
3679	strcat(keybuf, "/");
3680	if (keyprop != NULL)
3681	{
3682		strcat(keybuf, keyprop);
3683		strcat(keybuf, "=");
3684	}
3685	strcat(keybuf, keyval);
3686
3687	if (tTd(38, 21))
3688		printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
3689			keydir, keyprop, keyval, valprop, sepchar, keybuf);
3690	/*
3691	**  If the passed directory and property name are found
3692	**  in one of netinfo domains we need to search (starting
3693	**  from the local domain moving all the way back to the
3694	**  root domain) set propval to the property's value
3695	**  and return it.
3696	*/
3697
3698	for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
3699	{
3700		if (i == 0)
3701		{
3702			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
3703			if (tTd(38, 20))
3704				printf("ni_open(LOCAL) = %d\n", nis);
3705		}
3706		else
3707		{
3708			if (lastni != NULL)
3709				ni_free(lastni);
3710			lastni = ni;
3711			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
3712			if (tTd(38, 20))
3713				printf("ni_open(PARENT) = %d\n", nis);
3714		}
3715
3716		/*
3717		**  Don't bother if we didn't get a handle on a
3718		**  proper domain.  This is not necessarily an error.
3719		**  We would get a positive ni_status if, for instance
3720		**  we never found the directory or property and tried
3721		**  to open the parent of the root domain!
3722		*/
3723
3724		if (nis != 0)
3725			break;
3726
3727		/*
3728		**  Find the path to the server information.
3729		*/
3730
3731		if (ni_pathsearch(ni, &nid, keybuf) != 0)
3732			continue;
3733
3734		/*
3735		**  Find associated value information.
3736		*/
3737
3738		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
3739			continue;
3740
3741		if (tTd(38, 20))
3742			printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len);
3743		/*
3744		**  See if we have an acceptable number of values.
3745		*/
3746
3747		if (ninl.ni_namelist_len <= 0)
3748			continue;
3749
3750		if (sepchar == '\0' && ninl.ni_namelist_len > 1)
3751		{
3752			ni_namelist_free(&ninl);
3753			continue;
3754		}
3755
3756		/*
3757		**  Calculate number of bytes needed and build result
3758		*/
3759
3760		alen = 1;
3761		for (j = 0; j < ninl.ni_namelist_len; j++)
3762			alen += strlen(ninl.ni_namelist_val[j]) + 1;
3763		propval = p = xalloc(alen);
3764		for (j = 0; j < ninl.ni_namelist_len; j++)
3765		{
3766			strcpy(p, ninl.ni_namelist_val[j]);
3767			p += strlen(p);
3768			*p++ = sepchar;
3769		}
3770		*--p = '\0';
3771
3772		ni_namelist_free(&ninl);
3773	}
3774
3775	/*
3776	**  Clean up.
3777	*/
3778
3779	if (ni != NULL)
3780		ni_free(ni);
3781	if (lastni != NULL && ni != lastni)
3782		ni_free(lastni);
3783	if (tTd(38, 20))
3784		printf("ni_propval returns: '%s'\n", propval);
3785
3786	return propval;
3787}
3788
3789#endif /* NETINFO */
3790/*
3791**  TEXT (unindexed text file) Modules
3792**
3793**	This code donated by Sun Microsystems.
3794*/
3795
3796#define map_sff		map_lockfd	/* overload field */
3797
3798
3799/*
3800**  TEXT_MAP_OPEN -- open text table
3801*/
3802
3803bool
3804text_map_open(map, mode)
3805	MAP *map;
3806	int mode;
3807{
3808	int sff;
3809	int i;
3810
3811	if (tTd(38, 2))
3812		printf("text_map_open(%s, %s, %d)\n",
3813			map->map_mname, map->map_file, mode);
3814
3815	mode &= O_ACCMODE;
3816	if (mode != O_RDONLY)
3817	{
3818		errno = EPERM;
3819		return FALSE;
3820	}
3821
3822	if (*map->map_file == '\0')
3823	{
3824		syserr("text map \"%s\": file name required",
3825			map->map_mname);
3826		return FALSE;
3827	}
3828
3829	if (map->map_file[0] != '/')
3830	{
3831		syserr("text map \"%s\": file name must be fully qualified",
3832			map->map_mname);
3833		return FALSE;
3834	}
3835
3836	sff = SFF_ROOTOK|SFF_REGONLY;
3837	if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
3838		sff |= SFF_NOWLINK;
3839	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
3840		sff |= SFF_SAFEDIRPATH;
3841	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
3842			  sff, S_IRUSR, NULL)) != 0)
3843	{
3844		/* cannot open this map */
3845		if (tTd(38, 2))
3846			printf("\tunsafe map file: %d\n", i);
3847		if (!bitset(MF_OPTIONAL, map->map_mflags))
3848			syserr("text map \"%s\": unsafe map file %s",
3849				map->map_mname, map->map_file);
3850		return FALSE;
3851	}
3852
3853	if (map->map_keycolnm == NULL)
3854		map->map_keycolno = 0;
3855	else
3856	{
3857		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
3858		{
3859			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
3860				map->map_mname, map->map_file,
3861				map->map_keycolnm);
3862			return FALSE;
3863		}
3864		map->map_keycolno = atoi(map->map_keycolnm);
3865	}
3866
3867	if (map->map_valcolnm == NULL)
3868		map->map_valcolno = 0;
3869	else
3870	{
3871		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
3872		{
3873			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
3874					map->map_mname, map->map_file,
3875					map->map_valcolnm);
3876			return FALSE;
3877		}
3878		map->map_valcolno = atoi(map->map_valcolnm);
3879	}
3880
3881	if (tTd(38, 2))
3882	{
3883		printf("text_map_open(%s, %s): delimiter = ",
3884			map->map_mname, map->map_file);
3885		if (map->map_coldelim == '\0')
3886			printf("(white space)\n");
3887		else
3888			printf("%c\n", map->map_coldelim);
3889	}
3890
3891	map->map_sff = sff;
3892	return TRUE;
3893}
3894
3895
3896/*
3897**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
3898*/
3899
3900char *
3901text_map_lookup(map, name, av, statp)
3902	MAP *map;
3903	char *name;
3904	char **av;
3905	int *statp;
3906{
3907	char *vp;
3908	auto int vsize;
3909	int buflen;
3910	FILE *f;
3911	char delim;
3912	int key_idx;
3913	bool found_it;
3914	int sff = map->map_sff;
3915	char search_key[MAXNAME + 1];
3916	char linebuf[MAXLINE];
3917	char buf[MAXNAME + 1];
3918	extern char *get_column __P((char *, int, char, char *, int));
3919
3920	found_it = FALSE;
3921	if (tTd(38, 20))
3922		printf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
3923
3924	buflen = strlen(name);
3925	if (buflen > sizeof search_key - 1)
3926		buflen = sizeof search_key - 1;
3927	bcopy(name, search_key, buflen);
3928	search_key[buflen] = '\0';
3929	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3930		makelower(search_key);
3931
3932	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
3933	if (f == NULL)
3934	{
3935		map->map_mflags &= ~(MF_VALID|MF_OPEN);
3936		*statp = EX_UNAVAILABLE;
3937		return NULL;
3938	}
3939	key_idx = map->map_keycolno;
3940	delim = map->map_coldelim;
3941	while (fgets(linebuf, MAXLINE, f) != NULL)
3942	{
3943		char *p;
3944
3945		/* skip comment line */
3946		if (linebuf[0] == '#')
3947			continue;
3948		p = strchr(linebuf, '\n');
3949		if (p != NULL)
3950			*p = '\0';
3951		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
3952		if (p != NULL && strcasecmp(search_key, p) == 0)
3953		{
3954			found_it = TRUE;
3955			break;
3956		}
3957	}
3958	fclose(f);
3959	if (!found_it)
3960	{
3961		*statp = EX_NOTFOUND;
3962		return NULL;
3963	}
3964	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
3965	if (vp == NULL)
3966	{
3967		*statp = EX_NOTFOUND;
3968		return NULL;
3969	}
3970	vsize = strlen(vp);
3971	*statp = EX_OK;
3972	if (bitset(MF_MATCHONLY, map->map_mflags))
3973		return map_rewrite(map, name, strlen(name), NULL);
3974	else
3975		return map_rewrite(map, vp, vsize, av);
3976}
3977
3978
3979/*
3980**  TEXT_GETCANONNAME -- look up canonical name in hosts file
3981*/
3982
3983bool
3984text_getcanonname(name, hbsize, statp)
3985	char *name;
3986	int hbsize;
3987	int *statp;
3988{
3989	bool found;
3990	FILE *f;
3991	char linebuf[MAXLINE];
3992	char cbuf[MAXNAME + 1];
3993	char nbuf[MAXNAME + 1];
3994
3995	if (tTd(38, 20))
3996		printf("text_getcanonname(%s)\n", name);
3997
3998	if (strlen(name) >= (SIZE_T) sizeof nbuf)
3999	{
4000		*statp = EX_UNAVAILABLE;
4001		return FALSE;
4002	}
4003	(void) strcpy(nbuf, name);
4004	shorten_hostname(nbuf);
4005
4006	f = fopen(HostsFile, "r");
4007	if (f == NULL)
4008	{
4009		*statp = EX_UNAVAILABLE;
4010		return FALSE;
4011	}
4012	found = FALSE;
4013	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
4014	{
4015		char *p = strpbrk(linebuf, "#\n");
4016
4017		if (p != NULL)
4018			*p = '\0';
4019		if (linebuf[0] != '\0')
4020			found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
4021	}
4022	fclose(f);
4023	if (!found)
4024	{
4025		*statp = EX_NOHOST;
4026		return FALSE;
4027	}
4028
4029	if ((SIZE_T) hbsize >= strlen(cbuf))
4030	{
4031		strcpy(name, cbuf);
4032		*statp = EX_OK;
4033		return TRUE;
4034	}
4035	*statp = EX_UNAVAILABLE;
4036	return FALSE;
4037}
4038/*
4039**  STAB (Symbol Table) Modules
4040*/
4041
4042
4043/*
4044**  STAB_MAP_LOOKUP -- look up alias in symbol table
4045*/
4046
4047/* ARGSUSED2 */
4048char *
4049stab_map_lookup(map, name, av, pstat)
4050	register MAP *map;
4051	char *name;
4052	char **av;
4053	int *pstat;
4054{
4055	register STAB *s;
4056
4057	if (tTd(38, 20))
4058		printf("stab_lookup(%s, %s)\n",
4059			map->map_mname, name);
4060
4061	s = stab(name, ST_ALIAS, ST_FIND);
4062	if (s != NULL)
4063		return (s->s_alias);
4064	return (NULL);
4065}
4066
4067
4068/*
4069**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
4070*/
4071
4072void
4073stab_map_store(map, lhs, rhs)
4074	register MAP *map;
4075	char *lhs;
4076	char *rhs;
4077{
4078	register STAB *s;
4079
4080	s = stab(lhs, ST_ALIAS, ST_ENTER);
4081	s->s_alias = newstr(rhs);
4082}
4083
4084
4085/*
4086**  STAB_MAP_OPEN -- initialize (reads data file)
4087**
4088**	This is a wierd case -- it is only intended as a fallback for
4089**	aliases.  For this reason, opens for write (only during a
4090**	"newaliases") always fails, and opens for read open the
4091**	actual underlying text file instead of the database.
4092*/
4093
4094bool
4095stab_map_open(map, mode)
4096	register MAP *map;
4097	int mode;
4098{
4099	FILE *af;
4100	int sff;
4101	struct stat st;
4102
4103	if (tTd(38, 2))
4104		printf("stab_map_open(%s, %s, %d)\n",
4105			map->map_mname, map->map_file, mode);
4106
4107	mode &= O_ACCMODE;
4108	if (mode != O_RDONLY)
4109	{
4110		errno = EPERM;
4111		return FALSE;
4112	}
4113
4114	sff = SFF_ROOTOK|SFF_REGONLY;
4115	if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
4116		sff |= SFF_NOWLINK;
4117	if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
4118		sff |= SFF_SAFEDIRPATH;
4119	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
4120	if (af == NULL)
4121		return FALSE;
4122	readaliases(map, af, FALSE, FALSE);
4123
4124	if (fstat(fileno(af), &st) >= 0)
4125		map->map_mtime = st.st_mtime;
4126	fclose(af);
4127
4128	return TRUE;
4129}
4130/*
4131**  Implicit Modules
4132**
4133**	Tries several types.  For back compatibility of aliases.
4134*/
4135
4136
4137/*
4138**  IMPL_MAP_LOOKUP -- lookup in best open database
4139*/
4140
4141char *
4142impl_map_lookup(map, name, av, pstat)
4143	MAP *map;
4144	char *name;
4145	char **av;
4146	int *pstat;
4147{
4148	if (tTd(38, 20))
4149		printf("impl_map_lookup(%s, %s)\n",
4150			map->map_mname, name);
4151
4152#ifdef NEWDB
4153	if (bitset(MF_IMPL_HASH, map->map_mflags))
4154		return db_map_lookup(map, name, av, pstat);
4155#endif
4156#ifdef NDBM
4157	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4158		return ndbm_map_lookup(map, name, av, pstat);
4159#endif
4160	return stab_map_lookup(map, name, av, pstat);
4161}
4162
4163/*
4164**  IMPL_MAP_STORE -- store in open databases
4165*/
4166
4167void
4168impl_map_store(map, lhs, rhs)
4169	MAP *map;
4170	char *lhs;
4171	char *rhs;
4172{
4173	if (tTd(38, 12))
4174		printf("impl_map_store(%s, %s, %s)\n",
4175			map->map_mname, lhs, rhs);
4176#ifdef NEWDB
4177	if (bitset(MF_IMPL_HASH, map->map_mflags))
4178		db_map_store(map, lhs, rhs);
4179#endif
4180#ifdef NDBM
4181	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4182		ndbm_map_store(map, lhs, rhs);
4183#endif
4184	stab_map_store(map, lhs, rhs);
4185}
4186
4187/*
4188**  IMPL_MAP_OPEN -- implicit database open
4189*/
4190
4191bool
4192impl_map_open(map, mode)
4193	MAP *map;
4194	int mode;
4195{
4196	if (tTd(38, 2))
4197		printf("impl_map_open(%s, %s, %d)\n",
4198			map->map_mname, map->map_file, mode);
4199
4200	mode &= O_ACCMODE;
4201#ifdef NEWDB
4202	map->map_mflags |= MF_IMPL_HASH;
4203	if (hash_map_open(map, mode))
4204	{
4205# ifdef NDBM_YP_COMPAT
4206		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
4207# endif
4208			return TRUE;
4209	}
4210	else
4211		map->map_mflags &= ~MF_IMPL_HASH;
4212#endif
4213#ifdef NDBM
4214	map->map_mflags |= MF_IMPL_NDBM;
4215	if (ndbm_map_open(map, mode))
4216	{
4217		return TRUE;
4218	}
4219	else
4220		map->map_mflags &= ~MF_IMPL_NDBM;
4221#endif
4222
4223#if defined(NEWDB) || defined(NDBM)
4224	if (Verbose)
4225		message("WARNING: cannot open alias database %s%s",
4226			map->map_file,
4227			mode == O_RDONLY ? "; reading text version" : "");
4228#else
4229	if (mode != O_RDONLY)
4230		usrerr("Cannot rebuild aliases: no database format defined");
4231#endif
4232
4233	if (mode == O_RDONLY)
4234		return stab_map_open(map, mode);
4235	else
4236		return FALSE;
4237}
4238
4239
4240/*
4241**  IMPL_MAP_CLOSE -- close any open database(s)
4242*/
4243
4244void
4245impl_map_close(map)
4246	MAP *map;
4247{
4248	if (tTd(38, 9))
4249		printf("impl_map_close(%s, %s, %lx)\n",
4250			map->map_mname, map->map_file, map->map_mflags);
4251#ifdef NEWDB
4252	if (bitset(MF_IMPL_HASH, map->map_mflags))
4253	{
4254		db_map_close(map);
4255		map->map_mflags &= ~MF_IMPL_HASH;
4256	}
4257#endif
4258
4259#ifdef NDBM
4260	if (bitset(MF_IMPL_NDBM, map->map_mflags))
4261	{
4262		ndbm_map_close(map);
4263		map->map_mflags &= ~MF_IMPL_NDBM;
4264	}
4265#endif
4266}
4267/*
4268**  User map class.
4269**
4270**	Provides access to the system password file.
4271*/
4272
4273/*
4274**  USER_MAP_OPEN -- open user map
4275**
4276**	Really just binds field names to field numbers.
4277*/
4278
4279bool
4280user_map_open(map, mode)
4281	MAP *map;
4282	int mode;
4283{
4284	if (tTd(38, 2))
4285		printf("user_map_open(%s, %d)\n",
4286			map->map_mname, mode);
4287
4288	mode &= O_ACCMODE;
4289	if (mode != O_RDONLY)
4290	{
4291		/* issue a pseudo-error message */
4292#ifdef ENOSYS
4293		errno = ENOSYS;
4294#else
4295# ifdef EFTYPE
4296		errno = EFTYPE;
4297# else
4298		errno = ENXIO;
4299# endif
4300#endif
4301		return FALSE;
4302	}
4303	if (map->map_valcolnm == NULL)
4304		/* nothing */ ;
4305	else if (strcasecmp(map->map_valcolnm, "name") == 0)
4306		map->map_valcolno = 1;
4307	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
4308		map->map_valcolno = 2;
4309	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
4310		map->map_valcolno = 3;
4311	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
4312		map->map_valcolno = 4;
4313	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
4314		map->map_valcolno = 5;
4315	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
4316		map->map_valcolno = 6;
4317	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
4318		map->map_valcolno = 7;
4319	else
4320	{
4321		syserr("User map %s: unknown column name %s",
4322			map->map_mname, map->map_valcolnm);
4323		return FALSE;
4324	}
4325	return TRUE;
4326}
4327
4328
4329/*
4330**  USER_MAP_LOOKUP -- look up a user in the passwd file.
4331*/
4332
4333/* ARGSUSED3 */
4334char *
4335user_map_lookup(map, key, av, statp)
4336	MAP *map;
4337	char *key;
4338	char **av;
4339	int *statp;
4340{
4341	struct passwd *pw;
4342	auto bool fuzzy;
4343
4344	if (tTd(38, 20))
4345		printf("user_map_lookup(%s, %s)\n",
4346			map->map_mname, key);
4347
4348	pw = finduser(key, &fuzzy);
4349	if (pw == NULL)
4350		return NULL;
4351	if (bitset(MF_MATCHONLY, map->map_mflags))
4352		return map_rewrite(map, key, strlen(key), NULL);
4353	else
4354	{
4355		char *rwval = NULL;
4356		char buf[30];
4357
4358		switch (map->map_valcolno)
4359		{
4360		  case 0:
4361		  case 1:
4362			rwval = pw->pw_name;
4363			break;
4364
4365		  case 2:
4366			rwval = pw->pw_passwd;
4367			break;
4368
4369		  case 3:
4370			snprintf(buf, sizeof buf, "%d", pw->pw_uid);
4371			rwval = buf;
4372			break;
4373
4374		  case 4:
4375			snprintf(buf, sizeof buf, "%d", pw->pw_gid);
4376			rwval = buf;
4377			break;
4378
4379		  case 5:
4380			rwval = pw->pw_gecos;
4381			break;
4382
4383		  case 6:
4384			rwval = pw->pw_dir;
4385			break;
4386
4387		  case 7:
4388			rwval = pw->pw_shell;
4389			break;
4390		}
4391		return map_rewrite(map, rwval, strlen(rwval), av);
4392	}
4393}
4394/*
4395**  Program map type.
4396**
4397**	This provides access to arbitrary programs.  It should be used
4398**	only very sparingly, since there is no way to bound the cost
4399**	of invoking an arbitrary program.
4400*/
4401
4402char *
4403prog_map_lookup(map, name, av, statp)
4404	MAP *map;
4405	char *name;
4406	char **av;
4407	int *statp;
4408{
4409	int i;
4410	register char *p;
4411	int fd;
4412	auto pid_t pid;
4413	char *rval;
4414	int stat;
4415	char *argv[MAXPV + 1];
4416	char buf[MAXLINE];
4417
4418	if (tTd(38, 20))
4419		printf("prog_map_lookup(%s, %s) %s\n",
4420			map->map_mname, name, map->map_file);
4421
4422	i = 0;
4423	argv[i++] = map->map_file;
4424	if (map->map_rebuild != NULL)
4425	{
4426		snprintf(buf, sizeof buf, "%s", map->map_rebuild);
4427		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
4428		{
4429			if (i >= MAXPV - 1)
4430				break;
4431			argv[i++] = p;
4432		}
4433	}
4434	argv[i++] = name;
4435	argv[i] = NULL;
4436	if (tTd(38, 21))
4437	{
4438		printf("prog_open:");
4439		for (i = 0; argv[i] != NULL; i++)
4440			printf(" %s", argv[i]);
4441		printf("\n");
4442	}
4443	(void) blocksignal(SIGCHLD);
4444	pid = prog_open(argv, &fd, CurEnv);
4445	if (pid < 0)
4446	{
4447		if (!bitset(MF_OPTIONAL, map->map_mflags))
4448			syserr("prog_map_lookup(%s) failed (%s) -- closing",
4449				map->map_mname, errstring(errno));
4450		else if (tTd(38, 9))
4451			printf("prog_map_lookup(%s) failed (%s) -- closing",
4452				map->map_mname, errstring(errno));
4453		map->map_mflags &= ~(MF_VALID|MF_OPEN);
4454		*statp = EX_OSFILE;
4455		return NULL;
4456	}
4457	i = read(fd, buf, sizeof buf - 1);
4458	if (i < 0)
4459	{
4460		syserr("prog_map_lookup(%s): read error %s\n",
4461			map->map_mname, errstring(errno));
4462		rval = NULL;
4463	}
4464	else if (i == 0)
4465	{
4466		if (tTd(38, 20))
4467			printf("prog_map_lookup(%s): empty answer\n",
4468				map->map_mname);
4469		rval = NULL;
4470	}
4471	else
4472	{
4473		buf[i] = '\0';
4474		p = strchr(buf, '\n');
4475		if (p != NULL)
4476			*p = '\0';
4477
4478		/* collect the return value */
4479		if (bitset(MF_MATCHONLY, map->map_mflags))
4480			rval = map_rewrite(map, name, strlen(name), NULL);
4481		else
4482			rval = map_rewrite(map, buf, strlen(buf), NULL);
4483
4484		/* now flush any additional output */
4485		while ((i = read(fd, buf, sizeof buf)) > 0)
4486			continue;
4487	}
4488
4489	/* wait for the process to terminate */
4490	close(fd);
4491	stat = waitfor(pid);
4492	(void) releasesignal(SIGCHLD);
4493
4494	if (stat == -1)
4495	{
4496		syserr("prog_map_lookup(%s): wait error %s\n",
4497			map->map_mname, errstring(errno));
4498		*statp = EX_SOFTWARE;
4499		rval = NULL;
4500	}
4501	else if (WIFEXITED(stat))
4502	{
4503		if ((*statp = WEXITSTATUS(stat)) != EX_OK)
4504			rval = NULL;
4505	}
4506	else
4507	{
4508		syserr("prog_map_lookup(%s): child died on signal %d",
4509			map->map_mname, stat);
4510		*statp = EX_UNAVAILABLE;
4511		rval = NULL;
4512	}
4513	return rval;
4514}
4515/*
4516**  Sequenced map type.
4517**
4518**	Tries each map in order until something matches, much like
4519**	implicit.  Stores go to the first map in the list that can
4520**	support storing.
4521**
4522**	This is slightly unusual in that there are two interfaces.
4523**	The "sequence" interface lets you stack maps arbitrarily.
4524**	The "switch" interface builds a sequence map by looking
4525**	at a system-dependent configuration file such as
4526**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
4527**
4528**	We don't need an explicit open, since all maps are
4529**	opened during startup, including underlying maps.
4530*/
4531
4532/*
4533**  SEQ_MAP_PARSE -- Sequenced map parsing
4534*/
4535
4536bool
4537seq_map_parse(map, ap)
4538	MAP *map;
4539	char *ap;
4540{
4541	int maxmap;
4542
4543	if (tTd(38, 2))
4544		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
4545	maxmap = 0;
4546	while (*ap != '\0')
4547	{
4548		register char *p;
4549		STAB *s;
4550
4551		/* find beginning of map name */
4552		while (isascii(*ap) && isspace(*ap))
4553			ap++;
4554		for (p = ap; isascii(*p) && isalnum(*p); p++)
4555			continue;
4556		if (*p != '\0')
4557			*p++ = '\0';
4558		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
4559			p++;
4560		if (*ap == '\0')
4561		{
4562			ap = p;
4563			continue;
4564		}
4565		s = stab(ap, ST_MAP, ST_FIND);
4566		if (s == NULL)
4567		{
4568			syserr("Sequence map %s: unknown member map %s",
4569				map->map_mname, ap);
4570		}
4571		else if (maxmap == MAXMAPSTACK)
4572		{
4573			syserr("Sequence map %s: too many member maps (%d max)",
4574				map->map_mname, MAXMAPSTACK);
4575			maxmap++;
4576		}
4577		else if (maxmap < MAXMAPSTACK)
4578		{
4579			map->map_stack[maxmap++] = &s->s_map;
4580		}
4581		ap = p;
4582	}
4583	return TRUE;
4584}
4585
4586
4587/*
4588**  SWITCH_MAP_OPEN -- open a switched map
4589**
4590**	This looks at the system-dependent configuration and builds
4591**	a sequence map that does the same thing.
4592**
4593**	Every system must define a switch_map_find routine in conf.c
4594**	that will return the list of service types associated with a
4595**	given service class.
4596*/
4597
4598bool
4599switch_map_open(map, mode)
4600	MAP *map;
4601	int mode;
4602{
4603	int mapno;
4604	int nmaps;
4605	char *maptype[MAXMAPSTACK];
4606
4607	if (tTd(38, 2))
4608		printf("switch_map_open(%s, %s, %d)\n",
4609			map->map_mname, map->map_file, mode);
4610
4611	mode &= O_ACCMODE;
4612	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
4613	if (tTd(38, 19))
4614	{
4615		printf("\tswitch_map_find => %d\n", nmaps);
4616		for (mapno = 0; mapno < nmaps; mapno++)
4617			printf("\t\t%s\n", maptype[mapno]);
4618	}
4619	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
4620		return FALSE;
4621
4622	for (mapno = 0; mapno < nmaps; mapno++)
4623	{
4624		register STAB *s;
4625		char nbuf[MAXNAME + 1];
4626
4627		if (maptype[mapno] == NULL)
4628			continue;
4629		(void) snprintf(nbuf, sizeof nbuf, "%s.%s",
4630			map->map_mname, maptype[mapno]);
4631		s = stab(nbuf, ST_MAP, ST_FIND);
4632		if (s == NULL)
4633		{
4634			syserr("Switch map %s: unknown member map %s",
4635				map->map_mname, nbuf);
4636		}
4637		else
4638		{
4639			map->map_stack[mapno] = &s->s_map;
4640			if (tTd(38, 4))
4641				printf("\tmap_stack[%d] = %s:%s\n",
4642					mapno, s->s_map.map_class->map_cname,
4643					nbuf);
4644		}
4645	}
4646	return TRUE;
4647}
4648
4649
4650/*
4651**  SEQ_MAP_CLOSE -- close all underlying maps
4652*/
4653
4654void
4655seq_map_close(map)
4656	MAP *map;
4657{
4658	int mapno;
4659
4660	if (tTd(38, 9))
4661		printf("seq_map_close(%s)\n", map->map_mname);
4662
4663	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4664	{
4665		MAP *mm = map->map_stack[mapno];
4666
4667		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
4668			continue;
4669		mm->map_class->map_close(mm);
4670		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4671	}
4672}
4673
4674
4675/*
4676**  SEQ_MAP_LOOKUP -- sequenced map lookup
4677*/
4678
4679char *
4680seq_map_lookup(map, key, args, pstat)
4681	MAP *map;
4682	char *key;
4683	char **args;
4684	int *pstat;
4685{
4686	int mapno;
4687	int mapbit = 0x01;
4688	bool tempfail = FALSE;
4689
4690	if (tTd(38, 20))
4691		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
4692
4693	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
4694	{
4695		MAP *mm = map->map_stack[mapno];
4696		char *rv;
4697
4698		if (mm == NULL)
4699			continue;
4700		if (!bitset(MF_OPEN, mm->map_mflags))
4701		{
4702			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
4703			{
4704				*pstat = EX_UNAVAILABLE;
4705				return NULL;
4706			}
4707			continue;
4708		}
4709		*pstat = EX_OK;
4710		rv = mm->map_class->map_lookup(mm, key, args, pstat);
4711		if (rv != NULL)
4712			return rv;
4713		if (*pstat == EX_TEMPFAIL)
4714		{
4715			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
4716				return NULL;
4717			tempfail = TRUE;
4718		}
4719		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
4720			break;
4721	}
4722	if (tempfail)
4723		*pstat = EX_TEMPFAIL;
4724	else if (*pstat == EX_OK)
4725		*pstat = EX_NOTFOUND;
4726	return NULL;
4727}
4728
4729
4730/*
4731**  SEQ_MAP_STORE -- sequenced map store
4732*/
4733
4734void
4735seq_map_store(map, key, val)
4736	MAP *map;
4737	char *key;
4738	char *val;
4739{
4740	int mapno;
4741
4742	if (tTd(38, 12))
4743		printf("seq_map_store(%s, %s, %s)\n",
4744			map->map_mname, key, val);
4745
4746	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4747	{
4748		MAP *mm = map->map_stack[mapno];
4749
4750		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
4751			continue;
4752
4753		mm->map_class->map_store(mm, key, val);
4754		return;
4755	}
4756	syserr("seq_map_store(%s, %s, %s): no writable map",
4757		map->map_mname, key, val);
4758}
4759/*
4760**  NULL stubs
4761*/
4762
4763/* ARGSUSED */
4764bool
4765null_map_open(map, mode)
4766	MAP *map;
4767	int mode;
4768{
4769	return TRUE;
4770}
4771
4772/* ARGSUSED */
4773void
4774null_map_close(map)
4775	MAP *map;
4776{
4777	return;
4778}
4779
4780char *
4781null_map_lookup(map, key, args, pstat)
4782	MAP *map;
4783	char *key;
4784	char **args;
4785	int *pstat;
4786{
4787	*pstat = EX_NOTFOUND;
4788	return NULL;
4789}
4790
4791/* ARGSUSED */
4792void
4793null_map_store(map, key, val)
4794	MAP *map;
4795	char *key;
4796	char *val;
4797{
4798	return;
4799}
4800
4801
4802/*
4803**  BOGUS stubs
4804*/
4805
4806char *
4807bogus_map_lookup(map, key, args, pstat)
4808	MAP *map;
4809	char *key;
4810	char **args;
4811	int *pstat;
4812{
4813	*pstat = EX_TEMPFAIL;
4814	return NULL;
4815}
4816
4817MAPCLASS	BogusMapClass =
4818{
4819	"bogus-map",		NULL,		0,
4820	NULL,		bogus_map_lookup,	null_map_store,
4821	null_map_open,	null_map_close,
4822};
4823/*
4824**  REGEX modules
4825*/
4826
4827#ifdef MAP_REGEX
4828
4829# include <regex.h>
4830
4831# define DEFAULT_DELIM	CONDELSE
4832
4833# define END_OF_FIELDS	-1
4834
4835# define ERRBUF_SIZE	80
4836# define MAX_MATCH	32
4837
4838# define xnalloc(s)	memset(xalloc(s), 0, s);
4839
4840struct regex_map
4841{
4842	regex_t	pattern_buf;		/* xalloc it */
4843	int	*regex_subfields;	/* move to type MAP */
4844	char	*delim;			/* move to type MAP */
4845};
4846
4847static int
4848parse_fields(s, ibuf, blen, nr_substrings)
4849	char *s;
4850	int *ibuf;		/* array */
4851	int blen;		/* number of elements in ibuf */
4852	int nr_substrings;	/* number of substrings in the pattern */
4853{
4854	register char *cp;
4855	int i = 0;
4856	bool lastone = FALSE;
4857
4858	blen--;		/* for terminating END_OF_FIELDS */
4859	cp = s;
4860	do
4861	{
4862		for (;; cp++)
4863		{
4864			if (*cp == ',')
4865			{
4866				*cp = '\0';
4867				break;
4868			}
4869			if (*cp == '\0')
4870			{
4871				lastone = TRUE;
4872				break;
4873			}
4874		}
4875		if (i < blen)
4876		{
4877			int val = atoi(s);
4878
4879			if (val < 0 || val >= nr_substrings)
4880			{
4881				syserr("field (%d) out of range, only %d substrings in pattern",
4882				       val, nr_substrings);
4883				return -1;
4884			}
4885			ibuf[i++] = val;
4886		}
4887		else
4888		{
4889			syserr("too many fields, %d max\n", blen);
4890			return -1;
4891		}
4892		s = ++cp;
4893	} while (!lastone);
4894	ibuf[i] = END_OF_FIELDS;
4895	return i;
4896}
4897
4898bool
4899regex_map_init(map, ap)
4900	MAP *map;
4901	char *ap;
4902{
4903	int regerr;
4904	struct regex_map *map_p;
4905	register char *p;
4906	char *sub_param = NULL;
4907	int pflags;
4908	static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
4909
4910	if (tTd(38, 2))
4911		printf("regex_map_init: mapname '%s', args '%s'\n",
4912				map->map_mname, ap);
4913
4914	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
4915
4916	p = ap;
4917
4918	map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map));
4919
4920	for (;;)
4921        {
4922		while (isascii(*p) && isspace(*p))
4923			p++;
4924		if (*p != '-')
4925			break;
4926		switch (*++p)
4927		{
4928		  case 'n':	/* not */
4929			map->map_mflags |= MF_REGEX_NOT;
4930			break;
4931
4932		  case 'f':	/* case sensitive */
4933			map->map_mflags |= MF_NOFOLDCASE;
4934			pflags &= ~REG_ICASE;
4935			break;
4936
4937		  case 'b':	/* basic regular expressions */
4938			pflags &= ~REG_EXTENDED;
4939			break;
4940
4941		  case 's':	/* substring match () syntax */
4942			sub_param = ++p;
4943			pflags &= ~REG_NOSUB;
4944			break;
4945
4946		  case 'd':	/* delimiter */
4947			map_p->delim = ++p;
4948			break;
4949
4950		  case 'a':	/* map append */
4951			map->map_app = ++p;
4952			break;
4953
4954		  case 'm':	/* matchonly */
4955			map->map_mflags |= MF_MATCHONLY;
4956			break;
4957
4958		}
4959                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4960                        p++;
4961                if (*p != '\0')
4962                        *p++ = '\0';
4963	}
4964	if (tTd(38, 3))
4965		printf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
4966
4967	if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0)
4968	{
4969		/* Errorhandling */
4970		char errbuf[ERRBUF_SIZE];
4971
4972		regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE);
4973		syserr("pattern-compile-error: %s\n", errbuf);
4974		free(map_p);
4975		return FALSE;
4976	}
4977
4978	if (map->map_app != NULL)
4979		map->map_app = newstr(map->map_app);
4980	if (map_p->delim != NULL)
4981		map_p->delim = newstr(map_p->delim);
4982	else
4983		map_p->delim = defdstr;
4984
4985	if (!bitset(REG_NOSUB, pflags))
4986	{
4987		/* substring matching */
4988		int substrings;
4989		int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1));
4990
4991		substrings = map_p->pattern_buf.re_nsub + 1;
4992
4993		if (tTd(38, 3))
4994			printf("regex_map_init: nr of substrings %d\n", substrings);
4995
4996		if (substrings >= MAX_MATCH)
4997		{
4998			syserr("too many substrings, %d max\n", MAX_MATCH);
4999			free(map_p);
5000			return FALSE;
5001		}
5002		if (sub_param != NULL && sub_param[0] != '\0')
5003		{
5004			/* optional parameter -sfields */
5005			if (parse_fields(sub_param, fields,
5006					 MAX_MATCH + 1, substrings) == -1)
5007				return FALSE;
5008		}
5009		else
5010		{
5011			/* set default fields  */
5012			int i;
5013
5014			for (i = 0; i < substrings; i++)
5015				fields[i] = i;
5016			fields[i] = END_OF_FIELDS;
5017		}
5018		map_p->regex_subfields = fields;
5019		if (tTd(38, 3))
5020		{
5021			int *ip;
5022
5023			printf("regex_map_init: subfields");
5024			for (ip = fields; *ip != END_OF_FIELDS; ip++)
5025				printf(" %d", *ip);
5026			printf("\n");
5027		}
5028	}
5029	map->map_db1 = (ARBPTR_T)map_p;	/* dirty hack */
5030
5031	return TRUE;
5032}
5033
5034static char *
5035regex_map_rewrite(map, s, slen, av)
5036	MAP *map;
5037	const char *s;
5038	size_t slen;
5039	char **av;
5040{
5041	if (bitset(MF_MATCHONLY, map->map_mflags))
5042		return map_rewrite(map, av[0], strlen(av[0]), NULL);
5043	else
5044		return map_rewrite(map, s, slen, NULL);
5045}
5046
5047char *
5048regex_map_lookup(map, name, av, statp)
5049	MAP *map;
5050	char *name;
5051	char **av;
5052	int *statp;
5053{
5054	int reg_res;
5055	struct regex_map *map_p;
5056	regmatch_t pmatch[MAX_MATCH];
5057
5058	if (tTd(38, 20))
5059	{
5060		char **cpp;
5061
5062		printf("regex_map_lookup: key '%s'\n", name);
5063		for (cpp = av; cpp && *cpp; cpp++)
5064			printf("regex_map_lookup: arg '%s'\n", *cpp);
5065	}
5066
5067	map_p = (struct regex_map *)(map->map_db1);
5068	reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0);
5069
5070	if (bitset(MF_REGEX_NOT, map->map_mflags))
5071	{
5072		/* option -n */
5073		if (reg_res == REG_NOMATCH)
5074			return regex_map_rewrite(map, "", (size_t)0, av);
5075		else
5076			return NULL;
5077	}
5078	if (reg_res == REG_NOMATCH)
5079		return NULL;
5080
5081	if (map_p->regex_subfields != NULL)
5082	{
5083		/* option -s */
5084		static char retbuf[MAXNAME];
5085		int fields[MAX_MATCH + 1];
5086		bool first = TRUE;
5087		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
5088		bool quotemode = FALSE, bslashmode = FALSE;
5089		register char *dp, *sp;
5090		char *endp, *ldp;
5091		int *ip;
5092
5093		dp = retbuf;
5094		ldp = retbuf + sizeof(retbuf) - 1;
5095
5096		if (av[1] != NULL)
5097		{
5098			if (parse_fields(av[1], fields, MAX_MATCH + 1,
5099				 (int) map_p->pattern_buf.re_nsub + 1) == -1)
5100			{
5101				*statp = EX_CONFIG;
5102				return NULL;
5103			}
5104			ip = fields;
5105		}
5106		else
5107			ip = map_p->regex_subfields;
5108
5109		for ( ; *ip != END_OF_FIELDS; ip++)
5110		{
5111			if (!first)
5112			{
5113				for (sp = map_p->delim; *sp; sp++)
5114				{
5115					if (dp < ldp)
5116						*dp++ = *sp;
5117				}
5118			}
5119			else
5120				first = FALSE;
5121
5122
5123			if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
5124				continue;
5125
5126			sp = name + pmatch[*ip].rm_so;
5127			endp = name + pmatch[*ip].rm_eo;
5128			for (; endp > sp; sp++)
5129			{
5130				if (dp < ldp)
5131				{
5132					if(bslashmode)
5133					{
5134						*dp++ = *sp;
5135						bslashmode = FALSE;
5136					}
5137					else if(quotemode && *sp != '"' &&
5138						*sp != '\\')
5139					{
5140						*dp++ = *sp;
5141					}
5142					else switch(*dp++ = *sp)
5143					{
5144						case '\\':
5145						bslashmode = TRUE;
5146						break;
5147
5148						case '(':
5149						cmntcnt++;
5150						break;
5151
5152						case ')':
5153						cmntcnt--;
5154						break;
5155
5156						case '<':
5157						anglecnt++;
5158						break;
5159
5160						case '>':
5161						anglecnt--;
5162						break;
5163
5164						case ' ':
5165						spacecnt++;
5166						break;
5167
5168						case '"':
5169						quotemode = !quotemode;
5170						break;
5171					}
5172				}
5173			}
5174		}
5175		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
5176		    bslashmode || spacecnt != 0)
5177		{
5178			sm_syslog(LOG_WARNING, NOQID,
5179				 "Warning: regex may cause prescan() failure map=%s lookup=%s",
5180				 map->map_mname, name);
5181			return NULL;
5182		}
5183
5184		*dp = '\0';
5185
5186		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
5187	}
5188	return regex_map_rewrite(map, "", (size_t)0, av);
5189}
5190#endif /* MAP_REGEX */
5191