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