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