xref: /csrg-svn/usr.sbin/sendmail/src/map.c (revision 69636)
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.64 (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 (domain == NULL)
1596 			domain = "";
1597 		if (hbsize > vsize + (int) strlen(domain) + 1)
1598 		{
1599 			if (domain[0] == '\0')
1600 				strcpy(name, vp);
1601 			else
1602 				sprintf(name, "%s.%s", vp, domain);
1603 			*statp = EX_OK;
1604 		}
1605 		else
1606 			*statp = EX_NOHOST;
1607 		nis_freeresult(result);
1608 		return TRUE;
1609 	}
1610 	else
1611 	{
1612 		if (result->status == NIS_NOTFOUND)
1613 			*statp = EX_NOHOST;
1614 		else if (result->status == NIS_TRYAGAIN)
1615 			*statp = EX_TEMPFAIL;
1616 		else
1617 			*statp = EX_UNAVAILABLE;
1618 	}
1619 	if (tTd(38, 20))
1620 		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1621 			name, result->status, *statp);
1622 	nis_freeresult(result);
1623 	return FALSE;
1624 }
1625 
1626 
1627 char *
1628 nisplus_default_domain()
1629 {
1630 	static char default_domain[MAXNAME + 1] = "";
1631 	char *p;
1632 
1633 	if (default_domain[0] != '\0')
1634 		return(default_domain);
1635 
1636 	p = nis_local_directory();
1637 	strcpy(default_domain, p);
1638 	return default_domain;
1639 }
1640 
1641 #endif /* NISPLUS */
1642 /*
1643 **  HESIOD Modules
1644 */
1645 
1646 #ifdef HESIOD
1647 
1648 #include <hesiod.h>
1649 
1650 char *
1651 hes_map_lookup(map, name, av, statp)
1652         MAP *map;
1653         char *name;
1654         char **av;
1655         int *statp;
1656 {
1657 	char **hp;
1658 	int i;
1659 
1660 	if (tTd(38, 20))
1661 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1662 
1663 	hp = hes_resolve(name, map->map_file);
1664 	if (hp == NULL || hp[0] == NULL)
1665 		return NULL;
1666 
1667 	if (bitset(MF_MATCHONLY, map->map_mflags))
1668 		return map_rewrite(map, name, strlen(name), NULL);
1669 	else
1670 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
1671 }
1672 
1673 #endif
1674 /*
1675 **  NeXT NETINFO Modules
1676 */
1677 
1678 #ifdef NETINFO
1679 
1680 #define NETINFO_DEFAULT_DIR		"/aliases"
1681 #define NETINFO_DEFAULT_PROPERTY	"members"
1682 
1683 
1684 /*
1685 **  NI_MAP_OPEN -- open NetInfo Aliases
1686 */
1687 
1688 bool
1689 ni_map_open(map, mode)
1690 	MAP *map;
1691 	int mode;
1692 {
1693 	char *p;
1694 
1695 	if (tTd(38, 20))
1696 		printf("ni_map_open: %s\n", map->map_file);
1697 
1698 	if (*map->map_file == '\0')
1699 		map->map_file = NETINFO_DEFAULT_DIR;
1700 
1701 	if (map->map_valcolnm == NULL)
1702 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1703 
1704 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1705 		map->map_coldelim = ',';
1706 
1707 	return TRUE;
1708 }
1709 
1710 
1711 /*
1712 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1713 */
1714 
1715 char *
1716 ni_map_lookup(map, name, av, statp)
1717 	MAP *map;
1718 	char *name;
1719 	char **av;
1720 	int *statp;
1721 {
1722 	char *res;
1723 	char *propval;
1724 	extern char *ni_propval();
1725 
1726 	if (tTd(38, 20))
1727 		printf("ni_map_lookup(%s, %s)\n",
1728 			map->map_mname, name);
1729 
1730 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1731 			     map->map_valcolnm, map->map_coldelim);
1732 
1733 	if (propval == NULL)
1734 		return NULL;
1735 
1736 	if (bitset(MF_MATCHONLY, map->map_mflags))
1737 		res = map_rewrite(map, name, strlen(name), NULL);
1738 	else
1739 		res = map_rewrite(map, propval, strlen(propval), av);
1740 	free(propval);
1741 	return res;
1742 }
1743 
1744 #endif
1745 /*
1746 **  TEXT (unindexed text file) Modules
1747 **
1748 **	This code donated by Sun Microsystems.
1749 */
1750 
1751 
1752 /*
1753 **  TEXT_MAP_OPEN -- open text table
1754 */
1755 
1756 bool
1757 text_map_open(map, mode)
1758 	MAP *map;
1759 	int mode;
1760 {
1761 	struct stat sbuf;
1762 
1763 	if (tTd(38, 2))
1764 		printf("text_map_open(%s, %s, %d)\n",
1765 			map->map_mname, map->map_file, mode);
1766 
1767 	if (mode != O_RDONLY)
1768 	{
1769 		errno = ENODEV;
1770 		return FALSE;
1771 	}
1772 
1773 	if (*map->map_file == '\0')
1774 	{
1775 		if (tTd(38, 2))
1776 			printf("text_map_open: file name required\n");
1777 		return FALSE;
1778 	}
1779 
1780 	if (map->map_file[0] != '/')
1781 	{
1782 		if (tTd(38, 2))
1783 			printf("text_map_open(%s): file name must be fully qualified\n",
1784 				map->map_file);
1785 		return FALSE;
1786 	}
1787 	/* check to see if this map actually accessable */
1788 	if (access(map->map_file, R_OK) <0)
1789 		return FALSE;
1790 
1791 	/* check to see if this map actually exist */
1792 	if (stat(map->map_file, &sbuf) <0)
1793 	{
1794 		if (tTd(38, 2))
1795 			printf("text_map_open(%s): can not stat %s\n",
1796 				map->map_file, map->map_file);
1797 		return FALSE;
1798 	}
1799 
1800 	if (!S_ISREG(sbuf.st_mode))
1801 	{
1802 		if (tTd(38, 2))
1803 			printf("text_map_open(%s): %s is not a file\n",
1804 				map->map_file, map->map_file);
1805 		return FALSE;
1806 	}
1807 
1808 	if (map->map_keycolnm == NULL)
1809 		map->map_keycolno = 0;
1810 	else
1811 	{
1812 		if (!isdigit(*map->map_keycolnm))
1813 		{
1814 			if (tTd(38, 2))
1815 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1816 					map->map_file, map->map_keycolnm);
1817 			return FALSE;
1818 		}
1819 		map->map_keycolno = atoi(map->map_keycolnm);
1820 	}
1821 
1822 	if (map->map_valcolnm == NULL)
1823 		map->map_valcolno = 0;
1824 	else
1825 	{
1826 		if (!isdigit(*map->map_valcolnm))
1827 		{
1828 			if (tTd(38, 2))
1829 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1830 					map->map_file, map->map_valcolnm);
1831 			return FALSE;
1832 		}
1833 		map->map_valcolno = atoi(map->map_valcolnm);
1834 	}
1835 
1836 	if (tTd(38, 2))
1837 	{
1838 		printf("text_map_open(%s): delimiter = ",
1839 			map->map_file);
1840 		if (map->map_coldelim == '\0')
1841 			printf("(white space)\n");
1842 		else
1843 			printf("%c\n", map->map_coldelim);
1844 	}
1845 
1846 	return TRUE;
1847 }
1848 
1849 
1850 /*
1851 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1852 */
1853 
1854 char *
1855 text_map_lookup(map, name, av, statp)
1856 	MAP *map;
1857 	char *name;
1858 	char **av;
1859 	int *statp;
1860 {
1861 	char *vp;
1862 	auto int vsize;
1863 	int buflen;
1864 	char search_key[MAXNAME + 1];
1865 	char linebuf[MAXLINE];
1866 	FILE *f;
1867 	char buf[MAXNAME + 1];
1868 	char delim;
1869 	int key_idx;
1870 	bool found_it;
1871 	extern char *get_column();
1872 
1873 
1874 	found_it = FALSE;
1875 	if (tTd(38, 20))
1876 		printf("text_map_lookup(%s)\n", name);
1877 
1878 	buflen = strlen(name);
1879 	if (buflen > sizeof search_key - 1)
1880 		buflen = sizeof search_key - 1;
1881 	bcopy(name, search_key, buflen + 1);
1882 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1883 		makelower(search_key);
1884 
1885 	f = fopen(map->map_file, "r");
1886 	if (f == NULL)
1887 	{
1888 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1889 		*statp = EX_UNAVAILABLE;
1890 		return NULL;
1891 	}
1892 	key_idx = map->map_keycolno;
1893 	delim = map->map_coldelim;
1894 	while (fgets(linebuf, MAXLINE, f))
1895 	{
1896 		char *lf;
1897 		if (linebuf[0] == '#')
1898 			continue; /* skip comment line */
1899 		if (lf = strchr(linebuf, '\n'))
1900 			*lf = '\0';
1901 		if (!strcasecmp(search_key,
1902 				get_column(linebuf, key_idx, delim, buf)))
1903 		{
1904 			found_it = TRUE;
1905 			break;
1906 		}
1907 	}
1908 	fclose(f);
1909 	if (!found_it)
1910 	{
1911 #ifdef MAP_EXIT_STAT
1912 		*statp = EX_NOTFOUND;
1913 #endif
1914 		return(NULL);
1915 	}
1916 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1917 	vsize = strlen(vp);
1918 #ifdef MAP_EXIT_STAT
1919 	*statp = EX_OK;
1920 #endif
1921 	if (bitset(MF_MATCHONLY, map->map_mflags))
1922 		return map_rewrite(map, name, strlen(name), NULL);
1923 	else
1924 		return map_rewrite(map, vp, vsize, av);
1925 }
1926 
1927 
1928 /*
1929 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
1930 */
1931 
1932 bool
1933 text_getcanonname(name, hbsize, statp)
1934 	char *name;
1935 	int hbsize;
1936 	int *statp;
1937 {
1938 	int buflen;
1939 	char delim;
1940 	int key_idx;
1941 	char *cname;
1942 	bool found;
1943 	char *domain;
1944 	FILE *f;
1945 	char linebuf[MAXLINE];
1946 	char cbuf[MAXNAME + 1];
1947 	char buf[MAXNAME + 1];
1948 	extern char *get_column();
1949 
1950 	shorten_hostname(name);
1951 
1952 	/* we only accept single token search key */
1953 	if (strchr(name, '.') != NULL)
1954 	{
1955 		*statp = EX_NOHOST;
1956 		return FALSE;
1957 	}
1958 
1959 	found = FALSE;
1960 
1961 	f = fopen(HostsFile, "r");
1962 	if (f == NULL)
1963 	{
1964 #ifdef MAP_EXIT_STAT
1965 		*statp = EX_UNAVAILABLE;
1966 #endif
1967 		return FALSE;
1968 	}
1969 	delim = '\t';
1970 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
1971 	{
1972 		char *p;
1973 
1974 		if (linebuf[0] == '#')
1975 			continue;
1976 		if ((p = strchr(linebuf, '\n')) != NULL)
1977 			*p = '\0';
1978 		cname = get_column(linebuf, 1, delim, cbuf);
1979 		if (cname != NULL && strcasecmp(name,  cname) == 0)
1980 		{
1981 			found = TRUE;
1982 			break;
1983 		}
1984 
1985 		key_idx = 2;
1986 		while ((p = get_column(linebuf, key_idx, delim, buf)) != NULL)
1987 		{
1988 			if (strcasecmp(name, p) == 0)
1989 			{
1990 				found = TRUE;
1991 				break;
1992 			}
1993 			key_idx++;
1994 		}
1995 	}
1996 	fclose(f);
1997 	if (!found)
1998 	{
1999 		*statp = EX_NOHOST;
2000 		return FALSE;
2001 	}
2002 
2003 	if (hbsize >= strlen(cname))
2004 	{
2005 		strcpy(name, cname);
2006 		*statp = EX_OK;
2007 		return TRUE;
2008 	}
2009 	*statp = EX_UNAVAILABLE;
2010 	return FALSE;
2011 }
2012 /*
2013 **  STAB (Symbol Table) Modules
2014 */
2015 
2016 
2017 /*
2018 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2019 */
2020 
2021 char *
2022 stab_map_lookup(map, name, av, pstat)
2023 	register MAP *map;
2024 	char *name;
2025 	char **av;
2026 	int *pstat;
2027 {
2028 	register STAB *s;
2029 
2030 	if (tTd(38, 20))
2031 		printf("stab_lookup(%s, %s)\n",
2032 			map->map_mname, name);
2033 
2034 	s = stab(name, ST_ALIAS, ST_FIND);
2035 	if (s != NULL)
2036 		return (s->s_alias);
2037 	return (NULL);
2038 }
2039 
2040 
2041 /*
2042 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2043 */
2044 
2045 void
2046 stab_map_store(map, lhs, rhs)
2047 	register MAP *map;
2048 	char *lhs;
2049 	char *rhs;
2050 {
2051 	register STAB *s;
2052 
2053 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2054 	s->s_alias = newstr(rhs);
2055 }
2056 
2057 
2058 /*
2059 **  STAB_MAP_OPEN -- initialize (reads data file)
2060 **
2061 **	This is a wierd case -- it is only intended as a fallback for
2062 **	aliases.  For this reason, opens for write (only during a
2063 **	"newaliases") always fails, and opens for read open the
2064 **	actual underlying text file instead of the database.
2065 */
2066 
2067 bool
2068 stab_map_open(map, mode)
2069 	register MAP *map;
2070 	int mode;
2071 {
2072 	FILE *af;
2073 	struct stat st;
2074 
2075 	if (tTd(38, 2))
2076 		printf("stab_map_open(%s, %s)\n",
2077 			map->map_mname, map->map_file);
2078 
2079 	if (mode != O_RDONLY)
2080 	{
2081 		errno = ENODEV;
2082 		return FALSE;
2083 	}
2084 
2085 	af = fopen(map->map_file, "r");
2086 	if (af == NULL)
2087 		return FALSE;
2088 	readaliases(map, af, FALSE, FALSE);
2089 
2090 	if (fstat(fileno(af), &st) >= 0)
2091 		map->map_mtime = st.st_mtime;
2092 	fclose(af);
2093 
2094 	return TRUE;
2095 }
2096 /*
2097 **  Implicit Modules
2098 **
2099 **	Tries several types.  For back compatibility of aliases.
2100 */
2101 
2102 
2103 /*
2104 **  IMPL_MAP_LOOKUP -- lookup in best open database
2105 */
2106 
2107 char *
2108 impl_map_lookup(map, name, av, pstat)
2109 	MAP *map;
2110 	char *name;
2111 	char **av;
2112 	int *pstat;
2113 {
2114 	if (tTd(38, 20))
2115 		printf("impl_map_lookup(%s, %s)\n",
2116 			map->map_mname, name);
2117 
2118 #ifdef NEWDB
2119 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2120 		return db_map_lookup(map, name, av, pstat);
2121 #endif
2122 #ifdef NDBM
2123 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2124 		return ndbm_map_lookup(map, name, av, pstat);
2125 #endif
2126 	return stab_map_lookup(map, name, av, pstat);
2127 }
2128 
2129 /*
2130 **  IMPL_MAP_STORE -- store in open databases
2131 */
2132 
2133 void
2134 impl_map_store(map, lhs, rhs)
2135 	MAP *map;
2136 	char *lhs;
2137 	char *rhs;
2138 {
2139 #ifdef NEWDB
2140 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2141 		db_map_store(map, lhs, rhs);
2142 #endif
2143 #ifdef NDBM
2144 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2145 		ndbm_map_store(map, lhs, rhs);
2146 #endif
2147 	stab_map_store(map, lhs, rhs);
2148 }
2149 
2150 /*
2151 **  IMPL_MAP_OPEN -- implicit database open
2152 */
2153 
2154 bool
2155 impl_map_open(map, mode)
2156 	MAP *map;
2157 	int mode;
2158 {
2159 	struct stat stb;
2160 
2161 	if (tTd(38, 2))
2162 		printf("impl_map_open(%s, %s, %d)\n",
2163 			map->map_mname, map->map_file, mode);
2164 
2165 	if (stat(map->map_file, &stb) < 0)
2166 	{
2167 		/* no alias file at all */
2168 		if (tTd(38, 3))
2169 			printf("no map file\n");
2170 		return FALSE;
2171 	}
2172 
2173 #ifdef NEWDB
2174 	map->map_mflags |= MF_IMPL_HASH;
2175 	if (hash_map_open(map, mode))
2176 	{
2177 #if defined(NDBM) && defined(NIS)
2178 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
2179 #endif
2180 			return TRUE;
2181 	}
2182 	else
2183 		map->map_mflags &= ~MF_IMPL_HASH;
2184 #endif
2185 #ifdef NDBM
2186 	map->map_mflags |= MF_IMPL_NDBM;
2187 	if (ndbm_map_open(map, mode))
2188 	{
2189 		return TRUE;
2190 	}
2191 	else
2192 		map->map_mflags &= ~MF_IMPL_NDBM;
2193 #endif
2194 
2195 #if defined(NEWDB) || defined(NDBM)
2196 	if (Verbose)
2197 		message("WARNING: cannot open alias database %s", map->map_file);
2198 #else
2199 	if (mode != O_RDONLY)
2200 		usrerr("Cannot rebuild aliases: no database format defined");
2201 #endif
2202 
2203 	return stab_map_open(map, mode);
2204 }
2205 
2206 
2207 /*
2208 **  IMPL_MAP_CLOSE -- close any open database(s)
2209 */
2210 
2211 void
2212 impl_map_close(map)
2213 	MAP *map;
2214 {
2215 	if (tTd(38, 20))
2216 		printf("impl_map_close(%s, %s, %x)\n",
2217 			map->map_mname, map->map_file, map->map_mflags);
2218 #ifdef NEWDB
2219 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2220 	{
2221 		db_map_close(map);
2222 		map->map_mflags &= ~MF_IMPL_HASH;
2223 	}
2224 #endif
2225 
2226 #ifdef NDBM
2227 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2228 	{
2229 		ndbm_map_close(map);
2230 		map->map_mflags &= ~MF_IMPL_NDBM;
2231 	}
2232 #endif
2233 }
2234 /*
2235 **  User map class.
2236 **
2237 **	Provides access to the system password file.
2238 */
2239 
2240 /*
2241 **  USER_MAP_OPEN -- open user map
2242 **
2243 **	Really just binds field names to field numbers.
2244 */
2245 
2246 bool
2247 user_map_open(map, mode)
2248 	MAP *map;
2249 	int mode;
2250 {
2251 	if (tTd(38, 2))
2252 		printf("user_map_open(%s)\n", map->map_mname);
2253 
2254 	if (mode != O_RDONLY)
2255 	{
2256 		/* issue a pseudo-error message */
2257 #ifdef ENOSYS
2258 		errno = ENOSYS;
2259 #else
2260 # ifdef EFTYPE
2261 		errno = EFTYPE;
2262 # else
2263 		errno = ENXIO;
2264 # endif
2265 #endif
2266 		return FALSE;
2267 	}
2268 	if (map->map_valcolnm == NULL)
2269 		/* nothing */ ;
2270 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2271 		map->map_valcolno = 1;
2272 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2273 		map->map_valcolno = 2;
2274 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2275 		map->map_valcolno = 3;
2276 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2277 		map->map_valcolno = 4;
2278 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2279 		map->map_valcolno = 5;
2280 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2281 		map->map_valcolno = 6;
2282 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2283 		map->map_valcolno = 7;
2284 	else
2285 	{
2286 		syserr("User map %s: unknown column name %s",
2287 			map->map_mname, map->map_valcolnm);
2288 		return FALSE;
2289 	}
2290 	return TRUE;
2291 }
2292 
2293 
2294 /*
2295 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2296 */
2297 
2298 char *
2299 user_map_lookup(map, key, av, statp)
2300 	MAP *map;
2301 	char *key;
2302 	char **av;
2303 	int *statp;
2304 {
2305 	struct passwd *pw;
2306 
2307 	if (tTd(38, 20))
2308 		printf("user_map_lookup(%s, %s)\n",
2309 			map->map_mname, key);
2310 
2311 	pw = sm_getpwnam(key);
2312 	if (pw == NULL)
2313 		return NULL;
2314 	if (bitset(MF_MATCHONLY, map->map_mflags))
2315 		return map_rewrite(map, key, strlen(key), NULL);
2316 	else
2317 	{
2318 		char *rwval = NULL;
2319 		char buf[30];
2320 
2321 		switch (map->map_valcolno)
2322 		{
2323 		  case 0:
2324 		  case 1:
2325 			rwval = pw->pw_name;
2326 			break;
2327 
2328 		  case 2:
2329 			rwval = pw->pw_passwd;
2330 			break;
2331 
2332 		  case 3:
2333 			sprintf(buf, "%d", pw->pw_uid);
2334 			rwval = buf;
2335 			break;
2336 
2337 		  case 4:
2338 			sprintf(buf, "%d", pw->pw_gid);
2339 			rwval = buf;
2340 			break;
2341 
2342 		  case 5:
2343 			rwval = pw->pw_gecos;
2344 			break;
2345 
2346 		  case 6:
2347 			rwval = pw->pw_dir;
2348 			break;
2349 
2350 		  case 7:
2351 			rwval = pw->pw_shell;
2352 			break;
2353 		}
2354 		return map_rewrite(map, rwval, strlen(rwval), av);
2355 	}
2356 }
2357 /*
2358 **  BESTMX -- find the best MX for a name
2359 **
2360 **	This is really a hack, but I don't see any obvious way
2361 **	to generalize it at the moment.
2362 */
2363 
2364 #if NAMED_BIND
2365 
2366 char *
2367 bestmx_map_lookup(map, name, av, statp)
2368 	MAP *map;
2369 	char *name;
2370 	char **av;
2371 	int *statp;
2372 {
2373         int nmx;
2374         auto int rcode;
2375         char *mxhosts[MAXMXHOSTS + 1];
2376 
2377 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2378 	if (nmx <= 0)
2379 		return NULL;
2380 	if (bitset(MF_MATCHONLY, map->map_mflags))
2381 		return map_rewrite(map, name, strlen(name), NULL);
2382 	else
2383 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2384 }
2385 
2386 #endif
2387 /*
2388 **  Program map type.
2389 **
2390 **	This provides access to arbitrary programs.  It should be used
2391 **	only very sparingly, since there is no way to bound the cost
2392 **	of invoking an arbitrary program.
2393 */
2394 
2395 char *
2396 prog_map_lookup(map, name, av, statp)
2397 	MAP *map;
2398 	char *name;
2399 	char **av;
2400 	int *statp;
2401 {
2402 	int i;
2403 	register char *p;
2404 	int fd;
2405 	auto pid_t pid;
2406 	char *argv[MAXPV + 1];
2407 	char buf[MAXLINE];
2408 
2409 	if (tTd(38, 20))
2410 		printf("prog_map_lookup(%s, %s) %s\n",
2411 			map->map_mname, name, map->map_file);
2412 
2413 	i = 0;
2414 	argv[i++] = map->map_file;
2415 	strcpy(buf, map->map_rebuild);
2416 	for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
2417 	{
2418 		if (i >= MAXPV - 1)
2419 			break;
2420 		argv[i++] = p;
2421 	}
2422 	argv[i++] = name;
2423 	argv[i] = NULL;
2424 	pid = prog_open(argv, &fd, CurEnv);
2425 	if (pid < 0)
2426 	{
2427 		if (tTd(38, 9))
2428 			printf("prog_map_lookup(%s) failed (%s) -- closing",
2429 				map->map_mname, errstring(errno));
2430 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
2431 		return NULL;
2432 	}
2433 	i = read(fd, buf, sizeof buf - 1);
2434 	if (i <= 0 && tTd(38, 2))
2435 		printf("prog_map_lookup(%s): read error %s\n",
2436 			map->map_mname, errstring(errno));
2437 	if (i > 0)
2438 	{
2439 		char *rval;
2440 
2441 		buf[i] = '\0';
2442 		p = strchr(buf, '\n');
2443 		if (p != NULL)
2444 			*p = '\0';
2445 
2446 		/* collect the return value */
2447 		if (bitset(MF_MATCHONLY, map->map_mflags))
2448 			rval = map_rewrite(map, name, strlen(name), NULL);
2449 		else
2450 			rval = map_rewrite(map, buf, strlen(buf), NULL);
2451 
2452 		/* now flush any additional output */
2453 		while ((i = read(fd, buf, sizeof buf)) > 0)
2454 			continue;
2455 		close(fd);
2456 
2457 		/* and wait for the process to terminate */
2458 		*statp = waitfor(pid);
2459 
2460 		return rval;
2461 	}
2462 
2463 	close(fd);
2464 	*statp = waitfor(pid);
2465 	return NULL;
2466 }
2467 /*
2468 **  Sequenced map type.
2469 **
2470 **	Tries each map in order until something matches, much like
2471 **	implicit.  Stores go to the first map in the list that can
2472 **	support storing.
2473 **
2474 **	This is slightly unusual in that there are two interfaces.
2475 **	The "sequence" interface lets you stack maps arbitrarily.
2476 **	The "switch" interface builds a sequence map by looking
2477 **	at a system-dependent configuration file such as
2478 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2479 **
2480 **	We don't need an explicit open, since all maps are
2481 **	opened during startup, including underlying maps.
2482 */
2483 
2484 /*
2485 **  SEQ_MAP_PARSE -- Sequenced map parsing
2486 */
2487 
2488 bool
2489 seq_map_parse(map, ap)
2490 	MAP *map;
2491 	char *ap;
2492 {
2493 	int maxmap;
2494 
2495 	if (tTd(38, 2))
2496 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2497 	maxmap = 0;
2498 	while (*ap != '\0')
2499 	{
2500 		register char *p;
2501 		STAB *s;
2502 
2503 		/* find beginning of map name */
2504 		while (isascii(*ap) && isspace(*ap))
2505 			ap++;
2506 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2507 			continue;
2508 		if (*p != '\0')
2509 			*p++ = '\0';
2510 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2511 			p++;
2512 		if (*ap == '\0')
2513 		{
2514 			ap = p;
2515 			continue;
2516 		}
2517 		s = stab(ap, ST_MAP, ST_FIND);
2518 		if (s == NULL)
2519 		{
2520 			syserr("Sequence map %s: unknown member map %s",
2521 				map->map_mname, ap);
2522 		}
2523 		else if (maxmap == MAXMAPSTACK)
2524 		{
2525 			syserr("Sequence map %s: too many member maps (%d max)",
2526 				map->map_mname, MAXMAPSTACK);
2527 			maxmap++;
2528 		}
2529 		else if (maxmap < MAXMAPSTACK)
2530 		{
2531 			map->map_stack[maxmap++] = &s->s_map;
2532 		}
2533 		ap = p;
2534 	}
2535 	return TRUE;
2536 }
2537 
2538 
2539 /*
2540 **  SWITCH_MAP_OPEN -- open a switched map
2541 **
2542 **	This looks at the system-dependent configuration and builds
2543 **	a sequence map that does the same thing.
2544 **
2545 **	Every system must define a switch_map_find routine in conf.c
2546 **	that will return the list of service types associated with a
2547 **	given service class.
2548 */
2549 
2550 bool
2551 switch_map_open(map, mode)
2552 	MAP *map;
2553 	int mode;
2554 {
2555 	int mapno;
2556 	int nmaps;
2557 	char *maptype[MAXMAPSTACK];
2558 
2559 	if (tTd(38, 2))
2560 		printf("switch_map_open(%s, %s, %d)\n",
2561 			map->map_mname, map->map_file, mode);
2562 
2563 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2564 	if (tTd(38, 19))
2565 	{
2566 		printf("\tswitch_map_find => %d\n", nmaps);
2567 		for (mapno = 0; mapno < nmaps; mapno++)
2568 			printf("\t\t%s\n", maptype[mapno]);
2569 	}
2570 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2571 		return FALSE;
2572 
2573 	for (mapno = 0; mapno < nmaps; mapno++)
2574 	{
2575 		register STAB *s;
2576 		char nbuf[MAXNAME + 1];
2577 
2578 		if (maptype[mapno] == NULL)
2579 			continue;
2580 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2581 		s = stab(nbuf, ST_MAP, ST_FIND);
2582 		if (s == NULL)
2583 		{
2584 			syserr("Switch map %s: unknown member map %s",
2585 				map->map_mname, nbuf);
2586 		}
2587 		else
2588 		{
2589 			map->map_stack[mapno] = &s->s_map;
2590 			if (tTd(38, 4))
2591 				printf("\tmap_stack[%d] = %s:%s\n",
2592 					mapno, s->s_map.map_class->map_cname,
2593 					nbuf);
2594 		}
2595 	}
2596 	return TRUE;
2597 }
2598 
2599 
2600 /*
2601 **  SEQ_MAP_CLOSE -- close all underlying maps
2602 */
2603 
2604 seq_map_close(map)
2605 	MAP *map;
2606 {
2607 	int mapno;
2608 
2609 	if (tTd(38, 20))
2610 		printf("seq_map_close(%s)\n", map->map_mname);
2611 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2612 	{
2613 		MAP *mm = map->map_stack[mapno];
2614 
2615 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2616 			continue;
2617 		mm->map_class->map_close(mm);
2618 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2619 	}
2620 }
2621 
2622 
2623 /*
2624 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2625 */
2626 
2627 char *
2628 seq_map_lookup(map, key, args, pstat)
2629 	MAP *map;
2630 	char *key;
2631 	char **args;
2632 	int *pstat;
2633 {
2634 	int mapno;
2635 	int mapbit = 0x01;
2636 
2637 	if (tTd(38, 20))
2638 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2639 
2640 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2641 	{
2642 		MAP *mm = map->map_stack[mapno];
2643 		int stat = 0;
2644 		char *rv;
2645 
2646 		if (mm == NULL)
2647 			continue;
2648 		if (!bitset(MF_OPEN, mm->map_mflags))
2649 		{
2650 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2651 			{
2652 				*pstat = EX_UNAVAILABLE;
2653 				return NULL;
2654 			}
2655 			continue;
2656 		}
2657 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2658 		if (rv != NULL)
2659 			return rv;
2660 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2661 			return NULL;
2662 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2663 		{
2664 			*pstat = stat;
2665 			return NULL;
2666 		}
2667 	}
2668 	return NULL;
2669 }
2670 
2671 
2672 /*
2673 **  SEQ_MAP_STORE -- sequenced map store
2674 */
2675 
2676 void
2677 seq_map_store(map, key, val)
2678 	MAP *map;
2679 	char *key;
2680 	char *val;
2681 {
2682 	int mapno;
2683 
2684 	if (tTd(38, 12))
2685 		printf("seq_map_store(%s, %s, %s)\n",
2686 			map->map_mname, key, val);
2687 
2688 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2689 	{
2690 		MAP *mm = map->map_stack[mapno];
2691 
2692 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2693 			continue;
2694 
2695 		mm->map_class->map_store(mm, key, val);
2696 		return;
2697 	}
2698 	syserr("seq_map_store(%s, %s, %s): no writable map",
2699 		map->map_mname, key, val);
2700 }
2701 /*
2702 **  GETCANONNAME -- look up name using service switch
2703 **
2704 **	Parameters:
2705 **		host -- the host name to look up.
2706 **		hbsize -- the size of the host buffer.
2707 **		trymx -- if set, try MX records.
2708 **
2709 **	Returns:
2710 **		TRUE -- if the host was found.
2711 **		FALSE -- otherwise.
2712 */
2713 
2714 bool
2715 getcanonname(host, hbsize, trymx)
2716 	char *host;
2717 	int hbsize;
2718 	bool trymx;
2719 {
2720 	int nmaps;
2721 	int mapno;
2722 	bool found = FALSE;
2723 	auto int stat;
2724 	char *maptype[MAXMAPSTACK];
2725 	short mapreturn[MAXMAPACTIONS];
2726 	extern int h_errno;
2727 
2728 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2729 	for (mapno = 0; mapno < nmaps; mapno++)
2730 	{
2731 		int i;
2732 
2733 		if (tTd(38, 20))
2734 			printf("getcanonname(%s), trying %s\n",
2735 				host, maptype[mapno]);
2736 		if (strcmp("files", maptype[mapno]) == 0)
2737 			found = text_getcanonname(host, hbsize, &stat);
2738 #ifdef NIS
2739 		else if (strcmp("nis", maptype[mapno]) == 0)
2740 			found = nis_getcanonname(host, hbsize, &stat);
2741 #endif
2742 #ifdef NISPLUS
2743 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2744 			found = nisplus_getcanonname(host, hbsize, &stat);
2745 #endif
2746 #if NAMED_BIND
2747 		else if (strcmp("dns", maptype[mapno]) == 0)
2748 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2749 #endif
2750 		else
2751 		{
2752 			found = FALSE;
2753 			stat = EX_UNAVAILABLE;
2754 		}
2755 		if (found)
2756 			break;
2757 
2758 		/* see if we should continue */
2759 		if (stat == EX_TEMPFAIL)
2760 			i = MA_TRYAGAIN;
2761 		else if (stat == EX_NOHOST)
2762 			i = MA_NOTFOUND;
2763 		else
2764 			i = MA_UNAVAIL;
2765 		if (bitset(1 << mapno, mapreturn[i]))
2766 			break;
2767 	}
2768 
2769 	if (found)
2770 	{
2771 		char *d;
2772 
2773 		if (tTd(38, 20))
2774 			printf("getcanonname(%s), found\n", host);
2775 
2776 		/*
2777 		**  If returned name is still single token, compensate
2778 		**  by tagging on $m.  This is because some sites set
2779 		**  up their DNS or NIS databases wrong.
2780 		*/
2781 
2782 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2783 		{
2784 			d = macvalue('m', CurEnv);
2785 			if (d != NULL &&
2786 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2787 			{
2788 				if (host[strlen(host) - 1] != '.')
2789 					strcat(host, ".");
2790 				strcat(host, d);
2791 			}
2792 			else
2793 			{
2794 				return FALSE;
2795 			}
2796 		}
2797 		return TRUE;
2798 	}
2799 
2800 	if (tTd(38, 20))
2801 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2802 
2803 #if NAMED_BIND
2804 	if (stat == EX_NOHOST)
2805 		h_errno = HOST_NOT_FOUND;
2806 	else
2807 		h_errno = TRY_AGAIN;
2808 #endif
2809 
2810 	return FALSE;
2811 }
2812 /*
2813 **  NULL stubs
2814 */
2815 
2816 bool
2817 null_map_open(map, mode)
2818 	MAP *map;
2819 	int mode;
2820 {
2821 	return TRUE;
2822 }
2823 
2824 void
2825 null_map_close(map)
2826 	MAP *map;
2827 {
2828 	return;
2829 }
2830 
2831 void
2832 null_map_store(map, key, val)
2833 	MAP *map;
2834 	char *key;
2835 	char *val;
2836 {
2837 	return;
2838 }
2839 
2840 
2841 /*
2842 **  BOGUS stubs
2843 */
2844 
2845 char *
2846 bogus_map_lookup(map, key, args, pstat)
2847 	MAP *map;
2848 	char *key;
2849 	char **args;
2850 	int *pstat;
2851 {
2852 	*pstat = EX_TEMPFAIL;
2853 	return NULL;
2854 }
2855 
2856 MAPCLASS	BogusMapClass =
2857 {
2858 	"bogus-map",		NULL,		0,
2859 	NULL,		bogus_map_lookup,	null_map_store,
2860 	null_map_open,	null_map_close,
2861 };
2862