xref: /freebsd/contrib/sendmail/src/map.c (revision ae83180158c4c937f170e31eff311b18c0286a93)
1 /*
2  * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #include <sendmail.h>
15 
16 SM_RCSID("@(#)$Id: map.c,v 8.645.2.1 2002/06/21 20:25:23 ca Exp $")
17 
18 #if LDAPMAP
19 # include <sm/ldap.h>
20 #endif /* LDAPMAP */
21 
22 #if NDBM
23 # include <ndbm.h>
24 # ifdef R_FIRST
25   ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
26   ERROR README:	the README file about tweaking Berkeley DB so it can
27   ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
28   ERROR README: and use -DNEWDB instead.
29 # endif /* R_FIRST */
30 #endif /* NDBM */
31 #if NEWDB
32 # include <db.h>
33 # ifndef DB_VERSION_MAJOR
34 #  define DB_VERSION_MAJOR 1
35 # endif /* ! DB_VERSION_MAJOR */
36 #endif /* NEWDB */
37 #if NIS
38   struct dom_binding;	/* forward reference needed on IRIX */
39 # include <rpcsvc/ypclnt.h>
40 # if NDBM
41 #  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
42 # endif /* NDBM */
43 #endif /* NIS */
44 
45 #if NEWDB
46 # if DB_VERSION_MAJOR < 2
47 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
48 # endif /* DB_VERSION_MAJOR < 2 */
49 # if DB_VERSION_MAJOR == 2
50 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
51 # endif /* DB_VERSION_MAJOR == 2 */
52 # if DB_VERSION_MAJOR > 2
53 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
54 # endif /* DB_VERSION_MAJOR > 2 */
55 #endif /* NEWDB */
56 static bool	extract_canonname __P((char *, char *, char *, char[], int));
57 static void	map_close __P((STAB *, int));
58 static void	map_init __P((STAB *, int));
59 #ifdef LDAPMAP
60 static STAB *	ldapmap_findconn __P((SM_LDAP_STRUCT *));
61 #endif /* LDAPMAP */
62 #if NISPLUS
63 static bool	nisplus_getcanonname __P((char *, int, int *));
64 #endif /* NISPLUS */
65 #if NIS
66 static bool	nis_getcanonname __P((char *, int, int *));
67 #endif /* NIS */
68 #if NETINFO
69 static bool	ni_getcanonname __P((char *, int, int *));
70 #endif /* NETINFO */
71 static bool	text_getcanonname __P((char *, int, int *));
72 
73 /* default error message for trying to open a map in write mode */
74 #ifdef ENOSYS
75 # define SM_EMAPCANTWRITE	ENOSYS
76 #else /* ENOSYS */
77 # ifdef EFTYPE
78 #  define SM_EMAPCANTWRITE	EFTYPE
79 # else /* EFTYPE */
80 #  define SM_EMAPCANTWRITE	ENXIO
81 # endif /* EFTYPE */
82 #endif /* ENOSYS */
83 
84 /*
85 **  MAP.C -- implementations for various map classes.
86 **
87 **	Each map class implements a series of functions:
88 **
89 **	bool map_parse(MAP *map, char *args)
90 **		Parse the arguments from the config file.  Return true
91 **		if they were ok, false otherwise.  Fill in map with the
92 **		values.
93 **
94 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
95 **		Look up the key in the given map.  If found, do any
96 **		rewriting the map wants (including "args" if desired)
97 **		and return the value.  Set *pstat to the appropriate status
98 **		on error and return NULL.  Args will be NULL if called
99 **		from the alias routines, although this should probably
100 **		not be relied upon.  It is suggested you call map_rewrite
101 **		to return the results -- it takes care of null termination
102 **		and uses a dynamically expanded buffer as needed.
103 **
104 **	void map_store(MAP *map, char *key, char *value)
105 **		Store the key:value pair in the map.
106 **
107 **	bool map_open(MAP *map, int mode)
108 **		Open the map for the indicated mode.  Mode should
109 **		be either O_RDONLY or O_RDWR.  Return true if it
110 **		was opened successfully, false otherwise.  If the open
111 **		failed and the MF_OPTIONAL flag is not set, it should
112 **		also print an error.  If the MF_ALIAS bit is set
113 **		and this map class understands the @:@ convention, it
114 **		should call aliaswait() before returning.
115 **
116 **	void map_close(MAP *map)
117 **		Close the map.
118 **
119 **	This file also includes the implementation for getcanonname.
120 **	It is currently implemented in a pretty ad-hoc manner; it ought
121 **	to be more properly integrated into the map structure.
122 */
123 
124 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
125 # define LOCK_ON_OPEN	1	/* we can open/create a locked file */
126 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
127 # define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
128 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
129 
130 /*
131 **  MAP_PARSEARGS -- parse config line arguments for database lookup
132 **
133 **	This is a generic version of the map_parse method.
134 **
135 **	Parameters:
136 **		map -- the map being initialized.
137 **		ap -- a pointer to the args on the config line.
138 **
139 **	Returns:
140 **		true -- if everything parsed OK.
141 **		false -- otherwise.
142 **
143 **	Side Effects:
144 **		null terminates the filename; stores it in map
145 */
146 
147 bool
148 map_parseargs(map, ap)
149 	MAP *map;
150 	char *ap;
151 {
152 	register char *p = ap;
153 
154 	/*
155 	**  There is no check whether there is really an argument,
156 	**  but that's not important enough to warrant extra code.
157 	*/
158 
159 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
160 	map->map_spacesub = SpaceSub;	/* default value */
161 	for (;;)
162 	{
163 		while (isascii(*p) && isspace(*p))
164 			p++;
165 		if (*p != '-')
166 			break;
167 		switch (*++p)
168 		{
169 		  case 'N':
170 			map->map_mflags |= MF_INCLNULL;
171 			map->map_mflags &= ~MF_TRY0NULL;
172 			break;
173 
174 		  case 'O':
175 			map->map_mflags &= ~MF_TRY1NULL;
176 			break;
177 
178 		  case 'o':
179 			map->map_mflags |= MF_OPTIONAL;
180 			break;
181 
182 		  case 'f':
183 			map->map_mflags |= MF_NOFOLDCASE;
184 			break;
185 
186 		  case 'm':
187 			map->map_mflags |= MF_MATCHONLY;
188 			break;
189 
190 		  case 'A':
191 			map->map_mflags |= MF_APPEND;
192 			break;
193 
194 		  case 'q':
195 			map->map_mflags |= MF_KEEPQUOTES;
196 			break;
197 
198 		  case 'a':
199 			map->map_app = ++p;
200 			break;
201 
202 		  case 'T':
203 			map->map_tapp = ++p;
204 			break;
205 
206 		  case 'k':
207 			while (isascii(*++p) && isspace(*p))
208 				continue;
209 			map->map_keycolnm = p;
210 			break;
211 
212 		  case 'v':
213 			while (isascii(*++p) && isspace(*p))
214 				continue;
215 			map->map_valcolnm = p;
216 			break;
217 
218 		  case 'z':
219 			if (*++p != '\\')
220 				map->map_coldelim = *p;
221 			else
222 			{
223 				switch (*++p)
224 				{
225 				  case 'n':
226 					map->map_coldelim = '\n';
227 					break;
228 
229 				  case 't':
230 					map->map_coldelim = '\t';
231 					break;
232 
233 				  default:
234 					map->map_coldelim = '\\';
235 				}
236 			}
237 			break;
238 
239 		  case 't':
240 			map->map_mflags |= MF_NODEFER;
241 			break;
242 
243 
244 		  case 'S':
245 			map->map_spacesub = *++p;
246 			break;
247 
248 		  case 'D':
249 			map->map_mflags |= MF_DEFER;
250 			break;
251 
252 		  default:
253 			syserr("Illegal option %c map %s", *p, map->map_mname);
254 			break;
255 		}
256 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
257 			p++;
258 		if (*p != '\0')
259 			*p++ = '\0';
260 	}
261 	if (map->map_app != NULL)
262 		map->map_app = newstr(map->map_app);
263 	if (map->map_tapp != NULL)
264 		map->map_tapp = newstr(map->map_tapp);
265 	if (map->map_keycolnm != NULL)
266 		map->map_keycolnm = newstr(map->map_keycolnm);
267 	if (map->map_valcolnm != NULL)
268 		map->map_valcolnm = newstr(map->map_valcolnm);
269 
270 	if (*p != '\0')
271 	{
272 		map->map_file = p;
273 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
274 			p++;
275 		if (*p != '\0')
276 			*p++ = '\0';
277 		map->map_file = newstr(map->map_file);
278 	}
279 
280 	while (*p != '\0' && isascii(*p) && isspace(*p))
281 		p++;
282 	if (*p != '\0')
283 		map->map_rebuild = newstr(p);
284 
285 	if (map->map_file == NULL &&
286 	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
287 	{
288 		syserr("No file name for %s map %s",
289 			map->map_class->map_cname, map->map_mname);
290 		return false;
291 	}
292 	return true;
293 }
294 /*
295 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
296 **
297 **	It also adds the map_app string.  It can be used as a utility
298 **	in the map_lookup method.
299 **
300 **	Parameters:
301 **		map -- the map that causes this.
302 **		s -- the string to rewrite, NOT necessarily null terminated.
303 **		slen -- the length of s.
304 **		av -- arguments to interpolate into buf.
305 **
306 **	Returns:
307 **		Pointer to rewritten result.  This is static data that
308 **		should be copied if it is to be saved!
309 */
310 
311 char *
312 map_rewrite(map, s, slen, av)
313 	register MAP *map;
314 	register const char *s;
315 	size_t slen;
316 	char **av;
317 {
318 	register char *bp;
319 	register char c;
320 	char **avp;
321 	register char *ap;
322 	size_t l;
323 	size_t len;
324 	static size_t buflen = 0;
325 	static char *buf = NULL;
326 
327 	if (tTd(39, 1))
328 	{
329 		sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
330 		if (av == NULL)
331 			sm_dprintf(" (nullv)");
332 		else
333 		{
334 			for (avp = av; *avp != NULL; avp++)
335 				sm_dprintf("\n\t%s", *avp);
336 		}
337 		sm_dprintf("\n");
338 	}
339 
340 	/* count expected size of output (can safely overestimate) */
341 	l = len = slen;
342 	if (av != NULL)
343 	{
344 		const char *sp = s;
345 
346 		while (l-- > 0 && (c = *sp++) != '\0')
347 		{
348 			if (c != '%')
349 				continue;
350 			if (l-- <= 0)
351 				break;
352 			c = *sp++;
353 			if (!(isascii(c) && isdigit(c)))
354 				continue;
355 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
356 				continue;
357 			if (*avp == NULL)
358 				continue;
359 			len += strlen(*avp);
360 		}
361 	}
362 	if (map->map_app != NULL)
363 		len += strlen(map->map_app);
364 	if (buflen < ++len)
365 	{
366 		/* need to malloc additional space */
367 		buflen = len;
368 		if (buf != NULL)
369 			sm_free(buf);
370 		buf = sm_pmalloc_x(buflen);
371 	}
372 
373 	bp = buf;
374 	if (av == NULL)
375 	{
376 		memmove(bp, s, slen);
377 		bp += slen;
378 
379 		/* assert(len > slen); */
380 		len -= slen;
381 	}
382 	else
383 	{
384 		while (slen-- > 0 && (c = *s++) != '\0')
385 		{
386 			if (c != '%')
387 			{
388   pushc:
389 				if (--len <= 0)
390 				     break;
391 				*bp++ = c;
392 				continue;
393 			}
394 			if (slen-- <= 0 || (c = *s++) == '\0')
395 				c = '%';
396 			if (c == '%')
397 				goto pushc;
398 			if (!(isascii(c) && isdigit(c)))
399 			{
400 				*bp++ = '%';
401 				--len;
402 				goto pushc;
403 			}
404 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
405 				continue;
406 			if (*avp == NULL)
407 				continue;
408 
409 			/* transliterate argument into output string */
410 			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
411 				*bp++ = c;
412 		}
413 	}
414 	if (map->map_app != NULL && len > 0)
415 		(void) sm_strlcpy(bp, map->map_app, len);
416 	else
417 		*bp = '\0';
418 	if (tTd(39, 1))
419 		sm_dprintf("map_rewrite => %s\n", buf);
420 	return buf;
421 }
422 /*
423 **  INITMAPS -- rebuild alias maps
424 **
425 **	Parameters:
426 **		none.
427 **
428 **	Returns:
429 **		none.
430 */
431 
432 void
433 initmaps()
434 {
435 #if XDEBUG
436 	checkfd012("entering initmaps");
437 #endif /* XDEBUG */
438 	stabapply(map_init, 0);
439 #if XDEBUG
440 	checkfd012("exiting initmaps");
441 #endif /* XDEBUG */
442 }
443 /*
444 **  MAP_INIT -- rebuild a map
445 **
446 **	Parameters:
447 **		s -- STAB entry: if map: try to rebuild
448 **		unused -- unused variable
449 **
450 **	Returns:
451 **		none.
452 **
453 **	Side Effects:
454 **		will close already open rebuildable map.
455 */
456 
457 /* ARGSUSED1 */
458 static void
459 map_init(s, unused)
460 	register STAB *s;
461 	int unused;
462 {
463 	register MAP *map;
464 
465 	/* has to be a map */
466 	if (s->s_symtype != ST_MAP)
467 		return;
468 
469 	map = &s->s_map;
470 	if (!bitset(MF_VALID, map->map_mflags))
471 		return;
472 
473 	if (tTd(38, 2))
474 		sm_dprintf("map_init(%s:%s, %s)\n",
475 			map->map_class->map_cname == NULL ? "NULL" :
476 				map->map_class->map_cname,
477 			map->map_mname == NULL ? "NULL" : map->map_mname,
478 			map->map_file == NULL ? "NULL" : map->map_file);
479 
480 	if (!bitset(MF_ALIAS, map->map_mflags) ||
481 	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
482 	{
483 		if (tTd(38, 3))
484 			sm_dprintf("\tnot rebuildable\n");
485 		return;
486 	}
487 
488 	/* if already open, close it (for nested open) */
489 	if (bitset(MF_OPEN, map->map_mflags))
490 	{
491 		map->map_mflags |= MF_CLOSING;
492 		map->map_class->map_close(map);
493 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
494 	}
495 
496 	(void) rebuildaliases(map, false);
497 	return;
498 }
499 /*
500 **  OPENMAP -- open a map
501 **
502 **	Parameters:
503 **		map -- map to open (it must not be open).
504 **
505 **	Returns:
506 **		whether open succeeded.
507 */
508 
509 bool
510 openmap(map)
511 	MAP *map;
512 {
513 	bool restore = false;
514 	bool savehold = HoldErrs;
515 	bool savequick = QuickAbort;
516 	int saveerrors = Errors;
517 
518 	if (!bitset(MF_VALID, map->map_mflags))
519 		return false;
520 
521 	/* better safe than sorry... */
522 	if (bitset(MF_OPEN, map->map_mflags))
523 		return true;
524 
525 	/* Don't send a map open error out via SMTP */
526 	if ((OnlyOneError || QuickAbort) &&
527 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
528 	{
529 		restore = true;
530 		HoldErrs = true;
531 		QuickAbort = false;
532 	}
533 
534 	errno = 0;
535 	if (map->map_class->map_open(map, O_RDONLY))
536 	{
537 		if (tTd(38, 4))
538 			sm_dprintf("openmap()\t%s:%s %s: valid\n",
539 				map->map_class->map_cname == NULL ? "NULL" :
540 					map->map_class->map_cname,
541 				map->map_mname == NULL ? "NULL" :
542 					map->map_mname,
543 				map->map_file == NULL ? "NULL" :
544 					map->map_file);
545 		map->map_mflags |= MF_OPEN;
546 		map->map_pid = CurrentPid;
547 	}
548 	else
549 	{
550 		if (tTd(38, 4))
551 			sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
552 				map->map_class->map_cname == NULL ? "NULL" :
553 					map->map_class->map_cname,
554 				map->map_mname == NULL ? "NULL" :
555 					map->map_mname,
556 				map->map_file == NULL ? "NULL" :
557 					map->map_file,
558 				errno == 0 ? "" : ": ",
559 				errno == 0 ? "" : sm_errstring(errno));
560 		if (!bitset(MF_OPTIONAL, map->map_mflags))
561 		{
562 			extern MAPCLASS BogusMapClass;
563 
564 			map->map_orgclass = map->map_class;
565 			map->map_class = &BogusMapClass;
566 			map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
567 			map->map_pid = CurrentPid;
568 		}
569 		else
570 		{
571 			/* don't try again */
572 			map->map_mflags &= ~MF_VALID;
573 		}
574 	}
575 
576 	if (restore)
577 	{
578 		Errors = saveerrors;
579 		HoldErrs = savehold;
580 		QuickAbort = savequick;
581 	}
582 
583 	return bitset(MF_OPEN, map->map_mflags);
584 }
585 /*
586 **  CLOSEMAPS -- close all open maps opened by the current pid.
587 **
588 **	Parameters:
589 **		bogus -- only close bogus maps.
590 **
591 **	Returns:
592 **		none.
593 */
594 
595 void
596 closemaps(bogus)
597 	bool bogus;
598 {
599 	stabapply(map_close, bogus);
600 }
601 /*
602 **  MAP_CLOSE -- close a map opened by the current pid.
603 **
604 **	Parameters:
605 **		s -- STAB entry: if map: try to close
606 **		bogus -- only close bogus maps or MCF_NOTPERSIST maps.
607 **
608 **	Returns:
609 **		none.
610 */
611 
612 /* ARGSUSED1 */
613 static void
614 map_close(s, bogus)
615 	register STAB *s;
616 	int bogus;	/* int because of stabapply(), used as bool */
617 {
618 	MAP *map;
619 	extern MAPCLASS BogusMapClass;
620 
621 	if (s->s_symtype != ST_MAP)
622 		return;
623 
624 	map = &s->s_map;
625 
626 	/*
627 	**  close the map iff:
628 	**  it is valid and open and opened by this process
629 	**  and (!bogus or it's a bogus map or it is not persistent)
630 	**  negate this: return iff
631 	**  it is not valid or it is not open or not opened by this process
632 	**  or (bogus and it's not a bogus map and it's not not-persistent)
633 	*/
634 
635 	if (!bitset(MF_VALID, map->map_mflags) ||
636 	    !bitset(MF_OPEN, map->map_mflags) ||
637 	    bitset(MF_CLOSING, map->map_mflags) ||
638 	    map->map_pid != CurrentPid ||
639 	    (bogus && map->map_class != &BogusMapClass &&
640 	     !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
641 		return;
642 
643 	if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
644 	    map->map_orgclass != &BogusMapClass)
645 		map->map_class = map->map_orgclass;
646 	if (tTd(38, 5))
647 		sm_dprintf("closemaps: closing %s (%s)\n",
648 			map->map_mname == NULL ? "NULL" : map->map_mname,
649 			map->map_file == NULL ? "NULL" : map->map_file);
650 
651 	if (!bitset(MF_OPENBOGUS, map->map_mflags))
652 	{
653 		map->map_mflags |= MF_CLOSING;
654 		map->map_class->map_close(map);
655 	}
656 	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
657 }
658 /*
659 **  GETCANONNAME -- look up name using service switch
660 **
661 **	Parameters:
662 **		host -- the host name to look up.
663 **		hbsize -- the size of the host buffer.
664 **		trymx -- if set, try MX records.
665 **		pttl -- pointer to return TTL (can be NULL).
666 **
667 **	Returns:
668 **		true -- if the host was found.
669 **		false -- otherwise.
670 */
671 
672 bool
673 getcanonname(host, hbsize, trymx, pttl)
674 	char *host;
675 	int hbsize;
676 	bool trymx;
677 	int *pttl;
678 {
679 	int nmaps;
680 	int mapno;
681 	bool found = false;
682 	bool got_tempfail = false;
683 	auto int status;
684 	char *maptype[MAXMAPSTACK];
685 	short mapreturn[MAXMAPACTIONS];
686 
687 	nmaps = switch_map_find("hosts", maptype, mapreturn);
688 	if (pttl != 0)
689 		*pttl = SM_DEFAULT_TTL;
690 	for (mapno = 0; mapno < nmaps; mapno++)
691 	{
692 		int i;
693 
694 		if (tTd(38, 20))
695 			sm_dprintf("getcanonname(%s), trying %s\n",
696 				host, maptype[mapno]);
697 		if (strcmp("files", maptype[mapno]) == 0)
698 		{
699 			found = text_getcanonname(host, hbsize, &status);
700 		}
701 #if NIS
702 		else if (strcmp("nis", maptype[mapno]) == 0)
703 		{
704 			found = nis_getcanonname(host, hbsize, &status);
705 		}
706 #endif /* NIS */
707 #if NISPLUS
708 		else if (strcmp("nisplus", maptype[mapno]) == 0)
709 		{
710 			found = nisplus_getcanonname(host, hbsize, &status);
711 		}
712 #endif /* NISPLUS */
713 #if NAMED_BIND
714 		else if (strcmp("dns", maptype[mapno]) == 0)
715 		{
716 			found = dns_getcanonname(host, hbsize, trymx, &status,							 pttl);
717 		}
718 #endif /* NAMED_BIND */
719 #if NETINFO
720 		else if (strcmp("netinfo", maptype[mapno]) == 0)
721 		{
722 			found = ni_getcanonname(host, hbsize, &status);
723 		}
724 #endif /* NETINFO */
725 		else
726 		{
727 			found = false;
728 			status = EX_UNAVAILABLE;
729 		}
730 
731 		/*
732 		**  Heuristic: if $m is not set, we are running during system
733 		**  startup.  In this case, when a name is apparently found
734 		**  but has no dot, treat is as not found.  This avoids
735 		**  problems if /etc/hosts has no FQDN but is listed first
736 		**  in the service switch.
737 		*/
738 
739 		if (found &&
740 		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
741 			break;
742 
743 		/* see if we should continue */
744 		if (status == EX_TEMPFAIL)
745 		{
746 			i = MA_TRYAGAIN;
747 			got_tempfail = true;
748 		}
749 		else if (status == EX_NOTFOUND)
750 			i = MA_NOTFOUND;
751 		else
752 			i = MA_UNAVAIL;
753 		if (bitset(1 << mapno, mapreturn[i]))
754 			break;
755 	}
756 
757 	if (found)
758 	{
759 		char *d;
760 
761 		if (tTd(38, 20))
762 			sm_dprintf("getcanonname(%s), found\n", host);
763 
764 		/*
765 		**  If returned name is still single token, compensate
766 		**  by tagging on $m.  This is because some sites set
767 		**  up their DNS or NIS databases wrong.
768 		*/
769 
770 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
771 		{
772 			d = macvalue('m', CurEnv);
773 			if (d != NULL &&
774 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
775 			{
776 				if (host[strlen(host) - 1] != '.')
777 					(void) sm_strlcat2(host, ".", d,
778 							   hbsize);
779 				else
780 					(void) sm_strlcat(host, d, hbsize);
781 			}
782 			else
783 				return false;
784 		}
785 		return true;
786 	}
787 
788 	if (tTd(38, 20))
789 		sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
790 			status);
791 
792 	if (got_tempfail)
793 		SM_SET_H_ERRNO(TRY_AGAIN);
794 	else
795 		SM_SET_H_ERRNO(HOST_NOT_FOUND);
796 
797 	return false;
798 }
799 /*
800 **  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
801 **
802 **	Parameters:
803 **		name -- the name against which to match.
804 **		dot -- where to reinsert '.' to get FQDN
805 **		line -- the /etc/hosts line.
806 **		cbuf -- the location to store the result.
807 **		cbuflen -- the size of cbuf.
808 **
809 **	Returns:
810 **		true -- if the line matched the desired name.
811 **		false -- otherwise.
812 */
813 
814 static bool
815 extract_canonname(name, dot, line, cbuf, cbuflen)
816 	char *name;
817 	char *dot;
818 	char *line;
819 	char cbuf[];
820 	int cbuflen;
821 {
822 	int i;
823 	char *p;
824 	bool found = false;
825 
826 	cbuf[0] = '\0';
827 	if (line[0] == '#')
828 		return false;
829 
830 	for (i = 1; ; i++)
831 	{
832 		char nbuf[MAXNAME + 1];
833 
834 		p = get_column(line, i, '\0', nbuf, sizeof nbuf);
835 		if (p == NULL)
836 			break;
837 		if (*p == '\0')
838 			continue;
839 		if (cbuf[0] == '\0' ||
840 		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
841 		{
842 			(void) sm_strlcpy(cbuf, p, cbuflen);
843 		}
844 		if (sm_strcasecmp(name, p) == 0)
845 			found = true;
846 		else if (dot != NULL)
847 		{
848 			/* try looking for the FQDN as well */
849 			*dot = '.';
850 			if (sm_strcasecmp(name, p) == 0)
851 				found = true;
852 			*dot = '\0';
853 		}
854 	}
855 	if (found && strchr(cbuf, '.') == NULL)
856 	{
857 		/* try to add a domain on the end of the name */
858 		char *domain = macvalue('m', CurEnv);
859 
860 		if (domain != NULL &&
861 		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
862 		{
863 			p = &cbuf[i];
864 			*p++ = '.';
865 			(void) sm_strlcpy(p, domain, cbuflen - i - 1);
866 		}
867 	}
868 	return found;
869 }
870 
871 /*
872 **  DNS modules
873 */
874 
875 #if NAMED_BIND
876 # if DNSMAP
877 
878 #  include "sm_resolve.h"
879 #  if NETINET || NETINET6
880 #   include <arpa/inet.h>
881 #  endif /* NETINET || NETINET6 */
882 
883 /*
884 **  DNS_MAP_OPEN -- stub to check proper value for dns map type
885 */
886 
887 bool
888 dns_map_open(map, mode)
889 	MAP *map;
890 	int mode;
891 {
892 	if (tTd(38,2))
893 		sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
894 
895 	mode &= O_ACCMODE;
896 	if (mode != O_RDONLY)
897 	{
898 		/* issue a pseudo-error message */
899 		errno = SM_EMAPCANTWRITE;
900 		return false;
901 	}
902 	return true;
903 }
904 
905 /*
906 **  DNS_MAP_PARSEARGS -- parse dns map definition args.
907 **
908 **	Parameters:
909 **		map -- pointer to MAP
910 **		args -- pointer to the args on the config line.
911 **
912 **	Returns:
913 **		true -- if everything parsed OK.
914 **		false -- otherwise.
915 */
916 
917 #  if _FFR_DNSMAP_MULTILIMIT
918 #   if !_FFR_DNSMAP_MULTI
919   ERROR README:	You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
920 #   endif /* ! _FFR_DNSMAP_MULTI */
921 #  endif /* _FFR_DNSMAP_MULTILIMIT */
922 
923 #  if _FFR_DNSMAP_MULTI
924 #   if _FFR_DNSMAP_MULTILIMIT
925 #    define map_sizelimit	map_lockfd	/* overload field */
926 #   endif /* _FFR_DNSMAP_MULTILIMIT */
927 #  endif /* _FFR_DNSMAP_MULTI */
928 
929 struct dns_map
930 {
931 	int dns_m_type;
932 };
933 
934 bool
935 dns_map_parseargs(map,args)
936 	MAP *map;
937 	char *args;
938 {
939 	register char *p = args;
940 	struct dns_map *map_p;
941 
942 	map_p = (struct dns_map *) xalloc(sizeof *map_p);
943 	map_p->dns_m_type = -1;
944 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
945 
946 	for (;;)
947 	{
948 		while (isascii(*p) && isspace(*p))
949 			p++;
950 		if (*p != '-')
951 			break;
952 		switch (*++p)
953 		{
954 		  case 'N':
955 			map->map_mflags |= MF_INCLNULL;
956 			map->map_mflags &= ~MF_TRY0NULL;
957 			break;
958 
959 		  case 'O':
960 			map->map_mflags &= ~MF_TRY1NULL;
961 			break;
962 
963 		  case 'o':
964 			map->map_mflags |= MF_OPTIONAL;
965 			break;
966 
967 		  case 'f':
968 			map->map_mflags |= MF_NOFOLDCASE;
969 			break;
970 
971 		  case 'm':
972 			map->map_mflags |= MF_MATCHONLY;
973 			break;
974 
975 		  case 'A':
976 			map->map_mflags |= MF_APPEND;
977 			break;
978 
979 		  case 'q':
980 			map->map_mflags |= MF_KEEPQUOTES;
981 			break;
982 
983 		  case 't':
984 			map->map_mflags |= MF_NODEFER;
985 			break;
986 
987 		  case 'a':
988 			map->map_app = ++p;
989 			break;
990 
991 		  case 'T':
992 			map->map_tapp = ++p;
993 			break;
994 
995 		  case 'd':
996 			{
997 				char *h;
998 
999 				++p;
1000 				h = strchr(p, ' ');
1001 				if (h != NULL)
1002 					*h = '\0';
1003 				map->map_timeout = convtime(p, 's');
1004 				if (h != NULL)
1005 					*h = ' ';
1006 			}
1007 			break;
1008 
1009 		  case 'r':
1010 			while (isascii(*++p) && isspace(*p))
1011 				continue;
1012 			map->map_retry = atoi(p);
1013 			break;
1014 
1015 #  if _FFR_DNSMAP_MULTI
1016 		  case 'z':
1017 			if (*++p != '\\')
1018 				map->map_coldelim = *p;
1019 			else
1020 			{
1021 				switch (*++p)
1022 				{
1023 				  case 'n':
1024 					map->map_coldelim = '\n';
1025 					break;
1026 
1027 				  case 't':
1028 					map->map_coldelim = '\t';
1029 					break;
1030 
1031 				  default:
1032 					map->map_coldelim = '\\';
1033 				}
1034 			}
1035 			break;
1036 
1037 #   if _FFR_DNSMAP_MULTILIMIT
1038 		  case 'Z':
1039 			while (isascii(*++p) && isspace(*p))
1040 				continue;
1041 			map->map_sizelimit = atoi(p);
1042 			break;
1043 #   endif /* _FFR_DNSMAP_MULTILIMIT */
1044 #  endif /* _FFR_DNSMAP_MULTI */
1045 
1046 			/* Start of dns_map specific args */
1047 		  case 'R':		/* search field */
1048 			{
1049 				char *h;
1050 
1051 				while (isascii(*++p) && isspace(*p))
1052 					continue;
1053 				h = strchr(p, ' ');
1054 				if (h != NULL)
1055 					*h = '\0';
1056 				map_p->dns_m_type = dns_string_to_type(p);
1057 				if (h != NULL)
1058 					*h = ' ';
1059 				if (map_p->dns_m_type < 0)
1060 					syserr("dns map %s: wrong type %s",
1061 						map->map_mname, p);
1062 			}
1063 			break;
1064 
1065 #  if _FFR_DNSMAP_BASE
1066 		  case 'B':		/* base domain */
1067 			{
1068 				char *h;
1069 
1070 				while (isascii(*++p) && isspace(*p))
1071 					continue;
1072 				h = strchr(p, ' ');
1073 				if (h != NULL)
1074 					*h = '\0';
1075 
1076 				/*
1077 				**  slight abuse of map->map_file; it isn't
1078 				**	used otherwise in this map type.
1079 				*/
1080 
1081 				map->map_file = newstr(p);
1082 				if (h != NULL)
1083 					*h = ' ';
1084 			}
1085 			break;
1086 #  endif /* _FFR_DNSMAP_BASE */
1087 
1088 		}
1089 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1090 			p++;
1091 		if (*p != '\0')
1092 			*p++ = '\0';
1093 	}
1094 	if (map_p->dns_m_type < 0)
1095 		syserr("dns map %s: missing -R type", map->map_mname);
1096 	if (map->map_app != NULL)
1097 		map->map_app = newstr(map->map_app);
1098 	if (map->map_tapp != NULL)
1099 		map->map_tapp = newstr(map->map_tapp);
1100 
1101 	/*
1102 	**  Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1103 	**  Even if this assumption is wrong, we use only one byte,
1104 	**  so it doesn't really matter.
1105 	*/
1106 
1107 	map->map_db1 = (ARBPTR_T) map_p;
1108 	return true;
1109 }
1110 
1111 /*
1112 **  DNS_MAP_LOOKUP -- perform dns map lookup.
1113 **
1114 **	Parameters:
1115 **		map -- pointer to MAP
1116 **		name -- name to lookup
1117 **		av -- arguments to interpolate into buf.
1118 **		statp -- pointer to status (EX_)
1119 **
1120 **	Returns:
1121 **		result of lookup if succeeded.
1122 **		NULL -- otherwise.
1123 */
1124 
1125 char *
1126 dns_map_lookup(map, name, av, statp)
1127 	MAP *map;
1128 	char *name;
1129 	char **av;
1130 	int *statp;
1131 {
1132 #  if _FFR_DNSMAP_MULTI
1133 #   if _FFR_DNSMAP_MULTILIMIT
1134 	int resnum = 0;
1135 #   endif /* _FFR_DNSMAP_MULTILIMIT */
1136 #  endif /* _FFR_DNSMAP_MULTI */
1137 	char *vp = NULL, *result = NULL;
1138 	size_t vsize;
1139 	struct dns_map *map_p;
1140 	RESOURCE_RECORD_T *rr = NULL;
1141 	DNS_REPLY_T *r = NULL;
1142 #  if NETINET6
1143 	static char buf6[INET6_ADDRSTRLEN];
1144 #  endif /* NETINET6 */
1145 
1146 	if (tTd(38, 20))
1147 		sm_dprintf("dns_map_lookup(%s, %s)\n",
1148 			   map->map_mname, name);
1149 
1150 	map_p = (struct dns_map *)(map->map_db1);
1151 #  if _FFR_DNSMAP_BASE
1152 	if (map->map_file != NULL && *map->map_file != '\0')
1153 	{
1154 		size_t len;
1155 		char *appdomain;
1156 
1157 		len = strlen(map->map_file) + strlen(name) + 2;
1158 		appdomain = (char *) sm_malloc(len);
1159 		if (appdomain == NULL)
1160 		{
1161 			*statp = EX_UNAVAILABLE;
1162 			return NULL;
1163 		}
1164 		(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1165 		r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1166 				   map->map_timeout, map->map_retry);
1167 		sm_free(appdomain);
1168 	}
1169 	else
1170 #  endif /* _FFR_DNSMAP_BASE */
1171 	{
1172 		r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1173 				   map->map_timeout, map->map_retry);
1174 	}
1175 
1176 	if (r == NULL)
1177 	{
1178 		result = NULL;
1179 		if (errno == ETIMEDOUT || h_errno == TRY_AGAIN ||
1180 		    errno == ECONNREFUSED)
1181 			*statp = EX_TEMPFAIL;
1182 		else
1183 			*statp = EX_NOTFOUND;
1184 		goto cleanup;
1185 	}
1186 	*statp = EX_OK;
1187 	for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1188 	{
1189 		char *type = NULL;
1190 		char *value = NULL;
1191 
1192 		switch (rr->rr_type)
1193 		{
1194 		  case T_NS:
1195 			type = "T_NS";
1196 			value = rr->rr_u.rr_txt;
1197 			break;
1198 		  case T_CNAME:
1199 			type = "T_CNAME";
1200 			value = rr->rr_u.rr_txt;
1201 			break;
1202 		  case T_AFSDB:
1203 			type = "T_AFSDB";
1204 			value = rr->rr_u.rr_mx->mx_r_domain;
1205 			break;
1206 		  case T_SRV:
1207 			type = "T_SRV";
1208 			value = rr->rr_u.rr_srv->srv_r_target;
1209 			break;
1210 		  case T_PTR:
1211 			type = "T_PTR";
1212 			value = rr->rr_u.rr_txt;
1213 			break;
1214 		  case T_TXT:
1215 			type = "T_TXT";
1216 			value = rr->rr_u.rr_txt;
1217 			break;
1218 		  case T_MX:
1219 			type = "T_MX";
1220 			value = rr->rr_u.rr_mx->mx_r_domain;
1221 			break;
1222 #  if NETINET
1223 		  case T_A:
1224 			type = "T_A";
1225 			value = inet_ntoa(*(rr->rr_u.rr_a));
1226 			break;
1227 #  endif /* NETINET */
1228 #  if NETINET6
1229 		  case T_AAAA:
1230 			type = "T_AAAA";
1231 			value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1232 					    sizeof buf6);
1233 			break;
1234 #  endif /* NETINET6 */
1235 		}
1236 
1237 		(void) strreplnonprt(value, 'X');
1238 		if (map_p->dns_m_type != rr->rr_type)
1239 		{
1240 			if (tTd(38, 40))
1241 				sm_dprintf("\tskipping type %s (%d) value %s\n",
1242 					   type != NULL ? type : "<UNKNOWN>",
1243 					   rr->rr_type,
1244 					   value != NULL ? value : "<NO VALUE>");
1245 			continue;
1246 		}
1247 
1248 #  if NETINET6
1249 		if (rr->rr_type == T_AAAA && value == NULL)
1250 		{
1251 			result = NULL;
1252 			*statp = EX_DATAERR;
1253 			if (tTd(38, 40))
1254 				sm_dprintf("\tbad T_AAAA conversion\n");
1255 			goto cleanup;
1256 		}
1257 #  endif /* NETINET6 */
1258 		if (tTd(38, 40))
1259 			sm_dprintf("\tfound type %s (%d) value %s\n",
1260 				   type != NULL ? type : "<UNKNOWN>",
1261 				   rr->rr_type,
1262 				   value != NULL ? value : "<NO VALUE>");
1263 #  if _FFR_DNSMAP_MULTI
1264 		if (value != NULL &&
1265 		    (map->map_coldelim == '\0' ||
1266 #   if _FFR_DNSMAP_MULTILIMIT
1267 		     map->map_sizelimit == 1 ||
1268 #   endif /* _FFR_DNSMAP_MULTILIMIT */
1269 		     bitset(MF_MATCHONLY, map->map_mflags)))
1270 		{
1271 			/* Only care about the first match */
1272 			vp = newstr(value);
1273 			break;
1274 		}
1275 		else if (vp == NULL)
1276 		{
1277 			/* First result */
1278 			vp = newstr(value);
1279 		}
1280 		else
1281 		{
1282 			/* concatenate the results */
1283 			int sz;
1284 			char *new;
1285 
1286 			sz = strlen(vp) + strlen(value) + 2;
1287 			new = xalloc(sz);
1288 			(void) sm_snprintf(new, sz, "%s%c%s",
1289 					   vp, map->map_coldelim, value);
1290 			sm_free(vp);
1291 			vp = new;
1292 #   if _FFR_DNSMAP_MULTILIMIT
1293 			if (map->map_sizelimit > 0 &&
1294 			    ++resnum >= map->map_sizelimit)
1295 				break;
1296 #   endif /* _FFR_DNSMAP_MULTILIMIT */
1297 		}
1298 #  else /* _FFR_DNSMAP_MULTI */
1299 		vp = value;
1300 		break;
1301 #  endif /* _FFR_DNSMAP_MULTI */
1302 	}
1303 	if (vp == NULL)
1304 	{
1305 		result = NULL;
1306 		*statp = EX_NOTFOUND;
1307 		if (tTd(38, 40))
1308 			sm_dprintf("\tno match found\n");
1309 		goto cleanup;
1310 	}
1311 
1312 #  if _FFR_DNSMAP_MULTI
1313 	/* Cleanly truncate for rulesets */
1314 	truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1315 #  endif /* _FFR_DNSMAP_MULTI */
1316 
1317 	vsize = strlen(vp);
1318 
1319 	if (LogLevel > 9)
1320 		sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1321 			  name, vp);
1322 	if (bitset(MF_MATCHONLY, map->map_mflags))
1323 		result = map_rewrite(map, name, strlen(name), NULL);
1324 	else
1325 		result = map_rewrite(map, vp, vsize, av);
1326 
1327   cleanup:
1328 #  if _FFR_DNSMAP_MULTI
1329 	if (vp != NULL)
1330 		sm_free(vp);
1331 #  endif /* _FFR_DNSMAP_MULTI */
1332 	if (r != NULL)
1333 		dns_free_data(r);
1334 	return result;
1335 }
1336 # endif /* DNSMAP */
1337 #endif /* NAMED_BIND */
1338 
1339 /*
1340 **  NDBM modules
1341 */
1342 
1343 #if NDBM
1344 
1345 /*
1346 **  NDBM_MAP_OPEN -- DBM-style map open
1347 */
1348 
1349 bool
1350 ndbm_map_open(map, mode)
1351 	MAP *map;
1352 	int mode;
1353 {
1354 	register DBM *dbm;
1355 	int save_errno;
1356 	int dfd;
1357 	int pfd;
1358 	long sff;
1359 	int ret;
1360 	int smode = S_IREAD;
1361 	char dirfile[MAXPATHLEN];
1362 	char pagfile[MAXPATHLEN];
1363 	struct stat st;
1364 	struct stat std, stp;
1365 
1366 	if (tTd(38, 2))
1367 		sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1368 			map->map_mname, map->map_file, mode);
1369 	map->map_lockfd = -1;
1370 	mode &= O_ACCMODE;
1371 
1372 	/* do initial file and directory checks */
1373 	if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
1374 			map->map_file, ".dir") >= sizeof dirfile ||
1375 	    sm_strlcpyn(pagfile, sizeof pagfile, 2,
1376 			map->map_file, ".pag") >= sizeof pagfile)
1377 	{
1378 		errno = 0;
1379 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1380 			syserr("dbm map \"%s\": map file %s name too long",
1381 				map->map_mname, map->map_file);
1382 		return false;
1383 	}
1384 	sff = SFF_ROOTOK|SFF_REGONLY;
1385 	if (mode == O_RDWR)
1386 	{
1387 		sff |= SFF_CREAT;
1388 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1389 			sff |= SFF_NOSLINK;
1390 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1391 			sff |= SFF_NOHLINK;
1392 		smode = S_IWRITE;
1393 	}
1394 	else
1395 	{
1396 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1397 			sff |= SFF_NOWLINK;
1398 	}
1399 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1400 		sff |= SFF_SAFEDIRPATH;
1401 	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1402 		       sff, smode, &std);
1403 	if (ret == 0)
1404 		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1405 			       sff, smode, &stp);
1406 
1407 	if (ret != 0)
1408 	{
1409 		char *prob = "unsafe";
1410 
1411 		/* cannot open this map */
1412 		if (ret == ENOENT)
1413 			prob = "missing";
1414 		if (tTd(38, 2))
1415 			sm_dprintf("\t%s map file: %d\n", prob, ret);
1416 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1417 			syserr("dbm map \"%s\": %s map file %s",
1418 				map->map_mname, prob, map->map_file);
1419 		return false;
1420 	}
1421 	if (std.st_mode == ST_MODE_NOFILE)
1422 		mode |= O_CREAT|O_EXCL;
1423 
1424 # if LOCK_ON_OPEN
1425 	if (mode == O_RDONLY)
1426 		mode |= O_SHLOCK;
1427 	else
1428 		mode |= O_TRUNC|O_EXLOCK;
1429 # else /* LOCK_ON_OPEN */
1430 	if ((mode & O_ACCMODE) == O_RDWR)
1431 	{
1432 #  if NOFTRUNCATE
1433 		/*
1434 		**  Warning: race condition.  Try to lock the file as
1435 		**  quickly as possible after opening it.
1436 		**	This may also have security problems on some systems,
1437 		**	but there isn't anything we can do about it.
1438 		*/
1439 
1440 		mode |= O_TRUNC;
1441 #  else /* NOFTRUNCATE */
1442 		/*
1443 		**  This ugly code opens the map without truncating it,
1444 		**  locks the file, then truncates it.  Necessary to
1445 		**  avoid race conditions.
1446 		*/
1447 
1448 		int dirfd;
1449 		int pagfd;
1450 		long sff = SFF_CREAT|SFF_OPENASROOT;
1451 
1452 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1453 			sff |= SFF_NOSLINK;
1454 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1455 			sff |= SFF_NOHLINK;
1456 
1457 		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1458 		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1459 
1460 		if (dirfd < 0 || pagfd < 0)
1461 		{
1462 			save_errno = errno;
1463 			if (dirfd >= 0)
1464 				(void) close(dirfd);
1465 			if (pagfd >= 0)
1466 				(void) close(pagfd);
1467 			errno = save_errno;
1468 			syserr("ndbm_map_open: cannot create database %s",
1469 				map->map_file);
1470 			return false;
1471 		}
1472 		if (ftruncate(dirfd, (off_t) 0) < 0 ||
1473 		    ftruncate(pagfd, (off_t) 0) < 0)
1474 		{
1475 			save_errno = errno;
1476 			(void) close(dirfd);
1477 			(void) close(pagfd);
1478 			errno = save_errno;
1479 			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1480 				map->map_file);
1481 			return false;
1482 		}
1483 
1484 		/* if new file, get "before" bits for later filechanged check */
1485 		if (std.st_mode == ST_MODE_NOFILE &&
1486 		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1487 		{
1488 			save_errno = errno;
1489 			(void) close(dirfd);
1490 			(void) close(pagfd);
1491 			errno = save_errno;
1492 			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1493 				map->map_file);
1494 			return false;
1495 		}
1496 
1497 		/* have to save the lock for the duration (bletch) */
1498 		map->map_lockfd = dirfd;
1499 		(void) close(pagfd);
1500 
1501 		/* twiddle bits for dbm_open */
1502 		mode &= ~(O_CREAT|O_EXCL);
1503 #  endif /* NOFTRUNCATE */
1504 	}
1505 # endif /* LOCK_ON_OPEN */
1506 
1507 	/* open the database */
1508 	dbm = dbm_open(map->map_file, mode, DBMMODE);
1509 	if (dbm == NULL)
1510 	{
1511 		save_errno = errno;
1512 		if (bitset(MF_ALIAS, map->map_mflags) &&
1513 		    aliaswait(map, ".pag", false))
1514 			return true;
1515 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1516 		if (map->map_lockfd >= 0)
1517 			(void) close(map->map_lockfd);
1518 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1519 		errno = save_errno;
1520 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1521 			syserr("Cannot open DBM database %s", map->map_file);
1522 		return false;
1523 	}
1524 	dfd = dbm_dirfno(dbm);
1525 	pfd = dbm_pagfno(dbm);
1526 	if (dfd == pfd)
1527 	{
1528 		/* heuristic: if files are linked, this is actually gdbm */
1529 		dbm_close(dbm);
1530 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1531 		if (map->map_lockfd >= 0)
1532 			(void) close(map->map_lockfd);
1533 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1534 		errno = 0;
1535 		syserr("dbm map \"%s\": cannot support GDBM",
1536 			map->map_mname);
1537 		return false;
1538 	}
1539 
1540 	if (filechanged(dirfile, dfd, &std) ||
1541 	    filechanged(pagfile, pfd, &stp))
1542 	{
1543 		save_errno = errno;
1544 		dbm_close(dbm);
1545 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1546 		if (map->map_lockfd >= 0)
1547 			(void) close(map->map_lockfd);
1548 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1549 		errno = save_errno;
1550 		syserr("ndbm_map_open(%s): file changed after open",
1551 			map->map_file);
1552 		return false;
1553 	}
1554 
1555 	map->map_db1 = (ARBPTR_T) dbm;
1556 
1557 	/*
1558 	**  Need to set map_mtime before the call to aliaswait()
1559 	**  as aliaswait() will call map_lookup() which requires
1560 	**  map_mtime to be set
1561 	*/
1562 
1563 	if (fstat(pfd, &st) >= 0)
1564 		map->map_mtime = st.st_mtime;
1565 
1566 	if (mode == O_RDONLY)
1567 	{
1568 # if LOCK_ON_OPEN
1569 		if (dfd >= 0)
1570 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1571 		if (pfd >= 0)
1572 			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1573 # endif /* LOCK_ON_OPEN */
1574 		if (bitset(MF_ALIAS, map->map_mflags) &&
1575 		    !aliaswait(map, ".pag", true))
1576 			return false;
1577 	}
1578 	else
1579 	{
1580 		map->map_mflags |= MF_LOCKED;
1581 		if (geteuid() == 0 && TrustedUid != 0)
1582 		{
1583 #  if HASFCHOWN
1584 			if (fchown(dfd, TrustedUid, -1) < 0 ||
1585 			    fchown(pfd, TrustedUid, -1) < 0)
1586 			{
1587 				int err = errno;
1588 
1589 				sm_syslog(LOG_ALERT, NOQID,
1590 					  "ownership change on %s failed: %s",
1591 					  map->map_file, sm_errstring(err));
1592 				message("050 ownership change on %s failed: %s",
1593 					map->map_file, sm_errstring(err));
1594 			}
1595 #  else /* HASFCHOWN */
1596 			sm_syslog(LOG_ALERT, NOQID,
1597 				  "no fchown(): cannot change ownership on %s",
1598 				  map->map_file);
1599 			message("050 no fchown(): cannot change ownership on %s",
1600 				map->map_file);
1601 #  endif /* HASFCHOWN */
1602 		}
1603 	}
1604 	return true;
1605 }
1606 
1607 
1608 /*
1609 **  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1610 */
1611 
1612 char *
1613 ndbm_map_lookup(map, name, av, statp)
1614 	MAP *map;
1615 	char *name;
1616 	char **av;
1617 	int *statp;
1618 {
1619 	datum key, val;
1620 	int dfd, pfd;
1621 	char keybuf[MAXNAME + 1];
1622 	struct stat stbuf;
1623 
1624 	if (tTd(38, 20))
1625 		sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1626 			map->map_mname, name);
1627 
1628 	key.dptr = name;
1629 	key.dsize = strlen(name);
1630 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1631 	{
1632 		if (key.dsize > sizeof keybuf - 1)
1633 			key.dsize = sizeof keybuf - 1;
1634 		memmove(keybuf, key.dptr, key.dsize);
1635 		keybuf[key.dsize] = '\0';
1636 		makelower(keybuf);
1637 		key.dptr = keybuf;
1638 	}
1639 lockdbm:
1640 	dfd = dbm_dirfno((DBM *) map->map_db1);
1641 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1642 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1643 	pfd = dbm_pagfno((DBM *) map->map_db1);
1644 	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1645 	    stbuf.st_mtime > map->map_mtime)
1646 	{
1647 		/* Reopen the database to sync the cache */
1648 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1649 								 : O_RDONLY;
1650 
1651 		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1652 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1653 		map->map_mflags |= MF_CLOSING;
1654 		map->map_class->map_close(map);
1655 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1656 		if (map->map_class->map_open(map, omode))
1657 		{
1658 			map->map_mflags |= MF_OPEN;
1659 			map->map_pid = CurrentPid;
1660 			if ((omode && O_ACCMODE) == O_RDWR)
1661 				map->map_mflags |= MF_WRITABLE;
1662 			goto lockdbm;
1663 		}
1664 		else
1665 		{
1666 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1667 			{
1668 				extern MAPCLASS BogusMapClass;
1669 
1670 				*statp = EX_TEMPFAIL;
1671 				map->map_orgclass = map->map_class;
1672 				map->map_class = &BogusMapClass;
1673 				map->map_mflags |= MF_OPEN;
1674 				map->map_pid = CurrentPid;
1675 				syserr("Cannot reopen NDBM database %s",
1676 					map->map_file);
1677 			}
1678 			return NULL;
1679 		}
1680 	}
1681 	val.dptr = NULL;
1682 	if (bitset(MF_TRY0NULL, map->map_mflags))
1683 	{
1684 		val = dbm_fetch((DBM *) map->map_db1, key);
1685 		if (val.dptr != NULL)
1686 			map->map_mflags &= ~MF_TRY1NULL;
1687 	}
1688 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1689 	{
1690 		key.dsize++;
1691 		val = dbm_fetch((DBM *) map->map_db1, key);
1692 		if (val.dptr != NULL)
1693 			map->map_mflags &= ~MF_TRY0NULL;
1694 	}
1695 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1696 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1697 	if (val.dptr == NULL)
1698 		return NULL;
1699 	if (bitset(MF_MATCHONLY, map->map_mflags))
1700 		return map_rewrite(map, name, strlen(name), NULL);
1701 	else
1702 		return map_rewrite(map, val.dptr, val.dsize, av);
1703 }
1704 
1705 
1706 /*
1707 **  NDBM_MAP_STORE -- store a datum in the database
1708 */
1709 
1710 void
1711 ndbm_map_store(map, lhs, rhs)
1712 	register MAP *map;
1713 	char *lhs;
1714 	char *rhs;
1715 {
1716 	datum key;
1717 	datum data;
1718 	int status;
1719 	char keybuf[MAXNAME + 1];
1720 
1721 	if (tTd(38, 12))
1722 		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1723 			map->map_mname, lhs, rhs);
1724 
1725 	key.dsize = strlen(lhs);
1726 	key.dptr = lhs;
1727 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1728 	{
1729 		if (key.dsize > sizeof keybuf - 1)
1730 			key.dsize = sizeof keybuf - 1;
1731 		memmove(keybuf, key.dptr, key.dsize);
1732 		keybuf[key.dsize] = '\0';
1733 		makelower(keybuf);
1734 		key.dptr = keybuf;
1735 	}
1736 
1737 	data.dsize = strlen(rhs);
1738 	data.dptr = rhs;
1739 
1740 	if (bitset(MF_INCLNULL, map->map_mflags))
1741 	{
1742 		key.dsize++;
1743 		data.dsize++;
1744 	}
1745 
1746 	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1747 	if (status > 0)
1748 	{
1749 		if (!bitset(MF_APPEND, map->map_mflags))
1750 			message("050 Warning: duplicate alias name %s", lhs);
1751 		else
1752 		{
1753 			static char *buf = NULL;
1754 			static int bufsiz = 0;
1755 			auto int xstat;
1756 			datum old;
1757 
1758 			old.dptr = ndbm_map_lookup(map, key.dptr,
1759 						   (char **) NULL, &xstat);
1760 			if (old.dptr != NULL && *(char *) old.dptr != '\0')
1761 			{
1762 				old.dsize = strlen(old.dptr);
1763 				if (data.dsize + old.dsize + 2 > bufsiz)
1764 				{
1765 					if (buf != NULL)
1766 						(void) sm_free(buf);
1767 					bufsiz = data.dsize + old.dsize + 2;
1768 					buf = sm_pmalloc_x(bufsiz);
1769 				}
1770 				(void) sm_strlcpyn(buf, bufsiz, 3,
1771 					data.dptr, ",", old.dptr);
1772 				data.dsize = data.dsize + old.dsize + 1;
1773 				data.dptr = buf;
1774 				if (tTd(38, 9))
1775 					sm_dprintf("ndbm_map_store append=%s\n",
1776 						data.dptr);
1777 			}
1778 		}
1779 		status = dbm_store((DBM *) map->map_db1,
1780 				   key, data, DBM_REPLACE);
1781 	}
1782 	if (status != 0)
1783 		syserr("readaliases: dbm put (%s): %d", lhs, status);
1784 }
1785 
1786 
1787 /*
1788 **  NDBM_MAP_CLOSE -- close the database
1789 */
1790 
1791 void
1792 ndbm_map_close(map)
1793 	register MAP  *map;
1794 {
1795 	if (tTd(38, 9))
1796 		sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1797 			map->map_mname, map->map_file, map->map_mflags);
1798 
1799 	if (bitset(MF_WRITABLE, map->map_mflags))
1800 	{
1801 # ifdef NDBM_YP_COMPAT
1802 		bool inclnull;
1803 		char buf[MAXHOSTNAMELEN];
1804 
1805 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
1806 		map->map_mflags &= ~MF_INCLNULL;
1807 
1808 		if (strstr(map->map_file, "/yp/") != NULL)
1809 		{
1810 			long save_mflags = map->map_mflags;
1811 
1812 			map->map_mflags |= MF_NOFOLDCASE;
1813 
1814 			(void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
1815 			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1816 
1817 			(void) gethostname(buf, sizeof buf);
1818 			ndbm_map_store(map, "YP_MASTER_NAME", buf);
1819 
1820 			map->map_mflags = save_mflags;
1821 		}
1822 
1823 		if (inclnull)
1824 			map->map_mflags |= MF_INCLNULL;
1825 # endif /* NDBM_YP_COMPAT */
1826 
1827 		/* write out the distinguished alias */
1828 		ndbm_map_store(map, "@", "@");
1829 	}
1830 	dbm_close((DBM *) map->map_db1);
1831 
1832 	/* release lock (if needed) */
1833 # if !LOCK_ON_OPEN
1834 	if (map->map_lockfd >= 0)
1835 		(void) close(map->map_lockfd);
1836 # endif /* !LOCK_ON_OPEN */
1837 }
1838 
1839 #endif /* NDBM */
1840 /*
1841 **  NEWDB (Hash and BTree) Modules
1842 */
1843 
1844 #if NEWDB
1845 
1846 /*
1847 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1848 **
1849 **	These do rather bizarre locking.  If you can lock on open,
1850 **	do that to avoid the condition of opening a database that
1851 **	is being rebuilt.  If you don't, we'll try to fake it, but
1852 **	there will be a race condition.  If opening for read-only,
1853 **	we immediately release the lock to avoid freezing things up.
1854 **	We really ought to hold the lock, but guarantee that we won't
1855 **	be pokey about it.  That's hard to do.
1856 */
1857 
1858 /* these should be K line arguments */
1859 # if DB_VERSION_MAJOR < 2
1860 #  define db_cachesize	cachesize
1861 #  define h_nelem	nelem
1862 #  ifndef DB_CACHE_SIZE
1863 #   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
1864 #  endif /* ! DB_CACHE_SIZE */
1865 #  ifndef DB_HASH_NELEM
1866 #   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
1867 #  endif /* ! DB_HASH_NELEM */
1868 # endif /* DB_VERSION_MAJOR < 2 */
1869 
1870 bool
1871 bt_map_open(map, mode)
1872 	MAP *map;
1873 	int mode;
1874 {
1875 # if DB_VERSION_MAJOR < 2
1876 	BTREEINFO btinfo;
1877 # endif /* DB_VERSION_MAJOR < 2 */
1878 # if DB_VERSION_MAJOR == 2
1879 	DB_INFO btinfo;
1880 # endif /* DB_VERSION_MAJOR == 2 */
1881 # if DB_VERSION_MAJOR > 2
1882 	void *btinfo = NULL;
1883 # endif /* DB_VERSION_MAJOR > 2 */
1884 
1885 	if (tTd(38, 2))
1886 		sm_dprintf("bt_map_open(%s, %s, %d)\n",
1887 			map->map_mname, map->map_file, mode);
1888 
1889 # if DB_VERSION_MAJOR < 3
1890 	memset(&btinfo, '\0', sizeof btinfo);
1891 #  ifdef DB_CACHE_SIZE
1892 	btinfo.db_cachesize = DB_CACHE_SIZE;
1893 #  endif /* DB_CACHE_SIZE */
1894 # endif /* DB_VERSION_MAJOR < 3 */
1895 
1896 	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1897 }
1898 
1899 bool
1900 hash_map_open(map, mode)
1901 	MAP *map;
1902 	int mode;
1903 {
1904 # if DB_VERSION_MAJOR < 2
1905 	HASHINFO hinfo;
1906 # endif /* DB_VERSION_MAJOR < 2 */
1907 # if DB_VERSION_MAJOR == 2
1908 	DB_INFO hinfo;
1909 # endif /* DB_VERSION_MAJOR == 2 */
1910 # if DB_VERSION_MAJOR > 2
1911 	void *hinfo = NULL;
1912 # endif /* DB_VERSION_MAJOR > 2 */
1913 
1914 	if (tTd(38, 2))
1915 		sm_dprintf("hash_map_open(%s, %s, %d)\n",
1916 			map->map_mname, map->map_file, mode);
1917 
1918 # if DB_VERSION_MAJOR < 3
1919 	memset(&hinfo, '\0', sizeof hinfo);
1920 #  ifdef DB_HASH_NELEM
1921 	hinfo.h_nelem = DB_HASH_NELEM;
1922 #  endif /* DB_HASH_NELEM */
1923 #  ifdef DB_CACHE_SIZE
1924 	hinfo.db_cachesize = DB_CACHE_SIZE;
1925 #  endif /* DB_CACHE_SIZE */
1926 # endif /* DB_VERSION_MAJOR < 3 */
1927 
1928 	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1929 }
1930 
1931 static bool
1932 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1933 	MAP *map;
1934 	int mode;
1935 	char *mapclassname;
1936 	DBTYPE dbtype;
1937 # if DB_VERSION_MAJOR < 2
1938 	const void *openinfo;
1939 # endif /* DB_VERSION_MAJOR < 2 */
1940 # if DB_VERSION_MAJOR == 2
1941 	DB_INFO *openinfo;
1942 # endif /* DB_VERSION_MAJOR == 2 */
1943 # if DB_VERSION_MAJOR > 2
1944 	void **openinfo;
1945 # endif /* DB_VERSION_MAJOR > 2 */
1946 {
1947 	DB *db = NULL;
1948 	int i;
1949 	int omode;
1950 	int smode = S_IREAD;
1951 	int fd;
1952 	long sff;
1953 	int save_errno;
1954 	struct stat st;
1955 	char buf[MAXPATHLEN];
1956 
1957 	/* do initial file and directory checks */
1958 	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
1959 	{
1960 		errno = 0;
1961 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1962 			syserr("map \"%s\": map file %s name too long",
1963 				map->map_mname, map->map_file);
1964 		return false;
1965 	}
1966 	i = strlen(buf);
1967 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1968 	{
1969 		if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
1970 		{
1971 			errno = 0;
1972 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1973 				syserr("map \"%s\": map file %s name too long",
1974 					map->map_mname, map->map_file);
1975 			return false;
1976 		}
1977 	}
1978 
1979 	mode &= O_ACCMODE;
1980 	omode = mode;
1981 
1982 	sff = SFF_ROOTOK|SFF_REGONLY;
1983 	if (mode == O_RDWR)
1984 	{
1985 		sff |= SFF_CREAT;
1986 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1987 			sff |= SFF_NOSLINK;
1988 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1989 			sff |= SFF_NOHLINK;
1990 		smode = S_IWRITE;
1991 	}
1992 	else
1993 	{
1994 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1995 			sff |= SFF_NOWLINK;
1996 	}
1997 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1998 		sff |= SFF_SAFEDIRPATH;
1999 	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2000 
2001 	if (i != 0)
2002 	{
2003 		char *prob = "unsafe";
2004 
2005 		/* cannot open this map */
2006 		if (i == ENOENT)
2007 			prob = "missing";
2008 		if (tTd(38, 2))
2009 			sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2010 		errno = i;
2011 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2012 			syserr("%s map \"%s\": %s map file %s",
2013 				mapclassname, map->map_mname, prob, buf);
2014 		return false;
2015 	}
2016 	if (st.st_mode == ST_MODE_NOFILE)
2017 		omode |= O_CREAT|O_EXCL;
2018 
2019 	map->map_lockfd = -1;
2020 
2021 # if LOCK_ON_OPEN
2022 	if (mode == O_RDWR)
2023 		omode |= O_TRUNC|O_EXLOCK;
2024 	else
2025 		omode |= O_SHLOCK;
2026 # else /* LOCK_ON_OPEN */
2027 	/*
2028 	**  Pre-lock the file to avoid race conditions.  In particular,
2029 	**  since dbopen returns NULL if the file is zero length, we
2030 	**  must have a locked instance around the dbopen.
2031 	*/
2032 
2033 	fd = open(buf, omode, DBMMODE);
2034 	if (fd < 0)
2035 	{
2036 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2037 			syserr("db_map_open: cannot pre-open database %s", buf);
2038 		return false;
2039 	}
2040 
2041 	/* make sure no baddies slipped in just before the open... */
2042 	if (filechanged(buf, fd, &st))
2043 	{
2044 		save_errno = errno;
2045 		(void) close(fd);
2046 		errno = save_errno;
2047 		syserr("db_map_open(%s): file changed after pre-open", buf);
2048 		return false;
2049 	}
2050 
2051 	/* if new file, get the "before" bits for later filechanged check */
2052 	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2053 	{
2054 		save_errno = errno;
2055 		(void) close(fd);
2056 		errno = save_errno;
2057 		syserr("db_map_open(%s): cannot fstat pre-opened file",
2058 			buf);
2059 		return false;
2060 	}
2061 
2062 	/* actually lock the pre-opened file */
2063 	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2064 		syserr("db_map_open: cannot lock %s", buf);
2065 
2066 	/* set up mode bits for dbopen */
2067 	if (mode == O_RDWR)
2068 		omode |= O_TRUNC;
2069 	omode &= ~(O_EXCL|O_CREAT);
2070 # endif /* LOCK_ON_OPEN */
2071 
2072 # if DB_VERSION_MAJOR < 2
2073 	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2074 # else /* DB_VERSION_MAJOR < 2 */
2075 	{
2076 		int flags = 0;
2077 #  if DB_VERSION_MAJOR > 2
2078 		int ret;
2079 #  endif /* DB_VERSION_MAJOR > 2 */
2080 
2081 		if (mode == O_RDONLY)
2082 			flags |= DB_RDONLY;
2083 		if (bitset(O_CREAT, omode))
2084 			flags |= DB_CREATE;
2085 		if (bitset(O_TRUNC, omode))
2086 			flags |= DB_TRUNCATE;
2087 
2088 #  if !HASFLOCK && defined(DB_FCNTL_LOCKING)
2089 		flags |= DB_FCNTL_LOCKING;
2090 #  endif /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */
2091 
2092 #  if DB_VERSION_MAJOR > 2
2093 		ret = db_create(&db, NULL, 0);
2094 #  ifdef DB_CACHE_SIZE
2095 		if (ret == 0 && db != NULL)
2096 		{
2097 			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2098 			if (ret != 0)
2099 			{
2100 				(void) db->close(db, 0);
2101 				db = NULL;
2102 			}
2103 		}
2104 #  endif /* DB_CACHE_SIZE */
2105 #  ifdef DB_HASH_NELEM
2106 		if (dbtype == DB_HASH && ret == 0 && db != NULL)
2107 		{
2108 			ret = db->set_h_nelem(db, DB_HASH_NELEM);
2109 			if (ret != 0)
2110 			{
2111 				(void) db->close(db, 0);
2112 				db = NULL;
2113 			}
2114 		}
2115 #  endif /* DB_HASH_NELEM */
2116 		if (ret == 0 && db != NULL)
2117 		{
2118 			ret = db->open(db, buf, NULL, dbtype, flags, DBMMODE);
2119 			if (ret != 0)
2120 			{
2121 #ifdef DB_OLD_VERSION
2122 				if (ret == DB_OLD_VERSION)
2123 					ret = EINVAL;
2124 #endif /* DB_OLD_VERSION */
2125 				(void) db->close(db, 0);
2126 				db = NULL;
2127 			}
2128 		}
2129 		errno = ret;
2130 #  else /* DB_VERSION_MAJOR > 2 */
2131 		errno = db_open(buf, dbtype, flags, DBMMODE,
2132 				NULL, openinfo, &db);
2133 #  endif /* DB_VERSION_MAJOR > 2 */
2134 	}
2135 # endif /* DB_VERSION_MAJOR < 2 */
2136 	save_errno = errno;
2137 
2138 # if !LOCK_ON_OPEN
2139 	if (mode == O_RDWR)
2140 		map->map_lockfd = fd;
2141 	else
2142 		(void) close(fd);
2143 # endif /* !LOCK_ON_OPEN */
2144 
2145 	if (db == NULL)
2146 	{
2147 		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2148 		    aliaswait(map, ".db", false))
2149 			return true;
2150 # if !LOCK_ON_OPEN
2151 		if (map->map_lockfd >= 0)
2152 			(void) close(map->map_lockfd);
2153 # endif /* !LOCK_ON_OPEN */
2154 		errno = save_errno;
2155 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2156 			syserr("Cannot open %s database %s",
2157 				mapclassname, buf);
2158 		return false;
2159 	}
2160 
2161 # if DB_VERSION_MAJOR < 2
2162 	fd = db->fd(db);
2163 # else /* DB_VERSION_MAJOR < 2 */
2164 	fd = -1;
2165 	errno = db->fd(db, &fd);
2166 # endif /* DB_VERSION_MAJOR < 2 */
2167 	if (filechanged(buf, fd, &st))
2168 	{
2169 		save_errno = errno;
2170 # if DB_VERSION_MAJOR < 2
2171 		(void) db->close(db);
2172 # else /* DB_VERSION_MAJOR < 2 */
2173 		errno = db->close(db, 0);
2174 # endif /* DB_VERSION_MAJOR < 2 */
2175 # if !LOCK_ON_OPEN
2176 		if (map->map_lockfd >= 0)
2177 			(void) close(map->map_lockfd);
2178 # endif /* !LOCK_ON_OPEN */
2179 		errno = save_errno;
2180 		syserr("db_map_open(%s): file changed after open", buf);
2181 		return false;
2182 	}
2183 
2184 	if (mode == O_RDWR)
2185 		map->map_mflags |= MF_LOCKED;
2186 # if LOCK_ON_OPEN
2187 	if (fd >= 0 && mode == O_RDONLY)
2188 	{
2189 		(void) lockfile(fd, buf, NULL, LOCK_UN);
2190 	}
2191 # endif /* LOCK_ON_OPEN */
2192 
2193 	/* try to make sure that at least the database header is on disk */
2194 	if (mode == O_RDWR)
2195 	{
2196 		(void) db->sync(db, 0);
2197 		if (geteuid() == 0 && TrustedUid != 0)
2198 		{
2199 #  if HASFCHOWN
2200 			if (fchown(fd, TrustedUid, -1) < 0)
2201 			{
2202 				int err = errno;
2203 
2204 				sm_syslog(LOG_ALERT, NOQID,
2205 					  "ownership change on %s failed: %s",
2206 					  buf, sm_errstring(err));
2207 				message("050 ownership change on %s failed: %s",
2208 					buf, sm_errstring(err));
2209 			}
2210 #  else /* HASFCHOWN */
2211 			sm_syslog(LOG_ALERT, NOQID,
2212 				  "no fchown(): cannot change ownership on %s",
2213 				  map->map_file);
2214 			message("050 no fchown(): cannot change ownership on %s",
2215 				map->map_file);
2216 #  endif /* HASFCHOWN */
2217 		}
2218 	}
2219 
2220 	map->map_db2 = (ARBPTR_T) db;
2221 
2222 	/*
2223 	**  Need to set map_mtime before the call to aliaswait()
2224 	**  as aliaswait() will call map_lookup() which requires
2225 	**  map_mtime to be set
2226 	*/
2227 
2228 	if (fd >= 0 && fstat(fd, &st) >= 0)
2229 		map->map_mtime = st.st_mtime;
2230 
2231 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2232 	    !aliaswait(map, ".db", true))
2233 		return false;
2234 	return true;
2235 }
2236 
2237 
2238 /*
2239 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2240 */
2241 
2242 char *
2243 db_map_lookup(map, name, av, statp)
2244 	MAP *map;
2245 	char *name;
2246 	char **av;
2247 	int *statp;
2248 {
2249 	DBT key, val;
2250 	register DB *db = (DB *) map->map_db2;
2251 	int i;
2252 	int st;
2253 	int save_errno;
2254 	int fd;
2255 	struct stat stbuf;
2256 	char keybuf[MAXNAME + 1];
2257 	char buf[MAXPATHLEN];
2258 
2259 	memset(&key, '\0', sizeof key);
2260 	memset(&val, '\0', sizeof val);
2261 
2262 	if (tTd(38, 20))
2263 		sm_dprintf("db_map_lookup(%s, %s)\n",
2264 			map->map_mname, name);
2265 
2266 	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
2267 	{
2268 		errno = 0;
2269 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2270 			syserr("map \"%s\": map file %s name too long",
2271 				map->map_mname, map->map_file);
2272 		return NULL;
2273 	}
2274 	i = strlen(buf);
2275 	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2276 		buf[i - 3] = '\0';
2277 
2278 	key.size = strlen(name);
2279 	if (key.size > sizeof keybuf - 1)
2280 		key.size = sizeof keybuf - 1;
2281 	key.data = keybuf;
2282 	memmove(keybuf, name, key.size);
2283 	keybuf[key.size] = '\0';
2284 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2285 		makelower(keybuf);
2286   lockdb:
2287 # if DB_VERSION_MAJOR < 2
2288 	fd = db->fd(db);
2289 # else /* DB_VERSION_MAJOR < 2 */
2290 	fd = -1;
2291 	errno = db->fd(db, &fd);
2292 # endif /* DB_VERSION_MAJOR < 2 */
2293 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2294 		(void) lockfile(fd, buf, ".db", LOCK_SH);
2295 	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2296 	{
2297 		/* Reopen the database to sync the cache */
2298 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2299 								 : O_RDONLY;
2300 
2301 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2302 			(void) lockfile(fd, buf, ".db", LOCK_UN);
2303 		map->map_mflags |= MF_CLOSING;
2304 		map->map_class->map_close(map);
2305 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2306 		if (map->map_class->map_open(map, omode))
2307 		{
2308 			map->map_mflags |= MF_OPEN;
2309 			map->map_pid = CurrentPid;
2310 			if ((omode && O_ACCMODE) == O_RDWR)
2311 				map->map_mflags |= MF_WRITABLE;
2312 			db = (DB *) map->map_db2;
2313 			goto lockdb;
2314 		}
2315 		else
2316 		{
2317 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2318 			{
2319 				extern MAPCLASS BogusMapClass;
2320 
2321 				*statp = EX_TEMPFAIL;
2322 				map->map_orgclass = map->map_class;
2323 				map->map_class = &BogusMapClass;
2324 				map->map_mflags |= MF_OPEN;
2325 				map->map_pid = CurrentPid;
2326 				syserr("Cannot reopen DB database %s",
2327 					map->map_file);
2328 			}
2329 			return NULL;
2330 		}
2331 	}
2332 
2333 	st = 1;
2334 	if (bitset(MF_TRY0NULL, map->map_mflags))
2335 	{
2336 # if DB_VERSION_MAJOR < 2
2337 		st = db->get(db, &key, &val, 0);
2338 # else /* DB_VERSION_MAJOR < 2 */
2339 		errno = db->get(db, NULL, &key, &val, 0);
2340 		switch (errno)
2341 		{
2342 		  case DB_NOTFOUND:
2343 		  case DB_KEYEMPTY:
2344 			st = 1;
2345 			break;
2346 
2347 		  case 0:
2348 			st = 0;
2349 			break;
2350 
2351 		  default:
2352 			st = -1;
2353 			break;
2354 		}
2355 # endif /* DB_VERSION_MAJOR < 2 */
2356 		if (st == 0)
2357 			map->map_mflags &= ~MF_TRY1NULL;
2358 	}
2359 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2360 	{
2361 		key.size++;
2362 # if DB_VERSION_MAJOR < 2
2363 		st = db->get(db, &key, &val, 0);
2364 # else /* DB_VERSION_MAJOR < 2 */
2365 		errno = db->get(db, NULL, &key, &val, 0);
2366 		switch (errno)
2367 		{
2368 		  case DB_NOTFOUND:
2369 		  case DB_KEYEMPTY:
2370 			st = 1;
2371 			break;
2372 
2373 		  case 0:
2374 			st = 0;
2375 			break;
2376 
2377 		  default:
2378 			st = -1;
2379 			break;
2380 		}
2381 # endif /* DB_VERSION_MAJOR < 2 */
2382 		if (st == 0)
2383 			map->map_mflags &= ~MF_TRY0NULL;
2384 	}
2385 	save_errno = errno;
2386 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2387 		(void) lockfile(fd, buf, ".db", LOCK_UN);
2388 	if (st != 0)
2389 	{
2390 		errno = save_errno;
2391 		if (st < 0)
2392 			syserr("db_map_lookup: get (%s)", name);
2393 		return NULL;
2394 	}
2395 	if (bitset(MF_MATCHONLY, map->map_mflags))
2396 		return map_rewrite(map, name, strlen(name), NULL);
2397 	else
2398 		return map_rewrite(map, val.data, val.size, av);
2399 }
2400 
2401 
2402 /*
2403 **  DB_MAP_STORE -- store a datum in the NEWDB database
2404 */
2405 
2406 void
2407 db_map_store(map, lhs, rhs)
2408 	register MAP *map;
2409 	char *lhs;
2410 	char *rhs;
2411 {
2412 	int status;
2413 	DBT key;
2414 	DBT data;
2415 	register DB *db = map->map_db2;
2416 	char keybuf[MAXNAME + 1];
2417 
2418 	memset(&key, '\0', sizeof key);
2419 	memset(&data, '\0', sizeof data);
2420 
2421 	if (tTd(38, 12))
2422 		sm_dprintf("db_map_store(%s, %s, %s)\n",
2423 			map->map_mname, lhs, rhs);
2424 
2425 	key.size = strlen(lhs);
2426 	key.data = lhs;
2427 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2428 	{
2429 		if (key.size > sizeof keybuf - 1)
2430 			key.size = sizeof keybuf - 1;
2431 		memmove(keybuf, key.data, key.size);
2432 		keybuf[key.size] = '\0';
2433 		makelower(keybuf);
2434 		key.data = keybuf;
2435 	}
2436 
2437 	data.size = strlen(rhs);
2438 	data.data = rhs;
2439 
2440 	if (bitset(MF_INCLNULL, map->map_mflags))
2441 	{
2442 		key.size++;
2443 		data.size++;
2444 	}
2445 
2446 # if DB_VERSION_MAJOR < 2
2447 	status = db->put(db, &key, &data, R_NOOVERWRITE);
2448 # else /* DB_VERSION_MAJOR < 2 */
2449 	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2450 	switch (errno)
2451 	{
2452 	  case DB_KEYEXIST:
2453 		status = 1;
2454 		break;
2455 
2456 	  case 0:
2457 		status = 0;
2458 		break;
2459 
2460 	  default:
2461 		status = -1;
2462 		break;
2463 	}
2464 # endif /* DB_VERSION_MAJOR < 2 */
2465 	if (status > 0)
2466 	{
2467 		if (!bitset(MF_APPEND, map->map_mflags))
2468 			message("050 Warning: duplicate alias name %s", lhs);
2469 		else
2470 		{
2471 			static char *buf = NULL;
2472 			static int bufsiz = 0;
2473 			DBT old;
2474 
2475 			memset(&old, '\0', sizeof old);
2476 
2477 			old.data = db_map_lookup(map, key.data,
2478 						 (char **) NULL, &status);
2479 			if (old.data != NULL)
2480 			{
2481 				old.size = strlen(old.data);
2482 				if (data.size + old.size + 2 > (size_t) bufsiz)
2483 				{
2484 					if (buf != NULL)
2485 						sm_free(buf);
2486 					bufsiz = data.size + old.size + 2;
2487 					buf = sm_pmalloc_x(bufsiz);
2488 				}
2489 				(void) sm_strlcpyn(buf, bufsiz, 3,
2490 					(char *) data.data, ",",
2491 					(char *) old.data);
2492 				data.size = data.size + old.size + 1;
2493 				data.data = buf;
2494 				if (tTd(38, 9))
2495 					sm_dprintf("db_map_store append=%s\n",
2496 						(char *) data.data);
2497 			}
2498 		}
2499 # if DB_VERSION_MAJOR < 2
2500 		status = db->put(db, &key, &data, 0);
2501 # else /* DB_VERSION_MAJOR < 2 */
2502 		status = errno = db->put(db, NULL, &key, &data, 0);
2503 # endif /* DB_VERSION_MAJOR < 2 */
2504 	}
2505 	if (status != 0)
2506 		syserr("readaliases: db put (%s)", lhs);
2507 }
2508 
2509 
2510 /*
2511 **  DB_MAP_CLOSE -- add distinguished entries and close the database
2512 */
2513 
2514 void
2515 db_map_close(map)
2516 	MAP *map;
2517 {
2518 	register DB *db = map->map_db2;
2519 
2520 	if (tTd(38, 9))
2521 		sm_dprintf("db_map_close(%s, %s, %lx)\n",
2522 			map->map_mname, map->map_file, map->map_mflags);
2523 
2524 	if (bitset(MF_WRITABLE, map->map_mflags))
2525 	{
2526 		/* write out the distinguished alias */
2527 		db_map_store(map, "@", "@");
2528 	}
2529 
2530 	(void) db->sync(db, 0);
2531 
2532 # if !LOCK_ON_OPEN
2533 	if (map->map_lockfd >= 0)
2534 		(void) close(map->map_lockfd);
2535 # endif /* !LOCK_ON_OPEN */
2536 
2537 # if DB_VERSION_MAJOR < 2
2538 	if (db->close(db) != 0)
2539 # else /* DB_VERSION_MAJOR < 2 */
2540 	/*
2541 	**  Berkeley DB can use internal shared memory
2542 	**  locking for its memory pool.  Closing a map
2543 	**  opened by another process will interfere
2544 	**  with the shared memory and locks of the parent
2545 	**  process leaving things in a bad state.
2546 	*/
2547 
2548 	/*
2549 	**  If this map was not opened by the current
2550 	**  process, do not close the map but recover
2551 	**  the file descriptor.
2552 	*/
2553 
2554 	if (map->map_pid != CurrentPid)
2555 	{
2556 		int fd = -1;
2557 
2558 		errno = db->fd(db, &fd);
2559 		if (fd >= 0)
2560 			(void) close(fd);
2561 		return;
2562 	}
2563 
2564 	if ((errno = db->close(db, 0)) != 0)
2565 # endif /* DB_VERSION_MAJOR < 2 */
2566 		syserr("db_map_close(%s, %s, %lx): db close failure",
2567 			map->map_mname, map->map_file, map->map_mflags);
2568 }
2569 #endif /* NEWDB */
2570 /*
2571 **  NIS Modules
2572 */
2573 
2574 #if NIS
2575 
2576 # ifndef YPERR_BUSY
2577 #  define YPERR_BUSY	16
2578 # endif /* ! YPERR_BUSY */
2579 
2580 /*
2581 **  NIS_MAP_OPEN -- open DBM map
2582 */
2583 
2584 bool
2585 nis_map_open(map, mode)
2586 	MAP *map;
2587 	int mode;
2588 {
2589 	int yperr;
2590 	register char *p;
2591 	auto char *vp;
2592 	auto int vsize;
2593 
2594 	if (tTd(38, 2))
2595 		sm_dprintf("nis_map_open(%s, %s, %d)\n",
2596 			map->map_mname, map->map_file, mode);
2597 
2598 	mode &= O_ACCMODE;
2599 	if (mode != O_RDONLY)
2600 	{
2601 		/* issue a pseudo-error message */
2602 		errno = SM_EMAPCANTWRITE;
2603 		return false;
2604 	}
2605 
2606 	p = strchr(map->map_file, '@');
2607 	if (p != NULL)
2608 	{
2609 		*p++ = '\0';
2610 		if (*p != '\0')
2611 			map->map_domain = p;
2612 	}
2613 
2614 	if (*map->map_file == '\0')
2615 		map->map_file = "mail.aliases";
2616 
2617 	if (map->map_domain == NULL)
2618 	{
2619 		yperr = yp_get_default_domain(&map->map_domain);
2620 		if (yperr != 0)
2621 		{
2622 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2623 				syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2624 				       map->map_file);
2625 			return false;
2626 		}
2627 	}
2628 
2629 	/* check to see if this map actually exists */
2630 	vp = NULL;
2631 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2632 			&vp, &vsize);
2633 	if (tTd(38, 10))
2634 		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2635 			map->map_domain, map->map_file, yperr_string(yperr));
2636 	if (vp != NULL)
2637 		sm_free(vp);
2638 
2639 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2640 	{
2641 		/*
2642 		**  We ought to be calling aliaswait() here if this is an
2643 		**  alias file, but powerful HP-UX NIS servers  apparently
2644 		**  don't insert the @:@ token into the alias map when it
2645 		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
2646 		*/
2647 
2648 # if 0
2649 		if (!bitset(MF_ALIAS, map->map_mflags) ||
2650 		    aliaswait(map, NULL, true))
2651 # endif /* 0 */
2652 			return true;
2653 	}
2654 
2655 	if (!bitset(MF_OPTIONAL, map->map_mflags))
2656 	{
2657 		syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2658 			map->map_file, map->map_domain, yperr_string(yperr));
2659 	}
2660 
2661 	return false;
2662 }
2663 
2664 
2665 /*
2666 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2667 */
2668 
2669 /* ARGSUSED3 */
2670 char *
2671 nis_map_lookup(map, name, av, statp)
2672 	MAP *map;
2673 	char *name;
2674 	char **av;
2675 	int *statp;
2676 {
2677 	char *vp;
2678 	auto int vsize;
2679 	int buflen;
2680 	int yperr;
2681 	char keybuf[MAXNAME + 1];
2682 	char *SM_NONVOLATILE result = NULL;
2683 
2684 	if (tTd(38, 20))
2685 		sm_dprintf("nis_map_lookup(%s, %s)\n",
2686 			map->map_mname, name);
2687 
2688 	buflen = strlen(name);
2689 	if (buflen > sizeof keybuf - 1)
2690 		buflen = sizeof keybuf - 1;
2691 	memmove(keybuf, name, buflen);
2692 	keybuf[buflen] = '\0';
2693 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2694 		makelower(keybuf);
2695 	yperr = YPERR_KEY;
2696 	vp = NULL;
2697 	if (bitset(MF_TRY0NULL, map->map_mflags))
2698 	{
2699 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2700 			     &vp, &vsize);
2701 		if (yperr == 0)
2702 			map->map_mflags &= ~MF_TRY1NULL;
2703 	}
2704 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2705 	{
2706 		SM_FREE_CLR(vp);
2707 		buflen++;
2708 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2709 			     &vp, &vsize);
2710 		if (yperr == 0)
2711 			map->map_mflags &= ~MF_TRY0NULL;
2712 	}
2713 	if (yperr != 0)
2714 	{
2715 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2716 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2717 		if (vp != NULL)
2718 			sm_free(vp);
2719 		return NULL;
2720 	}
2721 	SM_TRY
2722 		if (bitset(MF_MATCHONLY, map->map_mflags))
2723 			result = map_rewrite(map, name, strlen(name), NULL);
2724 		else
2725 			result = map_rewrite(map, vp, vsize, av);
2726 	SM_FINALLY
2727 		if (vp != NULL)
2728 			sm_free(vp);
2729 	SM_END_TRY
2730 	return result;
2731 }
2732 
2733 
2734 /*
2735 **  NIS_GETCANONNAME -- look up canonical name in NIS
2736 */
2737 
2738 static bool
2739 nis_getcanonname(name, hbsize, statp)
2740 	char *name;
2741 	int hbsize;
2742 	int *statp;
2743 {
2744 	char *vp;
2745 	auto int vsize;
2746 	int keylen;
2747 	int yperr;
2748 	static bool try0null = true;
2749 	static bool try1null = true;
2750 	static char *yp_domain = NULL;
2751 	char host_record[MAXLINE];
2752 	char cbuf[MAXNAME];
2753 	char nbuf[MAXNAME + 1];
2754 
2755 	if (tTd(38, 20))
2756 		sm_dprintf("nis_getcanonname(%s)\n", name);
2757 
2758 	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2759 	{
2760 		*statp = EX_UNAVAILABLE;
2761 		return false;
2762 	}
2763 	(void) shorten_hostname(nbuf);
2764 	keylen = strlen(nbuf);
2765 
2766 	if (yp_domain == NULL)
2767 		(void) yp_get_default_domain(&yp_domain);
2768 	makelower(nbuf);
2769 	yperr = YPERR_KEY;
2770 	vp = NULL;
2771 	if (try0null)
2772 	{
2773 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2774 			     &vp, &vsize);
2775 		if (yperr == 0)
2776 			try1null = false;
2777 	}
2778 	if (yperr == YPERR_KEY && try1null)
2779 	{
2780 		SM_FREE_CLR(vp);
2781 		keylen++;
2782 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2783 			     &vp, &vsize);
2784 		if (yperr == 0)
2785 			try0null = false;
2786 	}
2787 	if (yperr != 0)
2788 	{
2789 		if (yperr == YPERR_KEY)
2790 			*statp = EX_NOHOST;
2791 		else if (yperr == YPERR_BUSY)
2792 			*statp = EX_TEMPFAIL;
2793 		else
2794 			*statp = EX_UNAVAILABLE;
2795 		if (vp != NULL)
2796 			sm_free(vp);
2797 		return false;
2798 	}
2799 	(void) sm_strlcpy(host_record, vp, sizeof host_record);
2800 	sm_free(vp);
2801 	if (tTd(38, 44))
2802 		sm_dprintf("got record `%s'\n", host_record);
2803 	vp = strpbrk(host_record, "#\n");
2804 	if (vp != NULL)
2805 		*vp = '\0';
2806 	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
2807 	{
2808 		/* this should not happen, but.... */
2809 		*statp = EX_NOHOST;
2810 		return false;
2811 	}
2812 	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2813 	{
2814 		*statp = EX_UNAVAILABLE;
2815 		return false;
2816 	}
2817 	*statp = EX_OK;
2818 	return true;
2819 }
2820 
2821 #endif /* NIS */
2822 /*
2823 **  NISPLUS Modules
2824 **
2825 **	This code donated by Sun Microsystems.
2826 */
2827 
2828 #if NISPLUS
2829 
2830 # undef NIS		/* symbol conflict in nis.h */
2831 # undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2832 # include <rpcsvc/nis.h>
2833 # include <rpcsvc/nislib.h>
2834 
2835 # define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2836 # define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2837 # define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2838 # define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
2839 
2840 /*
2841 **  NISPLUS_MAP_OPEN -- open nisplus table
2842 */
2843 
2844 bool
2845 nisplus_map_open(map, mode)
2846 	MAP *map;
2847 	int mode;
2848 {
2849 	nis_result *res = NULL;
2850 	int retry_cnt, max_col, i;
2851 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2852 
2853 	if (tTd(38, 2))
2854 		sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2855 			map->map_mname, map->map_file, mode);
2856 
2857 	mode &= O_ACCMODE;
2858 	if (mode != O_RDONLY)
2859 	{
2860 		errno = EPERM;
2861 		return false;
2862 	}
2863 
2864 	if (*map->map_file == '\0')
2865 		map->map_file = "mail_aliases.org_dir";
2866 
2867 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2868 	{
2869 		/* set default NISPLUS Domain to $m */
2870 		map->map_domain = newstr(nisplus_default_domain());
2871 		if (tTd(38, 2))
2872 			sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2873 				map->map_file, map->map_domain);
2874 	}
2875 	if (!PARTIAL_NAME(map->map_file))
2876 	{
2877 		map->map_domain = newstr("");
2878 		(void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
2879 	}
2880 	else
2881 	{
2882 		/* check to see if this map actually exists */
2883 		(void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
2884 				   map->map_file, ".", map->map_domain);
2885 	}
2886 
2887 	retry_cnt = 0;
2888 	while (res == NULL || res->status != NIS_SUCCESS)
2889 	{
2890 		res = nis_lookup(qbuf, FOLLOW_LINKS);
2891 		switch (res->status)
2892 		{
2893 		  case NIS_SUCCESS:
2894 			break;
2895 
2896 		  case NIS_TRYAGAIN:
2897 		  case NIS_RPCERROR:
2898 		  case NIS_NAMEUNREACHABLE:
2899 			if (retry_cnt++ > 4)
2900 			{
2901 				errno = EAGAIN;
2902 				return false;
2903 			}
2904 			/* try not to overwhelm hosed server */
2905 			sleep(2);
2906 			break;
2907 
2908 		  default:		/* all other nisplus errors */
2909 # if 0
2910 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2911 				syserr("451 4.3.5 Cannot find table %s.%s: %s",
2912 					map->map_file, map->map_domain,
2913 					nis_sperrno(res->status));
2914 # endif /* 0 */
2915 			errno = EAGAIN;
2916 			return false;
2917 		}
2918 	}
2919 
2920 	if (NIS_RES_NUMOBJ(res) != 1 ||
2921 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2922 	{
2923 		if (tTd(38, 10))
2924 			sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2925 # if 0
2926 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2927 			syserr("451 4.3.5 %s.%s: %s is not a table",
2928 				map->map_file, map->map_domain,
2929 				nis_sperrno(res->status));
2930 # endif /* 0 */
2931 		errno = EBADF;
2932 		return false;
2933 	}
2934 	/* default key column is column 0 */
2935 	if (map->map_keycolnm == NULL)
2936 		map->map_keycolnm = newstr(COL_NAME(res,0));
2937 
2938 	max_col = COL_MAX(res);
2939 
2940 	/* verify the key column exist */
2941 	for (i = 0; i < max_col; i++)
2942 	{
2943 		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2944 			break;
2945 	}
2946 	if (i == max_col)
2947 	{
2948 		if (tTd(38, 2))
2949 			sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2950 				map->map_file, map->map_keycolnm);
2951 		errno = ENOENT;
2952 		return false;
2953 	}
2954 
2955 	/* default value column is the last column */
2956 	if (map->map_valcolnm == NULL)
2957 	{
2958 		map->map_valcolno = max_col - 1;
2959 		return true;
2960 	}
2961 
2962 	for (i = 0; i< max_col; i++)
2963 	{
2964 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2965 		{
2966 			map->map_valcolno = i;
2967 			return true;
2968 		}
2969 	}
2970 
2971 	if (tTd(38, 2))
2972 		sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2973 			map->map_file, map->map_keycolnm);
2974 	errno = ENOENT;
2975 	return false;
2976 }
2977 
2978 
2979 /*
2980 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2981 */
2982 
2983 char *
2984 nisplus_map_lookup(map, name, av, statp)
2985 	MAP *map;
2986 	char *name;
2987 	char **av;
2988 	int *statp;
2989 {
2990 	char *p;
2991 	auto int vsize;
2992 	char *skp;
2993 	int skleft;
2994 	char search_key[MAXNAME + 4];
2995 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2996 	nis_result *result;
2997 
2998 	if (tTd(38, 20))
2999 		sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3000 			map->map_mname, name);
3001 
3002 	if (!bitset(MF_OPEN, map->map_mflags))
3003 	{
3004 		if (nisplus_map_open(map, O_RDONLY))
3005 		{
3006 			map->map_mflags |= MF_OPEN;
3007 			map->map_pid = CurrentPid;
3008 		}
3009 		else
3010 		{
3011 			*statp = EX_UNAVAILABLE;
3012 			return NULL;
3013 		}
3014 	}
3015 
3016 	/*
3017 	**  Copy the name to the key buffer, escaping double quote characters
3018 	**  by doubling them and quoting "]" and "," to avoid having the
3019 	**  NIS+ parser choke on them.
3020 	*/
3021 
3022 	skleft = sizeof search_key - 4;
3023 	skp = search_key;
3024 	for (p = name; *p != '\0' && skleft > 0; p++)
3025 	{
3026 		switch (*p)
3027 		{
3028 		  case ']':
3029 		  case ',':
3030 			/* quote the character */
3031 			*skp++ = '"';
3032 			*skp++ = *p;
3033 			*skp++ = '"';
3034 			skleft -= 3;
3035 			break;
3036 
3037 		  case '"':
3038 			/* double the quote */
3039 			*skp++ = '"';
3040 			skleft--;
3041 			/* FALLTHROUGH */
3042 
3043 		  default:
3044 			*skp++ = *p;
3045 			skleft--;
3046 			break;
3047 		}
3048 	}
3049 	*skp = '\0';
3050 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3051 		makelower(search_key);
3052 
3053 	/* construct the query */
3054 	if (PARTIAL_NAME(map->map_file))
3055 		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
3056 			map->map_keycolnm, search_key, map->map_file,
3057 			map->map_domain);
3058 	else
3059 		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
3060 			map->map_keycolnm, search_key, map->map_file);
3061 
3062 	if (tTd(38, 20))
3063 		sm_dprintf("qbuf=%s\n", qbuf);
3064 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3065 	if (result->status == NIS_SUCCESS)
3066 	{
3067 		int count;
3068 		char *str;
3069 
3070 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
3071 		{
3072 			if (LogLevel > 10)
3073 				sm_syslog(LOG_WARNING, CurEnv->e_id,
3074 					  "%s: lookup error, expected 1 entry, got %d",
3075 					  map->map_file, count);
3076 
3077 			/* ignore second entry */
3078 			if (tTd(38, 20))
3079 				sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3080 					name, count);
3081 		}
3082 
3083 		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3084 		/* set the length of the result */
3085 		if (p == NULL)
3086 			p = "";
3087 		vsize = strlen(p);
3088 		if (tTd(38, 20))
3089 			sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3090 				name, p);
3091 		if (bitset(MF_MATCHONLY, map->map_mflags))
3092 			str = map_rewrite(map, name, strlen(name), NULL);
3093 		else
3094 			str = map_rewrite(map, p, vsize, av);
3095 		nis_freeresult(result);
3096 		*statp = EX_OK;
3097 		return str;
3098 	}
3099 	else
3100 	{
3101 		if (result->status == NIS_NOTFOUND)
3102 			*statp = EX_NOTFOUND;
3103 		else if (result->status == NIS_TRYAGAIN)
3104 			*statp = EX_TEMPFAIL;
3105 		else
3106 		{
3107 			*statp = EX_UNAVAILABLE;
3108 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
3109 		}
3110 	}
3111 	if (tTd(38, 20))
3112 		sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3113 	nis_freeresult(result);
3114 	return NULL;
3115 }
3116 
3117 
3118 
3119 /*
3120 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3121 */
3122 
3123 static bool
3124 nisplus_getcanonname(name, hbsize, statp)
3125 	char *name;
3126 	int hbsize;
3127 	int *statp;
3128 {
3129 	char *vp;
3130 	auto int vsize;
3131 	nis_result *result;
3132 	char *p;
3133 	char nbuf[MAXNAME + 1];
3134 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
3135 
3136 	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
3137 	{
3138 		*statp = EX_UNAVAILABLE;
3139 		return false;
3140 	}
3141 	(void) shorten_hostname(nbuf);
3142 
3143 	p = strchr(nbuf, '.');
3144 	if (p == NULL)
3145 	{
3146 		/* single token */
3147 		(void) sm_snprintf(qbuf, sizeof qbuf,
3148 			"[name=%s],hosts.org_dir", nbuf);
3149 	}
3150 	else if (p[1] != '\0')
3151 	{
3152 		/* multi token -- take only first token in nbuf */
3153 		*p = '\0';
3154 		(void) sm_snprintf(qbuf, sizeof qbuf,
3155 				   "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3156 	}
3157 	else
3158 	{
3159 		*statp = EX_NOHOST;
3160 		return false;
3161 	}
3162 
3163 	if (tTd(38, 20))
3164 		sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3165 			   name, qbuf);
3166 
3167 	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3168 			  NULL, NULL);
3169 
3170 	if (result->status == NIS_SUCCESS)
3171 	{
3172 		int count;
3173 		char *domain;
3174 
3175 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
3176 		{
3177 			if (LogLevel > 10)
3178 				sm_syslog(LOG_WARNING, CurEnv->e_id,
3179 					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3180 					  count);
3181 
3182 			/* ignore second entry */
3183 			if (tTd(38, 20))
3184 				sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3185 					   name, count);
3186 		}
3187 
3188 		if (tTd(38, 20))
3189 			sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3190 				   name, (NIS_RES_OBJECT(result))->zo_domain);
3191 
3192 
3193 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3194 		vsize = strlen(vp);
3195 		if (tTd(38, 20))
3196 			sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3197 				   name, vp);
3198 		if (strchr(vp, '.') != NULL)
3199 		{
3200 			domain = "";
3201 		}
3202 		else
3203 		{
3204 			domain = macvalue('m', CurEnv);
3205 			if (domain == NULL)
3206 				domain = "";
3207 		}
3208 		if (hbsize > vsize + (int) strlen(domain) + 1)
3209 		{
3210 			if (domain[0] == '\0')
3211 				(void) sm_strlcpy(name, vp, hbsize);
3212 			else
3213 				(void) sm_snprintf(name, hbsize,
3214 						   "%s.%s", vp, domain);
3215 			*statp = EX_OK;
3216 		}
3217 		else
3218 			*statp = EX_NOHOST;
3219 		nis_freeresult(result);
3220 		return true;
3221 	}
3222 	else
3223 	{
3224 		if (result->status == NIS_NOTFOUND)
3225 			*statp = EX_NOHOST;
3226 		else if (result->status == NIS_TRYAGAIN)
3227 			*statp = EX_TEMPFAIL;
3228 		else
3229 			*statp = EX_UNAVAILABLE;
3230 	}
3231 	if (tTd(38, 20))
3232 		sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3233 			   name, result->status, *statp);
3234 	nis_freeresult(result);
3235 	return false;
3236 }
3237 
3238 char *
3239 nisplus_default_domain()
3240 {
3241 	static char default_domain[MAXNAME + 1] = "";
3242 	char *p;
3243 
3244 	if (default_domain[0] != '\0')
3245 		return default_domain;
3246 
3247 	p = nis_local_directory();
3248 	(void) sm_strlcpy(default_domain, p, sizeof default_domain);
3249 	return default_domain;
3250 }
3251 
3252 #endif /* NISPLUS */
3253 /*
3254 **  LDAP Modules
3255 */
3256 
3257 /*
3258 **  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3259 */
3260 
3261 #if defined(LDAPMAP) || defined(PH_MAP)
3262 
3263 # if PH_MAP
3264 #  define ph_map_dequote ldapmap_dequote
3265 # endif /* PH_MAP */
3266 
3267 static char *ldapmap_dequote __P((char *));
3268 
3269 static char *
3270 ldapmap_dequote(str)
3271 	char *str;
3272 {
3273 	char *p;
3274 	char *start;
3275 
3276 	if (str == NULL)
3277 		return NULL;
3278 
3279 	p = str;
3280 	if (*p == '"')
3281 	{
3282 		/* Should probably swallow initial whitespace here */
3283 		start = ++p;
3284 	}
3285 	else
3286 		return str;
3287 	while (*p != '"' && *p != '\0')
3288 		p++;
3289 	if (*p != '\0')
3290 		*p = '\0';
3291 	return start;
3292 }
3293 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3294 
3295 #if LDAPMAP
3296 
3297 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3298 
3299 /*
3300 **  LDAPMAP_OPEN -- open LDAP map
3301 **
3302 **	Connect to the LDAP server.  Re-use existing connections since a
3303 **	single server connection to a host (with the same host, port,
3304 **	bind DN, and secret) can answer queries for multiple maps.
3305 */
3306 
3307 bool
3308 ldapmap_open(map, mode)
3309 	MAP *map;
3310 	int mode;
3311 {
3312 	SM_LDAP_STRUCT *lmap;
3313 	STAB *s;
3314 
3315 	if (tTd(38, 2))
3316 		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3317 
3318 	mode &= O_ACCMODE;
3319 
3320 	/* sendmail doesn't have the ability to write to LDAP (yet) */
3321 	if (mode != O_RDONLY)
3322 	{
3323 		/* issue a pseudo-error message */
3324 		errno = SM_EMAPCANTWRITE;
3325 		return false;
3326 	}
3327 
3328 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
3329 
3330 	s = ldapmap_findconn(lmap);
3331 	if (s->s_lmap != NULL)
3332 	{
3333 		/* Already have a connection open to this LDAP server */
3334 		lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3335 		lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3336 
3337 		/* Add this map as head of linked list */
3338 		lmap->ldap_next = s->s_lmap;
3339 		s->s_lmap = map;
3340 
3341 		if (tTd(38, 2))
3342 			sm_dprintf("using cached connection\n");
3343 		return true;
3344 	}
3345 
3346 	if (tTd(38, 2))
3347 		sm_dprintf("opening new connection\n");
3348 
3349 	/* No connection yet, connect */
3350 	if (!sm_ldap_start(map->map_mname, lmap))
3351 	{
3352 		if (errno == ETIMEDOUT)
3353 		{
3354 			if (LogLevel > 1)
3355 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
3356 					  "timeout conning to LDAP server %.100s",
3357 					  lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target);
3358 		}
3359 
3360 		if (!bitset(MF_OPTIONAL, map->map_mflags))
3361 		{
3362 			if (bitset(MF_NODEFER, map->map_mflags))
3363 				syserr("%s failed to %s in map %s",
3364 # if USE_LDAP_INIT
3365 				       "ldap_init/ldap_bind",
3366 # else /* USE_LDAP_INIT */
3367 				       "ldap_open",
3368 # endif /* USE_LDAP_INIT */
3369 				       lmap->ldap_target == NULL ? "localhost"
3370 								 : lmap->ldap_target,
3371 				       map->map_mname);
3372 			else
3373 				syserr("451 4.3.5 %s failed to %s in map %s",
3374 # if USE_LDAP_INIT
3375 				       "ldap_init/ldap_bind",
3376 # else /* USE_LDAP_INIT */
3377 				       "ldap_open",
3378 # endif /* USE_LDAP_INIT */
3379 				       lmap->ldap_target == NULL ? "localhost"
3380 								 : lmap->ldap_target,
3381 				       map->map_mname);
3382 		}
3383 		return false;
3384 	}
3385 
3386 	/* Save connection for reuse */
3387 	s->s_lmap = map;
3388 	return true;
3389 }
3390 
3391 /*
3392 **  LDAPMAP_CLOSE -- close ldap map
3393 */
3394 
3395 void
3396 ldapmap_close(map)
3397 	MAP *map;
3398 {
3399 	SM_LDAP_STRUCT *lmap;
3400 	STAB *s;
3401 
3402 	if (tTd(38, 2))
3403 		sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3404 
3405 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
3406 
3407 	/* Check if already closed */
3408 	if (lmap->ldap_ld == NULL)
3409 		return;
3410 
3411 	/* Close the LDAP connection */
3412 	sm_ldap_close(lmap);
3413 
3414 	/* Mark all the maps that share the connection as closed */
3415 	s = ldapmap_findconn(lmap);
3416 
3417 	while (s->s_lmap != NULL)
3418 	{
3419 		MAP *smap = s->s_lmap;
3420 
3421 		if (tTd(38, 2) && smap != map)
3422 			sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3423 				   map->map_mname, smap->map_mname);
3424 		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3425 		lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3426 		lmap->ldap_ld = NULL;
3427 		s->s_lmap = lmap->ldap_next;
3428 		lmap->ldap_next = NULL;
3429 	}
3430 }
3431 
3432 # ifdef SUNET_ID
3433 /*
3434 **  SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3435 **  This only makes sense at Stanford University.
3436 */
3437 
3438 static char *
3439 sunet_id_hash(str)
3440 	char *str;
3441 {
3442 	char *p, *p_last;
3443 
3444 	p = str;
3445 	p_last = p;
3446 	while (*p != '\0')
3447 	{
3448 		if (islower(*p) || isdigit(*p))
3449 		{
3450 			*p_last = *p;
3451 			p_last++;
3452 		}
3453 		else if (isupper(*p))
3454 		{
3455 			*p_last = tolower(*p);
3456 			p_last++;
3457 		}
3458 		++p;
3459 	}
3460 	if (*p_last != '\0')
3461 		*p_last = '\0';
3462 	return str;
3463 }
3464 # endif /* SUNET_ID */
3465 
3466 /*
3467 **  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3468 */
3469 
3470 char *
3471 ldapmap_lookup(map, name, av, statp)
3472 	MAP *map;
3473 	char *name;
3474 	char **av;
3475 	int *statp;
3476 {
3477 # if _FFR_LDAP_RECURSION
3478 	int plen = 0;
3479 	int psize = 0;
3480 # else /* _FFR_LDAP_RECURSION */
3481 	int entries = 0;
3482 	int i;
3483 	int ret;
3484 	int vsize;
3485 # endif /* _FFR_LDAP_RECURSION */
3486 	int msgid;
3487 	int save_errno;
3488 	char *vp, *p;
3489 	char *result = NULL;
3490 	SM_LDAP_STRUCT *lmap = NULL;
3491 	char keybuf[MAXNAME + 1];
3492 
3493 	if (tTd(38, 20))
3494 		sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3495 
3496 	/* Get ldap struct pointer from map */
3497 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
3498 	sm_ldap_setopts(lmap->ldap_ld, lmap);
3499 
3500 	(void) sm_strlcpy(keybuf, name, sizeof keybuf);
3501 
3502 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3503 	{
3504 # ifdef SUNET_ID
3505 		sunet_id_hash(keybuf);
3506 # else /* SUNET_ID */
3507 		makelower(keybuf);
3508 # endif /* SUNET_ID */
3509 	}
3510 
3511 	msgid = sm_ldap_search(lmap, keybuf);
3512 	if (msgid == -1)
3513 	{
3514 		errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3515 		save_errno = errno;
3516 		if (!bitset(MF_OPTIONAL, map->map_mflags))
3517 		{
3518 			if (bitset(MF_NODEFER, map->map_mflags))
3519 				syserr("Error in ldap_search using %s in map %s",
3520 				       keybuf, map->map_mname);
3521 			else
3522 				syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3523 				       keybuf, map->map_mname);
3524 		}
3525 		*statp = EX_TEMPFAIL;
3526 		switch (save_errno - E_LDAPBASE)
3527 		{
3528 # ifdef LDAP_SERVER_DOWN
3529 		  case LDAP_SERVER_DOWN:
3530 # endif /* LDAP_SERVER_DOWN */
3531 		  case LDAP_TIMEOUT:
3532 		  case LDAP_UNAVAILABLE:
3533 			/* server disappeared, try reopen on next search */
3534 			ldapmap_close(map);
3535 			break;
3536 		}
3537 		errno = save_errno;
3538 		return NULL;
3539 	}
3540 
3541 	*statp = EX_NOTFOUND;
3542 	vp = NULL;
3543 
3544 # if _FFR_LDAP_RECURSION
3545 	{
3546 		int flags;
3547 		SM_RPOOL_T *rpool;
3548 
3549 		flags = 0;
3550 		if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551 			flags |= SM_LDAP_SINGLEMATCH;
3552 		if (bitset(MF_MATCHONLY, map->map_mflags))
3553 			flags |= SM_LDAP_MATCHONLY;
3554 
3555 		/* Create an rpool for search related memory usage */
3556 		rpool = sm_rpool_new_x(NULL);
3557 
3558 		p = NULL;
3559 		*statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3560 					 rpool, &p, &plen, &psize, NULL);
3561 		save_errno = errno;
3562 
3563 		/* Copy result so rpool can be freed */
3564 		if (*statp == EX_OK && p != NULL)
3565 			vp = newstr(p);
3566 		sm_rpool_free(rpool);
3567 
3568 		/* need to restart LDAP connection? */
3569 		if (*statp == EX_RESTART)
3570 		{
3571 			*statp = EX_TEMPFAIL;
3572 			ldapmap_close(map);
3573 		}
3574 
3575 		errno = save_errno;
3576 		if (*statp != EX_OK && *statp != EX_NOTFOUND)
3577 		{
3578 			if (!bitset(MF_OPTIONAL, map->map_mflags))
3579 			{
3580 				if (bitset(MF_NODEFER, map->map_mflags))
3581 					syserr("Error getting LDAP results in map %s",
3582 					       map->map_mname);
3583 				else
3584 					syserr("451 4.3.5 Error getting LDAP results in map %s",
3585 					       map->map_mname);
3586 			}
3587 			errno = save_errno;
3588 			return NULL;
3589 		}
3590 	}
3591 # else /* _FFR_LDAP_RECURSION */
3592 
3593 	/* Get results */
3594 	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
3595 				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
3596 				   &(lmap->ldap_timeout)),
3597 				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
3598 	{
3599 		LDAPMessage *entry;
3600 
3601 		if (bitset(MF_SINGLEMATCH, map->map_mflags))
3602 		{
3603 			entries += ldap_count_entries(lmap->ldap_ld,
3604 						      lmap->ldap_res);
3605 			if (entries > 1)
3606 			{
3607 				*statp = EX_NOTFOUND;
3608 				if (lmap->ldap_res != NULL)
3609 				{
3610 					ldap_msgfree(lmap->ldap_res);
3611 					lmap->ldap_res = NULL;
3612 				}
3613 				(void) ldap_abandon(lmap->ldap_ld, msgid);
3614 				if (vp != NULL)
3615 					sm_free(vp); /* XXX */
3616 				if (tTd(38, 25))
3617 					sm_dprintf("ldap search found multiple on a single match query\n");
3618 				return NULL;
3619 			}
3620 		}
3621 
3622 		/* If we don't want multiple values and we have one, break */
3623 		if (map->map_coldelim == '\0' && vp != NULL)
3624 			break;
3625 
3626 		/* Cycle through all entries */
3627 		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
3628 		     entry != NULL;
3629 		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
3630 		{
3631 			BerElement *ber;
3632 			char *attr;
3633 			char **vals = NULL;
3634 
3635 			/*
3636 			**  If matching only and found an entry,
3637 			**  no need to spin through attributes
3638 			*/
3639 
3640 			if (*statp == EX_OK &&
3641 			    bitset(MF_MATCHONLY, map->map_mflags))
3642 				continue;
3643 
3644 #  if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3645 			/*
3646 			**  Reset value to prevent lingering
3647 			**  LDAP_DECODING_ERROR due to
3648 			**  OpenLDAP 1.X's hack (see below)
3649 			*/
3650 
3651 			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3652 #  endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3653 
3654 			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
3655 							 &ber);
3656 			     attr != NULL;
3657 			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
3658 							ber))
3659 			{
3660 				char *tmp, *vp_tmp;
3661 
3662 				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3663 				{
3664 					vals = ldap_get_values(lmap->ldap_ld,
3665 							       entry,
3666 							       attr);
3667 					if (vals == NULL)
3668 					{
3669 						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3670 						if (save_errno == LDAP_SUCCESS)
3671 						{
3672 							ldap_memfree(attr);
3673 							continue;
3674 						}
3675 
3676 						/* Must be an error */
3677 						save_errno += E_LDAPBASE;
3678 						if (!bitset(MF_OPTIONAL,
3679 							    map->map_mflags))
3680 						{
3681 							errno = save_errno;
3682 							if (bitset(MF_NODEFER,
3683 								   map->map_mflags))
3684 								syserr("Error getting LDAP values in map %s",
3685 								       map->map_mname);
3686 							else
3687 								syserr("451 4.3.5 Error getting LDAP values in map %s",
3688 								       map->map_mname);
3689 						}
3690 						*statp = EX_TEMPFAIL;
3691 						ldap_memfree(attr);
3692 						if (lmap->ldap_res != NULL)
3693 						{
3694 							ldap_msgfree(lmap->ldap_res);
3695 							lmap->ldap_res = NULL;
3696 						}
3697 						(void) ldap_abandon(lmap->ldap_ld,
3698 								    msgid);
3699 						if (vp != NULL)
3700 							sm_free(vp); /* XXX */
3701 						errno = save_errno;
3702 						return NULL;
3703 					}
3704 				}
3705 
3706 				*statp = EX_OK;
3707 
3708 #  if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3709 				/*
3710 				**  Reset value to prevent lingering
3711 				**  LDAP_DECODING_ERROR due to
3712 				**  OpenLDAP 1.X's hack (see below)
3713 				*/
3714 
3715 				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3716 #  endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3717 
3718 				/*
3719 				**  If matching only,
3720 				**  no need to spin through entries
3721 				*/
3722 
3723 				if (bitset(MF_MATCHONLY, map->map_mflags))
3724 				{
3725 					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3726 						ldap_value_free(vals);
3727 
3728 					ldap_memfree(attr);
3729 					continue;
3730 				}
3731 
3732 				/*
3733 				**  If we don't want multiple values,
3734 				**  return first found.
3735 				*/
3736 
3737 				if (map->map_coldelim == '\0')
3738 				{
3739 					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3740 					{
3741 						vp = newstr(attr);
3742 						ldap_memfree(attr);
3743 						break;
3744 					}
3745 
3746 					if (vals[0] == NULL)
3747 					{
3748 						ldap_value_free(vals);
3749 						ldap_memfree(attr);
3750 						continue;
3751 					}
3752 
3753 					vsize = strlen(vals[0]) + 1;
3754 					if (lmap->ldap_attrsep != '\0')
3755 						vsize += strlen(attr) + 1;
3756 					vp = xalloc(vsize);
3757 					if (lmap->ldap_attrsep != '\0')
3758 						sm_snprintf(vp, vsize,
3759 							    "%s%c%s",
3760 							    attr,
3761 							    lmap->ldap_attrsep,
3762 							    vals[0]);
3763 					else
3764 						sm_strlcpy(vp, vals[0], vsize);
3765 					ldap_value_free(vals);
3766 					ldap_memfree(attr);
3767 					break;
3768 				}
3769 
3770 				/* attributes only */
3771 				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3772 				{
3773 					if (vp == NULL)
3774 						vp = newstr(attr);
3775 					else
3776 					{
3777 						vsize = strlen(vp) +
3778 							strlen(attr) + 2;
3779 						tmp = xalloc(vsize);
3780 						(void) sm_snprintf(tmp,
3781 							vsize, "%s%c%s",
3782 							vp, map->map_coldelim,
3783 							attr);
3784 						sm_free(vp); /* XXX */
3785 						vp = tmp;
3786 					}
3787 					ldap_memfree(attr);
3788 					continue;
3789 				}
3790 
3791 				/*
3792 				**  If there is more than one,
3793 				**  munge then into a map_coldelim
3794 				**  separated string
3795 				*/
3796 
3797 				vsize = 0;
3798 				for (i = 0; vals[i] != NULL; i++)
3799 				{
3800 					vsize += strlen(vals[i]) + 1;
3801 					if (lmap->ldap_attrsep != '\0')
3802 						vsize += strlen(attr) + 1;
3803 				}
3804 				vp_tmp = xalloc(vsize);
3805 				*vp_tmp = '\0';
3806 
3807 				p = vp_tmp;
3808 				for (i = 0; vals[i] != NULL; i++)
3809 				{
3810 					if (lmap->ldap_attrsep != '\0')
3811 					{
3812 						p += sm_strlcpy(p, attr,
3813 								vsize - (p - vp_tmp));
3814 						*p++ = lmap->ldap_attrsep;
3815 					}
3816 					p += sm_strlcpy(p, vals[i],
3817 							vsize - (p - vp_tmp));
3818 					if (p >= vp_tmp + vsize)
3819 						syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3820 					if (vals[i + 1] != NULL)
3821 						*p++ = map->map_coldelim;
3822 				}
3823 
3824 				ldap_value_free(vals);
3825 				ldap_memfree(attr);
3826 				if (vp == NULL)
3827 				{
3828 					vp = vp_tmp;
3829 					continue;
3830 				}
3831 				vsize = strlen(vp) + strlen(vp_tmp) + 2;
3832 				tmp = xalloc(vsize);
3833 				(void) sm_snprintf(tmp, vsize, "%s%c%s",
3834 					 vp, map->map_coldelim, vp_tmp);
3835 
3836 				sm_free(vp); /* XXX */
3837 				sm_free(vp_tmp); /* XXX */
3838 				vp = tmp;
3839 			}
3840 			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3841 
3842 			/*
3843 			**  We check errno != LDAP_DECODING_ERROR since
3844 			**  OpenLDAP 1.X has a very ugly *undocumented*
3845 			**  hack of returning this error code from
3846 			**  ldap_next_attribute() if the library freed the
3847 			**  ber attribute.  See:
3848 			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
3849 			*/
3850 
3851 			if (save_errno != LDAP_SUCCESS &&
3852 			    save_errno != LDAP_DECODING_ERROR)
3853 			{
3854 				/* Must be an error */
3855 				save_errno += E_LDAPBASE;
3856 				if (!bitset(MF_OPTIONAL, map->map_mflags))
3857 				{
3858 					errno = save_errno;
3859 					if (bitset(MF_NODEFER, map->map_mflags))
3860 						syserr("Error getting LDAP attributes in map %s",
3861 						       map->map_mname);
3862 					else
3863 						syserr("451 4.3.5 Error getting LDAP attributes in map %s",
3864 						       map->map_mname);
3865 				}
3866 				*statp = EX_TEMPFAIL;
3867 				if (lmap->ldap_res != NULL)
3868 				{
3869 					ldap_msgfree(lmap->ldap_res);
3870 					lmap->ldap_res = NULL;
3871 				}
3872 				(void) ldap_abandon(lmap->ldap_ld, msgid);
3873 				if (vp != NULL)
3874 					sm_free(vp); /* XXX */
3875 				errno = save_errno;
3876 				return NULL;
3877 			}
3878 
3879 			/* We don't want multiple values and we have one */
3880 			if (map->map_coldelim == '\0' && vp != NULL)
3881 				break;
3882 		}
3883 		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3884 		if (save_errno != LDAP_SUCCESS &&
3885 		    save_errno != LDAP_DECODING_ERROR)
3886 		{
3887 			/* Must be an error */
3888 			save_errno += E_LDAPBASE;
3889 			if (!bitset(MF_OPTIONAL, map->map_mflags))
3890 			{
3891 				errno = save_errno;
3892 				if (bitset(MF_NODEFER, map->map_mflags))
3893 					syserr("Error getting LDAP entries in map %s",
3894 					       map->map_mname);
3895 				else
3896 					syserr("451 4.3.5 Error getting LDAP entries in map %s",
3897 					       map->map_mname);
3898 			}
3899 			*statp = EX_TEMPFAIL;
3900 			if (lmap->ldap_res != NULL)
3901 			{
3902 				ldap_msgfree(lmap->ldap_res);
3903 				lmap->ldap_res = NULL;
3904 			}
3905 			(void) ldap_abandon(lmap->ldap_ld, msgid);
3906 			if (vp != NULL)
3907 				sm_free(vp); /* XXX */
3908 			errno = save_errno;
3909 			return NULL;
3910 		}
3911 		ldap_msgfree(lmap->ldap_res);
3912 		lmap->ldap_res = NULL;
3913 	}
3914 
3915 	if (ret == 0)
3916 		save_errno = ETIMEDOUT;
3917 	else
3918 		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3919 	if (save_errno != LDAP_SUCCESS)
3920 	{
3921 		if (ret != 0)
3922 			save_errno += E_LDAPBASE;
3923 
3924 		if (!bitset(MF_OPTIONAL, map->map_mflags))
3925 		{
3926 			errno = save_errno;
3927 			if (bitset(MF_NODEFER, map->map_mflags))
3928 				syserr("Error getting LDAP results in map %s",
3929 				       map->map_mname);
3930 			else
3931 				syserr("451 4.3.5 Error getting LDAP results in map %s",
3932 				       map->map_mname);
3933 		}
3934 		*statp = EX_TEMPFAIL;
3935 		if (vp != NULL)
3936 			sm_free(vp); /* XXX */
3937 
3938 		switch (save_errno - E_LDAPBASE)
3939 		{
3940 #  ifdef LDAP_SERVER_DOWN
3941 		  case LDAP_SERVER_DOWN:
3942 #  endif /* LDAP_SERVER_DOWN */
3943 		  case LDAP_TIMEOUT:
3944 		  case LDAP_UNAVAILABLE:
3945 			/* server disappeared, try reopen on next search */
3946 			ldapmap_close(map);
3947 			break;
3948 		}
3949 		errno = save_errno;
3950 		return NULL;
3951 	}
3952 # endif /* _FFR_LDAP_RECURSION */
3953 
3954 	/* Did we match anything? */
3955 	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3956 		return NULL;
3957 
3958 	if (*statp == EX_OK)
3959 	{
3960 		if (LogLevel > 9)
3961 			sm_syslog(LOG_INFO, CurEnv->e_id,
3962 				  "ldap %.100s => %s", name,
3963 				  vp == NULL ? "<NULL>" : vp);
3964 		if (bitset(MF_MATCHONLY, map->map_mflags))
3965 			result = map_rewrite(map, name, strlen(name), NULL);
3966 		else
3967 		{
3968 			/* vp != NULL according to test above */
3969 			result = map_rewrite(map, vp, strlen(vp), av);
3970 		}
3971 		if (vp != NULL)
3972 			sm_free(vp); /* XXX */
3973 	}
3974 	return result;
3975 }
3976 
3977 /*
3978 **  LDAPMAP_FINDCONN -- find an LDAP connection to the server
3979 **
3980 **	Cache LDAP connections based on the host, port, bind DN,
3981 **	secret, and PID so we don't have multiple connections open to
3982 **	the same server for different maps.  Need a separate connection
3983 **	per PID since a parent process may close the map before the
3984 **	child is done with it.
3985 **
3986 **	Parameters:
3987 **		lmap -- LDAP map information
3988 **
3989 **	Returns:
3990 **		Symbol table entry for the LDAP connection.
3991 */
3992 
3993 static STAB *
3994 ldapmap_findconn(lmap)
3995 	SM_LDAP_STRUCT *lmap;
3996 {
3997 	char *format;
3998 	char *nbuf;
3999 	STAB *SM_NONVOLATILE s = NULL;
4000 
4001 # if _FFR_LDAP_SETVERSION
4002 	format = "%s%c%d%c%d%c%s%c%s%d";
4003 # else /* _FFR_LDAP_SETVERSION */
4004 	format = "%s%c%d%c%s%c%s%d";
4005 # endif /* _FFR_LDAP_SETVERSION */
4006 	nbuf = sm_stringf_x(format,
4007 			    (lmap->ldap_target == NULL ? "localhost"
4008 						       : lmap->ldap_target),
4009 			    CONDELSE,
4010 			    lmap->ldap_port,
4011 			    CONDELSE,
4012 # if _FFR_LDAP_SETVERSION
4013 			    lmap->ldap_version,
4014 			    CONDELSE,
4015 # endif /* _FFR_LDAP_SETVERSION */
4016 			    (lmap->ldap_binddn == NULL ? ""
4017 						       : lmap->ldap_binddn),
4018 			    CONDELSE,
4019 			    (lmap->ldap_secret == NULL ? ""
4020 						       : lmap->ldap_secret),
4021 			    (int) CurrentPid);
4022 	SM_TRY
4023 		s = stab(nbuf, ST_LMAP, ST_ENTER);
4024 	SM_FINALLY
4025 		sm_free(nbuf);
4026 	SM_END_TRY
4027 	return s;
4028 }
4029 /*
4030 **  LDAPMAP_PARSEARGS -- parse ldap map definition args.
4031 */
4032 
4033 static struct lamvalues LDAPAuthMethods[] =
4034 {
4035 	{	"none",		LDAP_AUTH_NONE		},
4036 	{	"simple",	LDAP_AUTH_SIMPLE	},
4037 # ifdef LDAP_AUTH_KRBV4
4038 	{	"krbv4",	LDAP_AUTH_KRBV4		},
4039 # endif /* LDAP_AUTH_KRBV4 */
4040 	{	NULL,		0			}
4041 };
4042 
4043 static struct ladvalues LDAPAliasDereference[] =
4044 {
4045 	{	"never",	LDAP_DEREF_NEVER	},
4046 	{	"always",	LDAP_DEREF_ALWAYS	},
4047 	{	"search",	LDAP_DEREF_SEARCHING	},
4048 	{	"find",		LDAP_DEREF_FINDING	},
4049 	{	NULL,		0			}
4050 };
4051 
4052 static struct lssvalues LDAPSearchScope[] =
4053 {
4054 	{	"base",		LDAP_SCOPE_BASE		},
4055 	{	"one",		LDAP_SCOPE_ONELEVEL	},
4056 	{	"sub",		LDAP_SCOPE_SUBTREE	},
4057 	{	NULL,		0			}
4058 };
4059 
4060 bool
4061 ldapmap_parseargs(map, args)
4062 	MAP *map;
4063 	char *args;
4064 {
4065 	bool secretread = true;
4066 # if _FFR_LDAP_URI
4067 	bool ldaphost = false;
4068 # endif /* _FFR_LDAP_URI */
4069 	int i;
4070 	register char *p = args;
4071 	SM_LDAP_STRUCT *lmap;
4072 	struct lamvalues *lam;
4073 	struct ladvalues *lad;
4074 	struct lssvalues *lss;
4075 	char ldapfilt[MAXLINE];
4076 	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
4077 
4078 	/* Get ldap struct pointer from map */
4079 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
4080 
4081 	/* Check if setting the initial LDAP defaults */
4082 	if (lmap == NULL || lmap != LDAPDefaults)
4083 	{
4084 		/* We need to alloc an SM_LDAP_STRUCT struct */
4085 		lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
4086 		if (LDAPDefaults == NULL)
4087 			sm_ldap_clear(lmap);
4088 		else
4089 			STRUCTCOPY(*LDAPDefaults, *lmap);
4090 	}
4091 
4092 	/* there is no check whether there is really an argument */
4093 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4094 	map->map_spacesub = SpaceSub;	/* default value */
4095 
4096 	/* Check if setting up an alias or file class LDAP map */
4097 	if (bitset(MF_ALIAS, map->map_mflags))
4098 	{
4099 		/* Comma separate if used as an alias file */
4100 		map->map_coldelim = ',';
4101 		if (*args == '\0')
4102 		{
4103 			int n;
4104 			char *lc;
4105 			char jbuf[MAXHOSTNAMELEN];
4106 			char lcbuf[MAXLINE];
4107 
4108 			/* Get $j */
4109 			expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
4110 			if (jbuf[0] == '\0')
4111 			{
4112 				(void) sm_strlcpy(jbuf, "localhost",
4113 						  sizeof jbuf);
4114 			}
4115 
4116 			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
4117 			if (lc == NULL)
4118 				lc = "";
4119 			else
4120 			{
4121 				expand(lc, lcbuf, sizeof lcbuf, CurEnv);
4122 				lc = lcbuf;
4123 			}
4124 
4125 			n = sm_snprintf(ldapfilt, sizeof ldapfilt,
4126 					"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
4127 					lc, jbuf);
4128 			if (n >= sizeof ldapfilt)
4129 			{
4130 				syserr("%s: Default LDAP string too long",
4131 				       map->map_mname);
4132 				return false;
4133 			}
4134 
4135 			/* default args for an alias LDAP entry */
4136 			lmap->ldap_filter = ldapfilt;
4137 			lmap->ldap_attr[0] = "sendmailMTAAliasValue";
4138 			lmap->ldap_attr[1] = NULL;
4139 		}
4140 	}
4141 	else if (bitset(MF_FILECLASS, map->map_mflags))
4142 	{
4143 		/* Space separate if used as a file class file */
4144 		map->map_coldelim = ' ';
4145 	}
4146 
4147 	for (;;)
4148 	{
4149 		while (isascii(*p) && isspace(*p))
4150 			p++;
4151 		if (*p != '-')
4152 			break;
4153 		switch (*++p)
4154 		{
4155 		  case 'N':
4156 			map->map_mflags |= MF_INCLNULL;
4157 			map->map_mflags &= ~MF_TRY0NULL;
4158 			break;
4159 
4160 		  case 'O':
4161 			map->map_mflags &= ~MF_TRY1NULL;
4162 			break;
4163 
4164 		  case 'o':
4165 			map->map_mflags |= MF_OPTIONAL;
4166 			break;
4167 
4168 		  case 'f':
4169 			map->map_mflags |= MF_NOFOLDCASE;
4170 			break;
4171 
4172 		  case 'm':
4173 			map->map_mflags |= MF_MATCHONLY;
4174 			break;
4175 
4176 		  case 'A':
4177 			map->map_mflags |= MF_APPEND;
4178 			break;
4179 
4180 		  case 'q':
4181 			map->map_mflags |= MF_KEEPQUOTES;
4182 			break;
4183 
4184 		  case 'a':
4185 			map->map_app = ++p;
4186 			break;
4187 
4188 		  case 'T':
4189 			map->map_tapp = ++p;
4190 			break;
4191 
4192 		  case 't':
4193 			map->map_mflags |= MF_NODEFER;
4194 			break;
4195 
4196 		  case 'S':
4197 			map->map_spacesub = *++p;
4198 			break;
4199 
4200 		  case 'D':
4201 			map->map_mflags |= MF_DEFER;
4202 			break;
4203 
4204 		  case 'z':
4205 			if (*++p != '\\')
4206 				map->map_coldelim = *p;
4207 			else
4208 			{
4209 				switch (*++p)
4210 				{
4211 				  case 'n':
4212 					map->map_coldelim = '\n';
4213 					break;
4214 
4215 				  case 't':
4216 					map->map_coldelim = '\t';
4217 					break;
4218 
4219 				  default:
4220 					map->map_coldelim = '\\';
4221 				}
4222 			}
4223 			break;
4224 
4225 			/* Start of ldapmap specific args */
4226 		  case 'V':
4227 			if (*++p != '\\')
4228 				lmap->ldap_attrsep = *p;
4229 			else
4230 			{
4231 				switch (*++p)
4232 				{
4233 				  case 'n':
4234 					lmap->ldap_attrsep = '\n';
4235 					break;
4236 
4237 				  case 't':
4238 					lmap->ldap_attrsep = '\t';
4239 					break;
4240 
4241 				  default:
4242 					lmap->ldap_attrsep = '\\';
4243 				}
4244 			}
4245 			break;
4246 
4247 		  case 'k':		/* search field */
4248 			while (isascii(*++p) && isspace(*p))
4249 				continue;
4250 			lmap->ldap_filter = p;
4251 			break;
4252 
4253 		  case 'v':		/* attr to return */
4254 			while (isascii(*++p) && isspace(*p))
4255 				continue;
4256 			lmap->ldap_attr[0] = p;
4257 			lmap->ldap_attr[1] = NULL;
4258 			break;
4259 
4260 		  case '1':
4261 			map->map_mflags |= MF_SINGLEMATCH;
4262 			break;
4263 
4264 			/* args stolen from ldapsearch.c */
4265 		  case 'R':		/* don't auto chase referrals */
4266 # ifdef LDAP_REFERRALS
4267 			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4268 # else /* LDAP_REFERRALS */
4269 			syserr("compile with -DLDAP_REFERRALS for referral support");
4270 # endif /* LDAP_REFERRALS */
4271 			break;
4272 
4273 		  case 'n':		/* retrieve attribute names only */
4274 			lmap->ldap_attrsonly = LDAPMAP_TRUE;
4275 			break;
4276 
4277 		  case 'r':		/* alias dereferencing */
4278 			while (isascii(*++p) && isspace(*p))
4279 				continue;
4280 
4281 			if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4282 				p += 11;
4283 
4284 			for (lad = LDAPAliasDereference;
4285 			     lad != NULL && lad->lad_name != NULL; lad++)
4286 			{
4287 				if (sm_strncasecmp(p, lad->lad_name,
4288 						   strlen(lad->lad_name)) == 0)
4289 					break;
4290 			}
4291 			if (lad->lad_name != NULL)
4292 				lmap->ldap_deref = lad->lad_code;
4293 			else
4294 			{
4295 				/* bad config line */
4296 				if (!bitset(MCF_OPTFILE,
4297 					    map->map_class->map_cflags))
4298 				{
4299 					char *ptr;
4300 
4301 					if ((ptr = strchr(p, ' ')) != NULL)
4302 						*ptr = '\0';
4303 					syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4304 						p, map->map_mname);
4305 					if (ptr != NULL)
4306 						*ptr = ' ';
4307 					return false;
4308 				}
4309 			}
4310 			break;
4311 
4312 		  case 's':		/* search scope */
4313 			while (isascii(*++p) && isspace(*p))
4314 				continue;
4315 
4316 			if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4317 				p += 11;
4318 
4319 			for (lss = LDAPSearchScope;
4320 			     lss != NULL && lss->lss_name != NULL; lss++)
4321 			{
4322 				if (sm_strncasecmp(p, lss->lss_name,
4323 						   strlen(lss->lss_name)) == 0)
4324 					break;
4325 			}
4326 			if (lss->lss_name != NULL)
4327 				lmap->ldap_scope = lss->lss_code;
4328 			else
4329 			{
4330 				/* bad config line */
4331 				if (!bitset(MCF_OPTFILE,
4332 					    map->map_class->map_cflags))
4333 				{
4334 					char *ptr;
4335 
4336 					if ((ptr = strchr(p, ' ')) != NULL)
4337 						*ptr = '\0';
4338 					syserr("Scope must be [base|one|sub] (not %s) in map %s",
4339 						p, map->map_mname);
4340 					if (ptr != NULL)
4341 						*ptr = ' ';
4342 					return false;
4343 				}
4344 			}
4345 			break;
4346 
4347 		  case 'h':		/* ldap host */
4348 			while (isascii(*++p) && isspace(*p))
4349 				continue;
4350 # if _FFR_LDAP_URI
4351 			if (lmap->ldap_uri)
4352 			{
4353 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4354 				       map->map_mname);
4355 				return false;
4356 			}
4357 			ldaphost = true;
4358 # endif /* _FFR_LDAP_URI */
4359 			lmap->ldap_target = p;
4360 			break;
4361 
4362 		  case 'b':		/* search base */
4363 			while (isascii(*++p) && isspace(*p))
4364 				continue;
4365 			lmap->ldap_base = p;
4366 			break;
4367 
4368 		  case 'p':		/* ldap port */
4369 			while (isascii(*++p) && isspace(*p))
4370 				continue;
4371 			lmap->ldap_port = atoi(p);
4372 			break;
4373 
4374 		  case 'l':		/* time limit */
4375 			while (isascii(*++p) && isspace(*p))
4376 				continue;
4377 			lmap->ldap_timelimit = atoi(p);
4378 			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4379 			break;
4380 
4381 		  case 'Z':
4382 			while (isascii(*++p) && isspace(*p))
4383 				continue;
4384 			lmap->ldap_sizelimit = atoi(p);
4385 			break;
4386 
4387 		  case 'd':		/* Dn to bind to server as */
4388 			while (isascii(*++p) && isspace(*p))
4389 				continue;
4390 			lmap->ldap_binddn = p;
4391 			break;
4392 
4393 		  case 'M':		/* Method for binding */
4394 			while (isascii(*++p) && isspace(*p))
4395 				continue;
4396 
4397 			if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4398 				p += 10;
4399 
4400 			for (lam = LDAPAuthMethods;
4401 			     lam != NULL && lam->lam_name != NULL; lam++)
4402 			{
4403 				if (sm_strncasecmp(p, lam->lam_name,
4404 						   strlen(lam->lam_name)) == 0)
4405 					break;
4406 			}
4407 			if (lam->lam_name != NULL)
4408 				lmap->ldap_method = lam->lam_code;
4409 			else
4410 			{
4411 				/* bad config line */
4412 				if (!bitset(MCF_OPTFILE,
4413 					    map->map_class->map_cflags))
4414 				{
4415 					char *ptr;
4416 
4417 					if ((ptr = strchr(p, ' ')) != NULL)
4418 						*ptr = '\0';
4419 					syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4420 						p, map->map_mname);
4421 					if (ptr != NULL)
4422 						*ptr = ' ';
4423 					return false;
4424 				}
4425 			}
4426 
4427 			break;
4428 
4429 			/*
4430 			**  This is a string that is dependent on the
4431 			**  method used defined above.
4432 			*/
4433 
4434 		  case 'P':		/* Secret password for binding */
4435 			 while (isascii(*++p) && isspace(*p))
4436 				continue;
4437 			lmap->ldap_secret = p;
4438 			secretread = false;
4439 			break;
4440 
4441 # if _FFR_LDAP_URI
4442 		  case 'H':		/* Use LDAP URI */
4443 #  if !USE_LDAP_INIT
4444 			syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4445 			       map->map_mname);
4446 			return false;
4447 #  else /* !USE_LDAP_INIT */
4448 			if (ldaphost)
4449 			{
4450 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4451 				       map->map_mname);
4452 				return false;
4453 			}
4454 			while (isascii(*++p) && isspace(*p))
4455 				continue;
4456 			lmap->ldap_target = p;
4457 			lmap->ldap_uri = true;
4458 			break;
4459 #  endif /* !USE_LDAP_INIT */
4460 # endif /* _FFR_LDAP_URI */
4461 
4462 # if _FFR_LDAP_SETVERSION
4463 		  case 'w':
4464 			/* -w should be for passwd, -P should be for version */
4465 			while (isascii(*++p) && isspace(*p))
4466 				continue;
4467 			lmap->ldap_version = atoi(p);
4468 #  ifdef LDAP_VERSION_MAX
4469 			if (lmap->ldap_version > LDAP_VERSION_MAX)
4470 			{
4471 				syserr("LDAP version %d exceeds max of %d in map %s",
4472 				       lmap->ldap_version, LDAP_VERSION_MAX,
4473 				       map->map_mname);
4474 				return false;
4475 			}
4476 #  endif /* LDAP_VERSION_MAX */
4477 #  ifdef LDAP_VERSION_MIN
4478 			if (lmap->ldap_version < LDAP_VERSION_MIN)
4479 			{
4480 				syserr("LDAP version %d is lower than min of %d in map %s",
4481 				       lmap->ldap_version, LDAP_VERSION_MIN,
4482 				       map->map_mname);
4483 				return false;
4484 			}
4485 #  endif /* LDAP_VERSION_MIN */
4486 			break;
4487 # endif /* _FFR_LDAP_SETVERSION */
4488 
4489 		  default:
4490 			syserr("Illegal option %c map %s", *p, map->map_mname);
4491 			break;
4492 		}
4493 
4494 		/* need to account for quoted strings here */
4495 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4496 		{
4497 			if (*p == '"')
4498 			{
4499 				while (*++p != '"' && *p != '\0')
4500 					continue;
4501 				if (*p != '\0')
4502 					p++;
4503 			}
4504 			else
4505 				p++;
4506 		}
4507 
4508 		if (*p != '\0')
4509 			*p++ = '\0';
4510 	}
4511 
4512 	if (map->map_app != NULL)
4513 		map->map_app = newstr(ldapmap_dequote(map->map_app));
4514 	if (map->map_tapp != NULL)
4515 		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4516 
4517 	/*
4518 	**  We need to swallow up all the stuff into a struct
4519 	**  and dump it into map->map_dbptr1
4520 	*/
4521 
4522 	if (lmap->ldap_target != NULL &&
4523 	    (LDAPDefaults == NULL ||
4524 	     LDAPDefaults == lmap ||
4525 	     LDAPDefaults->ldap_target != lmap->ldap_target))
4526 		lmap->ldap_target = newstr(ldapmap_dequote(lmap->ldap_target));
4527 	map->map_domain = lmap->ldap_target;
4528 
4529 	if (lmap->ldap_binddn != NULL &&
4530 	    (LDAPDefaults == NULL ||
4531 	     LDAPDefaults == lmap ||
4532 	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4533 		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4534 
4535 	if (lmap->ldap_secret != NULL &&
4536 	    (LDAPDefaults == NULL ||
4537 	     LDAPDefaults == lmap ||
4538 	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4539 	{
4540 		SM_FILE_T *sfd;
4541 		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4542 
4543 		if (DontLockReadFiles)
4544 			sff |= SFF_NOLOCK;
4545 
4546 		/* need to use method to map secret to passwd string */
4547 		switch (lmap->ldap_method)
4548 		{
4549 		  case LDAP_AUTH_NONE:
4550 			/* Do nothing */
4551 			break;
4552 
4553 		  case LDAP_AUTH_SIMPLE:
4554 
4555 			/*
4556 			**  Secret is the name of a file with
4557 			**  the first line as the password.
4558 			*/
4559 
4560 			/* Already read in the secret? */
4561 			if (secretread)
4562 				break;
4563 
4564 			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4565 					O_RDONLY, 0, sff);
4566 			if (sfd == NULL)
4567 			{
4568 				syserr("LDAP map: cannot open secret %s",
4569 				       ldapmap_dequote(lmap->ldap_secret));
4570 				return false;
4571 			}
4572 			lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4573 						   sfd, TimeOuts.to_fileopen,
4574 						   "ldapmap_parseargs");
4575 			(void) sm_io_close(sfd, SM_TIME_DEFAULT);
4576 			if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4577 			{
4578 				syserr("LDAP map: secret in %s too long",
4579 				       ldapmap_dequote(lmap->ldap_secret));
4580 				return false;
4581 			}
4582 			if (lmap->ldap_secret != NULL &&
4583 			    strlen(m_tmp) > 0)
4584 			{
4585 				/* chomp newline */
4586 				if (m_tmp[strlen(m_tmp) - 1] == '\n')
4587 					m_tmp[strlen(m_tmp) - 1] = '\0';
4588 
4589 				lmap->ldap_secret = m_tmp;
4590 			}
4591 			break;
4592 
4593 # ifdef LDAP_AUTH_KRBV4
4594 		  case LDAP_AUTH_KRBV4:
4595 
4596 			/*
4597 			**  Secret is where the ticket file is
4598 			**  stashed
4599 			*/
4600 
4601 			(void) sm_snprintf(m_tmp, sizeof m_tmp,
4602 				"KRBTKFILE=%s",
4603 				ldapmap_dequote(lmap->ldap_secret));
4604 			lmap->ldap_secret = m_tmp;
4605 			break;
4606 # endif /* LDAP_AUTH_KRBV4 */
4607 
4608 		  default:	       /* Should NEVER get here */
4609 			syserr("LDAP map: Illegal value in lmap method");
4610 			return false;
4611 			/* NOTREACHED */
4612 			break;
4613 		}
4614 	}
4615 
4616 	if (lmap->ldap_secret != NULL &&
4617 	    (LDAPDefaults == NULL ||
4618 	     LDAPDefaults == lmap ||
4619 	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4620 		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4621 
4622 	if (lmap->ldap_base != NULL &&
4623 	    (LDAPDefaults == NULL ||
4624 	     LDAPDefaults == lmap ||
4625 	     LDAPDefaults->ldap_base != lmap->ldap_base))
4626 		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4627 
4628 	/*
4629 	**  Save the server from extra work.  If request is for a single
4630 	**  match, tell the server to only return enough records to
4631 	**  determine if there is a single match or not.  This can not
4632 	**  be one since the server would only return one and we wouldn't
4633 	**  know if there were others available.
4634 	*/
4635 
4636 	if (bitset(MF_SINGLEMATCH, map->map_mflags))
4637 		lmap->ldap_sizelimit = 2;
4638 
4639 	/* If setting defaults, don't process ldap_filter and ldap_attr */
4640 	if (lmap == LDAPDefaults)
4641 		return true;
4642 
4643 	if (lmap->ldap_filter != NULL)
4644 		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4645 	else
4646 	{
4647 		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4648 		{
4649 			syserr("No filter given in map %s", map->map_mname);
4650 			return false;
4651 		}
4652 	}
4653 
4654 	if (lmap->ldap_attr[0] != NULL)
4655 	{
4656 # if _FFR_LDAP_RECURSION
4657 		bool recurse = false;
4658 		bool normalseen = false;
4659 # endif /* _FFR_LDAP_RECURSION */
4660 
4661 		i = 0;
4662 		p = ldapmap_dequote(lmap->ldap_attr[0]);
4663 		lmap->ldap_attr[0] = NULL;
4664 
4665 # if _FFR_LDAP_RECURSION
4666 		/* Prime the attr list with the objectClass attribute */
4667 		lmap->ldap_attr[i] = "objectClass";
4668 		lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4669 		lmap->ldap_attr_needobjclass[i] = NULL;
4670 		i++;
4671 # endif /* _FFR_LDAP_RECURSION */
4672 
4673 		while (p != NULL)
4674 		{
4675 			char *v;
4676 
4677 			while (isascii(*p) && isspace(*p))
4678 				p++;
4679 			if (*p == '\0')
4680 				break;
4681 			v = p;
4682 			p = strchr(v, ',');
4683 			if (p != NULL)
4684 				*p++ = '\0';
4685 
4686 			if (i >= LDAPMAP_MAX_ATTR)
4687 			{
4688 				syserr("Too many return attributes in %s (max %d)",
4689 				       map->map_mname, LDAPMAP_MAX_ATTR);
4690 				return false;
4691 			}
4692 			if (*v != '\0')
4693 			{
4694 # if _FFR_LDAP_RECURSION
4695 				int j;
4696 				int use;
4697 				char *type;
4698 				char *needobjclass;
4699 
4700 				type = strchr(v, ':');
4701 				if (type != NULL)
4702 				{
4703 					*type++ = '\0';
4704 					needobjclass = strchr(type, ':');
4705 					if (needobjclass != NULL)
4706 						*needobjclass++ = '\0';
4707 				}
4708 				else
4709 				{
4710 					needobjclass = NULL;
4711 				}
4712 
4713 				use = i;
4714 
4715 				/* allow override on "objectClass" type */
4716 				if (sm_strcasecmp(v, "objectClass") == 0 &&
4717 				    lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4718 				{
4719 					use = 0;
4720 				}
4721 				else
4722 				{
4723 					/*
4724 					**  Don't add something to attribute
4725 					**  list twice.
4726 					*/
4727 
4728 					for (j = 1; j < i; j++)
4729 					{
4730 						if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4731 						{
4732 							syserr("Duplicate attribute (%s) in %s",
4733 							       v, map->map_mname);
4734 							return false;
4735 						}
4736 					}
4737 
4738 					lmap->ldap_attr[use] = newstr(v);
4739 					if (needobjclass != NULL &&
4740 					    *needobjclass != '\0' &&
4741 					    *needobjclass != '*')
4742 					{
4743 						lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4744 					}
4745 					else
4746 					{
4747 						lmap->ldap_attr_needobjclass[use] = NULL;
4748 					}
4749 
4750 				}
4751 
4752 				if (type != NULL && *type != '\0')
4753 				{
4754 					if (sm_strcasecmp(type, "dn") == 0)
4755 					{
4756 						recurse = true;
4757 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4758 					}
4759 					else if (sm_strcasecmp(type, "filter") == 0)
4760 					{
4761 						recurse = true;
4762 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4763 					}
4764 					else if (sm_strcasecmp(type, "url") == 0)
4765 					{
4766 						recurse = true;
4767 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4768 					}
4769 					else if (sm_strcasecmp(type, "normal") == 0)
4770 					{
4771 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4772 						normalseen = true;
4773 					}
4774 					else
4775 					{
4776 						syserr("Unknown attribute type (%s) in %s",
4777 						       type, map->map_mname);
4778 						return false;
4779 					}
4780 				}
4781 				else
4782 				{
4783 					lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4784 					normalseen = true;
4785 				}
4786 # else /* _FFR_LDAP_RECURSION */
4787 				lmap->ldap_attr[i] = newstr(v);
4788 # endif /* _FFR_LDAP_RECURSION */
4789 				i++;
4790 			}
4791 		}
4792 		lmap->ldap_attr[i] = NULL;
4793 # if _FFR_LDAP_RECURSION
4794 		if (recurse && !normalseen)
4795 		{
4796 			syserr("LDAP recursion requested in %s but no returnable attribute given",
4797 			       map->map_mname);
4798 			return false;
4799 		}
4800 		if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4801 		{
4802 			syserr("LDAP recursion requested in %s can not be used with -n",
4803 			       map->map_mname);
4804 			return false;
4805 		}
4806 # endif /* _FFR_LDAP_RECURSION */
4807 	}
4808 	map->map_db1 = (ARBPTR_T) lmap;
4809 	return true;
4810 }
4811 
4812 /*
4813 **  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4814 **
4815 **	Parameters:
4816 **		spec -- map argument string from LDAPDefaults option
4817 **
4818 **	Returns:
4819 **		None.
4820 */
4821 
4822 void
4823 ldapmap_set_defaults(spec)
4824 	char *spec;
4825 {
4826 	STAB *class;
4827 	MAP map;
4828 
4829 	/* Allocate and set the default values */
4830 	if (LDAPDefaults == NULL)
4831 		LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4832 	sm_ldap_clear(LDAPDefaults);
4833 
4834 	memset(&map, '\0', sizeof map);
4835 
4836 	/* look up the class */
4837 	class = stab("ldap", ST_MAPCLASS, ST_FIND);
4838 	if (class == NULL)
4839 	{
4840 		syserr("readcf: LDAPDefaultSpec: class ldap not available");
4841 		return;
4842 	}
4843 	map.map_class = &class->s_mapclass;
4844 	map.map_db1 = (ARBPTR_T) LDAPDefaults;
4845 	map.map_mname = "O LDAPDefaultSpec";
4846 
4847 	(void) ldapmap_parseargs(&map, spec);
4848 
4849 	/* These should never be set in LDAPDefaults */
4850 	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4851 	    map.map_spacesub != SpaceSub ||
4852 	    map.map_app != NULL ||
4853 	    map.map_tapp != NULL)
4854 	{
4855 		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4856 		SM_FREE_CLR(map.map_app);
4857 		SM_FREE_CLR(map.map_tapp);
4858 	}
4859 
4860 	if (LDAPDefaults->ldap_filter != NULL)
4861 	{
4862 		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4863 
4864 		/* don't free, it isn't malloc'ed in parseargs */
4865 		LDAPDefaults->ldap_filter = NULL;
4866 	}
4867 
4868 	if (LDAPDefaults->ldap_attr[0] != NULL)
4869 	{
4870 		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4871 		/* don't free, they aren't malloc'ed in parseargs */
4872 		LDAPDefaults->ldap_attr[0] = NULL;
4873 	}
4874 }
4875 #endif /* LDAPMAP */
4876 /*
4877 **  PH map
4878 */
4879 
4880 #if PH_MAP
4881 
4882 /*
4883 **  Support for the CCSO Nameserver (ph/qi).
4884 **  This code is intended to replace the so-called "ph mailer".
4885 **  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
4886 */
4887 
4888 /* what version of the ph map code we're running */
4889 static char phmap_id[PH_BUF_SIZE];
4890 
4891 /* sendmail version for phmap id string */
4892 extern const char Version[];
4893 
4894 /*
4895 **  PH_MAP_PARSEARGS -- parse ph map definition args.
4896 */
4897 
4898 bool
4899 ph_map_parseargs(map, args)
4900 	MAP *map;
4901 	char *args;
4902 {
4903 	register bool done;
4904 	register char *p = args;
4905 	PH_MAP_STRUCT *pmap = NULL;
4906 
4907 	/* initialize version string */
4908 	(void) sm_snprintf(phmap_id, sizeof phmap_id,
4909 			   "sendmail-%s phmap-20010529 libphclient-%s",
4910 			   Version, libphclient_version);
4911 
4912 	pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4913 
4914 	/* defaults */
4915 	pmap->ph_servers = NULL;
4916 	pmap->ph_field_list = NULL;
4917 	pmap->ph = NULL;
4918 	pmap->ph_timeout = 0;
4919 	pmap->ph_fastclose = 0;
4920 
4921 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4922 	for (;;)
4923 	{
4924 		while (isascii(*p) && isspace(*p))
4925 			p++;
4926 		if (*p != '-')
4927 			break;
4928 		switch (*++p)
4929 		{
4930 		  case 'N':
4931 			map->map_mflags |= MF_INCLNULL;
4932 			map->map_mflags &= ~MF_TRY0NULL;
4933 			break;
4934 
4935 		  case 'O':
4936 			map->map_mflags &= ~MF_TRY1NULL;
4937 			break;
4938 
4939 		  case 'o':
4940 			map->map_mflags |= MF_OPTIONAL;
4941 			break;
4942 
4943 		  case 'f':
4944 			map->map_mflags |= MF_NOFOLDCASE;
4945 			break;
4946 
4947 		  case 'm':
4948 			map->map_mflags |= MF_MATCHONLY;
4949 			break;
4950 
4951 		  case 'A':
4952 			map->map_mflags |= MF_APPEND;
4953 			break;
4954 
4955 		  case 'q':
4956 			map->map_mflags |= MF_KEEPQUOTES;
4957 			break;
4958 
4959 		  case 't':
4960 			map->map_mflags |= MF_NODEFER;
4961 			break;
4962 
4963 		  case 'a':
4964 			map->map_app = ++p;
4965 			break;
4966 
4967 		  case 'T':
4968 			map->map_tapp = ++p;
4969 			break;
4970 
4971 		  case 'l':
4972 			while (isascii(*++p) && isspace(*p))
4973 				continue;
4974 			pmap->ph_timeout = atoi(p);
4975 			break;
4976 
4977 		  case 'S':
4978 			map->map_spacesub = *++p;
4979 			break;
4980 
4981 		  case 'D':
4982 			map->map_mflags |= MF_DEFER;
4983 			break;
4984 
4985 		  case 'h':		/* PH server list */
4986 			while (isascii(*++p) && isspace(*p))
4987 				continue;
4988 			pmap->ph_servers = p;
4989 			break;
4990 
4991 		  case 'v':
4992 			sm_syslog(LOG_WARNING, NULL,
4993 				  "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead");
4994 			/* intentional fallthrough for backward compatibility */
4995 			/* FALLTHROUGH */
4996 
4997 		  case 'k':		/* fields to search for */
4998 			while (isascii(*++p) && isspace(*p))
4999 				continue;
5000 			pmap->ph_field_list = p;
5001 			break;
5002 
5003 		  default:
5004 			syserr("ph_map_parseargs: unknown option -%c", *p);
5005 		}
5006 
5007 		/* try to account for quoted strings */
5008 		done = isascii(*p) && isspace(*p);
5009 		while (*p != '\0' && !done)
5010 		{
5011 			if (*p == '"')
5012 			{
5013 				while (*++p != '"' && *p != '\0')
5014 					continue;
5015 				if (*p != '\0')
5016 					p++;
5017 			}
5018 			else
5019 				p++;
5020 			done = isascii(*p) && isspace(*p);
5021 		}
5022 
5023 		if (*p != '\0')
5024 			*p++ = '\0';
5025 	}
5026 
5027 	if (map->map_app != NULL)
5028 		map->map_app = newstr(ph_map_dequote(map->map_app));
5029 	if (map->map_tapp != NULL)
5030 		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
5031 
5032 	if (pmap->ph_field_list != NULL)
5033 		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
5034 
5035 	if (pmap->ph_servers != NULL)
5036 		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
5037 	else
5038 	{
5039 		syserr("ph_map_parseargs: -h flag is required");
5040 		return false;
5041 	}
5042 
5043 	map->map_db1 = (ARBPTR_T) pmap;
5044 	return true;
5045 }
5046 
5047 /*
5048 **  PH_MAP_CLOSE -- close the connection to the ph server
5049 */
5050 
5051 void
5052 ph_map_close(map)
5053 	MAP *map;
5054 {
5055 	PH_MAP_STRUCT *pmap;
5056 
5057 	pmap = (PH_MAP_STRUCT *)map->map_db1;
5058 	if (tTd(38, 9))
5059 		sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
5060 			   map->map_mname, pmap->ph_fastclose);
5061 
5062 
5063 	if (pmap->ph != NULL)
5064 	{
5065 		ph_set_sendhook(pmap->ph, NULL);
5066 		ph_set_recvhook(pmap->ph, NULL);
5067 		ph_close(pmap->ph, pmap->ph_fastclose);
5068 	}
5069 
5070 	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
5071 }
5072 
5073 static jmp_buf  PHTimeout;
5074 
5075 /* ARGSUSED */
5076 static void
5077 ph_timeout(unused)
5078 	int unused;
5079 {
5080 	/*
5081 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
5082 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
5083 	**	DOING.
5084 	*/
5085 
5086 	errno = ETIMEDOUT;
5087 	longjmp(PHTimeout, 1);
5088 }
5089 
5090 static void
5091 ph_map_send_debug(text)
5092 	char *text;
5093 {
5094 	if (LogLevel > 9)
5095 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
5096 			  "ph_map_send_debug: ==> %s", text);
5097 	if (tTd(38, 20))
5098 		sm_dprintf("ph_map_send_debug: ==> %s\n", text);
5099 }
5100 
5101 static void
5102 ph_map_recv_debug(text)
5103 	char *text;
5104 {
5105 	if (LogLevel > 10)
5106 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
5107 			  "ph_map_recv_debug: <== %s", text);
5108 	if (tTd(38, 21))
5109 		sm_dprintf("ph_map_recv_debug: <== %s\n", text);
5110 }
5111 
5112 /*
5113 **  PH_MAP_OPEN -- sub for opening PH map
5114 */
5115 bool
5116 ph_map_open(map, mode)
5117 	MAP *map;
5118 	int mode;
5119 {
5120 	PH_MAP_STRUCT *pmap;
5121 	register SM_EVENT *ev = NULL;
5122 	int save_errno = 0;
5123 	char *hostlist, *host;
5124 
5125 	if (tTd(38, 2))
5126 		sm_dprintf("ph_map_open(%s)\n", map->map_mname);
5127 
5128 	mode &= O_ACCMODE;
5129 	if (mode != O_RDONLY)
5130 	{
5131 		/* issue a pseudo-error message */
5132 		errno = SM_EMAPCANTWRITE;
5133 		return false;
5134 	}
5135 
5136 	if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
5137 	    bitset(MF_DEFER, map->map_mflags))
5138 	{
5139 		if (tTd(9, 1))
5140 			sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5141 				   map->map_mname);
5142 
5143 		/*
5144 		**  Unset MF_DEFER here so that map_lookup() returns
5145 		**  a temporary failure using the bogus map and
5146 		**  map->map_tapp instead of the default permanent error.
5147 		*/
5148 
5149 		map->map_mflags &= ~MF_DEFER;
5150 		return false;
5151 	}
5152 
5153 	pmap = (PH_MAP_STRUCT *)map->map_db1;
5154 	pmap->ph_fastclose = 0;		/* refresh field for reopen */
5155 
5156 	/* try each host in the list */
5157 	hostlist = newstr(pmap->ph_servers);
5158 	for (host = strtok(hostlist, " ");
5159 	     host != NULL;
5160 	     host = strtok(NULL, " "))
5161 	{
5162 		/* set timeout */
5163 		if (pmap->ph_timeout != 0)
5164 		{
5165 			if (setjmp(PHTimeout) != 0)
5166 			{
5167 				ev = NULL;
5168 				if (LogLevel > 1)
5169 					sm_syslog(LOG_NOTICE, CurEnv->e_id,
5170 						  "timeout connecting to PH server %.100s",
5171 						  host);
5172 				errno = ETIMEDOUT;
5173 				goto ph_map_open_abort;
5174 			}
5175 			ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5176 		}
5177 
5178 		/* open connection to server */
5179 		if (!ph_open(&(pmap->ph), host, PH_ROUNDROBIN|PH_DONTID,
5180 			     ph_map_send_debug, ph_map_recv_debug) &&
5181 		    !ph_id(pmap->ph, phmap_id))
5182 		{
5183 			if (ev != NULL)
5184 				sm_clrevent(ev);
5185 			sm_free(hostlist); /* XXX */
5186 			return true;
5187 		}
5188 
5189   ph_map_open_abort:
5190 		save_errno = errno;
5191 		if (ev != NULL)
5192 			sm_clrevent(ev);
5193 		pmap->ph_fastclose = PH_FASTCLOSE;
5194 		ph_map_close(map);
5195 		errno = save_errno;
5196 	}
5197 
5198 	if (bitset(MF_NODEFER, map->map_mflags))
5199 	{
5200 		if (errno == 0)
5201 			errno = EAGAIN;
5202 		syserr("ph_map_open: %s: cannot connect to PH server",
5203 		       map->map_mname);
5204 	}
5205 	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5206 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
5207 			  "ph_map_open: %s: cannot connect to PH server",
5208 			  map->map_mname);
5209 	sm_free(hostlist); /* XXX */
5210 	return false;
5211 }
5212 
5213 /*
5214 **  PH_MAP_LOOKUP -- look up key from ph server
5215 */
5216 
5217 char *
5218 ph_map_lookup(map, key, args, pstat)
5219 	MAP *map;
5220 	char *key;
5221 	char **args;
5222 	int *pstat;
5223 {
5224 	int i, save_errno = 0;
5225 	register SM_EVENT *ev = NULL;
5226 	PH_MAP_STRUCT *pmap;
5227 	char *value = NULL;
5228 
5229 	pmap = (PH_MAP_STRUCT *)map->map_db1;
5230 
5231 	*pstat = EX_OK;
5232 
5233 	/* set timeout */
5234 	if (pmap->ph_timeout != 0)
5235 	{
5236 		if (setjmp(PHTimeout) != 0)
5237 		{
5238 			ev = NULL;
5239 			if (LogLevel > 1)
5240 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
5241 					  "timeout during PH lookup of %.100s",
5242 					  key);
5243 			errno = ETIMEDOUT;
5244 			*pstat = EX_TEMPFAIL;
5245 			goto ph_map_lookup_abort;
5246 		}
5247 		ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5248 	}
5249 
5250 	/* perform lookup */
5251 	i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5252 	if (i == -1)
5253 		*pstat = EX_TEMPFAIL;
5254 	else if (i == PH_NOMATCH || i == PH_DATAERR)
5255 		*pstat = EX_UNAVAILABLE;
5256 
5257   ph_map_lookup_abort:
5258 	if (ev != NULL)
5259 		sm_clrevent(ev);
5260 
5261 	/*
5262 	**  Close the connection if the timer popped
5263 	**  or we got a temporary PH error
5264 	*/
5265 
5266 	if (*pstat == EX_TEMPFAIL)
5267 	{
5268 		save_errno = errno;
5269 		pmap->ph_fastclose = PH_FASTCLOSE;
5270 		ph_map_close(map);
5271 		errno = save_errno;
5272 	}
5273 
5274 	if (*pstat == EX_OK)
5275 	{
5276 		if (tTd(38,20))
5277 			sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5278 
5279 		if (bitset(MF_MATCHONLY, map->map_mflags))
5280 			return map_rewrite(map, key, strlen(key), NULL);
5281 		else
5282 			return map_rewrite(map, value, strlen(value), args);
5283 	}
5284 
5285 	return NULL;
5286 }
5287 #endif /* PH_MAP */
5288 /*
5289 **  syslog map
5290 */
5291 
5292 #define map_prio	map_lockfd	/* overload field */
5293 
5294 /*
5295 **  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5296 */
5297 
5298 bool
5299 syslog_map_parseargs(map, args)
5300 	MAP *map;
5301 	char *args;
5302 {
5303 	char *p = args;
5304 	char *priority = NULL;
5305 
5306 	/* there is no check whether there is really an argument */
5307 	while (*p != '\0')
5308 	{
5309 		while (isascii(*p) && isspace(*p))
5310 			p++;
5311 		if (*p != '-')
5312 			break;
5313 		++p;
5314 		if (*p == 'D')
5315 		{
5316 			map->map_mflags |= MF_DEFER;
5317 			++p;
5318 		}
5319 		else if (*p == 'S')
5320 		{
5321 			map->map_spacesub = *++p;
5322 			if (*p != '\0')
5323 				p++;
5324 		}
5325 		else if (*p == 'L')
5326 		{
5327 			while (*++p != '\0' && isascii(*p) && isspace(*p))
5328 				continue;
5329 			if (*p == '\0')
5330 				break;
5331 			priority = p;
5332 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5333 				p++;
5334 			if (*p != '\0')
5335 				*p++ = '\0';
5336 		}
5337 		else
5338 		{
5339 			syserr("Illegal option %c map syslog", *p);
5340 			++p;
5341 		}
5342 	}
5343 
5344 	if (priority == NULL)
5345 		map->map_prio = LOG_INFO;
5346 	else
5347 	{
5348 		if (sm_strncasecmp("LOG_", priority, 4) == 0)
5349 			priority += 4;
5350 
5351 #ifdef LOG_EMERG
5352 		if (sm_strcasecmp("EMERG", priority) == 0)
5353 			map->map_prio = LOG_EMERG;
5354 		else
5355 #endif /* LOG_EMERG */
5356 #ifdef LOG_ALERT
5357 		if (sm_strcasecmp("ALERT", priority) == 0)
5358 			map->map_prio = LOG_ALERT;
5359 		else
5360 #endif /* LOG_ALERT */
5361 #ifdef LOG_CRIT
5362 		if (sm_strcasecmp("CRIT", priority) == 0)
5363 			map->map_prio = LOG_CRIT;
5364 		else
5365 #endif /* LOG_CRIT */
5366 #ifdef LOG_ERR
5367 		if (sm_strcasecmp("ERR", priority) == 0)
5368 			map->map_prio = LOG_ERR;
5369 		else
5370 #endif /* LOG_ERR */
5371 #ifdef LOG_WARNING
5372 		if (sm_strcasecmp("WARNING", priority) == 0)
5373 			map->map_prio = LOG_WARNING;
5374 		else
5375 #endif /* LOG_WARNING */
5376 #ifdef LOG_NOTICE
5377 		if (sm_strcasecmp("NOTICE", priority) == 0)
5378 			map->map_prio = LOG_NOTICE;
5379 		else
5380 #endif /* LOG_NOTICE */
5381 #ifdef LOG_INFO
5382 		if (sm_strcasecmp("INFO", priority) == 0)
5383 			map->map_prio = LOG_INFO;
5384 		else
5385 #endif /* LOG_INFO */
5386 #ifdef LOG_DEBUG
5387 		if (sm_strcasecmp("DEBUG", priority) == 0)
5388 			map->map_prio = LOG_DEBUG;
5389 		else
5390 #endif /* LOG_DEBUG */
5391 		{
5392 			syserr("syslog_map_parseargs: Unknown priority %s",
5393 			       priority);
5394 			return false;
5395 		}
5396 	}
5397 	return true;
5398 }
5399 
5400 /*
5401 **  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
5402 */
5403 
5404 char *
5405 syslog_map_lookup(map, string, args, statp)
5406 	MAP *map;
5407 	char *string;
5408 	char **args;
5409 	int *statp;
5410 {
5411 	char *ptr = map_rewrite(map, string, strlen(string), args);
5412 
5413 	if (ptr != NULL)
5414 	{
5415 		if (tTd(38, 20))
5416 			sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5417 				map->map_mname, map->map_prio, ptr);
5418 
5419 		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5420 	}
5421 
5422 	*statp = EX_OK;
5423 	return "";
5424 }
5425 
5426 /*
5427 **  HESIOD Modules
5428 */
5429 
5430 #if HESIOD
5431 
5432 bool
5433 hes_map_open(map, mode)
5434 	MAP *map;
5435 	int mode;
5436 {
5437 	if (tTd(38, 2))
5438 		sm_dprintf("hes_map_open(%s, %s, %d)\n",
5439 			map->map_mname, map->map_file, mode);
5440 
5441 	if (mode != O_RDONLY)
5442 	{
5443 		/* issue a pseudo-error message */
5444 		errno = SM_EMAPCANTWRITE;
5445 		return false;
5446 	}
5447 
5448 # ifdef HESIOD_INIT
5449 	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5450 		return true;
5451 
5452 	if (!bitset(MF_OPTIONAL, map->map_mflags))
5453 		syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5454 			sm_errstring(errno));
5455 	return false;
5456 # else /* HESIOD_INIT */
5457 	if (hes_error() == HES_ER_UNINIT)
5458 		hes_init();
5459 	switch (hes_error())
5460 	{
5461 	  case HES_ER_OK:
5462 	  case HES_ER_NOTFOUND:
5463 		return true;
5464 	}
5465 
5466 	if (!bitset(MF_OPTIONAL, map->map_mflags))
5467 		syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5468 
5469 	return false;
5470 # endif /* HESIOD_INIT */
5471 }
5472 
5473 char *
5474 hes_map_lookup(map, name, av, statp)
5475 	MAP *map;
5476 	char *name;
5477 	char **av;
5478 	int *statp;
5479 {
5480 	char **hp;
5481 
5482 	if (tTd(38, 20))
5483 		sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5484 
5485 	if (name[0] == '\\')
5486 	{
5487 		char *np;
5488 		int nl;
5489 		int save_errno;
5490 		char nbuf[MAXNAME];
5491 
5492 		nl = strlen(name);
5493 		if (nl < sizeof nbuf - 1)
5494 			np = nbuf;
5495 		else
5496 			np = xalloc(strlen(name) + 2);
5497 		np[0] = '\\';
5498 		(void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5499 # ifdef HESIOD_INIT
5500 		hp = hesiod_resolve(HesiodContext, np, map->map_file);
5501 # else /* HESIOD_INIT */
5502 		hp = hes_resolve(np, map->map_file);
5503 # endif /* HESIOD_INIT */
5504 		save_errno = errno;
5505 		if (np != nbuf)
5506 			sm_free(np); /* XXX */
5507 		errno = save_errno;
5508 	}
5509 	else
5510 	{
5511 # ifdef HESIOD_INIT
5512 		hp = hesiod_resolve(HesiodContext, name, map->map_file);
5513 # else /* HESIOD_INIT */
5514 		hp = hes_resolve(name, map->map_file);
5515 # endif /* HESIOD_INIT */
5516 	}
5517 # ifdef HESIOD_INIT
5518 	if (hp == NULL || *hp == NULL)
5519 	{
5520 		switch (errno)
5521 		{
5522 		  case ENOENT:
5523 			  *statp = EX_NOTFOUND;
5524 			  break;
5525 		  case ECONNREFUSED:
5526 			  *statp = EX_TEMPFAIL;
5527 			  break;
5528 		  case EMSGSIZE:
5529 		  case ENOMEM:
5530 		  default:
5531 			  *statp = EX_UNAVAILABLE;
5532 			  break;
5533 		}
5534 		if (hp != NULL)
5535 			hesiod_free_list(HesiodContext, hp);
5536 		return NULL;
5537 	}
5538 # else /* HESIOD_INIT */
5539 	if (hp == NULL || hp[0] == NULL)
5540 	{
5541 		switch (hes_error())
5542 		{
5543 		  case HES_ER_OK:
5544 			*statp = EX_OK;
5545 			break;
5546 
5547 		  case HES_ER_NOTFOUND:
5548 			*statp = EX_NOTFOUND;
5549 			break;
5550 
5551 		  case HES_ER_CONFIG:
5552 			*statp = EX_UNAVAILABLE;
5553 			break;
5554 
5555 		  case HES_ER_NET:
5556 			*statp = EX_TEMPFAIL;
5557 			break;
5558 		}
5559 		return NULL;
5560 	}
5561 # endif /* HESIOD_INIT */
5562 
5563 	if (bitset(MF_MATCHONLY, map->map_mflags))
5564 		return map_rewrite(map, name, strlen(name), NULL);
5565 	else
5566 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
5567 }
5568 
5569 /*
5570 **  HES_MAP_CLOSE -- free the Hesiod context
5571 */
5572 
5573 void
5574 hes_map_close(map)
5575 	MAP *map;
5576 {
5577 	if (tTd(38, 20))
5578 		sm_dprintf("hes_map_close(%s)\n", map->map_file);
5579 
5580 # ifdef HESIOD_INIT
5581 	/* Free the hesiod context */
5582 	if (HesiodContext != NULL)
5583 	{
5584 		hesiod_end(HesiodContext);
5585 		HesiodContext = NULL;
5586 	}
5587 # endif /* HESIOD_INIT */
5588 }
5589 
5590 #endif /* HESIOD */
5591 /*
5592 **  NeXT NETINFO Modules
5593 */
5594 
5595 #if NETINFO
5596 
5597 # define NETINFO_DEFAULT_DIR		"/aliases"
5598 # define NETINFO_DEFAULT_PROPERTY	"members"
5599 
5600 /*
5601 **  NI_MAP_OPEN -- open NetInfo Aliases
5602 */
5603 
5604 bool
5605 ni_map_open(map, mode)
5606 	MAP *map;
5607 	int mode;
5608 {
5609 	if (tTd(38, 2))
5610 		sm_dprintf("ni_map_open(%s, %s, %d)\n",
5611 			map->map_mname, map->map_file, mode);
5612 	mode &= O_ACCMODE;
5613 
5614 	if (*map->map_file == '\0')
5615 		map->map_file = NETINFO_DEFAULT_DIR;
5616 
5617 	if (map->map_valcolnm == NULL)
5618 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5619 
5620 	if (map->map_coldelim == '\0')
5621 	{
5622 		if (bitset(MF_ALIAS, map->map_mflags))
5623 			map->map_coldelim = ',';
5624 		else if (bitset(MF_FILECLASS, map->map_mflags))
5625 			map->map_coldelim = ' ';
5626 	}
5627 	return true;
5628 }
5629 
5630 
5631 /*
5632 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
5633 */
5634 
5635 char *
5636 ni_map_lookup(map, name, av, statp)
5637 	MAP *map;
5638 	char *name;
5639 	char **av;
5640 	int *statp;
5641 {
5642 	char *res;
5643 	char *propval;
5644 
5645 	if (tTd(38, 20))
5646 		sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5647 
5648 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
5649 			     map->map_valcolnm, map->map_coldelim);
5650 
5651 	if (propval == NULL)
5652 		return NULL;
5653 
5654 	SM_TRY
5655 		if (bitset(MF_MATCHONLY, map->map_mflags))
5656 			res = map_rewrite(map, name, strlen(name), NULL);
5657 		else
5658 			res = map_rewrite(map, propval, strlen(propval), av);
5659 	SM_FINALLY
5660 		sm_free(propval);
5661 	SM_END_TRY
5662 	return res;
5663 }
5664 
5665 
5666 static bool
5667 ni_getcanonname(name, hbsize, statp)
5668 	char *name;
5669 	int hbsize;
5670 	int *statp;
5671 {
5672 	char *vptr;
5673 	char *ptr;
5674 	char nbuf[MAXNAME + 1];
5675 
5676 	if (tTd(38, 20))
5677 		sm_dprintf("ni_getcanonname(%s)\n", name);
5678 
5679 	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5680 	{
5681 		*statp = EX_UNAVAILABLE;
5682 		return false;
5683 	}
5684 	(void) shorten_hostname(nbuf);
5685 
5686 	/* we only accept single token search key */
5687 	if (strchr(nbuf, '.'))
5688 	{
5689 		*statp = EX_NOHOST;
5690 		return false;
5691 	}
5692 
5693 	/* Do the search */
5694 	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5695 
5696 	if (vptr == NULL)
5697 	{
5698 		*statp = EX_NOHOST;
5699 		return false;
5700 	}
5701 
5702 	/* Only want the first machine name */
5703 	if ((ptr = strchr(vptr, '\n')) != NULL)
5704 		*ptr = '\0';
5705 
5706 	if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5707 	{
5708 		sm_free(vptr);
5709 		*statp = EX_UNAVAILABLE;
5710 		return true;
5711 	}
5712 	sm_free(vptr);
5713 	*statp = EX_OK;
5714 	return false;
5715 }
5716 #endif /* NETINFO */
5717 /*
5718 **  TEXT (unindexed text file) Modules
5719 **
5720 **	This code donated by Sun Microsystems.
5721 */
5722 
5723 #define map_sff		map_lockfd	/* overload field */
5724 
5725 
5726 /*
5727 **  TEXT_MAP_OPEN -- open text table
5728 */
5729 
5730 bool
5731 text_map_open(map, mode)
5732 	MAP *map;
5733 	int mode;
5734 {
5735 	long sff;
5736 	int i;
5737 
5738 	if (tTd(38, 2))
5739 		sm_dprintf("text_map_open(%s, %s, %d)\n",
5740 			map->map_mname, map->map_file, mode);
5741 
5742 	mode &= O_ACCMODE;
5743 	if (mode != O_RDONLY)
5744 	{
5745 		errno = EPERM;
5746 		return false;
5747 	}
5748 
5749 	if (*map->map_file == '\0')
5750 	{
5751 		syserr("text map \"%s\": file name required",
5752 			map->map_mname);
5753 		return false;
5754 	}
5755 
5756 	if (map->map_file[0] != '/')
5757 	{
5758 		syserr("text map \"%s\": file name must be fully qualified",
5759 			map->map_mname);
5760 		return false;
5761 	}
5762 
5763 	sff = SFF_ROOTOK|SFF_REGONLY;
5764 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5765 		sff |= SFF_NOWLINK;
5766 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5767 		sff |= SFF_SAFEDIRPATH;
5768 	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5769 			  sff, S_IRUSR, NULL)) != 0)
5770 	{
5771 		int save_errno = errno;
5772 
5773 		/* cannot open this map */
5774 		if (tTd(38, 2))
5775 			sm_dprintf("\tunsafe map file: %d\n", i);
5776 		errno = save_errno;
5777 		if (!bitset(MF_OPTIONAL, map->map_mflags))
5778 			syserr("text map \"%s\": unsafe map file %s",
5779 				map->map_mname, map->map_file);
5780 		return false;
5781 	}
5782 
5783 	if (map->map_keycolnm == NULL)
5784 		map->map_keycolno = 0;
5785 	else
5786 	{
5787 		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5788 		{
5789 			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5790 				map->map_mname, map->map_file,
5791 				map->map_keycolnm);
5792 			return false;
5793 		}
5794 		map->map_keycolno = atoi(map->map_keycolnm);
5795 	}
5796 
5797 	if (map->map_valcolnm == NULL)
5798 		map->map_valcolno = 0;
5799 	else
5800 	{
5801 		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5802 		{
5803 			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5804 					map->map_mname, map->map_file,
5805 					map->map_valcolnm);
5806 			return false;
5807 		}
5808 		map->map_valcolno = atoi(map->map_valcolnm);
5809 	}
5810 
5811 	if (tTd(38, 2))
5812 	{
5813 		sm_dprintf("text_map_open(%s, %s): delimiter = ",
5814 			map->map_mname, map->map_file);
5815 		if (map->map_coldelim == '\0')
5816 			sm_dprintf("(white space)\n");
5817 		else
5818 			sm_dprintf("%c\n", map->map_coldelim);
5819 	}
5820 
5821 	map->map_sff = sff;
5822 	return true;
5823 }
5824 
5825 
5826 /*
5827 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5828 */
5829 
5830 char *
5831 text_map_lookup(map, name, av, statp)
5832 	MAP *map;
5833 	char *name;
5834 	char **av;
5835 	int *statp;
5836 {
5837 	char *vp;
5838 	auto int vsize;
5839 	int buflen;
5840 	SM_FILE_T *f;
5841 	char delim;
5842 	int key_idx;
5843 	bool found_it;
5844 	long sff = map->map_sff;
5845 	char search_key[MAXNAME + 1];
5846 	char linebuf[MAXLINE];
5847 	char buf[MAXNAME + 1];
5848 
5849 	found_it = false;
5850 	if (tTd(38, 20))
5851 		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
5852 
5853 	buflen = strlen(name);
5854 	if (buflen > sizeof search_key - 1)
5855 		buflen = sizeof search_key - 1;	/* XXX just cut if off? */
5856 	memmove(search_key, name, buflen);
5857 	search_key[buflen] = '\0';
5858 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5859 		makelower(search_key);
5860 
5861 	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5862 	if (f == NULL)
5863 	{
5864 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
5865 		*statp = EX_UNAVAILABLE;
5866 		return NULL;
5867 	}
5868 	key_idx = map->map_keycolno;
5869 	delim = map->map_coldelim;
5870 	while (sm_io_fgets(f, SM_TIME_DEFAULT,
5871 			   linebuf, sizeof linebuf) != NULL)
5872 	{
5873 		char *p;
5874 
5875 		/* skip comment line */
5876 		if (linebuf[0] == '#')
5877 			continue;
5878 		p = strchr(linebuf, '\n');
5879 		if (p != NULL)
5880 			*p = '\0';
5881 		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5882 		if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5883 		{
5884 			found_it = true;
5885 			break;
5886 		}
5887 	}
5888 	(void) sm_io_close(f, SM_TIME_DEFAULT);
5889 	if (!found_it)
5890 	{
5891 		*statp = EX_NOTFOUND;
5892 		return NULL;
5893 	}
5894 	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5895 	if (vp == NULL)
5896 	{
5897 		*statp = EX_NOTFOUND;
5898 		return NULL;
5899 	}
5900 	vsize = strlen(vp);
5901 	*statp = EX_OK;
5902 	if (bitset(MF_MATCHONLY, map->map_mflags))
5903 		return map_rewrite(map, name, strlen(name), NULL);
5904 	else
5905 		return map_rewrite(map, vp, vsize, av);
5906 }
5907 
5908 /*
5909 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
5910 */
5911 
5912 static bool
5913 text_getcanonname(name, hbsize, statp)
5914 	char *name;
5915 	int hbsize;
5916 	int *statp;
5917 {
5918 	bool found;
5919 	char *dot;
5920 	SM_FILE_T *f;
5921 	char linebuf[MAXLINE];
5922 	char cbuf[MAXNAME + 1];
5923 	char nbuf[MAXNAME + 1];
5924 
5925 	if (tTd(38, 20))
5926 		sm_dprintf("text_getcanonname(%s)\n", name);
5927 
5928 	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5929 	{
5930 		*statp = EX_UNAVAILABLE;
5931 		return false;
5932 	}
5933 	dot = shorten_hostname(nbuf);
5934 
5935 	f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5936 		       NULL);
5937 	if (f == NULL)
5938 	{
5939 		*statp = EX_UNAVAILABLE;
5940 		return false;
5941 	}
5942 	found = false;
5943 	while (!found &&
5944 		sm_io_fgets(f, SM_TIME_DEFAULT,
5945 			    linebuf, sizeof linebuf) != NULL)
5946 	{
5947 		char *p = strpbrk(linebuf, "#\n");
5948 
5949 		if (p != NULL)
5950 			*p = '\0';
5951 		if (linebuf[0] != '\0')
5952 			found = extract_canonname(nbuf, dot, linebuf,
5953 						  cbuf, sizeof cbuf);
5954 	}
5955 	(void) sm_io_close(f, SM_TIME_DEFAULT);
5956 	if (!found)
5957 	{
5958 		*statp = EX_NOHOST;
5959 		return false;
5960 	}
5961 
5962 	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5963 	{
5964 		*statp = EX_UNAVAILABLE;
5965 		return false;
5966 	}
5967 	*statp = EX_OK;
5968 	return true;
5969 }
5970 /*
5971 **  STAB (Symbol Table) Modules
5972 */
5973 
5974 
5975 /*
5976 **  STAB_MAP_LOOKUP -- look up alias in symbol table
5977 */
5978 
5979 /* ARGSUSED2 */
5980 char *
5981 stab_map_lookup(map, name, av, pstat)
5982 	register MAP *map;
5983 	char *name;
5984 	char **av;
5985 	int *pstat;
5986 {
5987 	register STAB *s;
5988 
5989 	if (tTd(38, 20))
5990 		sm_dprintf("stab_lookup(%s, %s)\n",
5991 			map->map_mname, name);
5992 
5993 	s = stab(name, ST_ALIAS, ST_FIND);
5994 	if (s != NULL)
5995 		return s->s_alias;
5996 	return NULL;
5997 }
5998 
5999 
6000 /*
6001 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
6002 */
6003 
6004 void
6005 stab_map_store(map, lhs, rhs)
6006 	register MAP *map;
6007 	char *lhs;
6008 	char *rhs;
6009 {
6010 	register STAB *s;
6011 
6012 	s = stab(lhs, ST_ALIAS, ST_ENTER);
6013 	s->s_alias = newstr(rhs);
6014 }
6015 
6016 
6017 /*
6018 **  STAB_MAP_OPEN -- initialize (reads data file)
6019 **
6020 **	This is a wierd case -- it is only intended as a fallback for
6021 **	aliases.  For this reason, opens for write (only during a
6022 **	"newaliases") always fails, and opens for read open the
6023 **	actual underlying text file instead of the database.
6024 */
6025 
6026 bool
6027 stab_map_open(map, mode)
6028 	register MAP *map;
6029 	int mode;
6030 {
6031 	SM_FILE_T *af;
6032 	long sff;
6033 	struct stat st;
6034 
6035 	if (tTd(38, 2))
6036 		sm_dprintf("stab_map_open(%s, %s, %d)\n",
6037 			map->map_mname, map->map_file, mode);
6038 
6039 	mode &= O_ACCMODE;
6040 	if (mode != O_RDONLY)
6041 	{
6042 		errno = EPERM;
6043 		return false;
6044 	}
6045 
6046 	sff = SFF_ROOTOK|SFF_REGONLY;
6047 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6048 		sff |= SFF_NOWLINK;
6049 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6050 		sff |= SFF_SAFEDIRPATH;
6051 	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6052 	if (af == NULL)
6053 		return false;
6054 	readaliases(map, af, false, false);
6055 
6056 	if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6057 		map->map_mtime = st.st_mtime;
6058 	(void) sm_io_close(af, SM_TIME_DEFAULT);
6059 
6060 	return true;
6061 }
6062 /*
6063 **  Implicit Modules
6064 **
6065 **	Tries several types.  For back compatibility of aliases.
6066 */
6067 
6068 
6069 /*
6070 **  IMPL_MAP_LOOKUP -- lookup in best open database
6071 */
6072 
6073 char *
6074 impl_map_lookup(map, name, av, pstat)
6075 	MAP *map;
6076 	char *name;
6077 	char **av;
6078 	int *pstat;
6079 {
6080 	if (tTd(38, 20))
6081 		sm_dprintf("impl_map_lookup(%s, %s)\n",
6082 			map->map_mname, name);
6083 
6084 #if NEWDB
6085 	if (bitset(MF_IMPL_HASH, map->map_mflags))
6086 		return db_map_lookup(map, name, av, pstat);
6087 #endif /* NEWDB */
6088 #if NDBM
6089 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6090 		return ndbm_map_lookup(map, name, av, pstat);
6091 #endif /* NDBM */
6092 	return stab_map_lookup(map, name, av, pstat);
6093 }
6094 
6095 /*
6096 **  IMPL_MAP_STORE -- store in open databases
6097 */
6098 
6099 void
6100 impl_map_store(map, lhs, rhs)
6101 	MAP *map;
6102 	char *lhs;
6103 	char *rhs;
6104 {
6105 	if (tTd(38, 12))
6106 		sm_dprintf("impl_map_store(%s, %s, %s)\n",
6107 			map->map_mname, lhs, rhs);
6108 #if NEWDB
6109 	if (bitset(MF_IMPL_HASH, map->map_mflags))
6110 		db_map_store(map, lhs, rhs);
6111 #endif /* NEWDB */
6112 #if NDBM
6113 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6114 		ndbm_map_store(map, lhs, rhs);
6115 #endif /* NDBM */
6116 	stab_map_store(map, lhs, rhs);
6117 }
6118 
6119 /*
6120 **  IMPL_MAP_OPEN -- implicit database open
6121 */
6122 
6123 bool
6124 impl_map_open(map, mode)
6125 	MAP *map;
6126 	int mode;
6127 {
6128 	if (tTd(38, 2))
6129 		sm_dprintf("impl_map_open(%s, %s, %d)\n",
6130 			map->map_mname, map->map_file, mode);
6131 
6132 	mode &= O_ACCMODE;
6133 #if NEWDB
6134 	map->map_mflags |= MF_IMPL_HASH;
6135 	if (hash_map_open(map, mode))
6136 	{
6137 # ifdef NDBM_YP_COMPAT
6138 		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6139 # endif /* NDBM_YP_COMPAT */
6140 			return true;
6141 	}
6142 	else
6143 		map->map_mflags &= ~MF_IMPL_HASH;
6144 #endif /* NEWDB */
6145 #if NDBM
6146 	map->map_mflags |= MF_IMPL_NDBM;
6147 	if (ndbm_map_open(map, mode))
6148 	{
6149 		return true;
6150 	}
6151 	else
6152 		map->map_mflags &= ~MF_IMPL_NDBM;
6153 #endif /* NDBM */
6154 
6155 #if defined(NEWDB) || defined(NDBM)
6156 	if (Verbose)
6157 		message("WARNING: cannot open alias database %s%s",
6158 			map->map_file,
6159 			mode == O_RDONLY ? "; reading text version" : "");
6160 #else /* defined(NEWDB) || defined(NDBM) */
6161 	if (mode != O_RDONLY)
6162 		usrerr("Cannot rebuild aliases: no database format defined");
6163 #endif /* defined(NEWDB) || defined(NDBM) */
6164 
6165 	if (mode == O_RDONLY)
6166 		return stab_map_open(map, mode);
6167 	else
6168 		return false;
6169 }
6170 
6171 
6172 /*
6173 **  IMPL_MAP_CLOSE -- close any open database(s)
6174 */
6175 
6176 void
6177 impl_map_close(map)
6178 	MAP *map;
6179 {
6180 	if (tTd(38, 9))
6181 		sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6182 			map->map_mname, map->map_file, map->map_mflags);
6183 #if NEWDB
6184 	if (bitset(MF_IMPL_HASH, map->map_mflags))
6185 	{
6186 		db_map_close(map);
6187 		map->map_mflags &= ~MF_IMPL_HASH;
6188 	}
6189 #endif /* NEWDB */
6190 
6191 #if NDBM
6192 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6193 	{
6194 		ndbm_map_close(map);
6195 		map->map_mflags &= ~MF_IMPL_NDBM;
6196 	}
6197 #endif /* NDBM */
6198 }
6199 /*
6200 **  User map class.
6201 **
6202 **	Provides access to the system password file.
6203 */
6204 
6205 /*
6206 **  USER_MAP_OPEN -- open user map
6207 **
6208 **	Really just binds field names to field numbers.
6209 */
6210 
6211 bool
6212 user_map_open(map, mode)
6213 	MAP *map;
6214 	int mode;
6215 {
6216 	if (tTd(38, 2))
6217 		sm_dprintf("user_map_open(%s, %d)\n",
6218 			map->map_mname, mode);
6219 
6220 	mode &= O_ACCMODE;
6221 	if (mode != O_RDONLY)
6222 	{
6223 		/* issue a pseudo-error message */
6224 		errno = SM_EMAPCANTWRITE;
6225 		return false;
6226 	}
6227 	if (map->map_valcolnm == NULL)
6228 		/* EMPTY */
6229 		/* nothing */ ;
6230 	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6231 		map->map_valcolno = 1;
6232 	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6233 		map->map_valcolno = 2;
6234 	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6235 		map->map_valcolno = 3;
6236 	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6237 		map->map_valcolno = 4;
6238 	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6239 		map->map_valcolno = 5;
6240 	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6241 		map->map_valcolno = 6;
6242 	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6243 		map->map_valcolno = 7;
6244 	else
6245 	{
6246 		syserr("User map %s: unknown column name %s",
6247 			map->map_mname, map->map_valcolnm);
6248 		return false;
6249 	}
6250 	return true;
6251 }
6252 
6253 
6254 /*
6255 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
6256 */
6257 
6258 /* ARGSUSED3 */
6259 char *
6260 user_map_lookup(map, key, av, statp)
6261 	MAP *map;
6262 	char *key;
6263 	char **av;
6264 	int *statp;
6265 {
6266 	auto bool fuzzy;
6267 	SM_MBDB_T user;
6268 
6269 	if (tTd(38, 20))
6270 		sm_dprintf("user_map_lookup(%s, %s)\n",
6271 			map->map_mname, key);
6272 
6273 	*statp = finduser(key, &fuzzy, &user);
6274 	if (*statp != EX_OK)
6275 		return NULL;
6276 	if (bitset(MF_MATCHONLY, map->map_mflags))
6277 		return map_rewrite(map, key, strlen(key), NULL);
6278 	else
6279 	{
6280 		char *rwval = NULL;
6281 		char buf[30];
6282 
6283 		switch (map->map_valcolno)
6284 		{
6285 		  case 0:
6286 		  case 1:
6287 			rwval = user.mbdb_name;
6288 			break;
6289 
6290 		  case 2:
6291 			rwval = "x";	/* passwd no longer supported */
6292 			break;
6293 
6294 		  case 3:
6295 			(void) sm_snprintf(buf, sizeof buf, "%d",
6296 					   (int) user.mbdb_uid);
6297 			rwval = buf;
6298 			break;
6299 
6300 		  case 4:
6301 			(void) sm_snprintf(buf, sizeof buf, "%d",
6302 					   (int) user.mbdb_gid);
6303 			rwval = buf;
6304 			break;
6305 
6306 		  case 5:
6307 			rwval = user.mbdb_fullname;
6308 			break;
6309 
6310 		  case 6:
6311 			rwval = user.mbdb_homedir;
6312 			break;
6313 
6314 		  case 7:
6315 			rwval = user.mbdb_shell;
6316 			break;
6317 		}
6318 		return map_rewrite(map, rwval, strlen(rwval), av);
6319 	}
6320 }
6321 /*
6322 **  Program map type.
6323 **
6324 **	This provides access to arbitrary programs.  It should be used
6325 **	only very sparingly, since there is no way to bound the cost
6326 **	of invoking an arbitrary program.
6327 */
6328 
6329 char *
6330 prog_map_lookup(map, name, av, statp)
6331 	MAP *map;
6332 	char *name;
6333 	char **av;
6334 	int *statp;
6335 {
6336 	int i;
6337 	int save_errno;
6338 	int fd;
6339 	int status;
6340 	auto pid_t pid;
6341 	register char *p;
6342 	char *rval;
6343 	char *argv[MAXPV + 1];
6344 	char buf[MAXLINE];
6345 
6346 	if (tTd(38, 20))
6347 		sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6348 			map->map_mname, name, map->map_file);
6349 
6350 	i = 0;
6351 	argv[i++] = map->map_file;
6352 	if (map->map_rebuild != NULL)
6353 	{
6354 		(void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6355 		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6356 		{
6357 			if (i >= MAXPV - 1)
6358 				break;
6359 			argv[i++] = p;
6360 		}
6361 	}
6362 	argv[i++] = name;
6363 	argv[i] = NULL;
6364 	if (tTd(38, 21))
6365 	{
6366 		sm_dprintf("prog_open:");
6367 		for (i = 0; argv[i] != NULL; i++)
6368 			sm_dprintf(" %s", argv[i]);
6369 		sm_dprintf("\n");
6370 	}
6371 	(void) sm_blocksignal(SIGCHLD);
6372 	pid = prog_open(argv, &fd, CurEnv);
6373 	if (pid < 0)
6374 	{
6375 		if (!bitset(MF_OPTIONAL, map->map_mflags))
6376 			syserr("prog_map_lookup(%s) failed (%s) -- closing",
6377 			       map->map_mname, sm_errstring(errno));
6378 		else if (tTd(38, 9))
6379 			sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6380 				   map->map_mname, sm_errstring(errno));
6381 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
6382 		*statp = EX_OSFILE;
6383 		return NULL;
6384 	}
6385 	i = read(fd, buf, sizeof buf - 1);
6386 	if (i < 0)
6387 	{
6388 		syserr("prog_map_lookup(%s): read error %s",
6389 		       map->map_mname, sm_errstring(errno));
6390 		rval = NULL;
6391 	}
6392 	else if (i == 0)
6393 	{
6394 		if (tTd(38, 20))
6395 			sm_dprintf("prog_map_lookup(%s): empty answer\n",
6396 				   map->map_mname);
6397 		rval = NULL;
6398 	}
6399 	else
6400 	{
6401 		buf[i] = '\0';
6402 		p = strchr(buf, '\n');
6403 		if (p != NULL)
6404 			*p = '\0';
6405 
6406 		/* collect the return value */
6407 		if (bitset(MF_MATCHONLY, map->map_mflags))
6408 			rval = map_rewrite(map, name, strlen(name), NULL);
6409 		else
6410 			rval = map_rewrite(map, buf, strlen(buf), av);
6411 
6412 		/* now flush any additional output */
6413 		while ((i = read(fd, buf, sizeof buf)) > 0)
6414 			continue;
6415 	}
6416 
6417 	/* wait for the process to terminate */
6418 	(void) close(fd);
6419 	status = waitfor(pid);
6420 	save_errno = errno;
6421 	(void) sm_releasesignal(SIGCHLD);
6422 	errno = save_errno;
6423 
6424 	if (status == -1)
6425 	{
6426 		syserr("prog_map_lookup(%s): wait error %s",
6427 		       map->map_mname, sm_errstring(errno));
6428 		*statp = EX_SOFTWARE;
6429 		rval = NULL;
6430 	}
6431 	else if (WIFEXITED(status))
6432 	{
6433 		if ((*statp = WEXITSTATUS(status)) != EX_OK)
6434 			rval = NULL;
6435 	}
6436 	else
6437 	{
6438 		syserr("prog_map_lookup(%s): child died on signal %d",
6439 		       map->map_mname, status);
6440 		*statp = EX_UNAVAILABLE;
6441 		rval = NULL;
6442 	}
6443 	return rval;
6444 }
6445 /*
6446 **  Sequenced map type.
6447 **
6448 **	Tries each map in order until something matches, much like
6449 **	implicit.  Stores go to the first map in the list that can
6450 **	support storing.
6451 **
6452 **	This is slightly unusual in that there are two interfaces.
6453 **	The "sequence" interface lets you stack maps arbitrarily.
6454 **	The "switch" interface builds a sequence map by looking
6455 **	at a system-dependent configuration file such as
6456 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6457 **
6458 **	We don't need an explicit open, since all maps are
6459 **	opened on demand.
6460 */
6461 
6462 /*
6463 **  SEQ_MAP_PARSE -- Sequenced map parsing
6464 */
6465 
6466 bool
6467 seq_map_parse(map, ap)
6468 	MAP *map;
6469 	char *ap;
6470 {
6471 	int maxmap;
6472 
6473 	if (tTd(38, 2))
6474 		sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6475 	maxmap = 0;
6476 	while (*ap != '\0')
6477 	{
6478 		register char *p;
6479 		STAB *s;
6480 
6481 		/* find beginning of map name */
6482 		while (isascii(*ap) && isspace(*ap))
6483 			ap++;
6484 		for (p = ap;
6485 		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6486 		     p++)
6487 			continue;
6488 		if (*p != '\0')
6489 			*p++ = '\0';
6490 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6491 			p++;
6492 		if (*ap == '\0')
6493 		{
6494 			ap = p;
6495 			continue;
6496 		}
6497 		s = stab(ap, ST_MAP, ST_FIND);
6498 		if (s == NULL)
6499 		{
6500 			syserr("Sequence map %s: unknown member map %s",
6501 				map->map_mname, ap);
6502 		}
6503 		else if (maxmap >= MAXMAPSTACK)
6504 		{
6505 			syserr("Sequence map %s: too many member maps (%d max)",
6506 				map->map_mname, MAXMAPSTACK);
6507 			maxmap++;
6508 		}
6509 		else if (maxmap < MAXMAPSTACK)
6510 		{
6511 			map->map_stack[maxmap++] = &s->s_map;
6512 		}
6513 		ap = p;
6514 	}
6515 	return true;
6516 }
6517 
6518 /*
6519 **  SWITCH_MAP_OPEN -- open a switched map
6520 **
6521 **	This looks at the system-dependent configuration and builds
6522 **	a sequence map that does the same thing.
6523 **
6524 **	Every system must define a switch_map_find routine in conf.c
6525 **	that will return the list of service types associated with a
6526 **	given service class.
6527 */
6528 
6529 bool
6530 switch_map_open(map, mode)
6531 	MAP *map;
6532 	int mode;
6533 {
6534 	int mapno;
6535 	int nmaps;
6536 	char *maptype[MAXMAPSTACK];
6537 
6538 	if (tTd(38, 2))
6539 		sm_dprintf("switch_map_open(%s, %s, %d)\n",
6540 			map->map_mname, map->map_file, mode);
6541 
6542 	mode &= O_ACCMODE;
6543 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6544 	if (tTd(38, 19))
6545 	{
6546 		sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6547 		for (mapno = 0; mapno < nmaps; mapno++)
6548 			sm_dprintf("\t\t%s\n", maptype[mapno]);
6549 	}
6550 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6551 		return false;
6552 
6553 	for (mapno = 0; mapno < nmaps; mapno++)
6554 	{
6555 		register STAB *s;
6556 		char nbuf[MAXNAME + 1];
6557 
6558 		if (maptype[mapno] == NULL)
6559 			continue;
6560 		(void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6561 				   map->map_mname, ".", maptype[mapno]);
6562 		s = stab(nbuf, ST_MAP, ST_FIND);
6563 		if (s == NULL)
6564 		{
6565 			syserr("Switch map %s: unknown member map %s",
6566 				map->map_mname, nbuf);
6567 		}
6568 		else
6569 		{
6570 			map->map_stack[mapno] = &s->s_map;
6571 			if (tTd(38, 4))
6572 				sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6573 					   mapno,
6574 					   s->s_map.map_class->map_cname,
6575 					   nbuf);
6576 		}
6577 	}
6578 	return true;
6579 }
6580 
6581 #if 0
6582 /*
6583 **  SEQ_MAP_CLOSE -- close all underlying maps
6584 */
6585 
6586 void
6587 seq_map_close(map)
6588 	MAP *map;
6589 {
6590 	int mapno;
6591 
6592 	if (tTd(38, 9))
6593 		sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6594 
6595 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6596 	{
6597 		MAP *mm = map->map_stack[mapno];
6598 
6599 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6600 			continue;
6601 		mm->map_mflags |= MF_CLOSING;
6602 		mm->map_class->map_close(mm);
6603 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6604 	}
6605 }
6606 #endif /* 0 */
6607 
6608 /*
6609 **  SEQ_MAP_LOOKUP -- sequenced map lookup
6610 */
6611 
6612 char *
6613 seq_map_lookup(map, key, args, pstat)
6614 	MAP *map;
6615 	char *key;
6616 	char **args;
6617 	int *pstat;
6618 {
6619 	int mapno;
6620 	int mapbit = 0x01;
6621 	bool tempfail = false;
6622 
6623 	if (tTd(38, 20))
6624 		sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6625 
6626 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6627 	{
6628 		MAP *mm = map->map_stack[mapno];
6629 		char *rv;
6630 
6631 		if (mm == NULL)
6632 			continue;
6633 		if (!bitset(MF_OPEN, mm->map_mflags) &&
6634 		    !openmap(mm))
6635 		{
6636 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6637 			{
6638 				*pstat = EX_UNAVAILABLE;
6639 				return NULL;
6640 			}
6641 			continue;
6642 		}
6643 		*pstat = EX_OK;
6644 		rv = mm->map_class->map_lookup(mm, key, args, pstat);
6645 		if (rv != NULL)
6646 			return rv;
6647 		if (*pstat == EX_TEMPFAIL)
6648 		{
6649 			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6650 				return NULL;
6651 			tempfail = true;
6652 		}
6653 		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6654 			break;
6655 	}
6656 	if (tempfail)
6657 		*pstat = EX_TEMPFAIL;
6658 	else if (*pstat == EX_OK)
6659 		*pstat = EX_NOTFOUND;
6660 	return NULL;
6661 }
6662 
6663 /*
6664 **  SEQ_MAP_STORE -- sequenced map store
6665 */
6666 
6667 void
6668 seq_map_store(map, key, val)
6669 	MAP *map;
6670 	char *key;
6671 	char *val;
6672 {
6673 	int mapno;
6674 
6675 	if (tTd(38, 12))
6676 		sm_dprintf("seq_map_store(%s, %s, %s)\n",
6677 			map->map_mname, key, val);
6678 
6679 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6680 	{
6681 		MAP *mm = map->map_stack[mapno];
6682 
6683 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6684 			continue;
6685 
6686 		mm->map_class->map_store(mm, key, val);
6687 		return;
6688 	}
6689 	syserr("seq_map_store(%s, %s, %s): no writable map",
6690 		map->map_mname, key, val);
6691 }
6692 /*
6693 **  NULL stubs
6694 */
6695 
6696 /* ARGSUSED */
6697 bool
6698 null_map_open(map, mode)
6699 	MAP *map;
6700 	int mode;
6701 {
6702 	return true;
6703 }
6704 
6705 /* ARGSUSED */
6706 void
6707 null_map_close(map)
6708 	MAP *map;
6709 {
6710 	return;
6711 }
6712 
6713 char *
6714 null_map_lookup(map, key, args, pstat)
6715 	MAP *map;
6716 	char *key;
6717 	char **args;
6718 	int *pstat;
6719 {
6720 	*pstat = EX_NOTFOUND;
6721 	return NULL;
6722 }
6723 
6724 /* ARGSUSED */
6725 void
6726 null_map_store(map, key, val)
6727 	MAP *map;
6728 	char *key;
6729 	char *val;
6730 {
6731 	return;
6732 }
6733 
6734 /*
6735 **  BOGUS stubs
6736 */
6737 
6738 char *
6739 bogus_map_lookup(map, key, args, pstat)
6740 	MAP *map;
6741 	char *key;
6742 	char **args;
6743 	int *pstat;
6744 {
6745 	*pstat = EX_TEMPFAIL;
6746 	return NULL;
6747 }
6748 
6749 MAPCLASS	BogusMapClass =
6750 {
6751 	"bogus-map",		NULL,			0,
6752 	NULL,			bogus_map_lookup,	null_map_store,
6753 	null_map_open,		null_map_close,
6754 };
6755 /*
6756 **  MACRO modules
6757 */
6758 
6759 char *
6760 macro_map_lookup(map, name, av, statp)
6761 	MAP *map;
6762 	char *name;
6763 	char **av;
6764 	int *statp;
6765 {
6766 	int mid;
6767 
6768 	if (tTd(38, 20))
6769 		sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6770 			name == NULL ? "NULL" : name);
6771 
6772 	if (name == NULL ||
6773 	    *name == '\0' ||
6774 	    (mid = macid(name)) == 0)
6775 	{
6776 		*statp = EX_CONFIG;
6777 		return NULL;
6778 	}
6779 
6780 	if (av[1] == NULL)
6781 		macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6782 	else
6783 		macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6784 
6785 	*statp = EX_OK;
6786 	return "";
6787 }
6788 /*
6789 **  REGEX modules
6790 */
6791 
6792 #if MAP_REGEX
6793 
6794 # include <regex.h>
6795 
6796 # define DEFAULT_DELIM	CONDELSE
6797 # define END_OF_FIELDS	-1
6798 # define ERRBUF_SIZE	80
6799 # define MAX_MATCH	32
6800 
6801 # define xnalloc(s)	memset(xalloc(s), '\0', s);
6802 
6803 struct regex_map
6804 {
6805 	regex_t	*regex_pattern_buf;	/* xalloc it */
6806 	int	*regex_subfields;	/* move to type MAP */
6807 	char	*regex_delim;		/* move to type MAP */
6808 };
6809 
6810 static int
6811 parse_fields(s, ibuf, blen, nr_substrings)
6812 	char *s;
6813 	int *ibuf;		/* array */
6814 	int blen;		/* number of elements in ibuf */
6815 	int nr_substrings;	/* number of substrings in the pattern */
6816 {
6817 	register char *cp;
6818 	int i = 0;
6819 	bool lastone = false;
6820 
6821 	blen--;		/* for terminating END_OF_FIELDS */
6822 	cp = s;
6823 	do
6824 	{
6825 		for (;; cp++)
6826 		{
6827 			if (*cp == ',')
6828 			{
6829 				*cp = '\0';
6830 				break;
6831 			}
6832 			if (*cp == '\0')
6833 			{
6834 				lastone = true;
6835 				break;
6836 			}
6837 		}
6838 		if (i < blen)
6839 		{
6840 			int val = atoi(s);
6841 
6842 			if (val < 0 || val >= nr_substrings)
6843 			{
6844 				syserr("field (%d) out of range, only %d substrings in pattern",
6845 				       val, nr_substrings);
6846 				return -1;
6847 			}
6848 			ibuf[i++] = val;
6849 		}
6850 		else
6851 		{
6852 			syserr("too many fields, %d max", blen);
6853 			return -1;
6854 		}
6855 		s = ++cp;
6856 	} while (!lastone);
6857 	ibuf[i] = END_OF_FIELDS;
6858 	return i;
6859 }
6860 
6861 bool
6862 regex_map_init(map, ap)
6863 	MAP *map;
6864 	char *ap;
6865 {
6866 	int regerr;
6867 	struct regex_map *map_p;
6868 	register char *p;
6869 	char *sub_param = NULL;
6870 	int pflags;
6871 	static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6872 
6873 	if (tTd(38, 2))
6874 		sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6875 			map->map_mname, ap);
6876 
6877 	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6878 	p = ap;
6879 	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6880 	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6881 
6882 	for (;;)
6883 	{
6884 		while (isascii(*p) && isspace(*p))
6885 			p++;
6886 		if (*p != '-')
6887 			break;
6888 		switch (*++p)
6889 		{
6890 		  case 'n':	/* not */
6891 			map->map_mflags |= MF_REGEX_NOT;
6892 			break;
6893 
6894 		  case 'f':	/* case sensitive */
6895 			map->map_mflags |= MF_NOFOLDCASE;
6896 			pflags &= ~REG_ICASE;
6897 			break;
6898 
6899 		  case 'b':	/* basic regular expressions */
6900 			pflags &= ~REG_EXTENDED;
6901 			break;
6902 
6903 		  case 's':	/* substring match () syntax */
6904 			sub_param = ++p;
6905 			pflags &= ~REG_NOSUB;
6906 			break;
6907 
6908 		  case 'd':	/* delimiter */
6909 			map_p->regex_delim = ++p;
6910 			break;
6911 
6912 		  case 'a':	/* map append */
6913 			map->map_app = ++p;
6914 			break;
6915 
6916 		  case 'm':	/* matchonly */
6917 			map->map_mflags |= MF_MATCHONLY;
6918 			break;
6919 
6920 		  case 'S':
6921 			map->map_spacesub = *++p;
6922 			break;
6923 
6924 		  case 'D':
6925 			map->map_mflags |= MF_DEFER;
6926 			break;
6927 
6928 		}
6929 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6930 			p++;
6931 		if (*p != '\0')
6932 			*p++ = '\0';
6933 	}
6934 	if (tTd(38, 3))
6935 		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6936 
6937 	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6938 	{
6939 		/* Errorhandling */
6940 		char errbuf[ERRBUF_SIZE];
6941 
6942 		(void) regerror(regerr, map_p->regex_pattern_buf,
6943 			 errbuf, sizeof errbuf);
6944 		syserr("pattern-compile-error: %s", errbuf);
6945 		sm_free(map_p->regex_pattern_buf); /* XXX */
6946 		sm_free(map_p); /* XXX */
6947 		return false;
6948 	}
6949 
6950 	if (map->map_app != NULL)
6951 		map->map_app = newstr(map->map_app);
6952 	if (map_p->regex_delim != NULL)
6953 		map_p->regex_delim = newstr(map_p->regex_delim);
6954 	else
6955 		map_p->regex_delim = defdstr;
6956 
6957 	if (!bitset(REG_NOSUB, pflags))
6958 	{
6959 		/* substring matching */
6960 		int substrings;
6961 		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6962 
6963 		substrings = map_p->regex_pattern_buf->re_nsub + 1;
6964 
6965 		if (tTd(38, 3))
6966 			sm_dprintf("regex_map_init: nr of substrings %d\n",
6967 				substrings);
6968 
6969 		if (substrings >= MAX_MATCH)
6970 		{
6971 			syserr("too many substrings, %d max", MAX_MATCH);
6972 			sm_free(map_p->regex_pattern_buf); /* XXX */
6973 			sm_free(map_p); /* XXX */
6974 			return false;
6975 		}
6976 		if (sub_param != NULL && sub_param[0] != '\0')
6977 		{
6978 			/* optional parameter -sfields */
6979 			if (parse_fields(sub_param, fields,
6980 					 MAX_MATCH + 1, substrings) == -1)
6981 				return false;
6982 		}
6983 		else
6984 		{
6985 			int i;
6986 
6987 			/* set default fields */
6988 			for (i = 0; i < substrings; i++)
6989 				fields[i] = i;
6990 			fields[i] = END_OF_FIELDS;
6991 		}
6992 		map_p->regex_subfields = fields;
6993 		if (tTd(38, 3))
6994 		{
6995 			int *ip;
6996 
6997 			sm_dprintf("regex_map_init: subfields");
6998 			for (ip = fields; *ip != END_OF_FIELDS; ip++)
6999 				sm_dprintf(" %d", *ip);
7000 			sm_dprintf("\n");
7001 		}
7002 	}
7003 	map->map_db1 = (ARBPTR_T) map_p;	/* dirty hack */
7004 	return true;
7005 }
7006 
7007 static char *
7008 regex_map_rewrite(map, s, slen, av)
7009 	MAP *map;
7010 	const char *s;
7011 	size_t slen;
7012 	char **av;
7013 {
7014 	if (bitset(MF_MATCHONLY, map->map_mflags))
7015 		return map_rewrite(map, av[0], strlen(av[0]), NULL);
7016 	else
7017 		return map_rewrite(map, s, slen, av);
7018 }
7019 
7020 char *
7021 regex_map_lookup(map, name, av, statp)
7022 	MAP *map;
7023 	char *name;
7024 	char **av;
7025 	int *statp;
7026 {
7027 	int reg_res;
7028 	struct regex_map *map_p;
7029 	regmatch_t pmatch[MAX_MATCH];
7030 
7031 	if (tTd(38, 20))
7032 	{
7033 		char **cpp;
7034 
7035 		sm_dprintf("regex_map_lookup: key '%s'\n", name);
7036 		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7037 			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7038 	}
7039 
7040 	map_p = (struct regex_map *)(map->map_db1);
7041 	reg_res = regexec(map_p->regex_pattern_buf,
7042 			  name, MAX_MATCH, pmatch, 0);
7043 
7044 	if (bitset(MF_REGEX_NOT, map->map_mflags))
7045 	{
7046 		/* option -n */
7047 		if (reg_res == REG_NOMATCH)
7048 			return regex_map_rewrite(map, "", (size_t) 0, av);
7049 		else
7050 			return NULL;
7051 	}
7052 	if (reg_res == REG_NOMATCH)
7053 		return NULL;
7054 
7055 	if (map_p->regex_subfields != NULL)
7056 	{
7057 		/* option -s */
7058 		static char retbuf[MAXNAME];
7059 		int fields[MAX_MATCH + 1];
7060 		bool first = true;
7061 		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7062 		bool quotemode = false, bslashmode = false;
7063 		register char *dp, *sp;
7064 		char *endp, *ldp;
7065 		int *ip;
7066 
7067 		dp = retbuf;
7068 		ldp = retbuf + sizeof(retbuf) - 1;
7069 
7070 		if (av[1] != NULL)
7071 		{
7072 			if (parse_fields(av[1], fields, MAX_MATCH + 1,
7073 					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7074 			{
7075 				*statp = EX_CONFIG;
7076 				return NULL;
7077 			}
7078 			ip = fields;
7079 		}
7080 		else
7081 			ip = map_p->regex_subfields;
7082 
7083 		for ( ; *ip != END_OF_FIELDS; ip++)
7084 		{
7085 			if (!first)
7086 			{
7087 				for (sp = map_p->regex_delim; *sp; sp++)
7088 				{
7089 					if (dp < ldp)
7090 						*dp++ = *sp;
7091 				}
7092 			}
7093 			else
7094 				first = false;
7095 
7096 			if (*ip >= MAX_MATCH ||
7097 			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7098 				continue;
7099 
7100 			sp = name + pmatch[*ip].rm_so;
7101 			endp = name + pmatch[*ip].rm_eo;
7102 			for (; endp > sp; sp++)
7103 			{
7104 				if (dp < ldp)
7105 				{
7106 					if (bslashmode)
7107 					{
7108 						*dp++ = *sp;
7109 						bslashmode = false;
7110 					}
7111 					else if (quotemode && *sp != '"' &&
7112 						*sp != '\\')
7113 					{
7114 						*dp++ = *sp;
7115 					}
7116 					else switch (*dp++ = *sp)
7117 					{
7118 					  case '\\':
7119 						bslashmode = true;
7120 						break;
7121 
7122 					  case '(':
7123 						cmntcnt++;
7124 						break;
7125 
7126 					  case ')':
7127 						cmntcnt--;
7128 						break;
7129 
7130 					  case '<':
7131 						anglecnt++;
7132 						break;
7133 
7134 					  case '>':
7135 						anglecnt--;
7136 						break;
7137 
7138 					  case ' ':
7139 						spacecnt++;
7140 						break;
7141 
7142 					  case '"':
7143 						quotemode = !quotemode;
7144 						break;
7145 					}
7146 				}
7147 			}
7148 		}
7149 		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7150 		    bslashmode || spacecnt != 0)
7151 		{
7152 			sm_syslog(LOG_WARNING, NOQID,
7153 				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
7154 				  map->map_mname, name);
7155 			return NULL;
7156 		}
7157 
7158 		*dp = '\0';
7159 
7160 		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7161 	}
7162 	return regex_map_rewrite(map, "", (size_t)0, av);
7163 }
7164 #endif /* MAP_REGEX */
7165 /*
7166 **  NSD modules
7167 */
7168 #if MAP_NSD
7169 
7170 # include <ndbm.h>
7171 # define _DATUM_DEFINED
7172 # include <ns_api.h>
7173 
7174 typedef struct ns_map_list
7175 {
7176 	ns_map_t		*map;		/* XXX ns_ ? */
7177 	char			*mapname;
7178 	struct ns_map_list	*next;
7179 } ns_map_list_t;
7180 
7181 static ns_map_t *
7182 ns_map_t_find(mapname)
7183 	char *mapname;
7184 {
7185 	static ns_map_list_t *ns_maps = NULL;
7186 	ns_map_list_t *ns_map;
7187 
7188 	/* walk the list of maps looking for the correctly named map */
7189 	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7190 	{
7191 		if (strcmp(ns_map->mapname, mapname) == 0)
7192 			break;
7193 	}
7194 
7195 	/* if we are looking at a NULL ns_map_list_t, then create a new one */
7196 	if (ns_map == NULL)
7197 	{
7198 		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
7199 		ns_map->mapname = newstr(mapname);
7200 		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
7201 		ns_map->next = ns_maps;
7202 		ns_maps = ns_map;
7203 	}
7204 	return ns_map->map;
7205 }
7206 
7207 char *
7208 nsd_map_lookup(map, name, av, statp)
7209 	MAP *map;
7210 	char *name;
7211 	char **av;
7212 	int *statp;
7213 {
7214 	int buflen, r;
7215 	char *p;
7216 	ns_map_t *ns_map;
7217 	char keybuf[MAXNAME + 1];
7218 	char buf[MAXLINE];
7219 
7220 	if (tTd(38, 20))
7221 		sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7222 
7223 	buflen = strlen(name);
7224 	if (buflen > sizeof keybuf - 1)
7225 		buflen = sizeof keybuf - 1;	/* XXX simply cut off? */
7226 	memmove(keybuf, name, buflen);
7227 	keybuf[buflen] = '\0';
7228 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7229 		makelower(keybuf);
7230 
7231 	ns_map = ns_map_t_find(map->map_file);
7232 	if (ns_map == NULL)
7233 	{
7234 		if (tTd(38, 20))
7235 			sm_dprintf("nsd_map_t_find failed\n");
7236 		*statp = EX_UNAVAILABLE;
7237 		return NULL;
7238 	}
7239 	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7240 		      buf, sizeof buf);
7241 	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7242 	{
7243 		*statp = EX_TEMPFAIL;
7244 		return NULL;
7245 	}
7246 	if (r == NS_BADREQ
7247 # ifdef NS_NOPERM
7248 	    || r == NS_NOPERM
7249 # endif /* NS_NOPERM */
7250 	    )
7251 	{
7252 		*statp = EX_CONFIG;
7253 		return NULL;
7254 	}
7255 	if (r != NS_SUCCESS)
7256 	{
7257 		*statp = EX_NOTFOUND;
7258 		return NULL;
7259 	}
7260 
7261 	*statp = EX_OK;
7262 
7263 	/* Null out trailing \n */
7264 	if ((p = strchr(buf, '\n')) != NULL)
7265 		*p = '\0';
7266 
7267 	return map_rewrite(map, buf, strlen(buf), av);
7268 }
7269 #endif /* MAP_NSD */
7270 
7271 char *
7272 arith_map_lookup(map, name, av, statp)
7273 	MAP *map;
7274 	char *name;
7275 	char **av;
7276 	int *statp;
7277 {
7278 	long r;
7279 	long v[2];
7280 	bool res = false;
7281 	bool boolres;
7282 	static char result[16];
7283 	char **cpp;
7284 
7285 	if (tTd(38, 2))
7286 	{
7287 		sm_dprintf("arith_map_lookup: key '%s'\n", name);
7288 		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7289 			sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7290 	}
7291 	r = 0;
7292 	boolres = false;
7293 	cpp = av;
7294 	*statp = EX_OK;
7295 
7296 	/*
7297 	**  read arguments for arith map
7298 	**  - no check is made whether they are really numbers
7299 	**  - just ignores args after the second
7300 	*/
7301 
7302 	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7303 		v[r++] = strtol(*cpp, NULL, 0);
7304 
7305 	/* operator and (at least) two operands given? */
7306 	if (name != NULL && r == 2)
7307 	{
7308 		switch (*name)
7309 		{
7310 		  case '|':
7311 			r = v[0] | v[1];
7312 			break;
7313 
7314 		  case '&':
7315 			r = v[0] & v[1];
7316 			break;
7317 
7318 		  case '%':
7319 			if (v[1] == 0)
7320 				return NULL;
7321 			r = v[0] % v[1];
7322 			break;
7323 		  case '+':
7324 			r = v[0] + v[1];
7325 			break;
7326 
7327 		  case '-':
7328 			r = v[0] - v[1];
7329 			break;
7330 
7331 		  case '*':
7332 			r = v[0] * v[1];
7333 			break;
7334 
7335 		  case '/':
7336 			if (v[1] == 0)
7337 				return NULL;
7338 			r = v[0] / v[1];
7339 			break;
7340 
7341 		  case 'l':
7342 			res = v[0] < v[1];
7343 			boolres = true;
7344 			break;
7345 
7346 		  case '=':
7347 			res = v[0] == v[1];
7348 			boolres = true;
7349 			break;
7350 
7351 		  default:
7352 			/* XXX */
7353 			*statp = EX_CONFIG;
7354 			if (LogLevel > 10)
7355 				sm_syslog(LOG_WARNING, NOQID,
7356 					  "arith_map: unknown operator %c",
7357 					  isprint(*name) ? *name : '?');
7358 			return NULL;
7359 		}
7360 		if (boolres)
7361 			(void) sm_snprintf(result, sizeof result,
7362 				res ? "TRUE" : "FALSE");
7363 		else
7364 			(void) sm_snprintf(result, sizeof result, "%ld", r);
7365 		return result;
7366 	}
7367 	*statp = EX_CONFIG;
7368 	return NULL;
7369 }
7370