xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 413c9d05)
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.27 (Berkeley) 05/20/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 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
123 			p++;
124 		if (*p != '\0')
125 			*p++ = '\0';
126 	}
127 	if (map->map_app != NULL)
128 		map->map_app = newstr(map->map_app);
129 
130 	if (*p != '\0')
131 	{
132 		map->map_file = p;
133 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
134 			p++;
135 		if (*p != '\0')
136 			*p++ = '\0';
137 		map->map_file = newstr(map->map_file);
138 	}
139 
140 	while (*p != '\0' && isascii(*p) && isspace(*p))
141 		p++;
142 	if (*p != '\0')
143 		map->map_rebuild = newstr(p);
144 
145 	if (map->map_file == NULL)
146 	{
147 		syserr("No file name for %s map %s",
148 			map->map_class->map_cname, map->map_mname);
149 		return FALSE;
150 	}
151 	return TRUE;
152 }
153 /*
154 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
155 **
156 **	It also adds the map_app string.  It can be used as a utility
157 **	in the map_lookup method.
158 **
159 **	Parameters:
160 **		map -- the map that causes this.
161 **		s -- the string to rewrite, NOT necessarily null terminated.
162 **		slen -- the length of s.
163 **		av -- arguments to interpolate into buf.
164 **
165 **	Returns:
166 **		Pointer to rewritten result.
167 **
168 **	Side Effects:
169 **		none.
170 */
171 
172 struct rwbuf
173 {
174 	int	rwb_len;	/* size of buffer */
175 	char	*rwb_buf;	/* ptr to buffer */
176 };
177 
178 struct rwbuf	RwBufs[2];	/* buffers for rewriting output */
179 
180 char *
181 map_rewrite(map, s, slen, av)
182 	register MAP *map;
183 	register char *s;
184 	int slen;
185 	char **av;
186 {
187 	register char *bp;
188 	register char c;
189 	char **avp;
190 	register char *ap;
191 	register struct rwbuf *rwb;
192 	int i;
193 	int len;
194 
195 	if (tTd(39, 1))
196 	{
197 		printf("map_rewrite(%.*s), av =", slen, s);
198 		if (av == NULL)
199 			printf(" (nullv)");
200 		else
201 		{
202 			for (avp = av; *avp != NULL; avp++)
203 				printf("\n\t%s", *avp);
204 		}
205 		printf("\n");
206 	}
207 
208 	rwb = RwBufs;
209 	if (av == NULL)
210 		rwb++;
211 
212 	/* count expected size of output (can safely overestimate) */
213 	i = len = slen;
214 	if (av != NULL)
215 	{
216 		bp = s;
217 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
218 		{
219 			if (c != '%')
220 				continue;
221 			if (--i < 0)
222 				break;
223 			c = *bp++;
224 			if (!(isascii(c) && isdigit(c)))
225 				continue;
226 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
227 				continue;
228 			if (*avp == NULL)
229 				continue;
230 			len += strlen(*avp);
231 		}
232 	}
233 	if (map->map_app != NULL)
234 		len += strlen(map->map_app);
235 	if (rwb->rwb_len < ++len)
236 	{
237 		/* need to malloc additional space */
238 		rwb->rwb_len = len;
239 		if (rwb->rwb_buf != NULL)
240 			free(rwb->rwb_buf);
241 		rwb->rwb_buf = xalloc(rwb->rwb_len);
242 	}
243 
244 	bp = rwb->rwb_buf;
245 	if (av == NULL)
246 	{
247 		bcopy(s, bp, slen);
248 		bp += slen;
249 	}
250 	else
251 	{
252 		while (--slen >= 0 && (c = *s++) != '\0')
253 		{
254 			if (c != '%')
255 			{
256   pushc:
257 				*bp++ = c;
258 				continue;
259 			}
260 			if (--slen < 0 || (c = *s++) == '\0')
261 				c = '%';
262 			if (c == '%')
263 				goto pushc;
264 			if (!(isascii(c) && isdigit(c)))
265 			{
266 				*bp++ = '%';
267 				goto pushc;
268 			}
269 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
270 				continue;
271 			if (*avp == NULL)
272 				continue;
273 
274 			/* transliterate argument into output string */
275 			for (ap = *avp; (c = *ap++) != '\0'; )
276 				*bp++ = c;
277 		}
278 	}
279 	if (map->map_app != NULL)
280 		strcpy(bp, map->map_app);
281 	else
282 		*bp = '\0';
283 	if (tTd(39, 1))
284 		printf("map_rewrite => %s\n", rwb->rwb_buf);
285 	return rwb->rwb_buf;
286 }
287 /*
288 **  INITMAPS -- initialize for aliasing
289 **
290 **	Parameters:
291 **		rebuild -- if TRUE, this rebuilds the cached versions.
292 **		e -- current envelope.
293 **
294 **	Returns:
295 **		none.
296 **
297 **	Side Effects:
298 **		initializes aliases:
299 **		if NDBM:  opens the database.
300 **		if ~NDBM: reads the aliases into the symbol table.
301 */
302 
303 initmaps(rebuild, e)
304 	bool rebuild;
305 	register ENVELOPE *e;
306 {
307 	extern void map_init();
308 
309 #ifdef XDEBUG
310 	checkfd012("entering initmaps");
311 #endif
312 	CurEnv = e;
313 	if (rebuild)
314 	{
315 		stabapply(map_init, 1);
316 		stabapply(map_init, 2);
317 	}
318 	else
319 	{
320 		stabapply(map_init, 0);
321 	}
322 #ifdef XDEBUG
323 	checkfd012("exiting initmaps");
324 #endif
325 }
326 
327 void
328 map_init(s, rebuild)
329 	register STAB *s;
330 	int rebuild;
331 {
332 	register MAP *map;
333 
334 	/* has to be a map */
335 	if (s->s_type != ST_MAP)
336 		return;
337 
338 	map = &s->s_map;
339 	if (!bitset(MF_VALID, map->map_mflags))
340 		return;
341 
342 	if (tTd(38, 2))
343 		printf("map_init(%s:%s, %d)\n",
344 			map->map_class->map_cname == NULL ? "NULL" :
345 				map->map_class->map_cname,
346 			map->map_file == NULL ? "NULL" : map->map_file,
347 			rebuild);
348 
349 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
350 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
351 	{
352 		if (tTd(38, 3))
353 			printf("\twrong pass\n");
354 		return;
355 	}
356 
357 	/* if already open, close it (for nested open) */
358 	if (bitset(MF_OPEN, map->map_mflags))
359 	{
360 		map->map_class->map_close(map);
361 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
362 	}
363 
364 	if (rebuild == 2)
365 	{
366 		rebuildaliases(map, FALSE);
367 	}
368 	else
369 	{
370 		if (map->map_class->map_open(map, O_RDONLY))
371 		{
372 			if (tTd(38, 4))
373 				printf("\t%s:%s: valid\n",
374 					map->map_class->map_cname == NULL ? "NULL" :
375 						map->map_class->map_cname,
376 					map->map_file == NULL ? "NULL" :
377 						map->map_file);
378 			map->map_mflags |= MF_OPEN;
379 		}
380 		else if (tTd(38, 4))
381 			printf("\t%s:%s: invalid: %s\n",
382 				map->map_class->map_cname == NULL ? "NULL" :
383 					map->map_class->map_cname,
384 				map->map_file == NULL ? "NULL" :
385 					map->map_file,
386 				errstring(errno));
387 	}
388 }
389 /*
390 **  NDBM modules
391 */
392 
393 #ifdef NDBM
394 
395 /*
396 **  DBM_MAP_OPEN -- DBM-style map open
397 */
398 
399 bool
400 ndbm_map_open(map, mode)
401 	MAP *map;
402 	int mode;
403 {
404 	register DBM *dbm;
405 	struct stat st;
406 
407 	if (tTd(38, 2))
408 		printf("ndbm_map_open(%s, %d)\n", map->map_file, mode);
409 
410 	if (mode == O_RDWR)
411 		mode |= O_CREAT|O_TRUNC;
412 
413 	/* open the database */
414 	dbm = dbm_open(map->map_file, mode, DBMMODE);
415 	if (dbm == NULL)
416 	{
417 #ifdef MAYBENEXTRELEASE
418 		if (aliaswait(map, ".pag", FALSE))
419 			return TRUE;
420 #endif
421 		if (!bitset(MF_OPTIONAL, map->map_mflags))
422 			syserr("Cannot open DBM database %s", map->map_file);
423 		return FALSE;
424 	}
425 	map->map_db1 = (void *) dbm;
426 	if (mode == O_RDONLY)
427 	{
428 		if (bitset(MF_ALIAS, map->map_mflags) &&
429 		    !aliaswait(map, ".pag", TRUE))
430 			return FALSE;
431 	}
432 	else
433 	{
434 		int fd;
435 
436 		/* exclusive lock for duration of rebuild */
437 		fd = dbm_dirfno((DBM *) map->map_db1);
438 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
439 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
440 			map->map_mflags |= MF_LOCKED;
441 	}
442 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
443 		map->map_mtime = st.st_mtime;
444 	return TRUE;
445 }
446 
447 
448 /*
449 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
450 */
451 
452 char *
453 ndbm_map_lookup(map, name, av, statp)
454 	MAP *map;
455 	char *name;
456 	char **av;
457 	int *statp;
458 {
459 	datum key, val;
460 	int fd;
461 	char keybuf[MAXNAME + 1];
462 
463 	if (tTd(38, 20))
464 		printf("ndbm_map_lookup(%s)\n", name);
465 
466 	key.dptr = name;
467 	key.dsize = strlen(name);
468 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
469 	{
470 		if (key.dsize > sizeof keybuf - 1)
471 			key.dsize = sizeof keybuf - 1;
472 		bcopy(key.dptr, keybuf, key.dsize + 1);
473 		makelower(keybuf);
474 		key.dptr = keybuf;
475 	}
476 	fd = dbm_dirfno((DBM *) map->map_db1);
477 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
478 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
479 	val.dptr = NULL;
480 	if (bitset(MF_TRY0NULL, map->map_mflags))
481 	{
482 		val = dbm_fetch((DBM *) map->map_db1, key);
483 		if (val.dptr != NULL)
484 			map->map_mflags &= ~MF_TRY1NULL;
485 	}
486 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
487 	{
488 		key.dsize++;
489 		val = dbm_fetch((DBM *) map->map_db1, key);
490 		if (val.dptr != NULL)
491 			map->map_mflags &= ~MF_TRY0NULL;
492 	}
493 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
494 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
495 	if (val.dptr == NULL)
496 		return NULL;
497 	if (bitset(MF_MATCHONLY, map->map_mflags))
498 		return map_rewrite(map, name, strlen(name), NULL);
499 	else
500 		return map_rewrite(map, val.dptr, val.dsize, av);
501 }
502 
503 
504 /*
505 **  DBM_MAP_STORE -- store a datum in the database
506 */
507 
508 void
509 ndbm_map_store(map, lhs, rhs)
510 	register MAP *map;
511 	char *lhs;
512 	char *rhs;
513 {
514 	datum key;
515 	datum data;
516 	int stat;
517 
518 	if (tTd(38, 12))
519 		printf("ndbm_map_store(%s, %s)\n", lhs, rhs);
520 
521 	key.dsize = strlen(lhs);
522 	key.dptr = lhs;
523 
524 	data.dsize = strlen(rhs);
525 	data.dptr = rhs;
526 
527 	if (bitset(MF_INCLNULL, map->map_mflags))
528 	{
529 		key.dsize++;
530 		data.dsize++;
531 	}
532 
533 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
534 	if (stat > 0)
535 	{
536 		usrerr("050 Warning: duplicate alias name %s", lhs);
537 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
538 	}
539 	if (stat != 0)
540 		syserr("readaliases: dbm put (%s)", lhs);
541 }
542 
543 
544 /*
545 **  NDBM_MAP_CLOSE -- close the database
546 */
547 
548 void
549 ndbm_map_close(map)
550 	register MAP  *map;
551 {
552 	if (tTd(38, 9))
553 		printf("ndbm_map_close(%s, %x)\n", map->map_file, map->map_mflags);
554 
555 	if (bitset(MF_WRITABLE, map->map_mflags))
556 	{
557 #ifdef NIS
558 		bool inclnull;
559 		char buf[200];
560 
561 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
562 		map->map_mflags &= ~MF_INCLNULL;
563 
564 		(void) sprintf(buf, "%010ld", curtime());
565 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
566 
567 		(void) gethostname(buf, sizeof buf);
568 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
569 
570 		if (inclnull)
571 			map->map_mflags |= MF_INCLNULL;
572 #endif
573 
574 		/* write out the distinguished alias */
575 		ndbm_map_store(map, "@", "@");
576 	}
577 	dbm_close((DBM *) map->map_db1);
578 }
579 
580 #endif
581 /*
582 **  NEWDB (Hash and BTree) Modules
583 */
584 
585 #ifdef NEWDB
586 
587 /*
588 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
589 **
590 **	These do rather bizarre locking.  If you can lock on open,
591 **	do that to avoid the condition of opening a database that
592 **	is being rebuilt.  If you don't, we'll try to fake it, but
593 **	there will be a race condition.  If opening for read-only,
594 **	we immediately release the lock to avoid freezing things up.
595 **	We really ought to hold the lock, but guarantee that we won't
596 **	be pokey about it.  That's hard to do.
597 */
598 
599 bool
600 bt_map_open(map, mode)
601 	MAP *map;
602 	int mode;
603 {
604 	DB *db;
605 	int i;
606 	int omode;
607 	int fd;
608 	struct stat st;
609 	char buf[MAXNAME];
610 
611 	if (tTd(38, 2))
612 		printf("bt_map_open(%s, %d)\n", map->map_file, mode);
613 
614 	omode = mode;
615 	if (omode == O_RDWR)
616 	{
617 		omode |= O_CREAT|O_TRUNC;
618 #if defined(O_EXLOCK) && HASFLOCK
619 		omode |= O_EXLOCK;
620 # if !OLD_NEWDB
621 	}
622 	else
623 	{
624 		omode |= O_SHLOCK;
625 # endif
626 #endif
627 	}
628 
629 	(void) strcpy(buf, map->map_file);
630 	i = strlen(buf);
631 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
632 		(void) strcat(buf, ".db");
633 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
634 	if (db == NULL)
635 	{
636 #ifdef MAYBENEXTRELEASE
637 		if (aliaswait(map, ".db", FALSE))
638 			return TRUE;
639 #endif
640 		if (!bitset(MF_OPTIONAL, map->map_mflags))
641 			syserr("Cannot open BTREE database %s", map->map_file);
642 		return FALSE;
643 	}
644 #if !OLD_NEWDB
645 	fd = db->fd(db);
646 # if HASFLOCK
647 #  if !defined(O_EXLOCK)
648 	if (mode == O_RDWR && fd >= 0)
649 	{
650 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
651 			map->map_mflags |= MF_LOCKED;
652 	}
653 #  else
654 	if (mode == O_RDONLY && fd >= 0)
655 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
656 	else
657 		map->map_mflags |= MF_LOCKED;
658 #  endif
659 # endif
660 #endif
661 
662 	/* try to make sure that at least the database header is on disk */
663 	if (mode == O_RDWR)
664 #if OLD_NEWDB
665 		(void) db->sync(db);
666 #else
667 		(void) db->sync(db, 0);
668 
669 	if (fd >= 0 && fstat(fd, &st) >= 0)
670 		map->map_mtime = st.st_mtime;
671 #endif
672 
673 	map->map_db2 = (void *) db;
674 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
675 		if (!aliaswait(map, ".db", TRUE))
676 			return FALSE;
677 	return TRUE;
678 }
679 
680 
681 /*
682 **  HASH_MAP_INIT -- HASH-style map initialization
683 */
684 
685 bool
686 hash_map_open(map, mode)
687 	MAP *map;
688 	int mode;
689 {
690 	DB *db;
691 	int i;
692 	int omode;
693 	int fd;
694 	struct stat st;
695 	char buf[MAXNAME];
696 
697 	if (tTd(38, 2))
698 		printf("hash_map_open(%s, %d)\n", map->map_file, mode);
699 
700 	omode = mode;
701 	if (omode == O_RDWR)
702 	{
703 		omode |= O_CREAT|O_TRUNC;
704 #if defined(O_EXLOCK) && HASFLOCK
705 		omode |= O_EXLOCK;
706 # if !OLD_NEWDB
707 	}
708 	else
709 	{
710 		omode |= O_SHLOCK;
711 # endif
712 #endif
713 	}
714 
715 	(void) strcpy(buf, map->map_file);
716 	i = strlen(buf);
717 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
718 		(void) strcat(buf, ".db");
719 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
720 	if (db == NULL)
721 	{
722 #ifdef MAYBENEXTRELEASE
723 		if (aliaswait(map, ".db", FALSE))
724 			return TRUE;
725 #endif
726 		if (!bitset(MF_OPTIONAL, map->map_mflags))
727 			syserr("Cannot open HASH database %s", map->map_file);
728 		return FALSE;
729 	}
730 #if !OLD_NEWDB
731 	fd = db->fd(db);
732 # if HASFLOCK
733 #  if !defined(O_EXLOCK)
734 	if (mode == O_RDWR && fd >= 0)
735 	{
736 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
737 			map->map_mflags |= MF_LOCKED;
738 	}
739 #  else
740 	if (mode == O_RDONLY && fd >= 0)
741 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
742 	else
743 		map->map_mflags |= MF_LOCKED;
744 #  endif
745 # endif
746 #endif
747 
748 	/* try to make sure that at least the database header is on disk */
749 	if (mode == O_RDWR)
750 #if OLD_NEWDB
751 		(void) db->sync(db);
752 #else
753 		(void) db->sync(db, 0);
754 
755 	if (fd >= 0 && fstat(fd, &st) >= 0)
756 		map->map_mtime = st.st_mtime;
757 #endif
758 
759 	map->map_db2 = (void *) db;
760 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
761 		if (!aliaswait(map, ".db", TRUE))
762 			return FALSE;
763 	return TRUE;
764 }
765 
766 
767 /*
768 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
769 */
770 
771 char *
772 db_map_lookup(map, name, av, statp)
773 	MAP *map;
774 	char *name;
775 	char **av;
776 	int *statp;
777 {
778 	DBT key, val;
779 	register DB *db = (DB *) map->map_db2;
780 	int st;
781 	int saveerrno;
782 	int fd;
783 	char keybuf[MAXNAME + 1];
784 
785 	if (tTd(38, 20))
786 		printf("db_map_lookup(%s)\n", name);
787 
788 	key.size = strlen(name);
789 	if (key.size > sizeof keybuf - 1)
790 		key.size = sizeof keybuf - 1;
791 	key.data = keybuf;
792 	bcopy(name, keybuf, key.size + 1);
793 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
794 		makelower(keybuf);
795 #if !OLD_NEWDB
796 	fd = db->fd(db);
797 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
798 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
799 #endif
800 	st = 1;
801 	if (bitset(MF_TRY0NULL, map->map_mflags))
802 	{
803 		st = db->get(db, &key, &val, 0);
804 		if (st == 0)
805 			map->map_mflags &= ~MF_TRY1NULL;
806 	}
807 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
808 	{
809 		key.size++;
810 		st = db->get(db, &key, &val, 0);
811 		if (st == 0)
812 			map->map_mflags &= ~MF_TRY0NULL;
813 	}
814 	saveerrno = errno;
815 #if !OLD_NEWDB
816 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
817 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
818 #endif
819 	if (st != 0)
820 	{
821 		errno = saveerrno;
822 		if (st < 0)
823 			syserr("db_map_lookup: get (%s)", name);
824 		return NULL;
825 	}
826 	if (bitset(MF_MATCHONLY, map->map_mflags))
827 		return map_rewrite(map, name, strlen(name), NULL);
828 	else
829 		return map_rewrite(map, val.data, val.size, av);
830 }
831 
832 
833 /*
834 **  DB_MAP_STORE -- store a datum in the NEWDB database
835 */
836 
837 void
838 db_map_store(map, lhs, rhs)
839 	register MAP *map;
840 	char *lhs;
841 	char *rhs;
842 {
843 	int stat;
844 	DBT key;
845 	DBT data;
846 	register DB *db = map->map_db2;
847 
848 	if (tTd(38, 20))
849 		printf("db_map_store(%s, %s)\n", lhs, rhs);
850 
851 	key.size = strlen(lhs);
852 	key.data = lhs;
853 
854 	data.size = strlen(rhs);
855 	data.data = rhs;
856 
857 	if (bitset(MF_INCLNULL, map->map_mflags))
858 	{
859 		key.size++;
860 		data.size++;
861 	}
862 
863 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
864 	if (stat > 0)
865 	{
866 		usrerr("050 Warning: duplicate alias name %s", lhs);
867 		stat = db->put(db, &key, &data, 0);
868 	}
869 	if (stat != 0)
870 		syserr("readaliases: db put (%s)", lhs);
871 }
872 
873 
874 /*
875 **  DB_MAP_CLOSE -- add distinguished entries and close the database
876 */
877 
878 void
879 db_map_close(map)
880 	MAP *map;
881 {
882 	register DB *db = map->map_db2;
883 
884 	if (tTd(38, 9))
885 		printf("db_map_close(%s, %x)\n", map->map_file, map->map_mflags);
886 
887 	if (bitset(MF_WRITABLE, map->map_mflags))
888 	{
889 		/* write out the distinguished alias */
890 		db_map_store(map, "@", "@");
891 	}
892 
893 	if (db->close(db) != 0)
894 		syserr("readaliases: db close failure");
895 }
896 
897 #endif
898 /*
899 **  NIS Modules
900 */
901 
902 # ifdef NIS
903 
904 # ifndef YPERR_BUSY
905 #  define YPERR_BUSY	16
906 # endif
907 
908 /*
909 **  NIS_MAP_OPEN -- open DBM map
910 */
911 
912 bool
913 nis_map_open(map, mode)
914 	MAP *map;
915 	int mode;
916 {
917 	int yperr;
918 	register char *p;
919 	auto char *vp;
920 	auto int vsize;
921 	char *master;
922 
923 	if (tTd(38, 2))
924 		printf("nis_map_open(%s)\n", map->map_file);
925 
926 	if (mode != O_RDONLY)
927 	{
928 		/* issue a pseudo-error message */
929 #ifdef ENOSYS
930 		errno = ENOSYS;
931 #else
932 # ifdef EFTYPE
933 		errno = EFTYPE;
934 # else
935 		errno = ENXIO;
936 # endif
937 #endif
938 		return FALSE;
939 	}
940 
941 	p = strchr(map->map_file, '@');
942 	if (p != NULL)
943 	{
944 		*p++ = '\0';
945 		if (*p != '\0')
946 			map->map_domain = p;
947 	}
948 
949 	if (*map->map_file == '\0')
950 		map->map_file = "mail.aliases";
951 
952 	if (map->map_domain == NULL)
953 	{
954 		yperr = yp_get_default_domain(&map->map_domain);
955 		if (yperr != 0)
956 		{
957 			if (!bitset(MF_OPTIONAL, map->map_mflags))
958 				syserr("NIS map %s specified, but NIS not running\n",
959 					map->map_file);
960 			return FALSE;
961 		}
962 	}
963 
964 	/* check to see if this map actually exists */
965 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
966 			&vp, &vsize);
967 	if (tTd(38, 10))
968 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
969 			map->map_domain, map->map_file, yperr_string(yperr));
970 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
971 		return TRUE;
972 
973 	if (!bitset(MF_OPTIONAL, map->map_mflags))
974 		syserr("Cannot bind to domain %s: %s", map->map_domain,
975 			yperr_string(yperr));
976 
977 	return FALSE;
978 }
979 
980 
981 /*
982 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
983 */
984 
985 char *
986 nis_map_lookup(map, name, av, statp)
987 	MAP *map;
988 	char *name;
989 	char **av;
990 	int *statp;
991 {
992 	char *vp;
993 	auto int vsize;
994 	int buflen;
995 	int yperr;
996 	char keybuf[MAXNAME + 1];
997 
998 	if (tTd(38, 20))
999 		printf("nis_map_lookup(%s)\n", name);
1000 
1001 	buflen = strlen(name);
1002 	if (buflen > sizeof keybuf - 1)
1003 		buflen = sizeof keybuf - 1;
1004 	bcopy(name, keybuf, buflen + 1);
1005 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1006 		makelower(keybuf);
1007 	yperr = YPERR_KEY;
1008 	if (bitset(MF_TRY0NULL, map->map_mflags))
1009 	{
1010 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1011 			     &vp, &vsize);
1012 		if (yperr == 0)
1013 			map->map_mflags &= ~MF_TRY1NULL;
1014 	}
1015 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1016 	{
1017 		buflen++;
1018 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1019 			     &vp, &vsize);
1020 		if (yperr == 0)
1021 			map->map_mflags &= ~MF_TRY0NULL;
1022 	}
1023 	if (yperr != 0)
1024 	{
1025 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1026 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1027 		return NULL;
1028 	}
1029 	if (bitset(MF_MATCHONLY, map->map_mflags))
1030 		return map_rewrite(map, name, strlen(name), NULL);
1031 	else
1032 		return map_rewrite(map, vp, vsize, av);
1033 }
1034 
1035 
1036 /*
1037 **  NIS_MAP_STORE
1038 */
1039 
1040 void
1041 nis_map_store(map, lhs, rhs)
1042 	MAP *map;
1043 	char *lhs;
1044 	char *rhs;
1045 {
1046 	/* nothing */
1047 }
1048 
1049 
1050 /*
1051 **  NIS_MAP_CLOSE
1052 */
1053 
1054 void
1055 nis_map_close(map)
1056 	MAP *map;
1057 {
1058 	/* nothing */
1059 }
1060 
1061 #endif /* NIS */
1062 /*
1063 **  STAB (Symbol Table) Modules
1064 */
1065 
1066 
1067 /*
1068 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1069 */
1070 
1071 char *
1072 stab_map_lookup(map, name, av, pstat)
1073 	register MAP *map;
1074 	char *name;
1075 	char **av;
1076 	int *pstat;
1077 {
1078 	register STAB *s;
1079 
1080 	if (tTd(38, 20))
1081 		printf("stab_lookup(%s)\n", name);
1082 
1083 	s = stab(name, ST_ALIAS, ST_FIND);
1084 	if (s != NULL)
1085 		return (s->s_alias);
1086 	return (NULL);
1087 }
1088 
1089 
1090 /*
1091 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1092 */
1093 
1094 void
1095 stab_map_store(map, lhs, rhs)
1096 	register MAP *map;
1097 	char *lhs;
1098 	char *rhs;
1099 {
1100 	register STAB *s;
1101 
1102 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1103 	s->s_alias = newstr(rhs);
1104 }
1105 
1106 
1107 /*
1108 **  STAB_MAP_OPEN -- initialize (reads data file)
1109 **
1110 **	This is a wierd case -- it is only intended as a fallback for
1111 **	aliases.  For this reason, opens for write (only during a
1112 **	"newaliases") always fails, and opens for read open the
1113 **	actual underlying text file instead of the database.
1114 */
1115 
1116 bool
1117 stab_map_open(map, mode)
1118 	register MAP *map;
1119 	int mode;
1120 {
1121 	FILE *af;
1122 	struct stat st;
1123 
1124 	if (tTd(38, 2))
1125 		printf("stab_map_open(%s)\n", map->map_file);
1126 
1127 	if (mode != O_RDONLY)
1128 	{
1129 		errno = ENODEV;
1130 		return FALSE;
1131 	}
1132 
1133 	af = fopen(map->map_file, "r");
1134 	if (af == NULL)
1135 		return FALSE;
1136 	readaliases(map, af, TRUE);
1137 
1138 	if (fstat(fileno(af), &st) >= 0)
1139 		map->map_mtime = st.st_mtime;
1140 	fclose(af);
1141 
1142 	return TRUE;
1143 }
1144 
1145 
1146 /*
1147 **  STAB_MAP_CLOSE -- close symbol table.
1148 **
1149 **	Since this is in memory, there is nothing to do.
1150 */
1151 
1152 void
1153 stab_map_close(map)
1154 	MAP *map;
1155 {
1156 	/* ignore it */
1157 }
1158 /*
1159 **  Implicit Modules
1160 **
1161 **	Tries several types.  For back compatibility of aliases.
1162 */
1163 
1164 
1165 /*
1166 **  IMPL_MAP_LOOKUP -- lookup in best open database
1167 */
1168 
1169 char *
1170 impl_map_lookup(map, name, av, pstat)
1171 	MAP *map;
1172 	char *name;
1173 	char **av;
1174 	int *pstat;
1175 {
1176 	if (tTd(38, 20))
1177 		printf("impl_map_lookup(%s)\n", name);
1178 
1179 #ifdef NEWDB
1180 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1181 		return db_map_lookup(map, name, av, pstat);
1182 #endif
1183 #ifdef NDBM
1184 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1185 		return ndbm_map_lookup(map, name, av, pstat);
1186 #endif
1187 	return stab_map_lookup(map, name, av, pstat);
1188 }
1189 
1190 /*
1191 **  IMPL_MAP_STORE -- store in open databases
1192 */
1193 
1194 void
1195 impl_map_store(map, lhs, rhs)
1196 	MAP *map;
1197 	char *lhs;
1198 	char *rhs;
1199 {
1200 #ifdef NEWDB
1201 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1202 		db_map_store(map, lhs, rhs);
1203 #endif
1204 #ifdef NDBM
1205 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1206 		ndbm_map_store(map, lhs, rhs);
1207 #endif
1208 	stab_map_store(map, lhs, rhs);
1209 }
1210 
1211 /*
1212 **  IMPL_MAP_OPEN -- implicit database open
1213 */
1214 
1215 bool
1216 impl_map_open(map, mode)
1217 	MAP *map;
1218 	int mode;
1219 {
1220 	struct stat stb;
1221 
1222 	if (tTd(38, 2))
1223 		printf("impl_map_open(%s, %d)\n", map->map_file, mode);
1224 
1225 	if (stat(map->map_file, &stb) < 0)
1226 	{
1227 		/* no alias file at all */
1228 		if (tTd(38, 3))
1229 			printf("no map file\n");
1230 		return FALSE;
1231 	}
1232 
1233 #ifdef NEWDB
1234 	map->map_mflags |= MF_IMPL_HASH;
1235 	if (hash_map_open(map, mode))
1236 	{
1237 #if defined(NDBM) && defined(NIS)
1238 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1239 #endif
1240 			return TRUE;
1241 	}
1242 	else
1243 		map->map_mflags &= ~MF_IMPL_HASH;
1244 #endif
1245 #ifdef NDBM
1246 	map->map_mflags |= MF_IMPL_NDBM;
1247 	if (ndbm_map_open(map, mode))
1248 	{
1249 		return TRUE;
1250 	}
1251 	else
1252 		map->map_mflags &= ~MF_IMPL_NDBM;
1253 #endif
1254 
1255 #if defined(NEWDB) || defined(NDBM)
1256 	if (Verbose)
1257 		message("WARNING: cannot open alias database %s", map->map_file);
1258 #else
1259 	if (mode != O_RDONLY)
1260 		usrerr("Cannot rebuild aliases: no database format defined");
1261 #endif
1262 
1263 	return stab_map_open(map, mode);
1264 }
1265 
1266 
1267 /*
1268 **  IMPL_MAP_CLOSE -- close any open database(s)
1269 */
1270 
1271 void
1272 impl_map_close(map)
1273 	MAP *map;
1274 {
1275 #ifdef NEWDB
1276 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1277 	{
1278 		db_map_close(map);
1279 		map->map_mflags &= ~MF_IMPL_HASH;
1280 	}
1281 #endif
1282 
1283 #ifdef NDBM
1284 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1285 	{
1286 		ndbm_map_close(map);
1287 		map->map_mflags &= ~MF_IMPL_NDBM;
1288 	}
1289 #endif
1290 }
1291 /*
1292 **  NULL stubs
1293 */
1294 
1295 bool
1296 null_map_open(map, mode)
1297 	MAP *map;
1298 	int mode;
1299 {
1300 	return TRUE;
1301 }
1302 
1303 void
1304 null_map_close(map)
1305 	MAP *map;
1306 {
1307 	return;
1308 }
1309 
1310 void
1311 null_map_store(map, key, val)
1312 	MAP *map;
1313 	char *key;
1314 	char *val;
1315 {
1316 	return;
1317 }
1318