xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 9a35f7df)
1 /*
2  * Copyright (c) 1992, 1995 Eric P. Allman.
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)map.c	8.66 (Berkeley) 05/24/95";
11 #endif /* not lint */
12 
13 #include "sendmail.h"
14 
15 #ifdef NDBM
16 # include <ndbm.h>
17 #endif
18 #ifdef NEWDB
19 # include <db.h>
20 #endif
21 #ifdef NIS
22   struct dom_binding;	/* forward reference needed on IRIX */
23 # include <rpcsvc/ypclnt.h>
24 #endif
25 
26 /*
27 **  MAP.C -- implementations for various map classes.
28 **
29 **	Each map class implements a series of functions:
30 **
31 **	bool map_parse(MAP *map, char *args)
32 **		Parse the arguments from the config file.  Return TRUE
33 **		if they were ok, FALSE otherwise.  Fill in map with the
34 **		values.
35 **
36 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
37 **		Look up the key in the given map.  If found, do any
38 **		rewriting the map wants (including "args" if desired)
39 **		and return the value.  Set *pstat to the appropriate status
40 **		on error and return NULL.  Args will be NULL if called
41 **		from the alias routines, although this should probably
42 **		not be relied upon.  It is suggested you call map_rewrite
43 **		to return the results -- it takes care of null termination
44 **		and uses a dynamically expanded buffer as needed.
45 **
46 **	void map_store(MAP *map, char *key, char *value)
47 **		Store the key:value pair in the map.
48 **
49 **	bool map_open(MAP *map, int mode)
50 **		Open the map for the indicated mode.  Mode should
51 **		be either O_RDONLY or O_RDWR.  Return TRUE if it
52 **		was opened successfully, FALSE otherwise.  If the open
53 **		failed an the MF_OPTIONAL flag is not set, it should
54 **		also print an error.  If the MF_ALIAS bit is set
55 **		and this map class understands the @:@ convention, it
56 **		should call aliaswait() before returning.
57 **
58 **	void map_close(MAP *map)
59 **		Close the map.
60 **
61 **	This file also includes the implementation for getcanonname.
62 **	It is currently implemented in a pretty ad-hoc manner; it ought
63 **	to be more properly integrated into the map structure.
64 */
65 
66 #define DBMMODE		0644
67 
68 extern bool	aliaswait __P((MAP *, char *, int));
69 /*
70 **  MAP_PARSEARGS -- parse config line arguments for database lookup
71 **
72 **	This is a generic version of the map_parse method.
73 **
74 **	Parameters:
75 **		map -- the map being initialized.
76 **		ap -- a pointer to the args on the config line.
77 **
78 **	Returns:
79 **		TRUE -- if everything parsed OK.
80 **		FALSE -- otherwise.
81 **
82 **	Side Effects:
83 **		null terminates the filename; stores it in map
84 */
85 
86 bool
87 map_parseargs(map, ap)
88 	MAP *map;
89 	char *ap;
90 {
91 	register char *p = ap;
92 
93 	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
94 	for (;;)
95 	{
96 		while (isascii(*p) && isspace(*p))
97 			p++;
98 		if (*p != '-')
99 			break;
100 		switch (*++p)
101 		{
102 		  case 'N':
103 			map->map_mflags |= MF_INCLNULL;
104 			map->map_mflags &= ~MF_TRY0NULL;
105 			break;
106 
107 		  case 'O':
108 			map->map_mflags &= ~MF_TRY1NULL;
109 			break;
110 
111 		  case 'o':
112 			map->map_mflags |= MF_OPTIONAL;
113 			break;
114 
115 		  case 'f':
116 			map->map_mflags |= MF_NOFOLDCASE;
117 			break;
118 
119 		  case 'm':
120 			map->map_mflags |= MF_MATCHONLY;
121 			break;
122 
123 		  case 'A':
124 			map->map_mflags |= MF_APPEND;
125 			break;
126 
127 		  case 'a':
128 			map->map_app = ++p;
129 			break;
130 
131 		  case 'k':
132 			while (isascii(*++p) && isspace(*p))
133 				continue;
134 			map->map_keycolnm = p;
135 			break;
136 
137 		  case 'v':
138 			while (isascii(*++p) && isspace(*p))
139 				continue;
140 			map->map_valcolnm = p;
141 			break;
142 
143 		  case 'z':
144 			if (*++p != '\\')
145 				map->map_coldelim = *p;
146 			else
147 			{
148 				switch (*++p)
149 				{
150 				  case 'n':
151 					map->map_coldelim = '\n';
152 					break;
153 
154 				  case 't':
155 					map->map_coldelim = '\t';
156 					break;
157 
158 				  default:
159 					map->map_coldelim = '\\';
160 				}
161 			}
162 			break;
163 #ifdef RESERVED_FOR_SUN
164 		  case 'd':
165 			map->map_mflags |= MF_DOMAIN_WIDE;
166 			break;
167 
168 		  case 's':
169 			/* info type */
170 			break;
171 #endif
172 		}
173 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
174 			p++;
175 		if (*p != '\0')
176 			*p++ = '\0';
177 	}
178 	if (map->map_app != NULL)
179 		map->map_app = newstr(map->map_app);
180 	if (map->map_keycolnm != NULL)
181 		map->map_keycolnm = newstr(map->map_keycolnm);
182 	if (map->map_valcolnm != NULL)
183 		map->map_valcolnm = newstr(map->map_valcolnm);
184 
185 	if (*p != '\0')
186 	{
187 		map->map_file = p;
188 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
189 			p++;
190 		if (*p != '\0')
191 			*p++ = '\0';
192 		map->map_file = newstr(map->map_file);
193 	}
194 
195 	while (*p != '\0' && isascii(*p) && isspace(*p))
196 		p++;
197 	if (*p != '\0')
198 		map->map_rebuild = newstr(p);
199 
200 	if (map->map_file == NULL &&
201 	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
202 	{
203 		syserr("No file name for %s map %s",
204 			map->map_class->map_cname, map->map_mname);
205 		return FALSE;
206 	}
207 	return TRUE;
208 }
209 /*
210 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
211 **
212 **	It also adds the map_app string.  It can be used as a utility
213 **	in the map_lookup method.
214 **
215 **	Parameters:
216 **		map -- the map that causes this.
217 **		s -- the string to rewrite, NOT necessarily null terminated.
218 **		slen -- the length of s.
219 **		av -- arguments to interpolate into buf.
220 **
221 **	Returns:
222 **		Pointer to rewritten result.  This is static data that
223 **		should be copied if it is to be saved!
224 **
225 **	Side Effects:
226 **		none.
227 */
228 
229 char *
230 map_rewrite(map, s, slen, av)
231 	register MAP *map;
232 	register char *s;
233 	int slen;
234 	char **av;
235 {
236 	register char *bp;
237 	register char c;
238 	char **avp;
239 	register char *ap;
240 	int i;
241 	int len;
242 	static int buflen = -1;
243 	static char *buf = NULL;
244 
245 	if (tTd(39, 1))
246 	{
247 		printf("map_rewrite(%.*s), av =", slen, s);
248 		if (av == NULL)
249 			printf(" (nullv)");
250 		else
251 		{
252 			for (avp = av; *avp != NULL; avp++)
253 				printf("\n\t%s", *avp);
254 		}
255 		printf("\n");
256 	}
257 
258 	/* count expected size of output (can safely overestimate) */
259 	i = len = slen;
260 	if (av != NULL)
261 	{
262 		bp = s;
263 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
264 		{
265 			if (c != '%')
266 				continue;
267 			if (--i < 0)
268 				break;
269 			c = *bp++;
270 			if (!(isascii(c) && isdigit(c)))
271 				continue;
272 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
273 				continue;
274 			if (*avp == NULL)
275 				continue;
276 			len += strlen(*avp);
277 		}
278 	}
279 	if (map->map_app != NULL)
280 		len += strlen(map->map_app);
281 	if (buflen < ++len)
282 	{
283 		/* need to malloc additional space */
284 		buflen = len;
285 		if (buf != NULL)
286 			free(buf);
287 		buf = xalloc(buflen);
288 	}
289 
290 	bp = buf;
291 	if (av == NULL)
292 	{
293 		bcopy(s, bp, slen);
294 		bp += slen;
295 	}
296 	else
297 	{
298 		while (--slen >= 0 && (c = *s++) != '\0')
299 		{
300 			if (c != '%')
301 			{
302   pushc:
303 				*bp++ = c;
304 				continue;
305 			}
306 			if (--slen < 0 || (c = *s++) == '\0')
307 				c = '%';
308 			if (c == '%')
309 				goto pushc;
310 			if (!(isascii(c) && isdigit(c)))
311 			{
312 				*bp++ = '%';
313 				goto pushc;
314 			}
315 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
316 				continue;
317 			if (*avp == NULL)
318 				continue;
319 
320 			/* transliterate argument into output string */
321 			for (ap = *avp; (c = *ap++) != '\0'; )
322 				*bp++ = c;
323 		}
324 	}
325 	if (map->map_app != NULL)
326 		strcpy(bp, map->map_app);
327 	else
328 		*bp = '\0';
329 	if (tTd(39, 1))
330 		printf("map_rewrite => %s\n", buf);
331 	return buf;
332 }
333 /*
334 **  INITMAPS -- initialize for aliasing
335 **
336 **	Parameters:
337 **		rebuild -- if TRUE, this rebuilds the cached versions.
338 **		e -- current envelope.
339 **
340 **	Returns:
341 **		none.
342 **
343 **	Side Effects:
344 **		initializes aliases:
345 **		if NDBM:  opens the database.
346 **		if ~NDBM: reads the aliases into the symbol table.
347 */
348 
349 initmaps(rebuild, e)
350 	bool rebuild;
351 	register ENVELOPE *e;
352 {
353 	extern void map_init();
354 
355 #ifdef XDEBUG
356 	checkfd012("entering initmaps");
357 #endif
358 	CurEnv = e;
359 	if (rebuild)
360 	{
361 		stabapply(map_init, 1);
362 		stabapply(map_init, 2);
363 	}
364 	else
365 	{
366 		stabapply(map_init, 0);
367 	}
368 #ifdef XDEBUG
369 	checkfd012("exiting initmaps");
370 #endif
371 }
372 
373 void
374 map_init(s, rebuild)
375 	register STAB *s;
376 	int rebuild;
377 {
378 	register MAP *map;
379 
380 	/* has to be a map */
381 	if (s->s_type != ST_MAP)
382 		return;
383 
384 	map = &s->s_map;
385 	if (!bitset(MF_VALID, map->map_mflags))
386 		return;
387 
388 	if (tTd(38, 2))
389 		printf("map_init(%s:%s, %s, %d)\n",
390 			map->map_class->map_cname == NULL ? "NULL" :
391 				map->map_class->map_cname,
392 			map->map_mname == NULL ? "NULL" : map->map_mname,
393 			map->map_file == NULL ? "NULL" : map->map_file,
394 			rebuild);
395 
396 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
397 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
398 	{
399 		if (tTd(38, 3))
400 			printf("\twrong pass\n");
401 		return;
402 	}
403 
404 	/* if already open, close it (for nested open) */
405 	if (bitset(MF_OPEN, map->map_mflags))
406 	{
407 		map->map_class->map_close(map);
408 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
409 	}
410 
411 	if (rebuild == 2)
412 	{
413 		rebuildaliases(map, FALSE);
414 	}
415 	else
416 	{
417 		if (map->map_class->map_open(map, O_RDONLY))
418 		{
419 			if (tTd(38, 4))
420 				printf("\t%s:%s %s: valid\n",
421 					map->map_class->map_cname == NULL ? "NULL" :
422 						map->map_class->map_cname,
423 					map->map_mname == NULL ? "NULL" :
424 						map->map_mname,
425 					map->map_file == NULL ? "NULL" :
426 						map->map_file);
427 			map->map_mflags |= MF_OPEN;
428 		}
429 		else
430 		{
431 			if (tTd(38, 4))
432 				printf("\t%s:%s %s: invalid: %s\n",
433 					map->map_class->map_cname == NULL ? "NULL" :
434 						map->map_class->map_cname,
435 					map->map_mname == NULL ? "NULL" :
436 						map->map_mname,
437 					map->map_file == NULL ? "NULL" :
438 						map->map_file,
439 					errstring(errno));
440 			if (!bitset(MF_OPTIONAL, map->map_mflags))
441 			{
442 				extern MAPCLASS BogusMapClass;
443 
444 				map->map_class = &BogusMapClass;
445 				map->map_mflags |= MF_OPEN;
446 			}
447 		}
448 	}
449 }
450 /*
451 **  NDBM modules
452 */
453 
454 #ifdef NDBM
455 
456 /*
457 **  DBM_MAP_OPEN -- DBM-style map open
458 */
459 
460 bool
461 ndbm_map_open(map, mode)
462 	MAP *map;
463 	int mode;
464 {
465 	register DBM *dbm;
466 	struct stat st;
467 
468 	if (tTd(38, 2))
469 		printf("ndbm_map_open(%s, %s, %d)\n",
470 			map->map_mname, map->map_file, mode);
471 
472 	if (mode == O_RDWR)
473 		mode |= O_CREAT|O_TRUNC;
474 
475 	/* open the database */
476 	dbm = dbm_open(map->map_file, mode, DBMMODE);
477 	if (dbm == NULL)
478 	{
479 		if (aliaswait(map, ".pag", FALSE))
480 			return TRUE;
481 		if (!bitset(MF_OPTIONAL, map->map_mflags))
482 			syserr("Cannot open DBM database %s", map->map_file);
483 		return FALSE;
484 	}
485 	map->map_db1 = (void *) dbm;
486 	if (mode == O_RDONLY)
487 	{
488 		if (bitset(MF_ALIAS, map->map_mflags) &&
489 		    !aliaswait(map, ".pag", TRUE))
490 			return FALSE;
491 	}
492 	else
493 	{
494 		int fd;
495 
496 		/* exclusive lock for duration of rebuild */
497 		fd = dbm_dirfno((DBM *) map->map_db1);
498 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
499 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
500 			map->map_mflags |= MF_LOCKED;
501 	}
502 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
503 		map->map_mtime = st.st_mtime;
504 	return TRUE;
505 }
506 
507 
508 /*
509 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
510 */
511 
512 char *
513 ndbm_map_lookup(map, name, av, statp)
514 	MAP *map;
515 	char *name;
516 	char **av;
517 	int *statp;
518 {
519 	datum key, val;
520 	int fd;
521 	char keybuf[MAXNAME + 1];
522 
523 	if (tTd(38, 20))
524 		printf("ndbm_map_lookup(%s, %s)\n",
525 			map->map_mname, name);
526 
527 	key.dptr = name;
528 	key.dsize = strlen(name);
529 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
530 	{
531 		if (key.dsize > sizeof keybuf - 1)
532 			key.dsize = sizeof keybuf - 1;
533 		bcopy(key.dptr, keybuf, key.dsize + 1);
534 		makelower(keybuf);
535 		key.dptr = keybuf;
536 	}
537 	fd = dbm_dirfno((DBM *) map->map_db1);
538 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
539 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
540 	val.dptr = NULL;
541 	if (bitset(MF_TRY0NULL, map->map_mflags))
542 	{
543 		val = dbm_fetch((DBM *) map->map_db1, key);
544 		if (val.dptr != NULL)
545 			map->map_mflags &= ~MF_TRY1NULL;
546 	}
547 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
548 	{
549 		key.dsize++;
550 		val = dbm_fetch((DBM *) map->map_db1, key);
551 		if (val.dptr != NULL)
552 			map->map_mflags &= ~MF_TRY0NULL;
553 	}
554 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
555 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
556 	if (val.dptr == NULL)
557 		return NULL;
558 	if (bitset(MF_MATCHONLY, map->map_mflags))
559 		return map_rewrite(map, name, strlen(name), NULL);
560 	else
561 		return map_rewrite(map, val.dptr, val.dsize, av);
562 }
563 
564 
565 /*
566 **  DBM_MAP_STORE -- store a datum in the database
567 */
568 
569 void
570 ndbm_map_store(map, lhs, rhs)
571 	register MAP *map;
572 	char *lhs;
573 	char *rhs;
574 {
575 	datum key;
576 	datum data;
577 	int stat;
578 
579 	if (tTd(38, 12))
580 		printf("ndbm_map_store(%s, %s, %s)\n",
581 			map->map_mname, lhs, rhs);
582 
583 	key.dsize = strlen(lhs);
584 	key.dptr = lhs;
585 
586 	data.dsize = strlen(rhs);
587 	data.dptr = rhs;
588 
589 	if (bitset(MF_INCLNULL, map->map_mflags))
590 	{
591 		key.dsize++;
592 		data.dsize++;
593 	}
594 
595 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
596 	if (stat > 0)
597 	{
598 		if (!bitset(MF_APPEND, map->map_mflags))
599 			usrerr("050 Warning: duplicate alias name %s", lhs);
600 		else
601 		{
602 			static char *buf = NULL;
603 			static int bufsiz = 0;
604 			auto int xstat;
605 			datum old;
606 
607 			old.dptr = ndbm_map_lookup(map, key.dptr, NULL, &xstat);
608 			if (old.dptr != NULL && *old.dptr != '\0')
609 			{
610 				old.dsize = strlen(old.dptr);
611 				if (data.dsize + old.dsize + 2 > bufsiz)
612 				{
613 					if (buf != NULL)
614 						(void) free(buf);
615 					bufsiz = data.dsize + old.dsize + 2;
616 					buf = xalloc(bufsiz);
617 				}
618 				sprintf(buf, "%s,%s", data.dptr, old.dptr);
619 				data.dsize = data.dsize + old.dsize + 1;
620 				data.dptr = buf;
621 				if (tTd(38, 9))
622 					printf("ndbm_map_store append=%s\n", data.dptr);
623 			}
624 		}
625 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
626 	}
627 	if (stat != 0)
628 		syserr("readaliases: dbm put (%s)", lhs);
629 }
630 
631 
632 /*
633 **  NDBM_MAP_CLOSE -- close the database
634 */
635 
636 void
637 ndbm_map_close(map)
638 	register MAP  *map;
639 {
640 	if (tTd(38, 9))
641 		printf("ndbm_map_close(%s, %s, %x)\n",
642 			map->map_mname, map->map_file, map->map_mflags);
643 
644 	if (bitset(MF_WRITABLE, map->map_mflags))
645 	{
646 #ifdef NIS
647 		bool inclnull;
648 		char buf[200];
649 
650 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
651 		map->map_mflags &= ~MF_INCLNULL;
652 
653 		if (strstr(map->map_file, "/yp/") != NULL)
654 		{
655 			(void) sprintf(buf, "%010ld", curtime());
656 			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
657 
658 			(void) gethostname(buf, sizeof buf);
659 			ndbm_map_store(map, "YP_MASTER_NAME", buf);
660 		}
661 
662 		if (inclnull)
663 			map->map_mflags |= MF_INCLNULL;
664 #endif
665 
666 		/* write out the distinguished alias */
667 		ndbm_map_store(map, "@", "@");
668 	}
669 	dbm_close((DBM *) map->map_db1);
670 }
671 
672 #endif
673 /*
674 **  NEWDB (Hash and BTree) Modules
675 */
676 
677 #ifdef NEWDB
678 
679 /*
680 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
681 **
682 **	These do rather bizarre locking.  If you can lock on open,
683 **	do that to avoid the condition of opening a database that
684 **	is being rebuilt.  If you don't, we'll try to fake it, but
685 **	there will be a race condition.  If opening for read-only,
686 **	we immediately release the lock to avoid freezing things up.
687 **	We really ought to hold the lock, but guarantee that we won't
688 **	be pokey about it.  That's hard to do.
689 */
690 
691 bool
692 bt_map_open(map, mode)
693 	MAP *map;
694 	int mode;
695 {
696 	DB *db;
697 	int i;
698 	int omode;
699 	int fd;
700 	struct stat st;
701 	char buf[MAXNAME + 1];
702 
703 	if (tTd(38, 2))
704 		printf("bt_map_open(%s, %s, %d)\n",
705 			map->map_mname, map->map_file, mode);
706 
707 	omode = mode;
708 	if (omode == O_RDWR)
709 	{
710 		omode |= O_CREAT|O_TRUNC;
711 #if defined(O_EXLOCK) && HASFLOCK
712 		omode |= O_EXLOCK;
713 # if !OLD_NEWDB
714 	}
715 	else
716 	{
717 		omode |= O_SHLOCK;
718 # endif
719 #endif
720 	}
721 
722 	(void) strcpy(buf, map->map_file);
723 	i = strlen(buf);
724 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
725 		(void) strcat(buf, ".db");
726 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
727 	if (db == NULL)
728 	{
729 #ifdef MAYBENEXTRELEASE
730 		if (aliaswait(map, ".db", FALSE))
731 			return TRUE;
732 #endif
733 		if (!bitset(MF_OPTIONAL, map->map_mflags))
734 			syserr("Cannot open BTREE database %s", map->map_file);
735 		return FALSE;
736 	}
737 #if !OLD_NEWDB
738 	fd = db->fd(db);
739 # if defined(O_EXLOCK) && HASFLOCK
740 	if (fd >= 0)
741 	{
742 		if (mode == O_RDONLY)
743 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
744 		else
745 			map->map_mflags |= MF_LOCKED;
746 	}
747 # else
748 	if (mode == O_RDWR && fd >= 0)
749 	{
750 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
751 			map->map_mflags |= MF_LOCKED;
752 	}
753 # endif
754 #endif
755 
756 	/* try to make sure that at least the database header is on disk */
757 	if (mode == O_RDWR)
758 #if OLD_NEWDB
759 		(void) db->sync(db);
760 #else
761 		(void) db->sync(db, 0);
762 
763 	if (fd >= 0 && fstat(fd, &st) >= 0)
764 		map->map_mtime = st.st_mtime;
765 #endif
766 
767 	map->map_db2 = (void *) db;
768 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
769 		if (!aliaswait(map, ".db", TRUE))
770 			return FALSE;
771 	return TRUE;
772 }
773 
774 
775 /*
776 **  HASH_MAP_INIT -- HASH-style map initialization
777 */
778 
779 bool
780 hash_map_open(map, mode)
781 	MAP *map;
782 	int mode;
783 {
784 	DB *db;
785 	int i;
786 	int omode;
787 	int fd;
788 	struct stat st;
789 	char buf[MAXNAME + 1];
790 
791 	if (tTd(38, 2))
792 		printf("hash_map_open(%s, %s, %d)\n",
793 			map->map_mname, map->map_file, mode);
794 
795 	omode = mode;
796 	if (omode == O_RDWR)
797 	{
798 		omode |= O_CREAT|O_TRUNC;
799 #if defined(O_EXLOCK) && HASFLOCK
800 		omode |= O_EXLOCK;
801 # if !OLD_NEWDB
802 	}
803 	else
804 	{
805 		omode |= O_SHLOCK;
806 # endif
807 #endif
808 	}
809 
810 	(void) strcpy(buf, map->map_file);
811 	i = strlen(buf);
812 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
813 		(void) strcat(buf, ".db");
814 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
815 	if (db == NULL)
816 	{
817 #ifdef MAYBENEXTRELEASE
818 		if (aliaswait(map, ".db", FALSE))
819 			return TRUE;
820 #endif
821 		if (!bitset(MF_OPTIONAL, map->map_mflags))
822 			syserr("Cannot open HASH database %s", map->map_file);
823 		return FALSE;
824 	}
825 #if !OLD_NEWDB
826 	fd = db->fd(db);
827 # if defined(O_EXLOCK) && HASFLOCK
828 	if (fd >= 0)
829 	{
830 		if (mode == O_RDONLY)
831 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
832 		else
833 			map->map_mflags |= MF_LOCKED;
834 	}
835 # else
836 	if (mode == O_RDWR && fd >= 0)
837 	{
838 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
839 			map->map_mflags |= MF_LOCKED;
840 	}
841 # endif
842 #endif
843 
844 	/* try to make sure that at least the database header is on disk */
845 	if (mode == O_RDWR)
846 #if OLD_NEWDB
847 		(void) db->sync(db);
848 #else
849 		(void) db->sync(db, 0);
850 
851 	if (fd >= 0 && fstat(fd, &st) >= 0)
852 		map->map_mtime = st.st_mtime;
853 #endif
854 
855 	map->map_db2 = (void *) db;
856 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
857 		if (!aliaswait(map, ".db", TRUE))
858 			return FALSE;
859 	return TRUE;
860 }
861 
862 
863 /*
864 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
865 */
866 
867 char *
868 db_map_lookup(map, name, av, statp)
869 	MAP *map;
870 	char *name;
871 	char **av;
872 	int *statp;
873 {
874 	DBT key, val;
875 	register DB *db = (DB *) map->map_db2;
876 	int st;
877 	int saveerrno;
878 	int fd;
879 	char keybuf[MAXNAME + 1];
880 
881 	if (tTd(38, 20))
882 		printf("db_map_lookup(%s, %s)\n",
883 			map->map_mname, name);
884 
885 	key.size = strlen(name);
886 	if (key.size > sizeof keybuf - 1)
887 		key.size = sizeof keybuf - 1;
888 	key.data = keybuf;
889 	bcopy(name, keybuf, key.size + 1);
890 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
891 		makelower(keybuf);
892 #if !OLD_NEWDB
893 	fd = db->fd(db);
894 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
895 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
896 #endif
897 	st = 1;
898 	if (bitset(MF_TRY0NULL, map->map_mflags))
899 	{
900 		st = db->get(db, &key, &val, 0);
901 		if (st == 0)
902 			map->map_mflags &= ~MF_TRY1NULL;
903 	}
904 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
905 	{
906 		key.size++;
907 		st = db->get(db, &key, &val, 0);
908 		if (st == 0)
909 			map->map_mflags &= ~MF_TRY0NULL;
910 	}
911 	saveerrno = errno;
912 #if !OLD_NEWDB
913 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
914 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
915 #endif
916 	if (st != 0)
917 	{
918 		errno = saveerrno;
919 		if (st < 0)
920 			syserr("db_map_lookup: get (%s)", name);
921 		return NULL;
922 	}
923 	if (bitset(MF_MATCHONLY, map->map_mflags))
924 		return map_rewrite(map, name, strlen(name), NULL);
925 	else
926 		return map_rewrite(map, val.data, val.size, av);
927 }
928 
929 
930 /*
931 **  DB_MAP_STORE -- store a datum in the NEWDB database
932 */
933 
934 void
935 db_map_store(map, lhs, rhs)
936 	register MAP *map;
937 	char *lhs;
938 	char *rhs;
939 {
940 	int stat;
941 	DBT key;
942 	DBT data;
943 	register DB *db = map->map_db2;
944 
945 	if (tTd(38, 20))
946 		printf("db_map_store(%s, %s, %s)\n",
947 			map->map_mname, lhs, rhs);
948 
949 	key.size = strlen(lhs);
950 	key.data = lhs;
951 
952 	data.size = strlen(rhs);
953 	data.data = rhs;
954 
955 	if (bitset(MF_INCLNULL, map->map_mflags))
956 	{
957 		key.size++;
958 		data.size++;
959 	}
960 
961 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
962 	if (stat > 0)
963 	{
964 		if (!bitset(MF_APPEND, map->map_mflags))
965 			usrerr("050 Warning: duplicate alias name %s", lhs);
966 		else
967 		{
968 			static char *buf = NULL;
969 			static int bufsiz = 0;
970 			DBT old;
971 
972 			old.data = db_map_lookup(map, key.data, NULL, &stat);
973 			if (old.data != NULL)
974 			{
975 				old.size = strlen(old.data);
976 				if (data.size + old.size + 2 > bufsiz)
977 				{
978 					if (buf != NULL)
979 						(void) free(buf);
980 					bufsiz = data.size + old.size + 2;
981 					buf = xalloc(bufsiz);
982 				}
983 				sprintf(buf, "%s,%s", data.data, old.data);
984 				data.size = data.size + old.size + 1;
985 				data.data = buf;
986 				if (tTd(38, 9))
987 					printf("db_map_store append=%s\n", data.data);
988 			}
989 		}
990 		stat = db->put(db, &key, &data, 0);
991 	}
992 	if (stat != 0)
993 		syserr("readaliases: db put (%s)", lhs);
994 }
995 
996 
997 /*
998 **  DB_MAP_CLOSE -- add distinguished entries and close the database
999 */
1000 
1001 void
1002 db_map_close(map)
1003 	MAP *map;
1004 {
1005 	register DB *db = map->map_db2;
1006 
1007 	if (tTd(38, 9))
1008 		printf("db_map_close(%s, %s, %x)\n",
1009 			map->map_mname, map->map_file, map->map_mflags);
1010 
1011 	if (bitset(MF_WRITABLE, map->map_mflags))
1012 	{
1013 		/* write out the distinguished alias */
1014 		db_map_store(map, "@", "@");
1015 	}
1016 
1017 	if (db->close(db) != 0)
1018 		syserr("readaliases: db close failure");
1019 }
1020 
1021 #endif
1022 /*
1023 **  NIS Modules
1024 */
1025 
1026 # ifdef NIS
1027 
1028 # ifndef YPERR_BUSY
1029 #  define YPERR_BUSY	16
1030 # endif
1031 
1032 /*
1033 **  NIS_MAP_OPEN -- open DBM map
1034 */
1035 
1036 bool
1037 nis_map_open(map, mode)
1038 	MAP *map;
1039 	int mode;
1040 {
1041 	int yperr;
1042 	register char *p;
1043 	auto char *vp;
1044 	auto int vsize;
1045 	char *master;
1046 
1047 	if (tTd(38, 2))
1048 		printf("nis_map_open(%s, %s)\n",
1049 			map->map_mname, map->map_file);
1050 
1051 	if (mode != O_RDONLY)
1052 	{
1053 		/* issue a pseudo-error message */
1054 #ifdef ENOSYS
1055 		errno = ENOSYS;
1056 #else
1057 # ifdef EFTYPE
1058 		errno = EFTYPE;
1059 # else
1060 		errno = ENXIO;
1061 # endif
1062 #endif
1063 		return FALSE;
1064 	}
1065 
1066 	p = strchr(map->map_file, '@');
1067 	if (p != NULL)
1068 	{
1069 		*p++ = '\0';
1070 		if (*p != '\0')
1071 			map->map_domain = p;
1072 	}
1073 
1074 	if (*map->map_file == '\0')
1075 		map->map_file = "mail.aliases";
1076 
1077 	if (map->map_domain == NULL)
1078 	{
1079 		yperr = yp_get_default_domain(&map->map_domain);
1080 		if (yperr != 0)
1081 		{
1082 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1083 				syserr("421 NIS map %s specified, but NIS not running\n",
1084 					map->map_file);
1085 			return FALSE;
1086 		}
1087 	}
1088 
1089 	/* check to see if this map actually exists */
1090 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1091 			&vp, &vsize);
1092 	if (tTd(38, 10))
1093 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1094 			map->map_domain, map->map_file, yperr_string(yperr));
1095 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1096 	{
1097 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1098 		    aliaswait(map, NULL, TRUE))
1099 			return TRUE;
1100 	}
1101 
1102 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1103 	{
1104 		syserr("421 Cannot bind to map %s in domain %s: %s",
1105 			map->map_file, map->map_domain, yperr_string(yperr));
1106 	}
1107 
1108 	return FALSE;
1109 }
1110 
1111 
1112 /*
1113 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1114 */
1115 
1116 char *
1117 nis_map_lookup(map, name, av, statp)
1118 	MAP *map;
1119 	char *name;
1120 	char **av;
1121 	int *statp;
1122 {
1123 	char *vp;
1124 	auto int vsize;
1125 	int buflen;
1126 	int yperr;
1127 	char keybuf[MAXNAME + 1];
1128 
1129 	if (tTd(38, 20))
1130 		printf("nis_map_lookup(%s, %s)\n",
1131 			map->map_mname, name);
1132 
1133 	buflen = strlen(name);
1134 	if (buflen > sizeof keybuf - 1)
1135 		buflen = sizeof keybuf - 1;
1136 	bcopy(name, keybuf, buflen + 1);
1137 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1138 		makelower(keybuf);
1139 	yperr = YPERR_KEY;
1140 	if (bitset(MF_TRY0NULL, map->map_mflags))
1141 	{
1142 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1143 			     &vp, &vsize);
1144 		if (yperr == 0)
1145 			map->map_mflags &= ~MF_TRY1NULL;
1146 	}
1147 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1148 	{
1149 		buflen++;
1150 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1151 			     &vp, &vsize);
1152 		if (yperr == 0)
1153 			map->map_mflags &= ~MF_TRY0NULL;
1154 	}
1155 	if (yperr != 0)
1156 	{
1157 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1158 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1159 		return NULL;
1160 	}
1161 	if (bitset(MF_MATCHONLY, map->map_mflags))
1162 		return map_rewrite(map, name, strlen(name), NULL);
1163 	else
1164 		return map_rewrite(map, vp, vsize, av);
1165 }
1166 
1167 
1168 /*
1169 **  NIS_GETCANONNAME -- look up canonical name in NIS
1170 */
1171 
1172 bool
1173 nis_getcanonname(name, hbsize, statp)
1174 	char *name;
1175 	int hbsize;
1176 	int *statp;
1177 {
1178 	char *vp;
1179 	auto int vsize;
1180 	int keylen;
1181 	int yperr;
1182 	static bool try0null = TRUE;
1183 	static bool try1null = TRUE;
1184 	static char *yp_domain = NULL;
1185 	char *domain, *p;
1186 	char host_record[MAXLINE];
1187 	char buf[MAXNAME];
1188 	char *cname;
1189 	extern char *get_column();
1190 
1191 	if (tTd(38, 20))
1192 		printf("nis_getcanonname(%s)\n", name);
1193 
1194 	shorten_hostname(name);
1195 
1196 	/* we only accept single token search key */
1197 	if (strchr(name, '.'))
1198 	{
1199 		*statp = EX_NOHOST;
1200 		return FALSE;
1201 	}
1202 
1203 	keylen = strlen(name);
1204 
1205 	if (yp_domain == NULL)
1206 		yp_get_default_domain(&yp_domain);
1207 	makelower(name);
1208 	yperr = YPERR_KEY;
1209 	if (try0null)
1210 	{
1211 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1212 			     &vp, &vsize);
1213 		if (yperr == 0)
1214 			try1null = FALSE;
1215 	}
1216 	if (yperr == YPERR_KEY && try1null)
1217 	{
1218 		keylen++;
1219 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1220 			     &vp, &vsize);
1221 		if (yperr == 0)
1222 			try0null = FALSE;
1223 	}
1224 	if (yperr != 0)
1225 	{
1226 		if (yperr == YPERR_KEY)
1227 			*statp = EX_NOHOST;
1228 		else if (yperr == YPERR_BUSY)
1229 			*statp = EX_TEMPFAIL;
1230 		else
1231 			*statp = EX_UNAVAILABLE;
1232 		return FALSE;
1233 	}
1234 	strncpy(host_record, vp, vsize);
1235 	host_record[vsize] = '\0';
1236 	if (tTd(38, 44))
1237 		printf("got record `%s'\n", host_record);
1238 	cname = get_column(host_record, 1, '\0', buf);
1239 	if (cname == NULL)
1240 	{
1241 		/* this should not happen, but.... */
1242 		*statp = EX_NOHOST;
1243 		return FALSE;
1244 	}
1245 
1246 	if (hbsize >= strlen(cname))
1247 	{
1248 		strcpy(name, cname);
1249 		*statp = EX_OK;
1250 		return TRUE;
1251 	}
1252 	*statp = EX_UNAVAILABLE;
1253 	return FALSE;
1254 }
1255 
1256 #endif
1257 /*
1258 **  NISPLUS Modules
1259 **
1260 **	This code donated by Sun Microsystems.
1261 */
1262 
1263 #ifdef NISPLUS
1264 
1265 #undef NIS /* symbol conflict in nis.h */
1266 #include <rpcsvc/nis.h>
1267 #include <rpcsvc/nislib.h>
1268 
1269 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1270 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1271 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1272 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1273 
1274 /*
1275 **  NISPLUS_MAP_OPEN -- open nisplus table
1276 */
1277 
1278 bool
1279 nisplus_map_open(map, mode)
1280 	MAP *map;
1281 	int mode;
1282 {
1283 	register char *p;
1284 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1285 	nis_result *res = NULL;
1286 	u_int objs_len;
1287 	nis_object *obj_ptr;
1288 	int retry_cnt, max_col, i;
1289 
1290 	if (tTd(38, 2))
1291 		printf("nisplus_map_open(%s, %s, %d)\n",
1292 			map->map_mname, map->map_file, mode);
1293 
1294 	if (mode != O_RDONLY)
1295 	{
1296 		errno = ENODEV;
1297 		return FALSE;
1298 	}
1299 
1300 	if (*map->map_file == '\0')
1301 		map->map_file = "mail_aliases.org_dir";
1302 
1303 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1304 	{
1305 		/* set default NISPLUS Domain to $m */
1306 		extern char *nisplus_default_domain();
1307 
1308 		map->map_domain = newstr(nisplus_default_domain());
1309 		if (tTd(38, 2))
1310 			printf("nisplus_map_open(%s): using domain %s\n",
1311 				 map->map_file, map->map_domain);
1312 	}
1313 	if (!PARTIAL_NAME(map->map_file))
1314 		map->map_domain = newstr("");
1315 
1316 	/* check to see if this map actually exists */
1317 	if (PARTIAL_NAME(map->map_file))
1318 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1319 	else
1320 		strcpy(qbuf, map->map_file);
1321 
1322 	retry_cnt = 0;
1323 	while (res == NULL || res->status != NIS_SUCCESS)
1324 	{
1325 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1326 		switch (res->status)
1327 		{
1328 		  case NIS_SUCCESS:
1329 		  case NIS_TRYAGAIN:
1330 		  case NIS_RPCERROR:
1331 		  case NIS_NAMEUNREACHABLE:
1332 			break;
1333 
1334 		  default:		/* all other nisplus errors */
1335 #if 0
1336 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1337 				syserr("421 Cannot find table %s.%s: %s",
1338 					map->map_file, map->map_domain,
1339 					nis_sperrno(res->status));
1340 #endif
1341 			errno = EBADR;
1342 			return FALSE;
1343 		}
1344 		sleep(2);		/* try not to overwhelm hosed server */
1345 		if (retry_cnt++ > 4)
1346 		{
1347 			errno = EBADR;
1348 			return FALSE;
1349 		}
1350 	}
1351 
1352 	if (NIS_RES_NUMOBJ(res) != 1 ||
1353 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1354 	{
1355 		if (tTd(38, 10))
1356 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1357 #if 0
1358 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1359 			syserr("421 %s.%s: %s is not a table",
1360 				map->map_file, map->map_domain,
1361 				nis_sperrno(res->status));
1362 #endif
1363 		errno = EBADR;
1364 		return FALSE;
1365 	}
1366 	/* default key column is column 0 */
1367 	if (map->map_keycolnm == NULL)
1368 		map->map_keycolnm = newstr(COL_NAME(res,0));
1369 
1370 	max_col = COL_MAX(res);
1371 
1372 	/* verify the key column exist */
1373 	for (i=0; i< max_col; i++)
1374 	{
1375 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1376 			break;
1377 	}
1378 	if (i == max_col)
1379 	{
1380 		if (tTd(38, 2))
1381 			printf("nisplus_map_open(%s): can not find key column %s\n",
1382 				map->map_file, map->map_keycolnm);
1383 		errno = EBADR;
1384 		return FALSE;
1385 	}
1386 
1387 	/* default value column is the last column */
1388 	if (map->map_valcolnm == NULL)
1389 	{
1390 		map->map_valcolno = max_col - 1;
1391 		return TRUE;
1392 	}
1393 
1394 	for (i=0; i< max_col; i++)
1395 	{
1396 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1397 		{
1398 			map->map_valcolno = i;
1399 			return TRUE;
1400 		}
1401 	}
1402 
1403 	if (tTd(38, 2))
1404 		printf("nisplus_map_open(%s): can not find column %s\n",
1405 			 map->map_file, map->map_keycolnm);
1406 	errno = EBADR;
1407 	return FALSE;
1408 }
1409 
1410 
1411 /*
1412 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1413 */
1414 
1415 char *
1416 nisplus_map_lookup(map, name, av, statp)
1417 	MAP *map;
1418 	char *name;
1419 	char **av;
1420 	int *statp;
1421 {
1422 	char *vp;
1423 	auto int vsize;
1424 	int buflen;
1425 	char search_key[MAXNAME + 1];
1426 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1427 	nis_result *result;
1428 
1429 	if (tTd(38, 20))
1430 		printf("nisplus_map_lookup(%s, %s)\n",
1431 			map->map_mname, name);
1432 
1433 	if (!bitset(MF_OPEN, map->map_mflags))
1434 	{
1435 		if (nisplus_map_open(map, O_RDONLY))
1436 			map->map_mflags |= MF_OPEN;
1437 		else
1438 		{
1439 			*statp = EX_UNAVAILABLE;
1440 			return NULL;
1441 		}
1442 	}
1443 
1444 	buflen = strlen(name);
1445 	if (buflen > sizeof search_key - 1)
1446 		buflen = sizeof search_key - 1;
1447 	bcopy(name, search_key, buflen + 1);
1448 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1449 		makelower(search_key);
1450 
1451 	/* construct the query */
1452 	if (PARTIAL_NAME(map->map_file))
1453 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1454 			search_key, map->map_file, map->map_domain);
1455 	else
1456 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1457 			search_key, map->map_file);
1458 
1459 	if (tTd(38, 20))
1460 		printf("qbuf=%s\n", qbuf);
1461 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1462 	if (result->status == NIS_SUCCESS)
1463 	{
1464 		int count;
1465 		char *str;
1466 
1467 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1468 		{
1469 			if (LogLevel > 10)
1470 				syslog(LOG_WARNING,
1471 				  "%s:Lookup error, expected 1 entry, got (%d)",
1472 				    map->map_file, count);
1473 
1474 			/* ignore second entry */
1475 			if (tTd(38, 20))
1476 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1477 					name, count);
1478 		}
1479 
1480 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1481 		/* set the length of the result */
1482 		if (vp == NULL)
1483 			vp = "";
1484 		vsize = strlen(vp);
1485 		if (tTd(38, 20))
1486 			printf("nisplus_map_lookup(%s), found %s\n",
1487 				name, vp);
1488 		if (bitset(MF_MATCHONLY, map->map_mflags))
1489 			str = map_rewrite(map, name, strlen(name), NULL);
1490 		else
1491 			str = map_rewrite(map, vp, vsize, av);
1492 		nis_freeresult(result);
1493 #ifdef MAP_EXIT_STAT
1494 		*statp = EX_OK;
1495 #endif
1496 		return str;
1497 	}
1498 	else
1499 	{
1500 #ifdef MAP_EXIT_STAT
1501 		if (result->status == NIS_NOTFOUND)
1502 			*statp = EX_NOTFOUND;
1503 		else if (result->status == NIS_TRYAGAIN)
1504 			*statp = EX_TEMPFAIL;
1505 		else
1506 		{
1507 			*statp = EX_UNAVAILABLE;
1508 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1509 		}
1510 #else
1511 		if ((result->status != NIS_NOTFOUND) &&
1512 		    (result->status != NIS_TRYAGAIN))
1513 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1514 #endif
1515 	}
1516 	if (tTd(38, 20))
1517 		printf("nisplus_map_lookup(%s), failed\n", name);
1518 	nis_freeresult(result);
1519 	return NULL;
1520 }
1521 
1522 
1523 
1524 /*
1525 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
1526 */
1527 
1528 bool
1529 nisplus_getcanonname(name, hbsize, statp)
1530 	char *name;
1531 	int hbsize;
1532 	int *statp;
1533 {
1534 	char *vp;
1535 	auto int vsize;
1536 	int buflen;
1537 	char buf[MAXLINE + NIS_MAXNAMELEN];
1538 	nis_result *result;
1539 	char *p;
1540 	int len;
1541 
1542 	shorten_hostname(name);
1543 
1544 	p = strchr(name, '.');
1545 	if (p == NULL)
1546 	{
1547 		/* single token */
1548 		sprintf(buf, "[name=%s],hosts.org_dir", name);
1549 	}
1550 	else if (p[1] != '\0')
1551 	{
1552 		/* multi token -- take only first token in name buf */
1553 		*p = '\0';
1554 		sprintf(buf, "[name=%s],hosts.org_dir.%s", name, &p[1]);
1555 	}
1556 	else
1557 	{
1558 		*statp = EX_NOHOST;
1559 		return FALSE;
1560 	}
1561 
1562 	if (tTd(38, 20))
1563 		printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
1564 			 name, buf);
1565 
1566 	result = nis_list(buf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
1567 		NULL, NULL);
1568 
1569 	if (result->status == NIS_SUCCESS)
1570 	{
1571 		int count;
1572 		char *str;
1573 		char *domain;
1574 
1575 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1576 		{
1577 #ifdef LOG
1578 			if (LogLevel > 10)
1579 				syslog(LOG_WARNING,
1580 				       "nisplus_getcanonname: Lookup error, expected 1 entry, got (%d)",
1581 				       count);
1582 #endif
1583 
1584 			/* ignore second entry */
1585 			if (tTd(38, 20))
1586 				printf("nisplus_getcanoname(%s), got %d entries, addtional entries ignores\n", name);
1587 		}
1588 
1589 		if (tTd(38, 20))
1590 			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
1591 			       name, (NIS_RES_OBJECT(result))->zo_domain);
1592 
1593 
1594 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
1595 		vsize = strlen(vp);
1596 		if (tTd(38, 20))
1597 			printf("nisplus_getcanonname(%s), found %s\n",
1598 				name, vp);
1599 		domain = macvalue('m', CurEnv);
1600 		if (domain == NULL)
1601 			domain = "";
1602 		if (hbsize > vsize + (int) strlen(domain) + 1)
1603 		{
1604 			if (domain[0] == '\0')
1605 				strcpy(name, vp);
1606 			else
1607 				sprintf(name, "%s.%s", vp, domain);
1608 			*statp = EX_OK;
1609 		}
1610 		else
1611 			*statp = EX_NOHOST;
1612 		nis_freeresult(result);
1613 		return TRUE;
1614 	}
1615 	else
1616 	{
1617 		if (result->status == NIS_NOTFOUND)
1618 			*statp = EX_NOHOST;
1619 		else if (result->status == NIS_TRYAGAIN)
1620 			*statp = EX_TEMPFAIL;
1621 		else
1622 			*statp = EX_UNAVAILABLE;
1623 	}
1624 	if (tTd(38, 20))
1625 		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1626 			name, result->status, *statp);
1627 	nis_freeresult(result);
1628 	return FALSE;
1629 }
1630 
1631 
1632 char *
1633 nisplus_default_domain()
1634 {
1635 	static char default_domain[MAXNAME + 1] = "";
1636 	char *p;
1637 
1638 	if (default_domain[0] != '\0')
1639 		return(default_domain);
1640 
1641 	p = nis_local_directory();
1642 	strcpy(default_domain, p);
1643 	return default_domain;
1644 }
1645 
1646 #endif /* NISPLUS */
1647 /*
1648 **  HESIOD Modules
1649 */
1650 
1651 #ifdef HESIOD
1652 
1653 #include <hesiod.h>
1654 
1655 char *
1656 hes_map_lookup(map, name, av, statp)
1657         MAP *map;
1658         char *name;
1659         char **av;
1660         int *statp;
1661 {
1662 	char **hp;
1663 	int i;
1664 
1665 	if (tTd(38, 20))
1666 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1667 
1668 	hp = hes_resolve(name, map->map_file);
1669 	if (hp == NULL || hp[0] == NULL)
1670 		return NULL;
1671 
1672 	if (bitset(MF_MATCHONLY, map->map_mflags))
1673 		return map_rewrite(map, name, strlen(name), NULL);
1674 	else
1675 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
1676 }
1677 
1678 #endif
1679 /*
1680 **  NeXT NETINFO Modules
1681 */
1682 
1683 #ifdef NETINFO
1684 
1685 #define NETINFO_DEFAULT_DIR		"/aliases"
1686 #define NETINFO_DEFAULT_PROPERTY	"members"
1687 
1688 
1689 /*
1690 **  NI_MAP_OPEN -- open NetInfo Aliases
1691 */
1692 
1693 bool
1694 ni_map_open(map, mode)
1695 	MAP *map;
1696 	int mode;
1697 {
1698 	char *p;
1699 
1700 	if (tTd(38, 20))
1701 		printf("ni_map_open: %s\n", map->map_file);
1702 
1703 	if (*map->map_file == '\0')
1704 		map->map_file = NETINFO_DEFAULT_DIR;
1705 
1706 	if (map->map_valcolnm == NULL)
1707 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1708 
1709 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1710 		map->map_coldelim = ',';
1711 
1712 	return TRUE;
1713 }
1714 
1715 
1716 /*
1717 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1718 */
1719 
1720 char *
1721 ni_map_lookup(map, name, av, statp)
1722 	MAP *map;
1723 	char *name;
1724 	char **av;
1725 	int *statp;
1726 {
1727 	char *res;
1728 	char *propval;
1729 	extern char *ni_propval();
1730 
1731 	if (tTd(38, 20))
1732 		printf("ni_map_lookup(%s, %s)\n",
1733 			map->map_mname, name);
1734 
1735 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1736 			     map->map_valcolnm, map->map_coldelim);
1737 
1738 	if (propval == NULL)
1739 		return NULL;
1740 
1741 	if (bitset(MF_MATCHONLY, map->map_mflags))
1742 		res = map_rewrite(map, name, strlen(name), NULL);
1743 	else
1744 		res = map_rewrite(map, propval, strlen(propval), av);
1745 	free(propval);
1746 	return res;
1747 }
1748 
1749 #endif
1750 /*
1751 **  TEXT (unindexed text file) Modules
1752 **
1753 **	This code donated by Sun Microsystems.
1754 */
1755 
1756 
1757 /*
1758 **  TEXT_MAP_OPEN -- open text table
1759 */
1760 
1761 bool
1762 text_map_open(map, mode)
1763 	MAP *map;
1764 	int mode;
1765 {
1766 	struct stat sbuf;
1767 
1768 	if (tTd(38, 2))
1769 		printf("text_map_open(%s, %s, %d)\n",
1770 			map->map_mname, map->map_file, mode);
1771 
1772 	if (mode != O_RDONLY)
1773 	{
1774 		errno = ENODEV;
1775 		return FALSE;
1776 	}
1777 
1778 	if (*map->map_file == '\0')
1779 	{
1780 		if (tTd(38, 2))
1781 			printf("text_map_open: file name required\n");
1782 		return FALSE;
1783 	}
1784 
1785 	if (map->map_file[0] != '/')
1786 	{
1787 		if (tTd(38, 2))
1788 			printf("text_map_open(%s): file name must be fully qualified\n",
1789 				map->map_file);
1790 		return FALSE;
1791 	}
1792 	/* check to see if this map actually accessable */
1793 	if (access(map->map_file, R_OK) <0)
1794 		return FALSE;
1795 
1796 	/* check to see if this map actually exist */
1797 	if (stat(map->map_file, &sbuf) <0)
1798 	{
1799 		if (tTd(38, 2))
1800 			printf("text_map_open(%s): can not stat %s\n",
1801 				map->map_file, map->map_file);
1802 		return FALSE;
1803 	}
1804 
1805 	if (!S_ISREG(sbuf.st_mode))
1806 	{
1807 		if (tTd(38, 2))
1808 			printf("text_map_open(%s): %s is not a file\n",
1809 				map->map_file, map->map_file);
1810 		return FALSE;
1811 	}
1812 
1813 	if (map->map_keycolnm == NULL)
1814 		map->map_keycolno = 0;
1815 	else
1816 	{
1817 		if (!isdigit(*map->map_keycolnm))
1818 		{
1819 			if (tTd(38, 2))
1820 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1821 					map->map_file, map->map_keycolnm);
1822 			return FALSE;
1823 		}
1824 		map->map_keycolno = atoi(map->map_keycolnm);
1825 	}
1826 
1827 	if (map->map_valcolnm == NULL)
1828 		map->map_valcolno = 0;
1829 	else
1830 	{
1831 		if (!isdigit(*map->map_valcolnm))
1832 		{
1833 			if (tTd(38, 2))
1834 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1835 					map->map_file, map->map_valcolnm);
1836 			return FALSE;
1837 		}
1838 		map->map_valcolno = atoi(map->map_valcolnm);
1839 	}
1840 
1841 	if (tTd(38, 2))
1842 	{
1843 		printf("text_map_open(%s): delimiter = ",
1844 			map->map_file);
1845 		if (map->map_coldelim == '\0')
1846 			printf("(white space)\n");
1847 		else
1848 			printf("%c\n", map->map_coldelim);
1849 	}
1850 
1851 	return TRUE;
1852 }
1853 
1854 
1855 /*
1856 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1857 */
1858 
1859 char *
1860 text_map_lookup(map, name, av, statp)
1861 	MAP *map;
1862 	char *name;
1863 	char **av;
1864 	int *statp;
1865 {
1866 	char *vp;
1867 	auto int vsize;
1868 	int buflen;
1869 	char search_key[MAXNAME + 1];
1870 	char linebuf[MAXLINE];
1871 	FILE *f;
1872 	char buf[MAXNAME + 1];
1873 	char delim;
1874 	int key_idx;
1875 	bool found_it;
1876 	extern char *get_column();
1877 
1878 
1879 	found_it = FALSE;
1880 	if (tTd(38, 20))
1881 		printf("text_map_lookup(%s)\n", name);
1882 
1883 	buflen = strlen(name);
1884 	if (buflen > sizeof search_key - 1)
1885 		buflen = sizeof search_key - 1;
1886 	bcopy(name, search_key, buflen + 1);
1887 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1888 		makelower(search_key);
1889 
1890 	f = fopen(map->map_file, "r");
1891 	if (f == NULL)
1892 	{
1893 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1894 		*statp = EX_UNAVAILABLE;
1895 		return NULL;
1896 	}
1897 	key_idx = map->map_keycolno;
1898 	delim = map->map_coldelim;
1899 	while (fgets(linebuf, MAXLINE, f))
1900 	{
1901 		char *lf;
1902 		if (linebuf[0] == '#')
1903 			continue; /* skip comment line */
1904 		if (lf = strchr(linebuf, '\n'))
1905 			*lf = '\0';
1906 		if (!strcasecmp(search_key,
1907 				get_column(linebuf, key_idx, delim, buf)))
1908 		{
1909 			found_it = TRUE;
1910 			break;
1911 		}
1912 	}
1913 	fclose(f);
1914 	if (!found_it)
1915 	{
1916 #ifdef MAP_EXIT_STAT
1917 		*statp = EX_NOTFOUND;
1918 #endif
1919 		return(NULL);
1920 	}
1921 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1922 	vsize = strlen(vp);
1923 #ifdef MAP_EXIT_STAT
1924 	*statp = EX_OK;
1925 #endif
1926 	if (bitset(MF_MATCHONLY, map->map_mflags))
1927 		return map_rewrite(map, name, strlen(name), NULL);
1928 	else
1929 		return map_rewrite(map, vp, vsize, av);
1930 }
1931 
1932 
1933 /*
1934 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
1935 */
1936 
1937 bool
1938 text_getcanonname(name, hbsize, statp)
1939 	char *name;
1940 	int hbsize;
1941 	int *statp;
1942 {
1943 	int buflen;
1944 	int key_idx;
1945 	char *cname;
1946 	bool found;
1947 	char *domain;
1948 	FILE *f;
1949 	char linebuf[MAXLINE];
1950 	char cbuf[MAXNAME + 1];
1951 	char buf[MAXNAME + 1];
1952 	extern char *get_column();
1953 
1954 	shorten_hostname(name);
1955 
1956 	/* we only accept single token search key */
1957 	if (strchr(name, '.') != NULL)
1958 	{
1959 		*statp = EX_NOHOST;
1960 		return FALSE;
1961 	}
1962 
1963 	found = FALSE;
1964 
1965 	f = fopen(HostsFile, "r");
1966 	if (f == NULL)
1967 	{
1968 #ifdef MAP_EXIT_STAT
1969 		*statp = EX_UNAVAILABLE;
1970 #endif
1971 		return FALSE;
1972 	}
1973 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
1974 	{
1975 		char *p;
1976 
1977 		if (linebuf[0] == '#')
1978 			continue;
1979 		if ((p = strchr(linebuf, '\n')) != NULL)
1980 			*p = '\0';
1981 		cname = get_column(linebuf, 1, '\0', cbuf);
1982 		if (cname != NULL && strcasecmp(name,  cname) == 0)
1983 		{
1984 			found = TRUE;
1985 			break;
1986 		}
1987 
1988 		key_idx = 2;
1989 		while ((p = get_column(linebuf, key_idx, '\0', buf)) != NULL)
1990 		{
1991 			if (strcasecmp(name, p) == 0)
1992 			{
1993 				found = TRUE;
1994 				break;
1995 			}
1996 			key_idx++;
1997 		}
1998 	}
1999 	fclose(f);
2000 	if (!found)
2001 	{
2002 		*statp = EX_NOHOST;
2003 		return FALSE;
2004 	}
2005 
2006 	if (hbsize >= strlen(cname))
2007 	{
2008 		strcpy(name, cname);
2009 		*statp = EX_OK;
2010 		return TRUE;
2011 	}
2012 	*statp = EX_UNAVAILABLE;
2013 	return FALSE;
2014 }
2015 /*
2016 **  STAB (Symbol Table) Modules
2017 */
2018 
2019 
2020 /*
2021 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2022 */
2023 
2024 char *
2025 stab_map_lookup(map, name, av, pstat)
2026 	register MAP *map;
2027 	char *name;
2028 	char **av;
2029 	int *pstat;
2030 {
2031 	register STAB *s;
2032 
2033 	if (tTd(38, 20))
2034 		printf("stab_lookup(%s, %s)\n",
2035 			map->map_mname, name);
2036 
2037 	s = stab(name, ST_ALIAS, ST_FIND);
2038 	if (s != NULL)
2039 		return (s->s_alias);
2040 	return (NULL);
2041 }
2042 
2043 
2044 /*
2045 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2046 */
2047 
2048 void
2049 stab_map_store(map, lhs, rhs)
2050 	register MAP *map;
2051 	char *lhs;
2052 	char *rhs;
2053 {
2054 	register STAB *s;
2055 
2056 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2057 	s->s_alias = newstr(rhs);
2058 }
2059 
2060 
2061 /*
2062 **  STAB_MAP_OPEN -- initialize (reads data file)
2063 **
2064 **	This is a wierd case -- it is only intended as a fallback for
2065 **	aliases.  For this reason, opens for write (only during a
2066 **	"newaliases") always fails, and opens for read open the
2067 **	actual underlying text file instead of the database.
2068 */
2069 
2070 bool
2071 stab_map_open(map, mode)
2072 	register MAP *map;
2073 	int mode;
2074 {
2075 	FILE *af;
2076 	struct stat st;
2077 
2078 	if (tTd(38, 2))
2079 		printf("stab_map_open(%s, %s)\n",
2080 			map->map_mname, map->map_file);
2081 
2082 	if (mode != O_RDONLY)
2083 	{
2084 		errno = ENODEV;
2085 		return FALSE;
2086 	}
2087 
2088 	af = fopen(map->map_file, "r");
2089 	if (af == NULL)
2090 		return FALSE;
2091 	readaliases(map, af, FALSE, FALSE);
2092 
2093 	if (fstat(fileno(af), &st) >= 0)
2094 		map->map_mtime = st.st_mtime;
2095 	fclose(af);
2096 
2097 	return TRUE;
2098 }
2099 /*
2100 **  Implicit Modules
2101 **
2102 **	Tries several types.  For back compatibility of aliases.
2103 */
2104 
2105 
2106 /*
2107 **  IMPL_MAP_LOOKUP -- lookup in best open database
2108 */
2109 
2110 char *
2111 impl_map_lookup(map, name, av, pstat)
2112 	MAP *map;
2113 	char *name;
2114 	char **av;
2115 	int *pstat;
2116 {
2117 	if (tTd(38, 20))
2118 		printf("impl_map_lookup(%s, %s)\n",
2119 			map->map_mname, name);
2120 
2121 #ifdef NEWDB
2122 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2123 		return db_map_lookup(map, name, av, pstat);
2124 #endif
2125 #ifdef NDBM
2126 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2127 		return ndbm_map_lookup(map, name, av, pstat);
2128 #endif
2129 	return stab_map_lookup(map, name, av, pstat);
2130 }
2131 
2132 /*
2133 **  IMPL_MAP_STORE -- store in open databases
2134 */
2135 
2136 void
2137 impl_map_store(map, lhs, rhs)
2138 	MAP *map;
2139 	char *lhs;
2140 	char *rhs;
2141 {
2142 #ifdef NEWDB
2143 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2144 		db_map_store(map, lhs, rhs);
2145 #endif
2146 #ifdef NDBM
2147 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2148 		ndbm_map_store(map, lhs, rhs);
2149 #endif
2150 	stab_map_store(map, lhs, rhs);
2151 }
2152 
2153 /*
2154 **  IMPL_MAP_OPEN -- implicit database open
2155 */
2156 
2157 bool
2158 impl_map_open(map, mode)
2159 	MAP *map;
2160 	int mode;
2161 {
2162 	struct stat stb;
2163 
2164 	if (tTd(38, 2))
2165 		printf("impl_map_open(%s, %s, %d)\n",
2166 			map->map_mname, map->map_file, mode);
2167 
2168 	if (stat(map->map_file, &stb) < 0)
2169 	{
2170 		/* no alias file at all */
2171 		if (tTd(38, 3))
2172 			printf("no map file\n");
2173 		return FALSE;
2174 	}
2175 
2176 #ifdef NEWDB
2177 	map->map_mflags |= MF_IMPL_HASH;
2178 	if (hash_map_open(map, mode))
2179 	{
2180 #if defined(NDBM) && defined(NIS)
2181 		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
2182 #endif
2183 			return TRUE;
2184 	}
2185 	else
2186 		map->map_mflags &= ~MF_IMPL_HASH;
2187 #endif
2188 #ifdef NDBM
2189 	map->map_mflags |= MF_IMPL_NDBM;
2190 	if (ndbm_map_open(map, mode))
2191 	{
2192 		return TRUE;
2193 	}
2194 	else
2195 		map->map_mflags &= ~MF_IMPL_NDBM;
2196 #endif
2197 
2198 #if defined(NEWDB) || defined(NDBM)
2199 	if (Verbose)
2200 		message("WARNING: cannot open alias database %s", map->map_file);
2201 #else
2202 	if (mode != O_RDONLY)
2203 		usrerr("Cannot rebuild aliases: no database format defined");
2204 #endif
2205 
2206 	return stab_map_open(map, mode);
2207 }
2208 
2209 
2210 /*
2211 **  IMPL_MAP_CLOSE -- close any open database(s)
2212 */
2213 
2214 void
2215 impl_map_close(map)
2216 	MAP *map;
2217 {
2218 	if (tTd(38, 20))
2219 		printf("impl_map_close(%s, %s, %x)\n",
2220 			map->map_mname, map->map_file, map->map_mflags);
2221 #ifdef NEWDB
2222 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2223 	{
2224 		db_map_close(map);
2225 		map->map_mflags &= ~MF_IMPL_HASH;
2226 	}
2227 #endif
2228 
2229 #ifdef NDBM
2230 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2231 	{
2232 		ndbm_map_close(map);
2233 		map->map_mflags &= ~MF_IMPL_NDBM;
2234 	}
2235 #endif
2236 }
2237 /*
2238 **  User map class.
2239 **
2240 **	Provides access to the system password file.
2241 */
2242 
2243 /*
2244 **  USER_MAP_OPEN -- open user map
2245 **
2246 **	Really just binds field names to field numbers.
2247 */
2248 
2249 bool
2250 user_map_open(map, mode)
2251 	MAP *map;
2252 	int mode;
2253 {
2254 	if (tTd(38, 2))
2255 		printf("user_map_open(%s)\n", map->map_mname);
2256 
2257 	if (mode != O_RDONLY)
2258 	{
2259 		/* issue a pseudo-error message */
2260 #ifdef ENOSYS
2261 		errno = ENOSYS;
2262 #else
2263 # ifdef EFTYPE
2264 		errno = EFTYPE;
2265 # else
2266 		errno = ENXIO;
2267 # endif
2268 #endif
2269 		return FALSE;
2270 	}
2271 	if (map->map_valcolnm == NULL)
2272 		/* nothing */ ;
2273 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2274 		map->map_valcolno = 1;
2275 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2276 		map->map_valcolno = 2;
2277 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2278 		map->map_valcolno = 3;
2279 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2280 		map->map_valcolno = 4;
2281 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2282 		map->map_valcolno = 5;
2283 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2284 		map->map_valcolno = 6;
2285 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2286 		map->map_valcolno = 7;
2287 	else
2288 	{
2289 		syserr("User map %s: unknown column name %s",
2290 			map->map_mname, map->map_valcolnm);
2291 		return FALSE;
2292 	}
2293 	return TRUE;
2294 }
2295 
2296 
2297 /*
2298 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2299 */
2300 
2301 char *
2302 user_map_lookup(map, key, av, statp)
2303 	MAP *map;
2304 	char *key;
2305 	char **av;
2306 	int *statp;
2307 {
2308 	struct passwd *pw;
2309 
2310 	if (tTd(38, 20))
2311 		printf("user_map_lookup(%s, %s)\n",
2312 			map->map_mname, key);
2313 
2314 	pw = sm_getpwnam(key);
2315 	if (pw == NULL)
2316 		return NULL;
2317 	if (bitset(MF_MATCHONLY, map->map_mflags))
2318 		return map_rewrite(map, key, strlen(key), NULL);
2319 	else
2320 	{
2321 		char *rwval = NULL;
2322 		char buf[30];
2323 
2324 		switch (map->map_valcolno)
2325 		{
2326 		  case 0:
2327 		  case 1:
2328 			rwval = pw->pw_name;
2329 			break;
2330 
2331 		  case 2:
2332 			rwval = pw->pw_passwd;
2333 			break;
2334 
2335 		  case 3:
2336 			sprintf(buf, "%d", pw->pw_uid);
2337 			rwval = buf;
2338 			break;
2339 
2340 		  case 4:
2341 			sprintf(buf, "%d", pw->pw_gid);
2342 			rwval = buf;
2343 			break;
2344 
2345 		  case 5:
2346 			rwval = pw->pw_gecos;
2347 			break;
2348 
2349 		  case 6:
2350 			rwval = pw->pw_dir;
2351 			break;
2352 
2353 		  case 7:
2354 			rwval = pw->pw_shell;
2355 			break;
2356 		}
2357 		return map_rewrite(map, rwval, strlen(rwval), av);
2358 	}
2359 }
2360 /*
2361 **  BESTMX -- find the best MX for a name
2362 **
2363 **	This is really a hack, but I don't see any obvious way
2364 **	to generalize it at the moment.
2365 */
2366 
2367 #if NAMED_BIND
2368 
2369 char *
2370 bestmx_map_lookup(map, name, av, statp)
2371 	MAP *map;
2372 	char *name;
2373 	char **av;
2374 	int *statp;
2375 {
2376         int nmx;
2377         auto int rcode;
2378         char *mxhosts[MAXMXHOSTS + 1];
2379 
2380 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2381 	if (nmx <= 0)
2382 		return NULL;
2383 	if (bitset(MF_MATCHONLY, map->map_mflags))
2384 		return map_rewrite(map, name, strlen(name), NULL);
2385 	else
2386 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2387 }
2388 
2389 #endif
2390 /*
2391 **  Program map type.
2392 **
2393 **	This provides access to arbitrary programs.  It should be used
2394 **	only very sparingly, since there is no way to bound the cost
2395 **	of invoking an arbitrary program.
2396 */
2397 
2398 char *
2399 prog_map_lookup(map, name, av, statp)
2400 	MAP *map;
2401 	char *name;
2402 	char **av;
2403 	int *statp;
2404 {
2405 	int i;
2406 	register char *p;
2407 	int fd;
2408 	auto pid_t pid;
2409 	char *argv[MAXPV + 1];
2410 	char buf[MAXLINE];
2411 
2412 	if (tTd(38, 20))
2413 		printf("prog_map_lookup(%s, %s) %s\n",
2414 			map->map_mname, name, map->map_file);
2415 
2416 	i = 0;
2417 	argv[i++] = map->map_file;
2418 	strcpy(buf, map->map_rebuild);
2419 	for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
2420 	{
2421 		if (i >= MAXPV - 1)
2422 			break;
2423 		argv[i++] = p;
2424 	}
2425 	argv[i++] = name;
2426 	argv[i] = NULL;
2427 	pid = prog_open(argv, &fd, CurEnv);
2428 	if (pid < 0)
2429 	{
2430 		if (tTd(38, 9))
2431 			printf("prog_map_lookup(%s) failed (%s) -- closing",
2432 				map->map_mname, errstring(errno));
2433 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
2434 		return NULL;
2435 	}
2436 	i = read(fd, buf, sizeof buf - 1);
2437 	if (i <= 0 && tTd(38, 2))
2438 		printf("prog_map_lookup(%s): read error %s\n",
2439 			map->map_mname, errstring(errno));
2440 	if (i > 0)
2441 	{
2442 		char *rval;
2443 
2444 		buf[i] = '\0';
2445 		p = strchr(buf, '\n');
2446 		if (p != NULL)
2447 			*p = '\0';
2448 
2449 		/* collect the return value */
2450 		if (bitset(MF_MATCHONLY, map->map_mflags))
2451 			rval = map_rewrite(map, name, strlen(name), NULL);
2452 		else
2453 			rval = map_rewrite(map, buf, strlen(buf), NULL);
2454 
2455 		/* now flush any additional output */
2456 		while ((i = read(fd, buf, sizeof buf)) > 0)
2457 			continue;
2458 		close(fd);
2459 
2460 		/* and wait for the process to terminate */
2461 		*statp = waitfor(pid);
2462 
2463 		return rval;
2464 	}
2465 
2466 	close(fd);
2467 	*statp = waitfor(pid);
2468 	return NULL;
2469 }
2470 /*
2471 **  Sequenced map type.
2472 **
2473 **	Tries each map in order until something matches, much like
2474 **	implicit.  Stores go to the first map in the list that can
2475 **	support storing.
2476 **
2477 **	This is slightly unusual in that there are two interfaces.
2478 **	The "sequence" interface lets you stack maps arbitrarily.
2479 **	The "switch" interface builds a sequence map by looking
2480 **	at a system-dependent configuration file such as
2481 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2482 **
2483 **	We don't need an explicit open, since all maps are
2484 **	opened during startup, including underlying maps.
2485 */
2486 
2487 /*
2488 **  SEQ_MAP_PARSE -- Sequenced map parsing
2489 */
2490 
2491 bool
2492 seq_map_parse(map, ap)
2493 	MAP *map;
2494 	char *ap;
2495 {
2496 	int maxmap;
2497 
2498 	if (tTd(38, 2))
2499 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2500 	maxmap = 0;
2501 	while (*ap != '\0')
2502 	{
2503 		register char *p;
2504 		STAB *s;
2505 
2506 		/* find beginning of map name */
2507 		while (isascii(*ap) && isspace(*ap))
2508 			ap++;
2509 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2510 			continue;
2511 		if (*p != '\0')
2512 			*p++ = '\0';
2513 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2514 			p++;
2515 		if (*ap == '\0')
2516 		{
2517 			ap = p;
2518 			continue;
2519 		}
2520 		s = stab(ap, ST_MAP, ST_FIND);
2521 		if (s == NULL)
2522 		{
2523 			syserr("Sequence map %s: unknown member map %s",
2524 				map->map_mname, ap);
2525 		}
2526 		else if (maxmap == MAXMAPSTACK)
2527 		{
2528 			syserr("Sequence map %s: too many member maps (%d max)",
2529 				map->map_mname, MAXMAPSTACK);
2530 			maxmap++;
2531 		}
2532 		else if (maxmap < MAXMAPSTACK)
2533 		{
2534 			map->map_stack[maxmap++] = &s->s_map;
2535 		}
2536 		ap = p;
2537 	}
2538 	return TRUE;
2539 }
2540 
2541 
2542 /*
2543 **  SWITCH_MAP_OPEN -- open a switched map
2544 **
2545 **	This looks at the system-dependent configuration and builds
2546 **	a sequence map that does the same thing.
2547 **
2548 **	Every system must define a switch_map_find routine in conf.c
2549 **	that will return the list of service types associated with a
2550 **	given service class.
2551 */
2552 
2553 bool
2554 switch_map_open(map, mode)
2555 	MAP *map;
2556 	int mode;
2557 {
2558 	int mapno;
2559 	int nmaps;
2560 	char *maptype[MAXMAPSTACK];
2561 
2562 	if (tTd(38, 2))
2563 		printf("switch_map_open(%s, %s, %d)\n",
2564 			map->map_mname, map->map_file, mode);
2565 
2566 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2567 	if (tTd(38, 19))
2568 	{
2569 		printf("\tswitch_map_find => %d\n", nmaps);
2570 		for (mapno = 0; mapno < nmaps; mapno++)
2571 			printf("\t\t%s\n", maptype[mapno]);
2572 	}
2573 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2574 		return FALSE;
2575 
2576 	for (mapno = 0; mapno < nmaps; mapno++)
2577 	{
2578 		register STAB *s;
2579 		char nbuf[MAXNAME + 1];
2580 
2581 		if (maptype[mapno] == NULL)
2582 			continue;
2583 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2584 		s = stab(nbuf, ST_MAP, ST_FIND);
2585 		if (s == NULL)
2586 		{
2587 			syserr("Switch map %s: unknown member map %s",
2588 				map->map_mname, nbuf);
2589 		}
2590 		else
2591 		{
2592 			map->map_stack[mapno] = &s->s_map;
2593 			if (tTd(38, 4))
2594 				printf("\tmap_stack[%d] = %s:%s\n",
2595 					mapno, s->s_map.map_class->map_cname,
2596 					nbuf);
2597 		}
2598 	}
2599 	return TRUE;
2600 }
2601 
2602 
2603 /*
2604 **  SEQ_MAP_CLOSE -- close all underlying maps
2605 */
2606 
2607 seq_map_close(map)
2608 	MAP *map;
2609 {
2610 	int mapno;
2611 
2612 	if (tTd(38, 20))
2613 		printf("seq_map_close(%s)\n", map->map_mname);
2614 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2615 	{
2616 		MAP *mm = map->map_stack[mapno];
2617 
2618 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2619 			continue;
2620 		mm->map_class->map_close(mm);
2621 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2622 	}
2623 }
2624 
2625 
2626 /*
2627 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2628 */
2629 
2630 char *
2631 seq_map_lookup(map, key, args, pstat)
2632 	MAP *map;
2633 	char *key;
2634 	char **args;
2635 	int *pstat;
2636 {
2637 	int mapno;
2638 	int mapbit = 0x01;
2639 
2640 	if (tTd(38, 20))
2641 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2642 
2643 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2644 	{
2645 		MAP *mm = map->map_stack[mapno];
2646 		int stat = 0;
2647 		char *rv;
2648 
2649 		if (mm == NULL)
2650 			continue;
2651 		if (!bitset(MF_OPEN, mm->map_mflags))
2652 		{
2653 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2654 			{
2655 				*pstat = EX_UNAVAILABLE;
2656 				return NULL;
2657 			}
2658 			continue;
2659 		}
2660 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2661 		if (rv != NULL)
2662 			return rv;
2663 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2664 			return NULL;
2665 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2666 		{
2667 			*pstat = stat;
2668 			return NULL;
2669 		}
2670 	}
2671 	return NULL;
2672 }
2673 
2674 
2675 /*
2676 **  SEQ_MAP_STORE -- sequenced map store
2677 */
2678 
2679 void
2680 seq_map_store(map, key, val)
2681 	MAP *map;
2682 	char *key;
2683 	char *val;
2684 {
2685 	int mapno;
2686 
2687 	if (tTd(38, 12))
2688 		printf("seq_map_store(%s, %s, %s)\n",
2689 			map->map_mname, key, val);
2690 
2691 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2692 	{
2693 		MAP *mm = map->map_stack[mapno];
2694 
2695 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2696 			continue;
2697 
2698 		mm->map_class->map_store(mm, key, val);
2699 		return;
2700 	}
2701 	syserr("seq_map_store(%s, %s, %s): no writable map",
2702 		map->map_mname, key, val);
2703 }
2704 /*
2705 **  GETCANONNAME -- look up name using service switch
2706 **
2707 **	Parameters:
2708 **		host -- the host name to look up.
2709 **		hbsize -- the size of the host buffer.
2710 **		trymx -- if set, try MX records.
2711 **
2712 **	Returns:
2713 **		TRUE -- if the host was found.
2714 **		FALSE -- otherwise.
2715 */
2716 
2717 bool
2718 getcanonname(host, hbsize, trymx)
2719 	char *host;
2720 	int hbsize;
2721 	bool trymx;
2722 {
2723 	int nmaps;
2724 	int mapno;
2725 	bool found = FALSE;
2726 	auto int stat;
2727 	char *maptype[MAXMAPSTACK];
2728 	short mapreturn[MAXMAPACTIONS];
2729 	extern int h_errno;
2730 
2731 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2732 	for (mapno = 0; mapno < nmaps; mapno++)
2733 	{
2734 		int i;
2735 
2736 		if (tTd(38, 20))
2737 			printf("getcanonname(%s), trying %s\n",
2738 				host, maptype[mapno]);
2739 		if (strcmp("files", maptype[mapno]) == 0)
2740 			found = text_getcanonname(host, hbsize, &stat);
2741 #ifdef NIS
2742 		else if (strcmp("nis", maptype[mapno]) == 0)
2743 			found = nis_getcanonname(host, hbsize, &stat);
2744 #endif
2745 #ifdef NISPLUS
2746 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2747 			found = nisplus_getcanonname(host, hbsize, &stat);
2748 #endif
2749 #if NAMED_BIND
2750 		else if (strcmp("dns", maptype[mapno]) == 0)
2751 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2752 #endif
2753 		else
2754 		{
2755 			found = FALSE;
2756 			stat = EX_UNAVAILABLE;
2757 		}
2758 		if (found)
2759 			break;
2760 
2761 		/* see if we should continue */
2762 		if (stat == EX_TEMPFAIL)
2763 			i = MA_TRYAGAIN;
2764 		else if (stat == EX_NOHOST)
2765 			i = MA_NOTFOUND;
2766 		else
2767 			i = MA_UNAVAIL;
2768 		if (bitset(1 << mapno, mapreturn[i]))
2769 			break;
2770 	}
2771 
2772 	if (found)
2773 	{
2774 		char *d;
2775 
2776 		if (tTd(38, 20))
2777 			printf("getcanonname(%s), found\n", host);
2778 
2779 		/*
2780 		**  If returned name is still single token, compensate
2781 		**  by tagging on $m.  This is because some sites set
2782 		**  up their DNS or NIS databases wrong.
2783 		*/
2784 
2785 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2786 		{
2787 			d = macvalue('m', CurEnv);
2788 			if (d != NULL &&
2789 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2790 			{
2791 				if (host[strlen(host) - 1] != '.')
2792 					strcat(host, ".");
2793 				strcat(host, d);
2794 			}
2795 			else
2796 			{
2797 				return FALSE;
2798 			}
2799 		}
2800 		return TRUE;
2801 	}
2802 
2803 	if (tTd(38, 20))
2804 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2805 
2806 #if NAMED_BIND
2807 	if (stat == EX_NOHOST)
2808 		h_errno = HOST_NOT_FOUND;
2809 	else
2810 		h_errno = TRY_AGAIN;
2811 #endif
2812 
2813 	return FALSE;
2814 }
2815 /*
2816 **  NULL stubs
2817 */
2818 
2819 bool
2820 null_map_open(map, mode)
2821 	MAP *map;
2822 	int mode;
2823 {
2824 	return TRUE;
2825 }
2826 
2827 void
2828 null_map_close(map)
2829 	MAP *map;
2830 {
2831 	return;
2832 }
2833 
2834 void
2835 null_map_store(map, key, val)
2836 	MAP *map;
2837 	char *key;
2838 	char *val;
2839 {
2840 	return;
2841 }
2842 
2843 
2844 /*
2845 **  BOGUS stubs
2846 */
2847 
2848 char *
2849 bogus_map_lookup(map, key, args, pstat)
2850 	MAP *map;
2851 	char *key;
2852 	char **args;
2853 	int *pstat;
2854 {
2855 	*pstat = EX_TEMPFAIL;
2856 	return NULL;
2857 }
2858 
2859 MAPCLASS	BogusMapClass =
2860 {
2861 	"bogus-map",		NULL,		0,
2862 	NULL,		bogus_map_lookup,	null_map_store,
2863 	null_map_open,	null_map_close,
2864 };
2865