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