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