xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision e579053f)
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.63 (Berkeley) 05/23/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 { int dummy; };	/* 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 		(void) sprintf(buf, "%010ld", curtime());
654 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
655 
656 		(void) gethostname(buf, sizeof buf);
657 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
658 
659 		if (inclnull)
660 			map->map_mflags |= MF_INCLNULL;
661 #endif
662 
663 		/* write out the distinguished alias */
664 		ndbm_map_store(map, "@", "@");
665 	}
666 	dbm_close((DBM *) map->map_db1);
667 }
668 
669 #endif
670 /*
671 **  NEWDB (Hash and BTree) Modules
672 */
673 
674 #ifdef NEWDB
675 
676 /*
677 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
678 **
679 **	These do rather bizarre locking.  If you can lock on open,
680 **	do that to avoid the condition of opening a database that
681 **	is being rebuilt.  If you don't, we'll try to fake it, but
682 **	there will be a race condition.  If opening for read-only,
683 **	we immediately release the lock to avoid freezing things up.
684 **	We really ought to hold the lock, but guarantee that we won't
685 **	be pokey about it.  That's hard to do.
686 */
687 
688 bool
689 bt_map_open(map, mode)
690 	MAP *map;
691 	int mode;
692 {
693 	DB *db;
694 	int i;
695 	int omode;
696 	int fd;
697 	struct stat st;
698 	char buf[MAXNAME + 1];
699 
700 	if (tTd(38, 2))
701 		printf("bt_map_open(%s, %s, %d)\n",
702 			map->map_mname, map->map_file, mode);
703 
704 	omode = mode;
705 	if (omode == O_RDWR)
706 	{
707 		omode |= O_CREAT|O_TRUNC;
708 #if defined(O_EXLOCK) && HASFLOCK
709 		omode |= O_EXLOCK;
710 # if !OLD_NEWDB
711 	}
712 	else
713 	{
714 		omode |= O_SHLOCK;
715 # endif
716 #endif
717 	}
718 
719 	(void) strcpy(buf, map->map_file);
720 	i = strlen(buf);
721 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
722 		(void) strcat(buf, ".db");
723 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
724 	if (db == NULL)
725 	{
726 #ifdef MAYBENEXTRELEASE
727 		if (aliaswait(map, ".db", FALSE))
728 			return TRUE;
729 #endif
730 		if (!bitset(MF_OPTIONAL, map->map_mflags))
731 			syserr("Cannot open BTREE database %s", map->map_file);
732 		return FALSE;
733 	}
734 #if !OLD_NEWDB
735 	fd = db->fd(db);
736 # if defined(O_EXLOCK) && HASFLOCK
737 	if (fd >= 0)
738 	{
739 		if (mode == O_RDONLY)
740 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
741 		else
742 			map->map_mflags |= MF_LOCKED;
743 	}
744 # else
745 	if (mode == O_RDWR && fd >= 0)
746 	{
747 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
748 			map->map_mflags |= MF_LOCKED;
749 	}
750 # endif
751 #endif
752 
753 	/* try to make sure that at least the database header is on disk */
754 	if (mode == O_RDWR)
755 #if OLD_NEWDB
756 		(void) db->sync(db);
757 #else
758 		(void) db->sync(db, 0);
759 
760 	if (fd >= 0 && fstat(fd, &st) >= 0)
761 		map->map_mtime = st.st_mtime;
762 #endif
763 
764 	map->map_db2 = (void *) db;
765 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
766 		if (!aliaswait(map, ".db", TRUE))
767 			return FALSE;
768 	return TRUE;
769 }
770 
771 
772 /*
773 **  HASH_MAP_INIT -- HASH-style map initialization
774 */
775 
776 bool
777 hash_map_open(map, mode)
778 	MAP *map;
779 	int mode;
780 {
781 	DB *db;
782 	int i;
783 	int omode;
784 	int fd;
785 	struct stat st;
786 	char buf[MAXNAME + 1];
787 
788 	if (tTd(38, 2))
789 		printf("hash_map_open(%s, %s, %d)\n",
790 			map->map_mname, map->map_file, mode);
791 
792 	omode = mode;
793 	if (omode == O_RDWR)
794 	{
795 		omode |= O_CREAT|O_TRUNC;
796 #if defined(O_EXLOCK) && HASFLOCK
797 		omode |= O_EXLOCK;
798 # if !OLD_NEWDB
799 	}
800 	else
801 	{
802 		omode |= O_SHLOCK;
803 # endif
804 #endif
805 	}
806 
807 	(void) strcpy(buf, map->map_file);
808 	i = strlen(buf);
809 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
810 		(void) strcat(buf, ".db");
811 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
812 	if (db == NULL)
813 	{
814 #ifdef MAYBENEXTRELEASE
815 		if (aliaswait(map, ".db", FALSE))
816 			return TRUE;
817 #endif
818 		if (!bitset(MF_OPTIONAL, map->map_mflags))
819 			syserr("Cannot open HASH database %s", map->map_file);
820 		return FALSE;
821 	}
822 #if !OLD_NEWDB
823 	fd = db->fd(db);
824 # if defined(O_EXLOCK) && HASFLOCK
825 	if (fd >= 0)
826 	{
827 		if (mode == O_RDONLY)
828 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
829 		else
830 			map->map_mflags |= MF_LOCKED;
831 	}
832 # else
833 	if (mode == O_RDWR && fd >= 0)
834 	{
835 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
836 			map->map_mflags |= MF_LOCKED;
837 	}
838 # endif
839 #endif
840 
841 	/* try to make sure that at least the database header is on disk */
842 	if (mode == O_RDWR)
843 #if OLD_NEWDB
844 		(void) db->sync(db);
845 #else
846 		(void) db->sync(db, 0);
847 
848 	if (fd >= 0 && fstat(fd, &st) >= 0)
849 		map->map_mtime = st.st_mtime;
850 #endif
851 
852 	map->map_db2 = (void *) db;
853 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
854 		if (!aliaswait(map, ".db", TRUE))
855 			return FALSE;
856 	return TRUE;
857 }
858 
859 
860 /*
861 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
862 */
863 
864 char *
865 db_map_lookup(map, name, av, statp)
866 	MAP *map;
867 	char *name;
868 	char **av;
869 	int *statp;
870 {
871 	DBT key, val;
872 	register DB *db = (DB *) map->map_db2;
873 	int st;
874 	int saveerrno;
875 	int fd;
876 	char keybuf[MAXNAME + 1];
877 
878 	if (tTd(38, 20))
879 		printf("db_map_lookup(%s, %s)\n",
880 			map->map_mname, name);
881 
882 	key.size = strlen(name);
883 	if (key.size > sizeof keybuf - 1)
884 		key.size = sizeof keybuf - 1;
885 	key.data = keybuf;
886 	bcopy(name, keybuf, key.size + 1);
887 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
888 		makelower(keybuf);
889 #if !OLD_NEWDB
890 	fd = db->fd(db);
891 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
892 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
893 #endif
894 	st = 1;
895 	if (bitset(MF_TRY0NULL, map->map_mflags))
896 	{
897 		st = db->get(db, &key, &val, 0);
898 		if (st == 0)
899 			map->map_mflags &= ~MF_TRY1NULL;
900 	}
901 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
902 	{
903 		key.size++;
904 		st = db->get(db, &key, &val, 0);
905 		if (st == 0)
906 			map->map_mflags &= ~MF_TRY0NULL;
907 	}
908 	saveerrno = errno;
909 #if !OLD_NEWDB
910 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
911 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
912 #endif
913 	if (st != 0)
914 	{
915 		errno = saveerrno;
916 		if (st < 0)
917 			syserr("db_map_lookup: get (%s)", name);
918 		return NULL;
919 	}
920 	if (bitset(MF_MATCHONLY, map->map_mflags))
921 		return map_rewrite(map, name, strlen(name), NULL);
922 	else
923 		return map_rewrite(map, val.data, val.size, av);
924 }
925 
926 
927 /*
928 **  DB_MAP_STORE -- store a datum in the NEWDB database
929 */
930 
931 void
932 db_map_store(map, lhs, rhs)
933 	register MAP *map;
934 	char *lhs;
935 	char *rhs;
936 {
937 	int stat;
938 	DBT key;
939 	DBT data;
940 	register DB *db = map->map_db2;
941 
942 	if (tTd(38, 20))
943 		printf("db_map_store(%s, %s, %s)\n",
944 			map->map_mname, lhs, rhs);
945 
946 	key.size = strlen(lhs);
947 	key.data = lhs;
948 
949 	data.size = strlen(rhs);
950 	data.data = rhs;
951 
952 	if (bitset(MF_INCLNULL, map->map_mflags))
953 	{
954 		key.size++;
955 		data.size++;
956 	}
957 
958 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
959 	if (stat > 0)
960 	{
961 		if (!bitset(MF_APPEND, map->map_mflags))
962 			usrerr("050 Warning: duplicate alias name %s", lhs);
963 		else
964 		{
965 			static char *buf = NULL;
966 			static int bufsiz = 0;
967 			DBT old;
968 
969 			old.data = db_map_lookup(map, key.data, NULL, &stat);
970 			if (old.data != NULL)
971 			{
972 				old.size = strlen(old.data);
973 				if (data.size + old.size + 2 > bufsiz)
974 				{
975 					if (buf != NULL)
976 						(void) free(buf);
977 					bufsiz = data.size + old.size + 2;
978 					buf = xalloc(bufsiz);
979 				}
980 				sprintf(buf, "%s,%s", data.data, old.data);
981 				data.size = data.size + old.size + 1;
982 				data.data = buf;
983 				if (tTd(38, 9))
984 					printf("db_map_store append=%s\n", data.data);
985 			}
986 		}
987 		stat = db->put(db, &key, &data, 0);
988 	}
989 	if (stat != 0)
990 		syserr("readaliases: db put (%s)", lhs);
991 }
992 
993 
994 /*
995 **  DB_MAP_CLOSE -- add distinguished entries and close the database
996 */
997 
998 void
999 db_map_close(map)
1000 	MAP *map;
1001 {
1002 	register DB *db = map->map_db2;
1003 
1004 	if (tTd(38, 9))
1005 		printf("db_map_close(%s, %s, %x)\n",
1006 			map->map_mname, map->map_file, map->map_mflags);
1007 
1008 	if (bitset(MF_WRITABLE, map->map_mflags))
1009 	{
1010 		/* write out the distinguished alias */
1011 		db_map_store(map, "@", "@");
1012 	}
1013 
1014 	if (db->close(db) != 0)
1015 		syserr("readaliases: db close failure");
1016 }
1017 
1018 #endif
1019 /*
1020 **  NIS Modules
1021 */
1022 
1023 # ifdef NIS
1024 
1025 # ifndef YPERR_BUSY
1026 #  define YPERR_BUSY	16
1027 # endif
1028 
1029 /*
1030 **  NIS_MAP_OPEN -- open DBM map
1031 */
1032 
1033 bool
1034 nis_map_open(map, mode)
1035 	MAP *map;
1036 	int mode;
1037 {
1038 	int yperr;
1039 	register char *p;
1040 	auto char *vp;
1041 	auto int vsize;
1042 	char *master;
1043 
1044 	if (tTd(38, 2))
1045 		printf("nis_map_open(%s, %s)\n",
1046 			map->map_mname, map->map_file);
1047 
1048 	if (mode != O_RDONLY)
1049 	{
1050 		/* issue a pseudo-error message */
1051 #ifdef ENOSYS
1052 		errno = ENOSYS;
1053 #else
1054 # ifdef EFTYPE
1055 		errno = EFTYPE;
1056 # else
1057 		errno = ENXIO;
1058 # endif
1059 #endif
1060 		return FALSE;
1061 	}
1062 
1063 	p = strchr(map->map_file, '@');
1064 	if (p != NULL)
1065 	{
1066 		*p++ = '\0';
1067 		if (*p != '\0')
1068 			map->map_domain = p;
1069 	}
1070 
1071 	if (*map->map_file == '\0')
1072 		map->map_file = "mail.aliases";
1073 
1074 	if (map->map_domain == NULL)
1075 	{
1076 		yperr = yp_get_default_domain(&map->map_domain);
1077 		if (yperr != 0)
1078 		{
1079 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1080 				syserr("421 NIS map %s specified, but NIS not running\n",
1081 					map->map_file);
1082 			return FALSE;
1083 		}
1084 	}
1085 
1086 	/* check to see if this map actually exists */
1087 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1088 			&vp, &vsize);
1089 	if (tTd(38, 10))
1090 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1091 			map->map_domain, map->map_file, yperr_string(yperr));
1092 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1093 	{
1094 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1095 		    aliaswait(map, NULL, TRUE))
1096 			return TRUE;
1097 	}
1098 
1099 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1100 	{
1101 		syserr("421 Cannot bind to map %s in domain %s: %s",
1102 			map->map_file, map->map_domain, yperr_string(yperr));
1103 	}
1104 
1105 	return FALSE;
1106 }
1107 
1108 
1109 /*
1110 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1111 */
1112 
1113 char *
1114 nis_map_lookup(map, name, av, statp)
1115 	MAP *map;
1116 	char *name;
1117 	char **av;
1118 	int *statp;
1119 {
1120 	char *vp;
1121 	auto int vsize;
1122 	int buflen;
1123 	int yperr;
1124 	char keybuf[MAXNAME + 1];
1125 
1126 	if (tTd(38, 20))
1127 		printf("nis_map_lookup(%s, %s)\n",
1128 			map->map_mname, name);
1129 
1130 	buflen = strlen(name);
1131 	if (buflen > sizeof keybuf - 1)
1132 		buflen = sizeof keybuf - 1;
1133 	bcopy(name, keybuf, buflen + 1);
1134 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1135 		makelower(keybuf);
1136 	yperr = YPERR_KEY;
1137 	if (bitset(MF_TRY0NULL, map->map_mflags))
1138 	{
1139 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1140 			     &vp, &vsize);
1141 		if (yperr == 0)
1142 			map->map_mflags &= ~MF_TRY1NULL;
1143 	}
1144 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1145 	{
1146 		buflen++;
1147 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1148 			     &vp, &vsize);
1149 		if (yperr == 0)
1150 			map->map_mflags &= ~MF_TRY0NULL;
1151 	}
1152 	if (yperr != 0)
1153 	{
1154 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1155 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1156 		return NULL;
1157 	}
1158 	if (bitset(MF_MATCHONLY, map->map_mflags))
1159 		return map_rewrite(map, name, strlen(name), NULL);
1160 	else
1161 		return map_rewrite(map, vp, vsize, av);
1162 }
1163 
1164 
1165 /*
1166 **  NIS_GETCANONNAME -- look up canonical name in NIS
1167 */
1168 
1169 bool
1170 nis_getcanonname(name, hbsize, statp)
1171 	char *name;
1172 	int hbsize;
1173 	int *statp;
1174 {
1175 	char *vp;
1176 	auto int vsize;
1177 	int keylen;
1178 	int yperr;
1179 	static bool try0null = TRUE;
1180 	static bool try1null = TRUE;
1181 	static char *yp_domain = NULL;
1182 	char *domain, *p;
1183 	char host_record[MAXLINE];
1184 	char buf[MAXNAME];
1185 	char *cname;
1186 	extern char *get_column();
1187 
1188 	if (tTd(38, 20))
1189 		printf("nis_getcanonname(%s)\n", name);
1190 
1191 	shorten_hostname(name);
1192 
1193 	/* we only accept single token search key */
1194 	if (strchr(name, '.'))
1195 	{
1196 		*statp = EX_NOHOST;
1197 		return FALSE;
1198 	}
1199 
1200 	keylen = strlen(name);
1201 
1202 	if (yp_domain == NULL)
1203 		yp_get_default_domain(&yp_domain);
1204 	makelower(name);
1205 	yperr = YPERR_KEY;
1206 	if (try0null)
1207 	{
1208 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1209 			     &vp, &vsize);
1210 		if (yperr == 0)
1211 			try1null = FALSE;
1212 	}
1213 	if (yperr == YPERR_KEY && try1null)
1214 	{
1215 		keylen++;
1216 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1217 			     &vp, &vsize);
1218 		if (yperr == 0)
1219 			try0null = FALSE;
1220 	}
1221 	if (yperr != 0)
1222 	{
1223 		if (yperr == YPERR_KEY)
1224 			*statp = EX_NOHOST;
1225 		else if (yperr == YPERR_BUSY)
1226 			*statp = EX_TEMPFAIL;
1227 		else
1228 			*statp = EX_UNAVAILABLE;
1229 		return FALSE;
1230 	}
1231 	strncpy(host_record, vp, vsize);
1232 	host_record[vsize] = '\0';
1233 	cname = get_column(host_record, 1, '\t', buf);
1234 	if (cname == NULL)
1235 	{
1236 		/* this should not happen, but.... */
1237 		*statp = EX_NOHOST;
1238 		return FALSE;
1239 	}
1240 
1241 	if (hbsize >= strlen(cname))
1242 	{
1243 		strcpy(name, cname);
1244 		*statp = EX_OK;
1245 		return TRUE;
1246 	}
1247 	*statp = EX_UNAVAILABLE;
1248 	return FALSE;
1249 }
1250 
1251 #endif
1252 /*
1253 **  NISPLUS Modules
1254 **
1255 **	This code donated by Sun Microsystems.
1256 */
1257 
1258 #ifdef NISPLUS
1259 
1260 #undef NIS /* symbol conflict in nis.h */
1261 #include <rpcsvc/nis.h>
1262 #include <rpcsvc/nislib.h>
1263 
1264 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1265 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1266 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1267 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1268 
1269 /*
1270 **  NISPLUS_MAP_OPEN -- open nisplus table
1271 */
1272 
1273 bool
1274 nisplus_map_open(map, mode)
1275 	MAP *map;
1276 	int mode;
1277 {
1278 	register char *p;
1279 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1280 	nis_result *res = NULL;
1281 	u_int objs_len;
1282 	nis_object *obj_ptr;
1283 	int retry_cnt, max_col, i;
1284 
1285 	if (tTd(38, 2))
1286 		printf("nisplus_map_open(%s, %s, %d)\n",
1287 			map->map_mname, map->map_file, mode);
1288 
1289 	if (mode != O_RDONLY)
1290 	{
1291 		errno = ENODEV;
1292 		return FALSE;
1293 	}
1294 
1295 	if (*map->map_file == '\0')
1296 		map->map_file = "mail_aliases.org_dir";
1297 
1298 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1299 	{
1300 		/* set default NISPLUS Domain to $m */
1301 		extern char *nisplus_default_domain();
1302 
1303 		map->map_domain = newstr(nisplus_default_domain());
1304 		if (tTd(38, 2))
1305 			printf("nisplus_map_open(%s): using domain %s\n",
1306 				 map->map_file, map->map_domain);
1307 	}
1308 	if (!PARTIAL_NAME(map->map_file))
1309 		map->map_domain = newstr("");
1310 
1311 	/* check to see if this map actually exists */
1312 	if (PARTIAL_NAME(map->map_file))
1313 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1314 	else
1315 		strcpy(qbuf, map->map_file);
1316 
1317 	retry_cnt = 0;
1318 	while (res == NULL || res->status != NIS_SUCCESS)
1319 	{
1320 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1321 		switch (res->status)
1322 		{
1323 		  case NIS_SUCCESS:
1324 		  case NIS_TRYAGAIN:
1325 		  case NIS_RPCERROR:
1326 		  case NIS_NAMEUNREACHABLE:
1327 			break;
1328 
1329 		  default:		/* all other nisplus errors */
1330 #if 0
1331 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1332 				syserr("421 Cannot find table %s.%s: %s",
1333 					map->map_file, map->map_domain,
1334 					nis_sperrno(res->status));
1335 #endif
1336 			errno = EBADR;
1337 			return FALSE;
1338 		}
1339 		sleep(2);		/* try not to overwhelm hosed server */
1340 		if (retry_cnt++ > 4)
1341 		{
1342 			errno = EBADR;
1343 			return FALSE;
1344 		}
1345 	}
1346 
1347 	if (NIS_RES_NUMOBJ(res) != 1 ||
1348 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1349 	{
1350 		if (tTd(38, 10))
1351 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1352 #if 0
1353 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1354 			syserr("421 %s.%s: %s is not a table",
1355 				map->map_file, map->map_domain,
1356 				nis_sperrno(res->status));
1357 #endif
1358 		errno = EBADR;
1359 		return FALSE;
1360 	}
1361 	/* default key column is column 0 */
1362 	if (map->map_keycolnm == NULL)
1363 		map->map_keycolnm = newstr(COL_NAME(res,0));
1364 
1365 	max_col = COL_MAX(res);
1366 
1367 	/* verify the key column exist */
1368 	for (i=0; i< max_col; i++)
1369 	{
1370 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1371 			break;
1372 	}
1373 	if (i == max_col)
1374 	{
1375 		if (tTd(38, 2))
1376 			printf("nisplus_map_open(%s): can not find key column %s\n",
1377 				map->map_file, map->map_keycolnm);
1378 		errno = EBADR;
1379 		return FALSE;
1380 	}
1381 
1382 	/* default value column is the last column */
1383 	if (map->map_valcolnm == NULL)
1384 	{
1385 		map->map_valcolno = max_col - 1;
1386 		return TRUE;
1387 	}
1388 
1389 	for (i=0; i< max_col; i++)
1390 	{
1391 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1392 		{
1393 			map->map_valcolno = i;
1394 			return TRUE;
1395 		}
1396 	}
1397 
1398 	if (tTd(38, 2))
1399 		printf("nisplus_map_open(%s): can not find column %s\n",
1400 			 map->map_file, map->map_keycolnm);
1401 	errno = EBADR;
1402 	return FALSE;
1403 }
1404 
1405 
1406 /*
1407 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1408 */
1409 
1410 char *
1411 nisplus_map_lookup(map, name, av, statp)
1412 	MAP *map;
1413 	char *name;
1414 	char **av;
1415 	int *statp;
1416 {
1417 	char *vp;
1418 	auto int vsize;
1419 	int buflen;
1420 	char search_key[MAXNAME + 1];
1421 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1422 	nis_result *result;
1423 
1424 	if (tTd(38, 20))
1425 		printf("nisplus_map_lookup(%s, %s)\n",
1426 			map->map_mname, name);
1427 
1428 	if (!bitset(MF_OPEN, map->map_mflags))
1429 	{
1430 		if (nisplus_map_open(map, O_RDONLY))
1431 			map->map_mflags |= MF_OPEN;
1432 		else
1433 		{
1434 			*statp = EX_UNAVAILABLE;
1435 			return NULL;
1436 		}
1437 	}
1438 
1439 	buflen = strlen(name);
1440 	if (buflen > sizeof search_key - 1)
1441 		buflen = sizeof search_key - 1;
1442 	bcopy(name, search_key, buflen + 1);
1443 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1444 		makelower(search_key);
1445 
1446 	/* construct the query */
1447 	if (PARTIAL_NAME(map->map_file))
1448 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1449 			search_key, map->map_file, map->map_domain);
1450 	else
1451 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1452 			search_key, map->map_file);
1453 
1454 	if (tTd(38, 20))
1455 		printf("qbuf=%s\n", qbuf);
1456 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1457 	if (result->status == NIS_SUCCESS)
1458 	{
1459 		int count;
1460 		char *str;
1461 
1462 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1463 		{
1464 			if (LogLevel > 10)
1465 				syslog(LOG_WARNING,
1466 				  "%s:Lookup error, expected 1 entry, got (%d)",
1467 				    map->map_file, count);
1468 
1469 			/* ignore second entry */
1470 			if (tTd(38, 20))
1471 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1472 					name, count);
1473 		}
1474 
1475 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1476 		/* set the length of the result */
1477 		if (vp == NULL)
1478 			vp = "";
1479 		vsize = strlen(vp);
1480 		if (tTd(38, 20))
1481 			printf("nisplus_map_lookup(%s), found %s\n",
1482 				name, vp);
1483 		if (bitset(MF_MATCHONLY, map->map_mflags))
1484 			str = map_rewrite(map, name, strlen(name), NULL);
1485 		else
1486 			str = map_rewrite(map, vp, vsize, av);
1487 		nis_freeresult(result);
1488 #ifdef MAP_EXIT_STAT
1489 		*statp = EX_OK;
1490 #endif
1491 		return str;
1492 	}
1493 	else
1494 	{
1495 #ifdef MAP_EXIT_STAT
1496 		if (result->status == NIS_NOTFOUND)
1497 			*statp = EX_NOTFOUND;
1498 		else if (result->status == NIS_TRYAGAIN)
1499 			*statp = EX_TEMPFAIL;
1500 		else
1501 		{
1502 			*statp = EX_UNAVAILABLE;
1503 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1504 		}
1505 #else
1506 		if ((result->status != NIS_NOTFOUND) &&
1507 		    (result->status != NIS_TRYAGAIN))
1508 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1509 #endif
1510 	}
1511 	if (tTd(38, 20))
1512 		printf("nisplus_map_lookup(%s), failed\n", name);
1513 	nis_freeresult(result);
1514 	return NULL;
1515 }
1516 
1517 
1518 
1519 /*
1520 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
1521 */
1522 
1523 bool
1524 nisplus_getcanonname(name, hbsize, statp)
1525 	char *name;
1526 	int hbsize;
1527 	int *statp;
1528 {
1529 	char *vp;
1530 	auto int vsize;
1531 	int buflen;
1532 	char buf[MAXLINE + NIS_MAXNAMELEN];
1533 	nis_result *result;
1534 	char *p;
1535 	int len;
1536 
1537 	shorten_hostname(name);
1538 
1539 	p = strchr(name, '.');
1540 	if (p == NULL)
1541 	{
1542 		/* single token */
1543 		sprintf(buf, "[name=%s],hosts.org_dir", name);
1544 	}
1545 	else if (p[1] != '\0')
1546 	{
1547 		/* multi token -- take only first token in name buf */
1548 		*p = '\0';
1549 		sprintf(buf, "[name=%s],hosts.org_dir.%s", name, &p[1]);
1550 	}
1551 	else
1552 	{
1553 		*statp = EX_NOHOST;
1554 		return FALSE;
1555 	}
1556 
1557 	if (tTd(38, 20))
1558 		printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
1559 			 name, buf);
1560 
1561 	result = nis_list(buf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
1562 		NULL, NULL);
1563 
1564 	if (result->status == NIS_SUCCESS)
1565 	{
1566 		int count;
1567 		char *str;
1568 		char *domain;
1569 
1570 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1571 		{
1572 #ifdef LOG
1573 			if (LogLevel > 10)
1574 				syslog(LOG_WARNING,
1575 				       "nisplus_getcanonname: Lookup error, expected 1 entry, got (%d)",
1576 				       count);
1577 #endif
1578 
1579 			/* ignore second entry */
1580 			if (tTd(38, 20))
1581 				printf("nisplus_getcanoname(%s), got %d entries, addtional entries ignores\n", name);
1582 		}
1583 
1584 		if (tTd(38, 20))
1585 			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
1586 			       name, (NIS_RES_OBJECT(result))->zo_domain);
1587 
1588 
1589 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
1590 		vsize = strlen(vp);
1591 		if (tTd(38, 20))
1592 			printf("nisplus_getcanonname(%s), found %s\n",
1593 				name, vp);
1594 		domain = macvalue('m', CurEnv);
1595 		if (hbsize > (vsize + ((int) strlen(domain))))
1596 		{
1597 			sprintf(name, "%s.%s", vp, domain);
1598 			*statp = EX_OK;
1599 		}
1600 		else
1601 			*statp = EX_NOHOST;
1602 		nis_freeresult(result);
1603 		return TRUE;
1604 	}
1605 	else
1606 	{
1607 		if (result->status == NIS_NOTFOUND)
1608 			*statp = EX_NOHOST;
1609 		else if (result->status == NIS_TRYAGAIN)
1610 			*statp = EX_TEMPFAIL;
1611 		else
1612 		{
1613 			*statp = EX_UNAVAILABLE;
1614 		}
1615 	}
1616 	if (tTd(38, 20))
1617 		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1618 			name, result->status, *statp);
1619 	nis_freeresult(result);
1620 	return FALSE;
1621 }
1622 
1623 
1624 char *
1625 nisplus_default_domain()
1626 {
1627 	static char default_domain[MAXNAME + 1] = "";
1628 	char *p;
1629 
1630 	if (default_domain[0] != '\0')
1631 		return(default_domain);
1632 
1633 	p = nis_local_directory();
1634 	strcpy(default_domain, p);
1635 	return default_domain;
1636 }
1637 
1638 #endif /* NISPLUS */
1639 /*
1640 **  HESIOD Modules
1641 */
1642 
1643 #ifdef HESIOD
1644 
1645 #include <hesiod.h>
1646 
1647 char *
1648 hes_map_lookup(map, name, av, statp)
1649         MAP *map;
1650         char *name;
1651         char **av;
1652         int *statp;
1653 {
1654 	char **hp;
1655 	int i;
1656 
1657 	if (tTd(38, 20))
1658 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1659 
1660 	hp = hes_resolve(name, map->map_file);
1661 	if (hp == NULL || hp[0] == NULL)
1662 		return NULL;
1663 
1664 	if (bitset(MF_MATCHONLY, map->map_mflags))
1665 		return map_rewrite(map, name, strlen(name), NULL);
1666 	else
1667 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
1668 }
1669 
1670 #endif
1671 /*
1672 **  NeXT NETINFO Modules
1673 */
1674 
1675 #ifdef NETINFO
1676 
1677 #define NETINFO_DEFAULT_DIR		"/aliases"
1678 #define NETINFO_DEFAULT_PROPERTY	"members"
1679 
1680 
1681 /*
1682 **  NI_MAP_OPEN -- open NetInfo Aliases
1683 */
1684 
1685 bool
1686 ni_map_open(map, mode)
1687 	MAP *map;
1688 	int mode;
1689 {
1690 	char *p;
1691 
1692 	if (tTd(38, 20))
1693 		printf("ni_map_open: %s\n", map->map_file);
1694 
1695 	if (*map->map_file == '\0')
1696 		map->map_file = NETINFO_DEFAULT_DIR;
1697 
1698 	if (map->map_valcolnm == NULL)
1699 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1700 
1701 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1702 		map->map_coldelim = ',';
1703 
1704 	return TRUE;
1705 }
1706 
1707 
1708 /*
1709 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1710 */
1711 
1712 char *
1713 ni_map_lookup(map, name, av, statp)
1714 	MAP *map;
1715 	char *name;
1716 	char **av;
1717 	int *statp;
1718 {
1719 	char *res;
1720 	char *propval;
1721 	extern char *ni_propval();
1722 
1723 	if (tTd(38, 20))
1724 		printf("ni_map_lookup(%s, %s)\n",
1725 			map->map_mname, name);
1726 
1727 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1728 			     map->map_valcolnm, map->map_coldelim);
1729 
1730 	if (propval == NULL)
1731 		return NULL;
1732 
1733 	if (bitset(MF_MATCHONLY, map->map_mflags))
1734 		res = map_rewrite(map, name, strlen(name), NULL);
1735 	else
1736 		res = map_rewrite(map, propval, strlen(propval), av);
1737 	free(propval);
1738 	return res;
1739 }
1740 
1741 #endif
1742 /*
1743 **  TEXT (unindexed text file) Modules
1744 **
1745 **	This code donated by Sun Microsystems.
1746 */
1747 
1748 
1749 /*
1750 **  TEXT_MAP_OPEN -- open text table
1751 */
1752 
1753 bool
1754 text_map_open(map, mode)
1755 	MAP *map;
1756 	int mode;
1757 {
1758 	struct stat sbuf;
1759 
1760 	if (tTd(38, 2))
1761 		printf("text_map_open(%s, %s, %d)\n",
1762 			map->map_mname, map->map_file, mode);
1763 
1764 	if (mode != O_RDONLY)
1765 	{
1766 		errno = ENODEV;
1767 		return FALSE;
1768 	}
1769 
1770 	if (*map->map_file == '\0')
1771 	{
1772 		if (tTd(38, 2))
1773 			printf("text_map_open: file name required\n");
1774 		return FALSE;
1775 	}
1776 
1777 	if (map->map_file[0] != '/')
1778 	{
1779 		if (tTd(38, 2))
1780 			printf("text_map_open(%s): file name must be fully qualified\n",
1781 				map->map_file);
1782 		return FALSE;
1783 	}
1784 	/* check to see if this map actually accessable */
1785 	if (access(map->map_file, R_OK) <0)
1786 		return FALSE;
1787 
1788 	/* check to see if this map actually exist */
1789 	if (stat(map->map_file, &sbuf) <0)
1790 	{
1791 		if (tTd(38, 2))
1792 			printf("text_map_open(%s): can not stat %s\n",
1793 				map->map_file, map->map_file);
1794 		return FALSE;
1795 	}
1796 
1797 	if (!S_ISREG(sbuf.st_mode))
1798 	{
1799 		if (tTd(38, 2))
1800 			printf("text_map_open(%s): %s is not a file\n",
1801 				map->map_file, map->map_file);
1802 		return FALSE;
1803 	}
1804 
1805 	if (map->map_keycolnm == NULL)
1806 		map->map_keycolno = 0;
1807 	else
1808 	{
1809 		if (!isdigit(*map->map_keycolnm))
1810 		{
1811 			if (tTd(38, 2))
1812 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1813 					map->map_file, map->map_keycolnm);
1814 			return FALSE;
1815 		}
1816 		map->map_keycolno = atoi(map->map_keycolnm);
1817 	}
1818 
1819 	if (map->map_valcolnm == NULL)
1820 		map->map_valcolno = 0;
1821 	else
1822 	{
1823 		if (!isdigit(*map->map_valcolnm))
1824 		{
1825 			if (tTd(38, 2))
1826 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1827 					map->map_file, map->map_valcolnm);
1828 			return FALSE;
1829 		}
1830 		map->map_valcolno = atoi(map->map_valcolnm);
1831 	}
1832 
1833 	if (tTd(38, 2))
1834 	{
1835 		printf("text_map_open(%s): delimiter = ",
1836 			map->map_file);
1837 		if (map->map_coldelim == '\0')
1838 			printf("(white space)\n");
1839 		else
1840 			printf("%c\n", map->map_coldelim);
1841 	}
1842 
1843 	return TRUE;
1844 }
1845 
1846 
1847 /*
1848 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1849 */
1850 
1851 char *
1852 text_map_lookup(map, name, av, statp)
1853 	MAP *map;
1854 	char *name;
1855 	char **av;
1856 	int *statp;
1857 {
1858 	char *vp;
1859 	auto int vsize;
1860 	int buflen;
1861 	char search_key[MAXNAME + 1];
1862 	char linebuf[MAXLINE];
1863 	FILE *f;
1864 	char buf[MAXNAME + 1];
1865 	char delim;
1866 	int key_idx;
1867 	bool found_it;
1868 	extern char *get_column();
1869 
1870 
1871 	found_it = FALSE;
1872 	if (tTd(38, 20))
1873 		printf("text_map_lookup(%s)\n", name);
1874 
1875 	buflen = strlen(name);
1876 	if (buflen > sizeof search_key - 1)
1877 		buflen = sizeof search_key - 1;
1878 	bcopy(name, search_key, buflen + 1);
1879 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1880 		makelower(search_key);
1881 
1882 	f = fopen(map->map_file, "r");
1883 	if (f == NULL)
1884 	{
1885 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1886 		*statp = EX_UNAVAILABLE;
1887 		return NULL;
1888 	}
1889 	key_idx = map->map_keycolno;
1890 	delim = map->map_coldelim;
1891 	while (fgets(linebuf, MAXLINE, f))
1892 	{
1893 		char *lf;
1894 		if (linebuf[0] == '#')
1895 			continue; /* skip comment line */
1896 		if (lf = strchr(linebuf, '\n'))
1897 			*lf = '\0';
1898 		if (!strcasecmp(search_key,
1899 				get_column(linebuf, key_idx, delim, buf)))
1900 		{
1901 			found_it = TRUE;
1902 			break;
1903 		}
1904 	}
1905 	fclose(f);
1906 	if (!found_it)
1907 	{
1908 #ifdef MAP_EXIT_STAT
1909 		*statp = EX_NOTFOUND;
1910 #endif
1911 		return(NULL);
1912 	}
1913 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1914 	vsize = strlen(vp);
1915 #ifdef MAP_EXIT_STAT
1916 	*statp = EX_OK;
1917 #endif
1918 	if (bitset(MF_MATCHONLY, map->map_mflags))
1919 		return map_rewrite(map, name, strlen(name), NULL);
1920 	else
1921 		return map_rewrite(map, vp, vsize, av);
1922 }
1923 
1924 
1925 /*
1926 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
1927 */
1928 
1929 bool
1930 text_getcanonname(name, hbsize, statp)
1931 	char *name;
1932 	int hbsize;
1933 	int *statp;
1934 {
1935 	int buflen;
1936 	char delim;
1937 	int key_idx;
1938 	char *cname;
1939 	bool found;
1940 	char *domain;
1941 	FILE *f;
1942 	char linebuf[MAXLINE];
1943 	char cbuf[MAXNAME + 1];
1944 	char buf[MAXNAME + 1];
1945 	extern char *get_column();
1946 
1947 	shorten_hostname(name);
1948 
1949 	/* we only accept single token search key */
1950 	if (strchr(name, '.') != NULL)
1951 	{
1952 		*statp = EX_NOHOST;
1953 		return FALSE;
1954 	}
1955 
1956 	found = FALSE;
1957 
1958 	f = fopen(HostsFile, "r");
1959 	if (f == NULL)
1960 	{
1961 #ifdef MAP_EXIT_STAT
1962 		*statp = EX_UNAVAILABLE;
1963 #endif
1964 		return FALSE;
1965 	}
1966 	delim = '\t';
1967 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
1968 	{
1969 		char *p;
1970 
1971 		if (linebuf[0] == '#')
1972 			continue;
1973 		if ((p = strchr(linebuf, '\n')) != NULL)
1974 			*p = '\0';
1975 		cname = get_column(linebuf, 1, delim, cbuf);
1976 		if (cname != NULL && strcasecmp(name,  cname) == 0)
1977 		{
1978 			found = TRUE;
1979 			break;
1980 		}
1981 
1982 		key_idx = 2;
1983 		while ((p = get_column(linebuf, key_idx, delim, buf)) != NULL)
1984 		{
1985 			if (strcasecmp(name, p) == 0)
1986 			{
1987 				found = TRUE;
1988 				break;
1989 			}
1990 			key_idx++;
1991 		}
1992 	}
1993 	fclose(f);
1994 	if (!found)
1995 	{
1996 		*statp = EX_NOHOST;
1997 		return FALSE;
1998 	}
1999 
2000 	if (hbsize >= strlen(cname))
2001 	{
2002 		strcpy(name, cname);
2003 		*statp = EX_OK;
2004 		return TRUE;
2005 	}
2006 	*statp = EX_UNAVAILABLE;
2007 	return FALSE;
2008 }
2009 /*
2010 **  STAB (Symbol Table) Modules
2011 */
2012 
2013 
2014 /*
2015 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2016 */
2017 
2018 char *
2019 stab_map_lookup(map, name, av, pstat)
2020 	register MAP *map;
2021 	char *name;
2022 	char **av;
2023 	int *pstat;
2024 {
2025 	register STAB *s;
2026 
2027 	if (tTd(38, 20))
2028 		printf("stab_lookup(%s, %s)\n",
2029 			map->map_mname, name);
2030 
2031 	s = stab(name, ST_ALIAS, ST_FIND);
2032 	if (s != NULL)
2033 		return (s->s_alias);
2034 	return (NULL);
2035 }
2036 
2037 
2038 /*
2039 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2040 */
2041 
2042 void
2043 stab_map_store(map, lhs, rhs)
2044 	register MAP *map;
2045 	char *lhs;
2046 	char *rhs;
2047 {
2048 	register STAB *s;
2049 
2050 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2051 	s->s_alias = newstr(rhs);
2052 }
2053 
2054 
2055 /*
2056 **  STAB_MAP_OPEN -- initialize (reads data file)
2057 **
2058 **	This is a wierd case -- it is only intended as a fallback for
2059 **	aliases.  For this reason, opens for write (only during a
2060 **	"newaliases") always fails, and opens for read open the
2061 **	actual underlying text file instead of the database.
2062 */
2063 
2064 bool
2065 stab_map_open(map, mode)
2066 	register MAP *map;
2067 	int mode;
2068 {
2069 	FILE *af;
2070 	struct stat st;
2071 
2072 	if (tTd(38, 2))
2073 		printf("stab_map_open(%s, %s)\n",
2074 			map->map_mname, map->map_file);
2075 
2076 	if (mode != O_RDONLY)
2077 	{
2078 		errno = ENODEV;
2079 		return FALSE;
2080 	}
2081 
2082 	af = fopen(map->map_file, "r");
2083 	if (af == NULL)
2084 		return FALSE;
2085 	readaliases(map, af, FALSE, FALSE);
2086 
2087 	if (fstat(fileno(af), &st) >= 0)
2088 		map->map_mtime = st.st_mtime;
2089 	fclose(af);
2090 
2091 	return TRUE;
2092 }
2093 /*
2094 **  Implicit Modules
2095 **
2096 **	Tries several types.  For back compatibility of aliases.
2097 */
2098 
2099 
2100 /*
2101 **  IMPL_MAP_LOOKUP -- lookup in best open database
2102 */
2103 
2104 char *
2105 impl_map_lookup(map, name, av, pstat)
2106 	MAP *map;
2107 	char *name;
2108 	char **av;
2109 	int *pstat;
2110 {
2111 	if (tTd(38, 20))
2112 		printf("impl_map_lookup(%s, %s)\n",
2113 			map->map_mname, name);
2114 
2115 #ifdef NEWDB
2116 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2117 		return db_map_lookup(map, name, av, pstat);
2118 #endif
2119 #ifdef NDBM
2120 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2121 		return ndbm_map_lookup(map, name, av, pstat);
2122 #endif
2123 	return stab_map_lookup(map, name, av, pstat);
2124 }
2125 
2126 /*
2127 **  IMPL_MAP_STORE -- store in open databases
2128 */
2129 
2130 void
2131 impl_map_store(map, lhs, rhs)
2132 	MAP *map;
2133 	char *lhs;
2134 	char *rhs;
2135 {
2136 #ifdef NEWDB
2137 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2138 		db_map_store(map, lhs, rhs);
2139 #endif
2140 #ifdef NDBM
2141 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2142 		ndbm_map_store(map, lhs, rhs);
2143 #endif
2144 	stab_map_store(map, lhs, rhs);
2145 }
2146 
2147 /*
2148 **  IMPL_MAP_OPEN -- implicit database open
2149 */
2150 
2151 bool
2152 impl_map_open(map, mode)
2153 	MAP *map;
2154 	int mode;
2155 {
2156 	struct stat stb;
2157 
2158 	if (tTd(38, 2))
2159 		printf("impl_map_open(%s, %s, %d)\n",
2160 			map->map_mname, map->map_file, mode);
2161 
2162 	if (stat(map->map_file, &stb) < 0)
2163 	{
2164 		/* no alias file at all */
2165 		if (tTd(38, 3))
2166 			printf("no map file\n");
2167 		return FALSE;
2168 	}
2169 
2170 #ifdef NEWDB
2171 	map->map_mflags |= MF_IMPL_HASH;
2172 	if (hash_map_open(map, mode))
2173 	{
2174 #if defined(NDBM) && defined(NIS)
2175 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
2176 #endif
2177 			return TRUE;
2178 	}
2179 	else
2180 		map->map_mflags &= ~MF_IMPL_HASH;
2181 #endif
2182 #ifdef NDBM
2183 	map->map_mflags |= MF_IMPL_NDBM;
2184 	if (ndbm_map_open(map, mode))
2185 	{
2186 		return TRUE;
2187 	}
2188 	else
2189 		map->map_mflags &= ~MF_IMPL_NDBM;
2190 #endif
2191 
2192 #if defined(NEWDB) || defined(NDBM)
2193 	if (Verbose)
2194 		message("WARNING: cannot open alias database %s", map->map_file);
2195 #else
2196 	if (mode != O_RDONLY)
2197 		usrerr("Cannot rebuild aliases: no database format defined");
2198 #endif
2199 
2200 	return stab_map_open(map, mode);
2201 }
2202 
2203 
2204 /*
2205 **  IMPL_MAP_CLOSE -- close any open database(s)
2206 */
2207 
2208 void
2209 impl_map_close(map)
2210 	MAP *map;
2211 {
2212 	if (tTd(38, 20))
2213 		printf("impl_map_close(%s, %s, %x)\n",
2214 			map->map_mname, map->map_file, map->map_mflags);
2215 #ifdef NEWDB
2216 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2217 	{
2218 		db_map_close(map);
2219 		map->map_mflags &= ~MF_IMPL_HASH;
2220 	}
2221 #endif
2222 
2223 #ifdef NDBM
2224 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2225 	{
2226 		ndbm_map_close(map);
2227 		map->map_mflags &= ~MF_IMPL_NDBM;
2228 	}
2229 #endif
2230 }
2231 /*
2232 **  User map class.
2233 **
2234 **	Provides access to the system password file.
2235 */
2236 
2237 /*
2238 **  USER_MAP_OPEN -- open user map
2239 **
2240 **	Really just binds field names to field numbers.
2241 */
2242 
2243 bool
2244 user_map_open(map, mode)
2245 	MAP *map;
2246 	int mode;
2247 {
2248 	if (tTd(38, 2))
2249 		printf("user_map_open(%s)\n", map->map_mname);
2250 
2251 	if (mode != O_RDONLY)
2252 	{
2253 		/* issue a pseudo-error message */
2254 #ifdef ENOSYS
2255 		errno = ENOSYS;
2256 #else
2257 # ifdef EFTYPE
2258 		errno = EFTYPE;
2259 # else
2260 		errno = ENXIO;
2261 # endif
2262 #endif
2263 		return FALSE;
2264 	}
2265 	if (map->map_valcolnm == NULL)
2266 		/* nothing */ ;
2267 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2268 		map->map_valcolno = 1;
2269 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2270 		map->map_valcolno = 2;
2271 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2272 		map->map_valcolno = 3;
2273 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2274 		map->map_valcolno = 4;
2275 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2276 		map->map_valcolno = 5;
2277 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2278 		map->map_valcolno = 6;
2279 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2280 		map->map_valcolno = 7;
2281 	else
2282 	{
2283 		syserr("User map %s: unknown column name %s",
2284 			map->map_mname, map->map_valcolnm);
2285 		return FALSE;
2286 	}
2287 	return TRUE;
2288 }
2289 
2290 
2291 /*
2292 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2293 */
2294 
2295 char *
2296 user_map_lookup(map, key, av, statp)
2297 	MAP *map;
2298 	char *key;
2299 	char **av;
2300 	int *statp;
2301 {
2302 	struct passwd *pw;
2303 
2304 	if (tTd(38, 20))
2305 		printf("user_map_lookup(%s, %s)\n",
2306 			map->map_mname, key);
2307 
2308 	pw = sm_getpwnam(key);
2309 	if (pw == NULL)
2310 		return NULL;
2311 	if (bitset(MF_MATCHONLY, map->map_mflags))
2312 		return map_rewrite(map, key, strlen(key), NULL);
2313 	else
2314 	{
2315 		char *rwval = NULL;
2316 		char buf[30];
2317 
2318 		switch (map->map_valcolno)
2319 		{
2320 		  case 0:
2321 		  case 1:
2322 			rwval = pw->pw_name;
2323 			break;
2324 
2325 		  case 2:
2326 			rwval = pw->pw_passwd;
2327 			break;
2328 
2329 		  case 3:
2330 			sprintf(buf, "%d", pw->pw_uid);
2331 			rwval = buf;
2332 			break;
2333 
2334 		  case 4:
2335 			sprintf(buf, "%d", pw->pw_gid);
2336 			rwval = buf;
2337 			break;
2338 
2339 		  case 5:
2340 			rwval = pw->pw_gecos;
2341 			break;
2342 
2343 		  case 6:
2344 			rwval = pw->pw_dir;
2345 			break;
2346 
2347 		  case 7:
2348 			rwval = pw->pw_shell;
2349 			break;
2350 		}
2351 		return map_rewrite(map, rwval, strlen(rwval), av);
2352 	}
2353 }
2354 /*
2355 **  BESTMX -- find the best MX for a name
2356 **
2357 **	This is really a hack, but I don't see any obvious way
2358 **	to generalize it at the moment.
2359 */
2360 
2361 #if NAMED_BIND
2362 
2363 char *
2364 bestmx_map_lookup(map, name, av, statp)
2365 	MAP *map;
2366 	char *name;
2367 	char **av;
2368 	int *statp;
2369 {
2370         int nmx;
2371         auto int rcode;
2372         char *mxhosts[MAXMXHOSTS + 1];
2373 
2374 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2375 	if (nmx <= 0)
2376 		return NULL;
2377 	if (bitset(MF_MATCHONLY, map->map_mflags))
2378 		return map_rewrite(map, name, strlen(name), NULL);
2379 	else
2380 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2381 }
2382 
2383 #endif
2384 /*
2385 **  Program map type.
2386 **
2387 **	This provides access to arbitrary programs.  It should be used
2388 **	only very sparingly, since there is no way to bound the cost
2389 **	of invoking an arbitrary program.
2390 */
2391 
2392 char *
2393 prog_map_lookup(map, name, av, statp)
2394 	MAP *map;
2395 	char *name;
2396 	char **av;
2397 	int *statp;
2398 {
2399 	int i;
2400 	register char *p;
2401 	int fd;
2402 	auto pid_t pid;
2403 	char *argv[MAXPV + 1];
2404 	char buf[MAXLINE];
2405 
2406 	if (tTd(38, 20))
2407 		printf("prog_map_lookup(%s, %s) %s\n",
2408 			map->map_mname, name, map->map_file);
2409 
2410 	i = 0;
2411 	argv[i++] = map->map_file;
2412 	strcpy(buf, map->map_rebuild);
2413 	for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
2414 	{
2415 		if (i >= MAXPV - 1)
2416 			break;
2417 		argv[i++] = p;
2418 	}
2419 	argv[i++] = name;
2420 	argv[i] = NULL;
2421 	pid = prog_open(argv, &fd, CurEnv);
2422 	if (pid < 0)
2423 	{
2424 		if (tTd(38, 9))
2425 			printf("prog_map_lookup(%s) failed (%s) -- closing",
2426 				map->map_mname, errstring(errno));
2427 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
2428 		return NULL;
2429 	}
2430 	i = read(fd, buf, sizeof buf - 1);
2431 	if (i <= 0 && tTd(38, 2))
2432 		printf("prog_map_lookup(%s): read error %s\n",
2433 			map->map_mname, errstring(errno));
2434 	if (i > 0)
2435 	{
2436 		char *rval;
2437 
2438 		buf[i] = '\0';
2439 		p = strchr(buf, '\n');
2440 		if (p != NULL)
2441 			*p = '\0';
2442 
2443 		/* collect the return value */
2444 		if (bitset(MF_MATCHONLY, map->map_mflags))
2445 			rval = map_rewrite(map, name, strlen(name), NULL);
2446 		else
2447 			rval = map_rewrite(map, buf, strlen(buf), NULL);
2448 
2449 		/* now flush any additional output */
2450 		while ((i = read(fd, buf, sizeof buf)) > 0)
2451 			continue;
2452 		close(fd);
2453 
2454 		/* and wait for the process to terminate */
2455 		*statp = waitfor(pid);
2456 
2457 		return rval;
2458 	}
2459 
2460 	close(fd);
2461 	*statp = waitfor(pid);
2462 	return NULL;
2463 }
2464 /*
2465 **  Sequenced map type.
2466 **
2467 **	Tries each map in order until something matches, much like
2468 **	implicit.  Stores go to the first map in the list that can
2469 **	support storing.
2470 **
2471 **	This is slightly unusual in that there are two interfaces.
2472 **	The "sequence" interface lets you stack maps arbitrarily.
2473 **	The "switch" interface builds a sequence map by looking
2474 **	at a system-dependent configuration file such as
2475 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2476 **
2477 **	We don't need an explicit open, since all maps are
2478 **	opened during startup, including underlying maps.
2479 */
2480 
2481 /*
2482 **  SEQ_MAP_PARSE -- Sequenced map parsing
2483 */
2484 
2485 bool
2486 seq_map_parse(map, ap)
2487 	MAP *map;
2488 	char *ap;
2489 {
2490 	int maxmap;
2491 
2492 	if (tTd(38, 2))
2493 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2494 	maxmap = 0;
2495 	while (*ap != '\0')
2496 	{
2497 		register char *p;
2498 		STAB *s;
2499 
2500 		/* find beginning of map name */
2501 		while (isascii(*ap) && isspace(*ap))
2502 			ap++;
2503 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2504 			continue;
2505 		if (*p != '\0')
2506 			*p++ = '\0';
2507 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2508 			p++;
2509 		if (*ap == '\0')
2510 		{
2511 			ap = p;
2512 			continue;
2513 		}
2514 		s = stab(ap, ST_MAP, ST_FIND);
2515 		if (s == NULL)
2516 		{
2517 			syserr("Sequence map %s: unknown member map %s",
2518 				map->map_mname, ap);
2519 		}
2520 		else if (maxmap == MAXMAPSTACK)
2521 		{
2522 			syserr("Sequence map %s: too many member maps (%d max)",
2523 				map->map_mname, MAXMAPSTACK);
2524 			maxmap++;
2525 		}
2526 		else if (maxmap < MAXMAPSTACK)
2527 		{
2528 			map->map_stack[maxmap++] = &s->s_map;
2529 		}
2530 		ap = p;
2531 	}
2532 	return TRUE;
2533 }
2534 
2535 
2536 /*
2537 **  SWITCH_MAP_OPEN -- open a switched map
2538 **
2539 **	This looks at the system-dependent configuration and builds
2540 **	a sequence map that does the same thing.
2541 **
2542 **	Every system must define a switch_map_find routine in conf.c
2543 **	that will return the list of service types associated with a
2544 **	given service class.
2545 */
2546 
2547 bool
2548 switch_map_open(map, mode)
2549 	MAP *map;
2550 	int mode;
2551 {
2552 	int mapno;
2553 	int nmaps;
2554 	char *maptype[MAXMAPSTACK];
2555 
2556 	if (tTd(38, 2))
2557 		printf("switch_map_open(%s, %s, %d)\n",
2558 			map->map_mname, map->map_file, mode);
2559 
2560 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2561 	if (tTd(38, 19))
2562 	{
2563 		printf("\tswitch_map_find => %d\n", nmaps);
2564 		for (mapno = 0; mapno < nmaps; mapno++)
2565 			printf("\t\t%s\n", maptype[mapno]);
2566 	}
2567 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2568 		return FALSE;
2569 
2570 	for (mapno = 0; mapno < nmaps; mapno++)
2571 	{
2572 		register STAB *s;
2573 		char nbuf[MAXNAME + 1];
2574 
2575 		if (maptype[mapno] == NULL)
2576 			continue;
2577 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2578 		s = stab(nbuf, ST_MAP, ST_FIND);
2579 		if (s == NULL)
2580 		{
2581 			syserr("Switch map %s: unknown member map %s",
2582 				map->map_mname, nbuf);
2583 		}
2584 		else
2585 		{
2586 			map->map_stack[mapno] = &s->s_map;
2587 			if (tTd(38, 4))
2588 				printf("\tmap_stack[%d] = %s:%s\n",
2589 					mapno, s->s_map.map_class->map_cname,
2590 					nbuf);
2591 		}
2592 	}
2593 	return TRUE;
2594 }
2595 
2596 
2597 /*
2598 **  SEQ_MAP_CLOSE -- close all underlying maps
2599 */
2600 
2601 seq_map_close(map)
2602 	MAP *map;
2603 {
2604 	int mapno;
2605 
2606 	if (tTd(38, 20))
2607 		printf("seq_map_close(%s)\n", map->map_mname);
2608 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2609 	{
2610 		MAP *mm = map->map_stack[mapno];
2611 
2612 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2613 			continue;
2614 		mm->map_class->map_close(mm);
2615 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2616 	}
2617 }
2618 
2619 
2620 /*
2621 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2622 */
2623 
2624 char *
2625 seq_map_lookup(map, key, args, pstat)
2626 	MAP *map;
2627 	char *key;
2628 	char **args;
2629 	int *pstat;
2630 {
2631 	int mapno;
2632 	int mapbit = 0x01;
2633 
2634 	if (tTd(38, 20))
2635 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2636 
2637 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2638 	{
2639 		MAP *mm = map->map_stack[mapno];
2640 		int stat = 0;
2641 		char *rv;
2642 
2643 		if (mm == NULL)
2644 			continue;
2645 		if (!bitset(MF_OPEN, mm->map_mflags))
2646 		{
2647 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2648 			{
2649 				*pstat = EX_UNAVAILABLE;
2650 				return NULL;
2651 			}
2652 			continue;
2653 		}
2654 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2655 		if (rv != NULL)
2656 			return rv;
2657 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2658 			return NULL;
2659 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2660 		{
2661 			*pstat = stat;
2662 			return NULL;
2663 		}
2664 	}
2665 	return NULL;
2666 }
2667 
2668 
2669 /*
2670 **  SEQ_MAP_STORE -- sequenced map store
2671 */
2672 
2673 void
2674 seq_map_store(map, key, val)
2675 	MAP *map;
2676 	char *key;
2677 	char *val;
2678 {
2679 	int mapno;
2680 
2681 	if (tTd(38, 12))
2682 		printf("seq_map_store(%s, %s, %s)\n",
2683 			map->map_mname, key, val);
2684 
2685 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2686 	{
2687 		MAP *mm = map->map_stack[mapno];
2688 
2689 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2690 			continue;
2691 
2692 		mm->map_class->map_store(mm, key, val);
2693 		return;
2694 	}
2695 	syserr("seq_map_store(%s, %s, %s): no writable map",
2696 		map->map_mname, key, val);
2697 }
2698 /*
2699 **  GETCANONNAME -- look up name using service switch
2700 **
2701 **	Parameters:
2702 **		host -- the host name to look up.
2703 **		hbsize -- the size of the host buffer.
2704 **		trymx -- if set, try MX records.
2705 **
2706 **	Returns:
2707 **		TRUE -- if the host was found.
2708 **		FALSE -- otherwise.
2709 */
2710 
2711 bool
2712 getcanonname(host, hbsize, trymx)
2713 	char *host;
2714 	int hbsize;
2715 	bool trymx;
2716 {
2717 	int nmaps;
2718 	int mapno;
2719 	bool found = FALSE;
2720 	auto int stat;
2721 	char *maptype[MAXMAPSTACK];
2722 	short mapreturn[MAXMAPACTIONS];
2723 	extern int h_errno;
2724 
2725 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2726 	for (mapno = 0; mapno < nmaps; mapno++)
2727 	{
2728 		int i;
2729 
2730 		if (tTd(38, 20))
2731 			printf("getcanonname(%s), trying %s\n",
2732 				host, maptype[mapno]);
2733 		if (strcmp("files", maptype[mapno]) == 0)
2734 			found = text_getcanonname(host, hbsize, &stat);
2735 #ifdef NIS
2736 		else if (strcmp("nis", maptype[mapno]) == 0)
2737 			found = nis_getcanonname(host, hbsize, &stat);
2738 #endif
2739 #ifdef NISPLUS
2740 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2741 			found = nisplus_getcanonname(host, hbsize, &stat);
2742 #endif
2743 #if NAMED_BIND
2744 		else if (strcmp("dns", maptype[mapno]) == 0)
2745 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2746 #endif
2747 		else
2748 		{
2749 			found = FALSE;
2750 			stat = EX_UNAVAILABLE;
2751 		}
2752 		if (found)
2753 			break;
2754 
2755 		/* see if we should continue */
2756 		if (stat == EX_TEMPFAIL)
2757 			i = MA_TRYAGAIN;
2758 		else if (stat == EX_NOHOST)
2759 			i = MA_NOTFOUND;
2760 		else
2761 			i = MA_UNAVAIL;
2762 		if (bitset(1 << mapno, mapreturn[i]))
2763 			break;
2764 	}
2765 
2766 	if (found)
2767 	{
2768 		char *d;
2769 
2770 		if (tTd(38, 20))
2771 			printf("getcanonname(%s), found\n", host);
2772 
2773 		/*
2774 		**  If returned name is still single token, compensate
2775 		**  by tagging on $m.  This is because some sites set
2776 		**  up their DNS or NIS databases wrong.
2777 		*/
2778 
2779 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2780 		{
2781 			d = macvalue('m', CurEnv);
2782 			if (d != NULL &&
2783 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2784 			{
2785 				if (host[strlen(host) - 1] != '.')
2786 					strcat(host, ".");
2787 				strcat(host, d);
2788 			}
2789 			else
2790 			{
2791 				return FALSE;
2792 			}
2793 		}
2794 		return TRUE;
2795 	}
2796 
2797 	if (tTd(38, 20))
2798 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2799 
2800 #if NAMED_BIND
2801 	if (stat == EX_NOHOST)
2802 		h_errno = HOST_NOT_FOUND;
2803 	else
2804 		h_errno = TRY_AGAIN;
2805 #endif
2806 
2807 	return FALSE;
2808 }
2809 /*
2810 **  NULL stubs
2811 */
2812 
2813 bool
2814 null_map_open(map, mode)
2815 	MAP *map;
2816 	int mode;
2817 {
2818 	return TRUE;
2819 }
2820 
2821 void
2822 null_map_close(map)
2823 	MAP *map;
2824 {
2825 	return;
2826 }
2827 
2828 void
2829 null_map_store(map, key, val)
2830 	MAP *map;
2831 	char *key;
2832 	char *val;
2833 {
2834 	return;
2835 }
2836 
2837 
2838 /*
2839 **  BOGUS stubs
2840 */
2841 
2842 char *
2843 bogus_map_lookup(map, key, args, pstat)
2844 	MAP *map;
2845 	char *key;
2846 	char **args;
2847 	int *pstat;
2848 {
2849 	*pstat = EX_TEMPFAIL;
2850 	return NULL;
2851 }
2852 
2853 MAPCLASS	BogusMapClass =
2854 {
2855 	"bogus-map",		NULL,		0,
2856 	NULL,		bogus_map_lookup,	null_map_store,
2857 	null_map_open,	null_map_close,
2858 };
2859