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