xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision deff14a8)
1 /*
2  * Copyright (c) 1992 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.34 (Berkeley) 10/25/94";
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 #include <rpcsvc/ypclnt.h>
23 #endif
24 
25 /*
26 **  MAP.C -- implementations for various map classes.
27 **
28 **	Each map class implements a series of functions:
29 **
30 **	bool map_parse(MAP *map, char *args)
31 **		Parse the arguments from the config file.  Return TRUE
32 **		if they were ok, FALSE otherwise.  Fill in map with the
33 **		values.
34 **
35 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
36 **		Look up the key in the given map.  If found, do any
37 **		rewriting the map wants (including "args" if desired)
38 **		and return the value.  Set *pstat to the appropriate status
39 **		on error and return NULL.  Args will be NULL if called
40 **		from the alias routines, although this should probably
41 **		not be relied upon.  It is suggested you call map_rewrite
42 **		to return the results -- it takes care of null termination
43 **		and uses a dynamically expanded buffer as needed.
44 **
45 **	void map_store(MAP *map, char *key, char *value)
46 **		Store the key:value pair in the map.
47 **
48 **	bool map_open(MAP *map, int mode)
49 **		Open the map for the indicated mode.  Mode should
50 **		be either O_RDONLY or O_RDWR.  Return TRUE if it
51 **		was opened successfully, FALSE otherwise.  If the open
52 **		failed an the MF_OPTIONAL flag is not set, it should
53 **		also print an error.  If the MF_ALIAS bit is set
54 **		and this map class understands the @:@ convention, it
55 **		should call aliaswait() before returning.
56 **
57 **	void map_close(MAP *map)
58 **		Close the map.
59 */
60 
61 #define DBMMODE		0644
62 
63 extern bool	aliaswait __P((MAP *, char *, int));
64 /*
65 **  MAP_PARSEARGS -- parse config line arguments for database lookup
66 **
67 **	This is a generic version of the map_parse method.
68 **
69 **	Parameters:
70 **		map -- the map being initialized.
71 **		ap -- a pointer to the args on the config line.
72 **
73 **	Returns:
74 **		TRUE -- if everything parsed OK.
75 **		FALSE -- otherwise.
76 **
77 **	Side Effects:
78 **		null terminates the filename; stores it in map
79 */
80 
81 bool
82 map_parseargs(map, ap)
83 	MAP *map;
84 	char *ap;
85 {
86 	register char *p = ap;
87 
88 	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
89 	for (;;)
90 	{
91 		while (isascii(*p) && isspace(*p))
92 			p++;
93 		if (*p != '-')
94 			break;
95 		switch (*++p)
96 		{
97 		  case 'N':
98 			map->map_mflags |= MF_INCLNULL;
99 			map->map_mflags &= ~MF_TRY0NULL;
100 			break;
101 
102 		  case 'O':
103 			map->map_mflags &= ~MF_TRY1NULL;
104 			break;
105 
106 		  case 'o':
107 			map->map_mflags |= MF_OPTIONAL;
108 			break;
109 
110 		  case 'f':
111 			map->map_mflags |= MF_NOFOLDCASE;
112 			break;
113 
114 		  case 'm':
115 			map->map_mflags |= MF_MATCHONLY;
116 			break;
117 
118 		  case 'a':
119 			map->map_app = ++p;
120 			break;
121 
122 		  case 'k':
123 			while (isascii(*++p) && isspace(*p))
124 				continue;
125 			map->map_keycolnm = p;
126 			break;
127 
128 		  case 'v':
129 			while (isascii(*++p) && isspace(*p))
130 				continue;
131 			map->map_valcolnm = p;
132 			break;
133 
134 		  case 'z':
135 			if (*++p != '\\')
136 				map->map_coldelim = *p;
137 			else
138 			{
139 				switch (*++p)
140 				{
141 				  case 'n':
142 					map->map_coldelim = '\n';
143 					break;
144 
145 				  case 't':
146 					map->map_coldelim = '\t';
147 					break;
148 
149 				  default:
150 					map->map_coldelim = '\\';
151 				}
152 			}
153 			break;
154 		}
155 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
156 			p++;
157 		if (*p != '\0')
158 			*p++ = '\0';
159 	}
160 	if (map->map_app != NULL)
161 		map->map_app = newstr(map->map_app);
162 	if (map->map_keycolnm != NULL)
163 		map->map_keycolnm = newstr(map->map_keycolnm);
164 	if (map->map_valcolnm != NULL)
165 		map->map_valcolnm = newstr(map->map_valcolnm);
166 
167 	if (*p != '\0')
168 	{
169 		map->map_file = p;
170 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
171 			p++;
172 		if (*p != '\0')
173 			*p++ = '\0';
174 		map->map_file = newstr(map->map_file);
175 	}
176 
177 	while (*p != '\0' && isascii(*p) && isspace(*p))
178 		p++;
179 	if (*p != '\0')
180 		map->map_rebuild = newstr(p);
181 
182 	if (map->map_file == NULL)
183 	{
184 		syserr("No file name for %s map %s",
185 			map->map_class->map_cname, map->map_mname);
186 		return FALSE;
187 	}
188 	return TRUE;
189 }
190 /*
191 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
192 **
193 **	It also adds the map_app string.  It can be used as a utility
194 **	in the map_lookup method.
195 **
196 **	Parameters:
197 **		map -- the map that causes this.
198 **		s -- the string to rewrite, NOT necessarily null terminated.
199 **		slen -- the length of s.
200 **		av -- arguments to interpolate into buf.
201 **
202 **	Returns:
203 **		Pointer to rewritten result.
204 **
205 **	Side Effects:
206 **		none.
207 */
208 
209 struct rwbuf
210 {
211 	int	rwb_len;	/* size of buffer */
212 	char	*rwb_buf;	/* ptr to buffer */
213 };
214 
215 struct rwbuf	RwBufs[2];	/* buffers for rewriting output */
216 
217 char *
218 map_rewrite(map, s, slen, av)
219 	register MAP *map;
220 	register char *s;
221 	int slen;
222 	char **av;
223 {
224 	register char *bp;
225 	register char c;
226 	char **avp;
227 	register char *ap;
228 	register struct rwbuf *rwb;
229 	int i;
230 	int len;
231 
232 	if (tTd(39, 1))
233 	{
234 		printf("map_rewrite(%.*s), av =", slen, s);
235 		if (av == NULL)
236 			printf(" (nullv)");
237 		else
238 		{
239 			for (avp = av; *avp != NULL; avp++)
240 				printf("\n\t%s", *avp);
241 		}
242 		printf("\n");
243 	}
244 
245 	rwb = RwBufs;
246 	if (av == NULL)
247 		rwb++;
248 
249 	/* count expected size of output (can safely overestimate) */
250 	i = len = slen;
251 	if (av != NULL)
252 	{
253 		bp = s;
254 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
255 		{
256 			if (c != '%')
257 				continue;
258 			if (--i < 0)
259 				break;
260 			c = *bp++;
261 			if (!(isascii(c) && isdigit(c)))
262 				continue;
263 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
264 				continue;
265 			if (*avp == NULL)
266 				continue;
267 			len += strlen(*avp);
268 		}
269 	}
270 	if (map->map_app != NULL)
271 		len += strlen(map->map_app);
272 	if (rwb->rwb_len < ++len)
273 	{
274 		/* need to malloc additional space */
275 		rwb->rwb_len = len;
276 		if (rwb->rwb_buf != NULL)
277 			free(rwb->rwb_buf);
278 		rwb->rwb_buf = xalloc(rwb->rwb_len);
279 	}
280 
281 	bp = rwb->rwb_buf;
282 	if (av == NULL)
283 	{
284 		bcopy(s, bp, slen);
285 		bp += slen;
286 	}
287 	else
288 	{
289 		while (--slen >= 0 && (c = *s++) != '\0')
290 		{
291 			if (c != '%')
292 			{
293   pushc:
294 				*bp++ = c;
295 				continue;
296 			}
297 			if (--slen < 0 || (c = *s++) == '\0')
298 				c = '%';
299 			if (c == '%')
300 				goto pushc;
301 			if (!(isascii(c) && isdigit(c)))
302 			{
303 				*bp++ = '%';
304 				goto pushc;
305 			}
306 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
307 				continue;
308 			if (*avp == NULL)
309 				continue;
310 
311 			/* transliterate argument into output string */
312 			for (ap = *avp; (c = *ap++) != '\0'; )
313 				*bp++ = c;
314 		}
315 	}
316 	if (map->map_app != NULL)
317 		strcpy(bp, map->map_app);
318 	else
319 		*bp = '\0';
320 	if (tTd(39, 1))
321 		printf("map_rewrite => %s\n", rwb->rwb_buf);
322 	return rwb->rwb_buf;
323 }
324 /*
325 **  INITMAPS -- initialize for aliasing
326 **
327 **	Parameters:
328 **		rebuild -- if TRUE, this rebuilds the cached versions.
329 **		e -- current envelope.
330 **
331 **	Returns:
332 **		none.
333 **
334 **	Side Effects:
335 **		initializes aliases:
336 **		if NDBM:  opens the database.
337 **		if ~NDBM: reads the aliases into the symbol table.
338 */
339 
340 initmaps(rebuild, e)
341 	bool rebuild;
342 	register ENVELOPE *e;
343 {
344 	extern void map_init();
345 
346 #ifdef XDEBUG
347 	checkfd012("entering initmaps");
348 #endif
349 	CurEnv = e;
350 	if (rebuild)
351 	{
352 		stabapply(map_init, 1);
353 		stabapply(map_init, 2);
354 	}
355 	else
356 	{
357 		stabapply(map_init, 0);
358 	}
359 #ifdef XDEBUG
360 	checkfd012("exiting initmaps");
361 #endif
362 }
363 
364 void
365 map_init(s, rebuild)
366 	register STAB *s;
367 	int rebuild;
368 {
369 	register MAP *map;
370 
371 	/* has to be a map */
372 	if (s->s_type != ST_MAP)
373 		return;
374 
375 	map = &s->s_map;
376 	if (!bitset(MF_VALID, map->map_mflags))
377 		return;
378 
379 	if (tTd(38, 2))
380 		printf("map_init(%s:%s, %s, %d)\n",
381 			map->map_class->map_cname == NULL ? "NULL" :
382 				map->map_class->map_cname,
383 			map->map_mname == NULL ? "NULL" : map->map_mname,
384 			map->map_file == NULL ? "NULL" : map->map_file,
385 			rebuild);
386 
387 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
388 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
389 	{
390 		if (tTd(38, 3))
391 			printf("\twrong pass\n");
392 		return;
393 	}
394 
395 	/* if already open, close it (for nested open) */
396 	if (bitset(MF_OPEN, map->map_mflags))
397 	{
398 		map->map_class->map_close(map);
399 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
400 	}
401 
402 	if (rebuild == 2)
403 	{
404 		rebuildaliases(map, FALSE);
405 	}
406 	else
407 	{
408 		if (map->map_class->map_open(map, O_RDONLY))
409 		{
410 			if (tTd(38, 4))
411 				printf("\t%s:%s %s: valid\n",
412 					map->map_class->map_cname == NULL ? "NULL" :
413 						map->map_class->map_cname,
414 					map->map_mname == NULL ? "NULL" :
415 						map->map_mname,
416 					map->map_file == NULL ? "NULL" :
417 						map->map_file);
418 			map->map_mflags |= MF_OPEN;
419 		}
420 		else if (tTd(38, 4))
421 			printf("\t%s:%s %s: invalid: %s\n",
422 				map->map_class->map_cname == NULL ? "NULL" :
423 					map->map_class->map_cname,
424 				map->map_mname == NULL ? "NULL" :
425 					map->map_mname,
426 				map->map_file == NULL ? "NULL" :
427 					map->map_file,
428 				errstring(errno));
429 	}
430 }
431 /*
432 **  NDBM modules
433 */
434 
435 #ifdef NDBM
436 
437 /*
438 **  DBM_MAP_OPEN -- DBM-style map open
439 */
440 
441 bool
442 ndbm_map_open(map, mode)
443 	MAP *map;
444 	int mode;
445 {
446 	register DBM *dbm;
447 	struct stat st;
448 
449 	if (tTd(38, 2))
450 		printf("ndbm_map_open(%s, %s, %d)\n",
451 			map->map_mname, map->map_file, mode);
452 
453 	if (mode == O_RDWR)
454 		mode |= O_CREAT|O_TRUNC;
455 
456 	/* open the database */
457 	dbm = dbm_open(map->map_file, mode, DBMMODE);
458 	if (dbm == NULL)
459 	{
460 #ifdef MAYBENEXTRELEASE
461 		if (aliaswait(map, ".pag", FALSE))
462 			return TRUE;
463 #endif
464 		if (!bitset(MF_OPTIONAL, map->map_mflags))
465 			syserr("Cannot open DBM database %s", map->map_file);
466 		return FALSE;
467 	}
468 	map->map_db1 = (void *) dbm;
469 	if (mode == O_RDONLY)
470 	{
471 		if (bitset(MF_ALIAS, map->map_mflags) &&
472 		    !aliaswait(map, ".pag", TRUE))
473 			return FALSE;
474 	}
475 	else
476 	{
477 		int fd;
478 
479 		/* exclusive lock for duration of rebuild */
480 		fd = dbm_dirfno((DBM *) map->map_db1);
481 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
482 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
483 			map->map_mflags |= MF_LOCKED;
484 	}
485 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
486 		map->map_mtime = st.st_mtime;
487 	return TRUE;
488 }
489 
490 
491 /*
492 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
493 */
494 
495 char *
496 ndbm_map_lookup(map, name, av, statp)
497 	MAP *map;
498 	char *name;
499 	char **av;
500 	int *statp;
501 {
502 	datum key, val;
503 	int fd;
504 	char keybuf[MAXNAME + 1];
505 
506 	if (tTd(38, 20))
507 		printf("ndbm_map_lookup(%s, %s)\n",
508 			map->map_mname, name);
509 
510 	key.dptr = name;
511 	key.dsize = strlen(name);
512 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
513 	{
514 		if (key.dsize > sizeof keybuf - 1)
515 			key.dsize = sizeof keybuf - 1;
516 		bcopy(key.dptr, keybuf, key.dsize + 1);
517 		makelower(keybuf);
518 		key.dptr = keybuf;
519 	}
520 	fd = dbm_dirfno((DBM *) map->map_db1);
521 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
522 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
523 	val.dptr = NULL;
524 	if (bitset(MF_TRY0NULL, map->map_mflags))
525 	{
526 		val = dbm_fetch((DBM *) map->map_db1, key);
527 		if (val.dptr != NULL)
528 			map->map_mflags &= ~MF_TRY1NULL;
529 	}
530 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
531 	{
532 		key.dsize++;
533 		val = dbm_fetch((DBM *) map->map_db1, key);
534 		if (val.dptr != NULL)
535 			map->map_mflags &= ~MF_TRY0NULL;
536 	}
537 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
538 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
539 	if (val.dptr == NULL)
540 		return NULL;
541 	if (bitset(MF_MATCHONLY, map->map_mflags))
542 		return map_rewrite(map, name, strlen(name), NULL);
543 	else
544 		return map_rewrite(map, val.dptr, val.dsize, av);
545 }
546 
547 
548 /*
549 **  DBM_MAP_STORE -- store a datum in the database
550 */
551 
552 void
553 ndbm_map_store(map, lhs, rhs)
554 	register MAP *map;
555 	char *lhs;
556 	char *rhs;
557 {
558 	datum key;
559 	datum data;
560 	int stat;
561 
562 	if (tTd(38, 12))
563 		printf("ndbm_map_store(%s, %s, %s)\n",
564 			map->map_mname, lhs, rhs);
565 
566 	key.dsize = strlen(lhs);
567 	key.dptr = lhs;
568 
569 	data.dsize = strlen(rhs);
570 	data.dptr = rhs;
571 
572 	if (bitset(MF_INCLNULL, map->map_mflags))
573 	{
574 		key.dsize++;
575 		data.dsize++;
576 	}
577 
578 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
579 	if (stat > 0)
580 	{
581 		usrerr("050 Warning: duplicate alias name %s", lhs);
582 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
583 	}
584 	if (stat != 0)
585 		syserr("readaliases: dbm put (%s)", lhs);
586 }
587 
588 
589 /*
590 **  NDBM_MAP_CLOSE -- close the database
591 */
592 
593 void
594 ndbm_map_close(map)
595 	register MAP  *map;
596 {
597 	if (tTd(38, 9))
598 		printf("ndbm_map_close(%s, %s, %x)\n",
599 			map->map_mname, map->map_file, map->map_mflags);
600 
601 	if (bitset(MF_WRITABLE, map->map_mflags))
602 	{
603 #ifdef NIS
604 		bool inclnull;
605 		char buf[200];
606 
607 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
608 		map->map_mflags &= ~MF_INCLNULL;
609 
610 		(void) sprintf(buf, "%010ld", curtime());
611 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
612 
613 		(void) gethostname(buf, sizeof buf);
614 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
615 
616 		if (inclnull)
617 			map->map_mflags |= MF_INCLNULL;
618 #endif
619 
620 		/* write out the distinguished alias */
621 		ndbm_map_store(map, "@", "@");
622 	}
623 	dbm_close((DBM *) map->map_db1);
624 }
625 
626 #endif
627 /*
628 **  NEWDB (Hash and BTree) Modules
629 */
630 
631 #ifdef NEWDB
632 
633 /*
634 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
635 **
636 **	These do rather bizarre locking.  If you can lock on open,
637 **	do that to avoid the condition of opening a database that
638 **	is being rebuilt.  If you don't, we'll try to fake it, but
639 **	there will be a race condition.  If opening for read-only,
640 **	we immediately release the lock to avoid freezing things up.
641 **	We really ought to hold the lock, but guarantee that we won't
642 **	be pokey about it.  That's hard to do.
643 */
644 
645 bool
646 bt_map_open(map, mode)
647 	MAP *map;
648 	int mode;
649 {
650 	DB *db;
651 	int i;
652 	int omode;
653 	int fd;
654 	struct stat st;
655 	char buf[MAXNAME];
656 
657 	if (tTd(38, 2))
658 		printf("bt_map_open(%s, %s, %d)\n",
659 			map->map_mname, map->map_file, mode);
660 
661 	omode = mode;
662 	if (omode == O_RDWR)
663 	{
664 		omode |= O_CREAT|O_TRUNC;
665 #if defined(O_EXLOCK) && HASFLOCK
666 		omode |= O_EXLOCK;
667 # if !OLD_NEWDB
668 	}
669 	else
670 	{
671 		omode |= O_SHLOCK;
672 # endif
673 #endif
674 	}
675 
676 	(void) strcpy(buf, map->map_file);
677 	i = strlen(buf);
678 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
679 		(void) strcat(buf, ".db");
680 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
681 	if (db == NULL)
682 	{
683 #ifdef MAYBENEXTRELEASE
684 		if (aliaswait(map, ".db", FALSE))
685 			return TRUE;
686 #endif
687 		if (!bitset(MF_OPTIONAL, map->map_mflags))
688 			syserr("Cannot open BTREE database %s", map->map_file);
689 		return FALSE;
690 	}
691 #if !OLD_NEWDB
692 	fd = db->fd(db);
693 # if HASFLOCK
694 #  if !defined(O_EXLOCK)
695 	if (mode == O_RDWR && fd >= 0)
696 	{
697 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
698 			map->map_mflags |= MF_LOCKED;
699 	}
700 #  else
701 	if (mode == O_RDONLY && fd >= 0)
702 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
703 	else
704 		map->map_mflags |= MF_LOCKED;
705 #  endif
706 # endif
707 #endif
708 
709 	/* try to make sure that at least the database header is on disk */
710 	if (mode == O_RDWR)
711 #if OLD_NEWDB
712 		(void) db->sync(db);
713 #else
714 		(void) db->sync(db, 0);
715 
716 	if (fd >= 0 && fstat(fd, &st) >= 0)
717 		map->map_mtime = st.st_mtime;
718 #endif
719 
720 	map->map_db2 = (void *) db;
721 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
722 		if (!aliaswait(map, ".db", TRUE))
723 			return FALSE;
724 	return TRUE;
725 }
726 
727 
728 /*
729 **  HASH_MAP_INIT -- HASH-style map initialization
730 */
731 
732 bool
733 hash_map_open(map, mode)
734 	MAP *map;
735 	int mode;
736 {
737 	DB *db;
738 	int i;
739 	int omode;
740 	int fd;
741 	struct stat st;
742 	char buf[MAXNAME];
743 
744 	if (tTd(38, 2))
745 		printf("hash_map_open(%s, %s, %d)\n",
746 			map->map_mname, map->map_file, mode);
747 
748 	omode = mode;
749 	if (omode == O_RDWR)
750 	{
751 		omode |= O_CREAT|O_TRUNC;
752 #if defined(O_EXLOCK) && HASFLOCK
753 		omode |= O_EXLOCK;
754 # if !OLD_NEWDB
755 	}
756 	else
757 	{
758 		omode |= O_SHLOCK;
759 # endif
760 #endif
761 	}
762 
763 	(void) strcpy(buf, map->map_file);
764 	i = strlen(buf);
765 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
766 		(void) strcat(buf, ".db");
767 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
768 	if (db == NULL)
769 	{
770 #ifdef MAYBENEXTRELEASE
771 		if (aliaswait(map, ".db", FALSE))
772 			return TRUE;
773 #endif
774 		if (!bitset(MF_OPTIONAL, map->map_mflags))
775 			syserr("Cannot open HASH database %s", map->map_file);
776 		return FALSE;
777 	}
778 #if !OLD_NEWDB
779 	fd = db->fd(db);
780 # if HASFLOCK
781 #  if !defined(O_EXLOCK)
782 	if (mode == O_RDWR && fd >= 0)
783 	{
784 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
785 			map->map_mflags |= MF_LOCKED;
786 	}
787 #  else
788 	if (mode == O_RDONLY && fd >= 0)
789 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
790 	else
791 		map->map_mflags |= MF_LOCKED;
792 #  endif
793 # endif
794 #endif
795 
796 	/* try to make sure that at least the database header is on disk */
797 	if (mode == O_RDWR)
798 #if OLD_NEWDB
799 		(void) db->sync(db);
800 #else
801 		(void) db->sync(db, 0);
802 
803 	if (fd >= 0 && fstat(fd, &st) >= 0)
804 		map->map_mtime = st.st_mtime;
805 #endif
806 
807 	map->map_db2 = (void *) db;
808 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
809 		if (!aliaswait(map, ".db", TRUE))
810 			return FALSE;
811 	return TRUE;
812 }
813 
814 
815 /*
816 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
817 */
818 
819 char *
820 db_map_lookup(map, name, av, statp)
821 	MAP *map;
822 	char *name;
823 	char **av;
824 	int *statp;
825 {
826 	DBT key, val;
827 	register DB *db = (DB *) map->map_db2;
828 	int st;
829 	int saveerrno;
830 	int fd;
831 	char keybuf[MAXNAME + 1];
832 
833 	if (tTd(38, 20))
834 		printf("db_map_lookup(%s, %s)\n",
835 			map->map_mname, name);
836 
837 	key.size = strlen(name);
838 	if (key.size > sizeof keybuf - 1)
839 		key.size = sizeof keybuf - 1;
840 	key.data = keybuf;
841 	bcopy(name, keybuf, key.size + 1);
842 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
843 		makelower(keybuf);
844 #if !OLD_NEWDB
845 	fd = db->fd(db);
846 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
847 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
848 #endif
849 	st = 1;
850 	if (bitset(MF_TRY0NULL, map->map_mflags))
851 	{
852 		st = db->get(db, &key, &val, 0);
853 		if (st == 0)
854 			map->map_mflags &= ~MF_TRY1NULL;
855 	}
856 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
857 	{
858 		key.size++;
859 		st = db->get(db, &key, &val, 0);
860 		if (st == 0)
861 			map->map_mflags &= ~MF_TRY0NULL;
862 	}
863 	saveerrno = errno;
864 #if !OLD_NEWDB
865 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
866 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
867 #endif
868 	if (st != 0)
869 	{
870 		errno = saveerrno;
871 		if (st < 0)
872 			syserr("db_map_lookup: get (%s)", name);
873 		return NULL;
874 	}
875 	if (bitset(MF_MATCHONLY, map->map_mflags))
876 		return map_rewrite(map, name, strlen(name), NULL);
877 	else
878 		return map_rewrite(map, val.data, val.size, av);
879 }
880 
881 
882 /*
883 **  DB_MAP_STORE -- store a datum in the NEWDB database
884 */
885 
886 void
887 db_map_store(map, lhs, rhs)
888 	register MAP *map;
889 	char *lhs;
890 	char *rhs;
891 {
892 	int stat;
893 	DBT key;
894 	DBT data;
895 	register DB *db = map->map_db2;
896 
897 	if (tTd(38, 20))
898 		printf("db_map_store(%s, %s, %s)\n",
899 			map->map_mname, lhs, rhs);
900 
901 	key.size = strlen(lhs);
902 	key.data = lhs;
903 
904 	data.size = strlen(rhs);
905 	data.data = rhs;
906 
907 	if (bitset(MF_INCLNULL, map->map_mflags))
908 	{
909 		key.size++;
910 		data.size++;
911 	}
912 
913 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
914 	if (stat > 0)
915 	{
916 		usrerr("050 Warning: duplicate alias name %s", lhs);
917 		stat = db->put(db, &key, &data, 0);
918 	}
919 	if (stat != 0)
920 		syserr("readaliases: db put (%s)", lhs);
921 }
922 
923 
924 /*
925 **  DB_MAP_CLOSE -- add distinguished entries and close the database
926 */
927 
928 void
929 db_map_close(map)
930 	MAP *map;
931 {
932 	register DB *db = map->map_db2;
933 
934 	if (tTd(38, 9))
935 		printf("db_map_close(%s, %s, %x)\n",
936 			map->map_mname, map->map_file, map->map_mflags);
937 
938 	if (bitset(MF_WRITABLE, map->map_mflags))
939 	{
940 		/* write out the distinguished alias */
941 		db_map_store(map, "@", "@");
942 	}
943 
944 	if (db->close(db) != 0)
945 		syserr("readaliases: db close failure");
946 }
947 
948 #endif
949 /*
950 **  NIS Modules
951 */
952 
953 # ifdef NIS
954 
955 # ifndef YPERR_BUSY
956 #  define YPERR_BUSY	16
957 # endif
958 
959 /*
960 **  NIS_MAP_OPEN -- open DBM map
961 */
962 
963 bool
964 nis_map_open(map, mode)
965 	MAP *map;
966 	int mode;
967 {
968 	int yperr;
969 	register char *p;
970 	auto char *vp;
971 	auto int vsize;
972 	char *master;
973 
974 	if (tTd(38, 2))
975 		printf("nis_map_open(%s, %s)\n",
976 			map->map_mname, map->map_file);
977 
978 	if (mode != O_RDONLY)
979 	{
980 		/* issue a pseudo-error message */
981 #ifdef ENOSYS
982 		errno = ENOSYS;
983 #else
984 # ifdef EFTYPE
985 		errno = EFTYPE;
986 # else
987 		errno = ENXIO;
988 # endif
989 #endif
990 		return FALSE;
991 	}
992 
993 	p = strchr(map->map_file, '@');
994 	if (p != NULL)
995 	{
996 		*p++ = '\0';
997 		if (*p != '\0')
998 			map->map_domain = p;
999 	}
1000 
1001 	if (*map->map_file == '\0')
1002 		map->map_file = "mail.aliases";
1003 
1004 	if (map->map_domain == NULL)
1005 	{
1006 		yperr = yp_get_default_domain(&map->map_domain);
1007 		if (yperr != 0)
1008 		{
1009 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1010 				syserr("NIS map %s specified, but NIS not running\n",
1011 					map->map_file);
1012 			return FALSE;
1013 		}
1014 	}
1015 
1016 	/* check to see if this map actually exists */
1017 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1018 			&vp, &vsize);
1019 	if (tTd(38, 10))
1020 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1021 			map->map_domain, map->map_file, yperr_string(yperr));
1022 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1023 		return TRUE;
1024 
1025 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1026 		syserr("Cannot bind to domain %s: %s", map->map_domain,
1027 			yperr_string(yperr));
1028 
1029 	return FALSE;
1030 }
1031 
1032 
1033 /*
1034 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1035 */
1036 
1037 char *
1038 nis_map_lookup(map, name, av, statp)
1039 	MAP *map;
1040 	char *name;
1041 	char **av;
1042 	int *statp;
1043 {
1044 	char *vp;
1045 	auto int vsize;
1046 	int buflen;
1047 	int yperr;
1048 	char keybuf[MAXNAME + 1];
1049 
1050 	if (tTd(38, 20))
1051 		printf("nis_map_lookup(%s, %s)\n",
1052 			map->map_mname, name);
1053 
1054 	buflen = strlen(name);
1055 	if (buflen > sizeof keybuf - 1)
1056 		buflen = sizeof keybuf - 1;
1057 	bcopy(name, keybuf, buflen + 1);
1058 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1059 		makelower(keybuf);
1060 	yperr = YPERR_KEY;
1061 	if (bitset(MF_TRY0NULL, map->map_mflags))
1062 	{
1063 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1064 			     &vp, &vsize);
1065 		if (yperr == 0)
1066 			map->map_mflags &= ~MF_TRY1NULL;
1067 	}
1068 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1069 	{
1070 		buflen++;
1071 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1072 			     &vp, &vsize);
1073 		if (yperr == 0)
1074 			map->map_mflags &= ~MF_TRY0NULL;
1075 	}
1076 	if (yperr != 0)
1077 	{
1078 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1079 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1080 		return NULL;
1081 	}
1082 	if (bitset(MF_MATCHONLY, map->map_mflags))
1083 		return map_rewrite(map, name, strlen(name), NULL);
1084 	else
1085 		return map_rewrite(map, vp, vsize, av);
1086 }
1087 
1088 #endif
1089 /*
1090 **  NISPLUS Modules
1091 **
1092 **	This code donated by Sun Microsystems.
1093 */
1094 
1095 #ifdef NISPLUS
1096 
1097 #undef NIS /* symbol conflict in nis.h */
1098 #include <rpcsvc/nis.h>
1099 #include <rpcsvc/nislib.h>
1100 
1101 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1102 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1103 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1104 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1105 
1106 /*
1107 **  NISPLUS_MAP_OPEN -- open nisplus table
1108 */
1109 
1110 bool
1111 nisplus_map_open(map, mode)
1112 	MAP *map;
1113 	int mode;
1114 {
1115 	register char *p;
1116 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1117 	nis_result *res = NULL;
1118 	u_int objs_len;
1119 	nis_object *obj_ptr;
1120 	int retry_cnt, max_col, i;
1121 
1122 	if (tTd(38, 2))
1123 		printf("nisplus_map_open(%s, %s, %d)\n",
1124 			map->map_mname, map->map_file, mode);
1125 
1126 	if (mode != O_RDONLY)
1127 	{
1128 		errno = ENODEV;
1129 		return FALSE;
1130 	}
1131 
1132 	if (*map->map_file == '\0')
1133 		map->map_file = "mail_aliases.org_dir";
1134 
1135 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1136 	{
1137 		/* set default NISPLUS Domain to $m */
1138 		extern char *nisplus_default_domain();
1139 
1140 		map->map_domain = newstr(nisplus_default_domain());
1141 		if (tTd(38, 2))
1142 			printf("nisplus_map_open(%s): using domain %s\n",
1143 				 map->map_file, map->map_domain);
1144 	}
1145 	if (!PARTIAL_NAME(map->map_file))
1146 		map->map_domain = newstr("");
1147 
1148 	/* check to see if this map actually exists */
1149 	if (PARTIAL_NAME(map->map_file))
1150 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1151 	else
1152 		strcpy(qbuf, map->map_file);
1153 
1154 	retry_cnt = 0;
1155 	while (res == NULL || res->status != NIS_SUCCESS)
1156 	{
1157 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1158 		switch (res->status)
1159 		{
1160 		  case NIS_SUCCESS:
1161 		  case NIS_TRYAGAIN:
1162 		  case NIS_RPCERROR:
1163 		  case NIS_NAMEUNREACHABLE:
1164 			break;
1165 
1166 		  default:		/* all other nisplus errors */
1167 #if 0
1168 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1169 				syserr("Cannot find table %s.%s: %s",
1170 					map->map_file, map->map_domain,
1171 					nis_sperrno(res->status));
1172 #endif
1173 			errno = EBADR;
1174 			return FALSE;
1175 		}
1176 		sleep(2);		/* try not to overwhelm hosed server */
1177 		if (retry_cnt++ > 4)
1178 		{
1179 			errno = EBADR;
1180 			return FALSE;
1181 		}
1182 	}
1183 
1184 	if (NIS_RES_NUMOBJ(res) != 1 ||
1185 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1186 	{
1187 		if (tTd(38, 10))
1188 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1189 #if 0
1190 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1191 			syserr("%s.%s: %s is not a table",
1192 				map->map_file, map->map_domain,
1193 				nis_sperrno(res->status));
1194 #endif
1195 		errno = EBADR;
1196 		return FALSE;
1197 	}
1198 	/* default key column is column 0 */
1199 	if (map->map_keycolnm == NULL)
1200 		map->map_keycolnm = newstr(COL_NAME(res,0));
1201 
1202 	max_col = COL_MAX(res);
1203 
1204 	/* verify the key column exist */
1205 	for (i=0; i< max_col; i++)
1206 	{
1207 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1208 			break;
1209 	}
1210 	if (i == max_col)
1211 	{
1212 		if (tTd(38, 2))
1213 			printf("nisplus_map_open(%s): can not find key column %s\n",
1214 				map->map_file, map->map_keycolnm);
1215 		errno = EBADR;
1216 		return FALSE;
1217 	}
1218 
1219 	/* default value column is the last column */
1220 	if (map->map_valcolnm == NULL)
1221 	{
1222 		map->map_valcolno = max_col - 1;
1223 		return TRUE;
1224 	}
1225 
1226 	for (i=0; i< max_col; i++)
1227 	{
1228 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1229 		{
1230 			map->map_valcolno = i;
1231 			return TRUE;
1232 		}
1233 	}
1234 
1235 	if (tTd(38, 2))
1236 		printf("nisplus_map_open(%s): can not find column %s\n",
1237 			 map->map_file, map->map_keycolnm);
1238 	errno = EBADR;
1239 	return FALSE;
1240 }
1241 
1242 
1243 /*
1244 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1245 */
1246 
1247 char *
1248 nisplus_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 	char search_key[MAXNAME + 1];
1258 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1259 	nis_result *result;
1260 
1261 	if (tTd(38, 20))
1262 		printf("nisplus_map_lookup(%s, %s)\n",
1263 			map->map_mname, name);
1264 
1265 	if (!bitset(MF_OPEN, map->map_mflags))
1266 	{
1267 		if (nisplus_map_open(map, O_RDONLY))
1268 			map->map_mflags |= MF_OPEN;
1269 		else
1270 		{
1271 			*statp = EX_UNAVAILABLE;
1272 			return NULL;
1273 		}
1274 	}
1275 
1276 	buflen = strlen(name);
1277 	if (buflen > sizeof search_key - 1)
1278 		buflen = sizeof search_key - 1;
1279 	bcopy(name, search_key, buflen + 1);
1280 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1281 		makelower(search_key);
1282 
1283 	/* construct the query */
1284 	if (PARTIAL_NAME(map->map_file))
1285 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1286 			search_key, map->map_file, map->map_domain);
1287 	else
1288 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1289 			search_key, map->map_file);
1290 
1291 	if (tTd(38, 20))
1292 		printf("qbuf=%s\n", qbuf);
1293 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1294 	if (result->status == NIS_SUCCESS)
1295 	{
1296 		int count;
1297 		char *str;
1298 
1299 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1300 		{
1301 			if (LogLevel > 10)
1302 				syslog(LOG_WARNING,
1303 				  "%s:Lookup error, expected 1 entry, got (%d)",
1304 				    map->map_file, count);
1305 
1306 			/* ignore second entry */
1307 			if (tTd(38, 20))
1308 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1309 					name, count);
1310 		}
1311 
1312 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1313 		/* set the length of the result */
1314 		if (vp == NULL)
1315 			vp = "";
1316 		vsize = strlen(vp);
1317 		if (tTd(38, 20))
1318 			printf("nisplus_map_lookup(%s), found %s\n",
1319 				name, vp);
1320 		if (bitset(MF_MATCHONLY, map->map_mflags))
1321 			str = map_rewrite(map, name, strlen(name), NULL);
1322 		else
1323 			str = map_rewrite(map, vp, vsize, av);
1324 		nis_freeresult(result);
1325 #ifdef MAP_EXIT_STAT
1326 		*statp = EX_OK;
1327 #endif
1328 		return str;
1329 	}
1330 	else
1331 	{
1332 #ifdef MAP_EXIT_STAT
1333 		if (result->status == NIS_NOTFOUND)
1334 			*statp = EX_NOTFOUND;
1335 		else if (result->status == NIS_TRYAGAIN)
1336 			*statp = EX_TEMPFAIL;
1337 		else
1338 		{
1339 			*statp = EX_UNAVAILABLE;
1340 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1341 		}
1342 #else
1343 		if ((result->status != NIS_NOTFOUND) &&
1344 		    (result->status != NIS_TRYAGAIN))
1345 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1346 #endif
1347 	}
1348 	if (tTd(38, 20))
1349 		printf("nisplus_map_lookup(%s), failed\n", name);
1350 	nis_freeresult(result);
1351 	return NULL;
1352 }
1353 
1354 
1355 char *
1356 nisplus_default_domain()
1357 {
1358 	static char default_domain[MAXNAME] = "";
1359 	nis_result *res = NULL;
1360 	char *p;
1361 
1362 	if (default_domain[0] != '\0')
1363 		return(default_domain);
1364 
1365 	if (VendorCode == VENDOR_SUN && ConfigLevel < 2)
1366 	{
1367 		/* for old config, user nis+ local directory        */
1368 		/* have to be backward compatible with bugs too :-( */
1369 		p = nis_local_directory();
1370 		strcpy(default_domain, p);
1371 		return default_domain;
1372 	}
1373 
1374 	if ((p = macvalue('m', CurEnv)) == NULL)
1375 	{
1376 		p = nis_local_directory();
1377 		strcpy(default_domain, p);
1378 		return default_domain;
1379 	}
1380 
1381 	strcpy(default_domain, p);
1382 	if (PARTIAL_NAME(default_domain))
1383 		strcat(default_domain, ".");
1384 
1385 	res = nis_lookup(default_domain, FOLLOW_LINKS);
1386 	if (res->status == NIS_NOTFOUND)
1387 	{
1388 		p = nis_local_directory();
1389 		strcpy(default_domain, p);
1390 	}
1391 	return(default_domain);
1392 }
1393 
1394 #endif /* NISPLUS */
1395 /*
1396 **  HESIOD Modules
1397 **
1398 **	Only works for aliases (for now).
1399 */
1400 
1401 #ifdef HESIOD
1402 
1403 char *
1404 hes_map_lookup(map, name, av, statp)
1405         MAP *map;
1406         char *name;
1407         char **av;
1408         int *statp;
1409 {
1410 	struct hes_postoffice *pobox;
1411 	char keybuf[MAXNAME + 1];
1412 
1413 	if (tTd(38, 20))
1414 		printf("hes_map_lookup(%s)\n", name);
1415 
1416 	pobox = hes_getmailhost(name);
1417 	if (pobox == NULL)
1418 		return NULL;
1419 
1420 	/* We only know how to deal with the SMTP types right now */
1421 	if (strcmp(pobox->po_type, "SMTP") != 0 &&
1422 	    strcmp(pobox->po_type, "ESMTP") != 0)
1423 		return NULL;
1424 
1425 	/* if just checking for a match, we are done */
1426 	if (bitset(MF_MATCHONLY, map->map_mflags))
1427 		return map_rewrite(map, name, strlen(name), NULL);
1428 
1429 	/* Do the rewriting with new values */
1430 	if (strlen(pobox->po_name) + strlen(pobox->po_host) + 1 > sizeof keybuf)
1431 	{
1432 		*statp = EX_DATAERR;
1433 		return NULL;
1434 	}
1435 	(void) sprintf(keybuf, "%s@%s", pobox->po_name, pobox->po_host);
1436 	return map_rewrite(map, keybuf, strlen(keybuf), av);
1437 }
1438 
1439 #endif
1440 /*
1441 **  NeXT NETINFO Modules
1442 */
1443 
1444 #ifdef NETINFO
1445 
1446 #define NETINFO_DEFAULT_DIR		"/aliases"
1447 #define NETINFO_DEFAULT_PROPERTY	"members"
1448 
1449 
1450 /*
1451 **  NI_MAP_OPEN -- open NetInfo Aliases
1452 */
1453 
1454 bool
1455 ni_map_open(map, mode)
1456 	MAP *map;
1457 	int mode;
1458 {
1459 	char *p;
1460 
1461 	if (tTd(38, 20))
1462 		printf("ni_map_open: %s\n", map->map_file);
1463 
1464 	p = strchr(map->map_file, '@');
1465 	if (p != NULL)
1466 	{
1467 		*p++ = '\0';
1468 		if (*p != '\0')
1469 			map->map_domain = p;
1470 	}
1471 	if (*map->map_file == '\0')
1472 		map->map_file = NETINFO_DEFAULT_DIR;
1473 
1474 	if (map->map_domain == NULL)
1475 		map->map_domain = NETINFO_DEFAULT_PROPERTY;
1476 
1477 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1478 		map->map_coldelim = ',';
1479 
1480 	return TRUE;
1481 }
1482 
1483 
1484 /*
1485 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1486 */
1487 
1488 char *
1489 ni_map_lookup(map, name, av, statp)
1490 	MAP *map;
1491 	char *name;
1492 	char **av;
1493 	int *statp;
1494 {
1495 	char *res;
1496 	char *propval;
1497 	extern char *ni_propval();
1498 
1499 	if (tTd(38, 20))
1500 		printf("ni_map_lookup(%s, %s)\n",
1501 			map->map_mname, name);
1502 
1503 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1504 			     map->map_valcolnm, map->map_coldelim);
1505 
1506 	if (propval == NULL)
1507 		return NULL;
1508 
1509 	if (bitset(MF_MATCHONLY, map->map_mflags))
1510 		res = map_rewrite(map, name, strlen(name), NULL);
1511 	else
1512 		res = map_rewrite(map, propval, strlen(propval), av);
1513 	free(propval);
1514 	return res;
1515 }
1516 
1517 #endif
1518 /*
1519 **  TEXT (unindexed text file) Modules
1520 **
1521 **	This code donated by Sun Microsystems.
1522 */
1523 
1524 
1525 /*
1526 **  TEXT_MAP_OPEN -- open text table
1527 */
1528 
1529 bool
1530 text_map_open(map, mode)
1531 	MAP *map;
1532 	int mode;
1533 {
1534 	struct stat sbuf;
1535 
1536 	if (tTd(38, 2))
1537 		printf("text_map_open(%s, %s, %d)\n",
1538 			map->map_mname, map->map_file, mode);
1539 
1540 	if (mode != O_RDONLY)
1541 	{
1542 		errno = ENODEV;
1543 		return FALSE;
1544 	}
1545 
1546 	if (*map->map_file == '\0')
1547 	{
1548 		if (tTd(38, 2))
1549 			printf("text_map_open: file name required\n");
1550 		return FALSE;
1551 	}
1552 
1553 	if (map->map_file[0] != '/')
1554 	{
1555 		if (tTd(38, 2))
1556 			printf("text_map_open(%s): file name must be fully qualified\n",
1557 				map->map_file);
1558 		return FALSE;
1559 	}
1560 	/* check to see if this map actually accessable */
1561 	if (access(map->map_file, R_OK) <0)
1562 		return FALSE;
1563 
1564 	/* check to see if this map actually exist */
1565 	if (stat(map->map_file, &sbuf) <0)
1566 	{
1567 		if (tTd(38, 2))
1568 			printf("text_map_open(%s): can not stat %s\n",
1569 				map->map_file, map->map_file);
1570 		return FALSE;
1571 	}
1572 
1573 	if (!S_ISREG(sbuf.st_mode))
1574 	{
1575 		if (tTd(38, 2))
1576 			printf("text_map_open(%s): %s is not a file\n",
1577 				map->map_file, map->map_file);
1578 		return FALSE;
1579 	}
1580 
1581 	if (map->map_keycolnm == NULL)
1582 		map->map_keycolno = 0;
1583 	else
1584 	{
1585 		if (!isdigit(*map->map_keycolnm))
1586 		{
1587 			if (tTd(38, 2))
1588 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1589 					map->map_file, map->map_keycolnm);
1590 			return FALSE;
1591 		}
1592 		map->map_keycolno = atoi(map->map_keycolnm);
1593 	}
1594 
1595 	if (map->map_valcolnm == NULL)
1596 		map->map_valcolno = 0;
1597 	else
1598 	{
1599 		if (!isdigit(*map->map_valcolnm))
1600 		{
1601 			if (tTd(38, 2))
1602 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1603 					map->map_file, map->map_valcolnm);
1604 			return FALSE;
1605 		}
1606 		map->map_valcolno = atoi(map->map_valcolnm);
1607 	}
1608 
1609 	if (map->map_coldelim == '\0')
1610 		map->map_coldelim = ':';
1611 
1612 	if (tTd(38, 2))
1613 	{
1614 		printf("text_map_open(%s): delimiter = %c\n",
1615 			map->map_file, map->map_coldelim);
1616 	}
1617 
1618 	return TRUE;
1619 }
1620 
1621 
1622 /*
1623 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1624 */
1625 
1626 char *
1627 text_map_lookup(map, name, av, statp)
1628 	MAP *map;
1629 	char *name;
1630 	char **av;
1631 	int *statp;
1632 {
1633 	char *vp;
1634 	auto int vsize;
1635 	int buflen;
1636 	char search_key[MAXNAME + 1];
1637 	char linebuf[MAXLINE];
1638 	FILE *f;
1639 	char buf[MAXNAME+1];
1640 	char delim;
1641 	int key_idx;
1642 	bool found_it;
1643 	extern char *get_column();
1644 
1645 
1646 	found_it = FALSE;
1647 	if (tTd(38, 20))
1648 		printf("text_map_lookup(%s)\n", name);
1649 
1650 	buflen = strlen(name);
1651 	if (buflen > sizeof search_key - 1)
1652 		buflen = sizeof search_key - 1;
1653 	bcopy(name, search_key, buflen + 1);
1654 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1655 		makelower(search_key);
1656 
1657 	f = fopen(map->map_file, "r");
1658 	if (f == NULL)
1659 	{
1660 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1661 		*statp = EX_UNAVAILABLE;
1662 		return NULL;
1663 	}
1664 	key_idx = map->map_keycolno;
1665 	delim = map->map_coldelim;
1666 	while (fgets(linebuf, MAXLINE, f))
1667 	{
1668 		char *lf;
1669 		if (linebuf[0] == '#')
1670 			continue; /* skip comment line */
1671 		if (lf = strchr(linebuf, '\n'))
1672 			*lf = '\0';
1673 		if (!strcasecmp(search_key,
1674 				get_column(linebuf, key_idx, delim, buf)))
1675 		{
1676 			found_it = TRUE;
1677 			break;
1678 		}
1679 	}
1680 	fclose(f);
1681 	if (!found_it)
1682 	{
1683 #ifdef MAP_EXIT_STAT
1684 		*statp = EX_NOTFOUND;
1685 #endif
1686 		return(NULL);
1687 	}
1688 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1689 	vsize = strlen(vp);
1690 #ifdef MAP_EXIT_STAT
1691 	*statp = EX_OK;
1692 #endif
1693 	if (bitset(MF_MATCHONLY, map->map_mflags))
1694 		return map_rewrite(map, name, strlen(name), NULL);
1695 	else
1696 		return map_rewrite(map, vp, vsize, av);
1697 }
1698 /*
1699 **  STAB (Symbol Table) Modules
1700 */
1701 
1702 
1703 /*
1704 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1705 */
1706 
1707 char *
1708 stab_map_lookup(map, name, av, pstat)
1709 	register MAP *map;
1710 	char *name;
1711 	char **av;
1712 	int *pstat;
1713 {
1714 	register STAB *s;
1715 
1716 	if (tTd(38, 20))
1717 		printf("stab_lookup(%s, %s)\n",
1718 			map->map_mname, name);
1719 
1720 	s = stab(name, ST_ALIAS, ST_FIND);
1721 	if (s != NULL)
1722 		return (s->s_alias);
1723 	return (NULL);
1724 }
1725 
1726 
1727 /*
1728 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1729 */
1730 
1731 void
1732 stab_map_store(map, lhs, rhs)
1733 	register MAP *map;
1734 	char *lhs;
1735 	char *rhs;
1736 {
1737 	register STAB *s;
1738 
1739 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1740 	s->s_alias = newstr(rhs);
1741 }
1742 
1743 
1744 /*
1745 **  STAB_MAP_OPEN -- initialize (reads data file)
1746 **
1747 **	This is a wierd case -- it is only intended as a fallback for
1748 **	aliases.  For this reason, opens for write (only during a
1749 **	"newaliases") always fails, and opens for read open the
1750 **	actual underlying text file instead of the database.
1751 */
1752 
1753 bool
1754 stab_map_open(map, mode)
1755 	register MAP *map;
1756 	int mode;
1757 {
1758 	FILE *af;
1759 	struct stat st;
1760 
1761 	if (tTd(38, 2))
1762 		printf("stab_map_open(%s, %s)\n",
1763 			map->map_mname, map->map_file);
1764 
1765 	if (mode != O_RDONLY)
1766 	{
1767 		errno = ENODEV;
1768 		return FALSE;
1769 	}
1770 
1771 	af = fopen(map->map_file, "r");
1772 	if (af == NULL)
1773 		return FALSE;
1774 	readaliases(map, af, FALSE, FALSE);
1775 
1776 	if (fstat(fileno(af), &st) >= 0)
1777 		map->map_mtime = st.st_mtime;
1778 	fclose(af);
1779 
1780 	return TRUE;
1781 }
1782 /*
1783 **  Implicit Modules
1784 **
1785 **	Tries several types.  For back compatibility of aliases.
1786 */
1787 
1788 
1789 /*
1790 **  IMPL_MAP_LOOKUP -- lookup in best open database
1791 */
1792 
1793 char *
1794 impl_map_lookup(map, name, av, pstat)
1795 	MAP *map;
1796 	char *name;
1797 	char **av;
1798 	int *pstat;
1799 {
1800 	if (tTd(38, 20))
1801 		printf("impl_map_lookup(%s, %s)\n",
1802 			map->map_mname, name);
1803 
1804 #ifdef NEWDB
1805 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1806 		return db_map_lookup(map, name, av, pstat);
1807 #endif
1808 #ifdef NDBM
1809 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1810 		return ndbm_map_lookup(map, name, av, pstat);
1811 #endif
1812 	return stab_map_lookup(map, name, av, pstat);
1813 }
1814 
1815 /*
1816 **  IMPL_MAP_STORE -- store in open databases
1817 */
1818 
1819 void
1820 impl_map_store(map, lhs, rhs)
1821 	MAP *map;
1822 	char *lhs;
1823 	char *rhs;
1824 {
1825 #ifdef NEWDB
1826 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1827 		db_map_store(map, lhs, rhs);
1828 #endif
1829 #ifdef NDBM
1830 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1831 		ndbm_map_store(map, lhs, rhs);
1832 #endif
1833 	stab_map_store(map, lhs, rhs);
1834 }
1835 
1836 /*
1837 **  IMPL_MAP_OPEN -- implicit database open
1838 */
1839 
1840 bool
1841 impl_map_open(map, mode)
1842 	MAP *map;
1843 	int mode;
1844 {
1845 	struct stat stb;
1846 
1847 	if (tTd(38, 2))
1848 		printf("impl_map_open(%s, %s, %d)\n",
1849 			map->map_mname, map->map_file, mode);
1850 
1851 	if (stat(map->map_file, &stb) < 0)
1852 	{
1853 		/* no alias file at all */
1854 		if (tTd(38, 3))
1855 			printf("no map file\n");
1856 		return FALSE;
1857 	}
1858 
1859 #ifdef NEWDB
1860 	map->map_mflags |= MF_IMPL_HASH;
1861 	if (hash_map_open(map, mode))
1862 	{
1863 #if defined(NDBM) && defined(NIS)
1864 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1865 #endif
1866 			return TRUE;
1867 	}
1868 	else
1869 		map->map_mflags &= ~MF_IMPL_HASH;
1870 #endif
1871 #ifdef NDBM
1872 	map->map_mflags |= MF_IMPL_NDBM;
1873 	if (ndbm_map_open(map, mode))
1874 	{
1875 		return TRUE;
1876 	}
1877 	else
1878 		map->map_mflags &= ~MF_IMPL_NDBM;
1879 #endif
1880 
1881 #if defined(NEWDB) || defined(NDBM)
1882 	if (Verbose)
1883 		message("WARNING: cannot open alias database %s", map->map_file);
1884 #else
1885 	if (mode != O_RDONLY)
1886 		usrerr("Cannot rebuild aliases: no database format defined");
1887 #endif
1888 
1889 	return stab_map_open(map, mode);
1890 }
1891 
1892 
1893 /*
1894 **  IMPL_MAP_CLOSE -- close any open database(s)
1895 */
1896 
1897 void
1898 impl_map_close(map)
1899 	MAP *map;
1900 {
1901 #ifdef NEWDB
1902 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1903 	{
1904 		db_map_close(map);
1905 		map->map_mflags &= ~MF_IMPL_HASH;
1906 	}
1907 #endif
1908 
1909 #ifdef NDBM
1910 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1911 	{
1912 		ndbm_map_close(map);
1913 		map->map_mflags &= ~MF_IMPL_NDBM;
1914 	}
1915 #endif
1916 }
1917 /*
1918 **  User map class.
1919 **
1920 **	Provides access to the system password file.
1921 */
1922 
1923 /*
1924 **  USER_MAP_OPEN -- open user map
1925 **
1926 **	Really just binds field names to field numbers.
1927 */
1928 
1929 bool
1930 user_map_open(map, mode)
1931 	MAP *map;
1932 	int mode;
1933 {
1934 	if (tTd(38, 2))
1935 		printf("user_map_open(%s)\n", map->map_mname);
1936 
1937 	if (mode != O_RDONLY)
1938 	{
1939 		/* issue a pseudo-error message */
1940 #ifdef ENOSYS
1941 		errno = ENOSYS;
1942 #else
1943 # ifdef EFTYPE
1944 		errno = EFTYPE;
1945 # else
1946 		errno = ENXIO;
1947 # endif
1948 #endif
1949 		return FALSE;
1950 	}
1951 	if (map->map_valcolnm == NULL)
1952 		/* nothing */ ;
1953 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1954 		map->map_valcolno = 1;
1955 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1956 		map->map_valcolno = 2;
1957 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1958 		map->map_valcolno = 3;
1959 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
1960 		map->map_valcolno = 4;
1961 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
1962 		map->map_valcolno = 5;
1963 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
1964 		map->map_valcolno = 6;
1965 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
1966 		map->map_valcolno = 7;
1967 	else
1968 	{
1969 		syserr("User map %s: unknown column name %s",
1970 			map->map_mname, map->map_valcolnm);
1971 		return FALSE;
1972 	}
1973 	return TRUE;
1974 }
1975 
1976 
1977 /*
1978 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
1979 */
1980 
1981 #include <pwd.h>
1982 
1983 char *
1984 user_map_lookup(map, key, av, statp)
1985 	MAP *map;
1986 	char *key;
1987 	char **av;
1988 	int *statp;
1989 {
1990 	struct passwd *pw;
1991 
1992 	if (tTd(38, 20))
1993 		printf("user_map_lookup(%s, %s)\n",
1994 			map->map_mname, key);
1995 
1996 	pw = getpwnam(key);
1997 	if (pw == NULL)
1998 		return NULL;
1999 	if (bitset(MF_MATCHONLY, map->map_mflags))
2000 		return map_rewrite(map, key, strlen(key), NULL);
2001 	else
2002 	{
2003 		char *rwval;
2004 		char buf[30];
2005 
2006 		switch (map->map_valcolno)
2007 		{
2008 		  case 0:
2009 		  case 1:
2010 			rwval = pw->pw_name;
2011 			break;
2012 
2013 		  case 2:
2014 			rwval = pw->pw_passwd;
2015 			break;
2016 
2017 		  case 3:
2018 			sprintf(buf, "%d", pw->pw_uid);
2019 			rwval = buf;
2020 			break;
2021 
2022 		  case 4:
2023 			sprintf(buf, "%d", pw->pw_gid);
2024 			rwval = buf;
2025 			break;
2026 
2027 		  case 5:
2028 			rwval = pw->pw_gecos;
2029 			break;
2030 
2031 		  case 6:
2032 			rwval = pw->pw_dir;
2033 			break;
2034 
2035 		  case 7:
2036 			rwval = pw->pw_shell;
2037 			break;
2038 		}
2039 		return map_rewrite(map, rwval, strlen(rwval), av);
2040 	}
2041 }
2042 /*
2043 **  Sequenced map type.
2044 **
2045 **	Tries each map in order until something matches, much like
2046 **	implicit.  Stores go to the first map in the list that can
2047 **	support storing.
2048 **
2049 **	This is slightly unusual in that there are two interfaces.
2050 **	The "sequence" interface lets you stack maps arbitrarily.
2051 **	The "switch" interface builds a sequence map by looking
2052 **	at a system-dependent configuration file such as
2053 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2054 **
2055 **	We don't need an explicit open, since all maps are
2056 **	opened during startup, including underlying maps.
2057 */
2058 
2059 /*
2060 **  SEQ_MAP_PARSE -- Sequenced map parsing
2061 */
2062 
2063 bool
2064 seq_map_parse(map, ap)
2065 	MAP *map;
2066 	char *ap;
2067 {
2068 	int maxmap;
2069 
2070 	if (tTd(38, 2))
2071 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2072 	maxmap = 0;
2073 	while (*ap != '\0')
2074 	{
2075 		register char *p;
2076 		STAB *s;
2077 
2078 		/* find beginning of map name */
2079 		while (isascii(*ap) && isspace(*ap))
2080 			ap++;
2081 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2082 			continue;
2083 		if (*p != '\0')
2084 			*p++ = '\0';
2085 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2086 			p++;
2087 		if (*ap == '\0')
2088 		{
2089 			ap = p;
2090 			continue;
2091 		}
2092 		s = stab(ap, ST_MAP, ST_FIND);
2093 		if (s == NULL)
2094 		{
2095 			syserr("Sequence map %s: unknown member map %s",
2096 				map->map_mname, ap);
2097 		}
2098 		else if (maxmap == MAXMAPSTACK)
2099 		{
2100 			syserr("Sequence map %s: too many member maps (%d max)",
2101 				map->map_mname, MAXMAPSTACK);
2102 			maxmap++;
2103 		}
2104 		else if (maxmap < MAXMAPSTACK)
2105 		{
2106 			map->map_stack[maxmap++] = &s->s_map;
2107 		}
2108 		ap = p;
2109 	}
2110 	return TRUE;
2111 }
2112 
2113 
2114 /*
2115 **  SWITCH_MAP_OPEN -- open a switched map
2116 **
2117 **	This looks at the system-dependent configuration and builds
2118 **	a sequence map that does the same thing.
2119 **
2120 **	Every system must define a switch_map_find routine in conf.c
2121 **	that will return the list of service types associated with a
2122 **	given service class.
2123 */
2124 
2125 bool
2126 switch_map_open(map, mode)
2127 	MAP *map;
2128 	int mode;
2129 {
2130 	int mapno;
2131 	int nmaps;
2132 	char *maptype[MAXMAPSTACK];
2133 
2134 	if (tTd(38, 2))
2135 		printf("switch_map_open(%s, %s, %d)\n",
2136 			map->map_mname, map->map_file, mode);
2137 
2138 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2139 	if (tTd(38, 19))
2140 	{
2141 		printf("\tswitch_map_find => %d\n", nmaps);
2142 		for (mapno = 0; mapno < nmaps; mapno++)
2143 			printf("\t\t%s\n", maptype[mapno]);
2144 	}
2145 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2146 		return FALSE;
2147 
2148 	for (mapno = 0; mapno < nmaps; mapno++)
2149 	{
2150 		register STAB *s;
2151 		char nbuf[MAXNAME + 1];
2152 
2153 		if (maptype[mapno] == NULL)
2154 			continue;
2155 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2156 		s = stab(nbuf, ST_MAP, ST_FIND);
2157 		if (s == NULL)
2158 		{
2159 			syserr("Switch map %s: unknown member map %s",
2160 				map->map_mname, nbuf);
2161 		}
2162 		else
2163 		{
2164 			map->map_stack[mapno] = &s->s_map;
2165 			if (tTd(38, 4))
2166 				printf("\tmap_stack[%d] = %s:%s\n",
2167 					mapno, s->s_map.map_class->map_cname,
2168 					nbuf);
2169 		}
2170 	}
2171 	return TRUE;
2172 }
2173 
2174 
2175 /*
2176 **  SEQ_MAP_CLOSE -- close all underlying maps
2177 */
2178 
2179 seq_map_close(map)
2180 	MAP *map;
2181 {
2182 	int mapno;
2183 
2184 	if (tTd(38, 20))
2185 		printf("seq_map_close(%s)\n", map->map_mname);
2186 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2187 	{
2188 		MAP *mm = map->map_stack[mapno];
2189 
2190 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2191 			continue;
2192 		mm->map_class->map_close(mm);
2193 	}
2194 }
2195 
2196 
2197 /*
2198 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2199 */
2200 
2201 char *
2202 seq_map_lookup(map, key, args, pstat)
2203 	MAP *map;
2204 	char *key;
2205 	char **args;
2206 	int *pstat;
2207 {
2208 	int mapno;
2209 	int mapbit = 0x01;
2210 
2211 	if (tTd(38, 20))
2212 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2213 
2214 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2215 	{
2216 		MAP *mm = map->map_stack[mapno];
2217 		int stat = 0;
2218 		char *rv;
2219 
2220 		if (mm == NULL)
2221 			continue;
2222 		if (!bitset(MF_OPEN, mm->map_mflags))
2223 		{
2224 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2225 			{
2226 				*pstat = EX_UNAVAILABLE;
2227 				return NULL;
2228 			}
2229 			continue;
2230 		}
2231 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2232 		if (rv != NULL)
2233 			return rv;
2234 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2235 			return NULL;
2236 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2237 		{
2238 			*pstat = stat;
2239 			return NULL;
2240 		}
2241 	}
2242 	return NULL;
2243 }
2244 
2245 
2246 /*
2247 **  SEQ_MAP_STORE -- sequenced map store
2248 */
2249 
2250 void
2251 seq_map_store(map, key, val)
2252 	MAP *map;
2253 	char *key;
2254 	char *val;
2255 {
2256 	int mapno;
2257 
2258 	if (tTd(38, 12))
2259 		printf("seq_map_store(%s, %s, %s)\n",
2260 			map->map_mname, key, val);
2261 
2262 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2263 	{
2264 		MAP *mm = map->map_stack[mapno];
2265 
2266 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2267 			continue;
2268 
2269 		mm->map_class->map_store(mm, key, val);
2270 		return;
2271 	}
2272 	syserr("seq_map_store(%s, %s, %s): no writable map",
2273 		map->map_mname, key, val);
2274 }
2275 /*
2276 **  NULL stubs
2277 */
2278 
2279 bool
2280 null_map_open(map, mode)
2281 	MAP *map;
2282 	int mode;
2283 {
2284 	return TRUE;
2285 }
2286 
2287 void
2288 null_map_close(map)
2289 	MAP *map;
2290 {
2291 	return;
2292 }
2293 
2294 void
2295 null_map_store(map, key, val)
2296 	MAP *map;
2297 	char *key;
2298 	char *val;
2299 {
2300 	return;
2301 }
2302