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