map.c revision 64562
1/*
2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: map.c,v 8.414.4.13 2000/07/14 16:48:21 ca Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19
20
21#ifdef NDBM
22# include <ndbm.h>
23# ifdef R_FIRST
24  ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
25  ERROR README:	the README file about tweaking Berkeley DB so it can
26  ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
27  ERROR README: and use -DNEWDB instead.
28# endif /* R_FIRST */
29#endif /* NDBM */
30#ifdef NEWDB
31# include <db.h>
32# ifndef DB_VERSION_MAJOR
33#  define DB_VERSION_MAJOR 1
34# endif /* ! DB_VERSION_MAJOR */
35#endif /* NEWDB */
36#ifdef NIS
37  struct dom_binding;	/* forward reference needed on IRIX */
38# include <rpcsvc/ypclnt.h>
39# ifdef NDBM
40#  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
41# endif /* NDBM */
42#endif /* NIS */
43
44#ifdef NEWDB
45# if DB_VERSION_MAJOR < 2
46static bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
47# endif /* DB_VERSION_MAJOR < 2 */
48# if DB_VERSION_MAJOR == 2
49static bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
50# endif /* DB_VERSION_MAJOR == 2 */
51# if DB_VERSION_MAJOR > 2
52static bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
53# endif /* DB_VERSION_MAJOR > 2 */
54#endif /* NEWDB */
55static bool	extract_canonname __P((char *, char *, char[], int));
56#ifdef LDAPMAP
57static void	ldapmap_clear __P((LDAPMAP_STRUCT *));
58static STAB	*ldapmap_findconn __P((LDAPMAP_STRUCT *));
59static int	ldapmap_geterrno __P((LDAP *));
60static void	ldapmap_setopts __P((LDAP *, LDAPMAP_STRUCT *));
61static bool	ldapmap_start __P((MAP *));
62static void	ldaptimeout __P((int));
63#endif /* LDAPMAP */
64static void	map_close __P((STAB *, int));
65static void	map_init __P((STAB *, int));
66#ifdef NISPLUS
67static bool	nisplus_getcanonname __P((char *, int, int *));
68#endif /* NISPLUS */
69#ifdef NIS
70static bool	nis_getcanonname __P((char *, int, int *));
71#endif /* NIS */
72#if NETINFO
73static bool	ni_getcanonname __P((char *, int, int *));
74#endif /* NETINFO */
75static bool	text_getcanonname __P((char *, int, int *));
76
77/*
78**  MAP.C -- implementations for various map classes.
79**
80**	Each map class implements a series of functions:
81**
82**	bool map_parse(MAP *map, char *args)
83**		Parse the arguments from the config file.  Return TRUE
84**		if they were ok, FALSE otherwise.  Fill in map with the
85**		values.
86**
87**	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
88**		Look up the key in the given map.  If found, do any
89**		rewriting the map wants (including "args" if desired)
90**		and return the value.  Set *pstat to the appropriate status
91**		on error and return NULL.  Args will be NULL if called
92**		from the alias routines, although this should probably
93**		not be relied upon.  It is suggested you call map_rewrite
94**		to return the results -- it takes care of null termination
95**		and uses a dynamically expanded buffer as needed.
96**
97**	void map_store(MAP *map, char *key, char *value)
98**		Store the key:value pair in the map.
99**
100**	bool map_open(MAP *map, int mode)
101**		Open the map for the indicated mode.  Mode should
102**		be either O_RDONLY or O_RDWR.  Return TRUE if it
103**		was opened successfully, FALSE otherwise.  If the open
104**		failed an the MF_OPTIONAL flag is not set, it should
105**		also print an error.  If the MF_ALIAS bit is set
106**		and this map class understands the @:@ convention, it
107**		should call aliaswait() before returning.
108**
109**	void map_close(MAP *map)
110**		Close the map.
111**
112**	This file also includes the implementation for getcanonname.
113**	It is currently implemented in a pretty ad-hoc manner; it ought
114**	to be more properly integrated into the map structure.
115*/
116
117#define DBMMODE		0644
118
119#ifndef EX_NOTFOUND
120# define EX_NOTFOUND	EX_NOHOST
121#endif /* ! EX_NOTFOUND */
122
123#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
124# define LOCK_ON_OPEN	1	/* we can open/create a locked file */
125#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
126# define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
127#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
128
129#ifndef O_ACCMODE
130# define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
131#endif /* ! O_ACCMODE */
132/*
133**  MAP_PARSEARGS -- parse config line arguments for database lookup
134**
135**	This is a generic version of the map_parse method.
136**
137**	Parameters:
138**		map -- the map being initialized.
139**		ap -- a pointer to the args on the config line.
140**
141**	Returns:
142**		TRUE -- if everything parsed OK.
143**		FALSE -- otherwise.
144**
145**	Side Effects:
146**		null terminates the filename; stores it in map
147*/
148
149bool
150map_parseargs(map, ap)
151	MAP *map;
152	char *ap;
153{
154	register char *p = ap;
155
156	/*
157	**  there is no check whether there is really an argument,
158	**  but that's not important enough to warrant extra code
159	*/
160	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
161	map->map_spacesub = SpaceSub;	/* default value */
162	for (;;)
163	{
164		while (isascii(*p) && isspace(*p))
165			p++;
166		if (*p != '-')
167			break;
168		switch (*++p)
169		{
170		  case 'N':
171			map->map_mflags |= MF_INCLNULL;
172			map->map_mflags &= ~MF_TRY0NULL;
173			break;
174
175		  case 'O':
176			map->map_mflags &= ~MF_TRY1NULL;
177			break;
178
179		  case 'o':
180			map->map_mflags |= MF_OPTIONAL;
181			break;
182
183		  case 'f':
184			map->map_mflags |= MF_NOFOLDCASE;
185			break;
186
187		  case 'm':
188			map->map_mflags |= MF_MATCHONLY;
189			break;
190
191		  case 'A':
192			map->map_mflags |= MF_APPEND;
193			break;
194
195		  case 'q':
196			map->map_mflags |= MF_KEEPQUOTES;
197			break;
198
199		  case 'a':
200			map->map_app = ++p;
201			break;
202
203		  case 'T':
204			map->map_tapp = ++p;
205			break;
206
207		  case 'k':
208			while (isascii(*++p) && isspace(*p))
209				continue;
210			map->map_keycolnm = p;
211			break;
212
213		  case 'v':
214			while (isascii(*++p) && isspace(*p))
215				continue;
216			map->map_valcolnm = p;
217			break;
218
219		  case 'z':
220			if (*++p != '\\')
221				map->map_coldelim = *p;
222			else
223			{
224				switch (*++p)
225				{
226				  case 'n':
227					map->map_coldelim = '\n';
228					break;
229
230				  case 't':
231					map->map_coldelim = '\t';
232					break;
233
234				  default:
235					map->map_coldelim = '\\';
236				}
237			}
238			break;
239
240		  case 't':
241			map->map_mflags |= MF_NODEFER;
242			break;
243
244
245		  case 'S':
246			map->map_spacesub = *++p;
247			break;
248
249		  case 'D':
250			map->map_mflags |= MF_DEFER;
251			break;
252
253		  default:
254			syserr("Illegal option %c map %s", *p, map->map_mname);
255			break;
256		}
257		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
258			p++;
259		if (*p != '\0')
260			*p++ = '\0';
261	}
262	if (map->map_app != NULL)
263		map->map_app = newstr(map->map_app);
264	if (map->map_tapp != NULL)
265		map->map_tapp = newstr(map->map_tapp);
266	if (map->map_keycolnm != NULL)
267		map->map_keycolnm = newstr(map->map_keycolnm);
268	if (map->map_valcolnm != NULL)
269		map->map_valcolnm = newstr(map->map_valcolnm);
270
271	if (*p != '\0')
272	{
273		map->map_file = p;
274		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
275			p++;
276		if (*p != '\0')
277			*p++ = '\0';
278		map->map_file = newstr(map->map_file);
279	}
280
281	while (*p != '\0' && isascii(*p) && isspace(*p))
282		p++;
283	if (*p != '\0')
284		map->map_rebuild = newstr(p);
285
286	if (map->map_file == NULL &&
287	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
288	{
289		syserr("No file name for %s map %s",
290			map->map_class->map_cname, map->map_mname);
291		return FALSE;
292	}
293	return TRUE;
294}
295/*
296**  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
297**
298**	It also adds the map_app string.  It can be used as a utility
299**	in the map_lookup method.
300**
301**	Parameters:
302**		map -- the map that causes this.
303**		s -- the string to rewrite, NOT necessarily null terminated.
304**		slen -- the length of s.
305**		av -- arguments to interpolate into buf.
306**
307**	Returns:
308**		Pointer to rewritten result.  This is static data that
309**		should be copied if it is to be saved!
310**
311**	Side Effects:
312**		none.
313*/
314
315char *
316map_rewrite(map, s, slen, av)
317	register MAP *map;
318	register const char *s;
319	size_t slen;
320	char **av;
321{
322	register char *bp;
323	register char c;
324	char **avp;
325	register char *ap;
326	size_t l;
327	size_t len;
328	static size_t buflen = 0;
329	static char *buf = NULL;
330
331	if (tTd(39, 1))
332	{
333		dprintf("map_rewrite(%.*s), av =", (int)slen, s);
334		if (av == NULL)
335			dprintf(" (nullv)");
336		else
337		{
338			for (avp = av; *avp != NULL; avp++)
339				dprintf("\n\t%s", *avp);
340		}
341		dprintf("\n");
342	}
343
344	/* count expected size of output (can safely overestimate) */
345	l = len = slen;
346	if (av != NULL)
347	{
348		const char *sp = s;
349
350		while (l-- > 0 && (c = *sp++) != '\0')
351		{
352			if (c != '%')
353				continue;
354			if (l-- <= 0)
355				break;
356			c = *sp++;
357			if (!(isascii(c) && isdigit(c)))
358				continue;
359			for (avp = av; --c >= '0' && *avp != NULL; avp++)
360				continue;
361			if (*avp == NULL)
362				continue;
363			len += strlen(*avp);
364		}
365	}
366	if (map->map_app != NULL)
367		len += strlen(map->map_app);
368	if (buflen < ++len)
369	{
370		/* need to malloc additional space */
371		buflen = len;
372		if (buf != NULL)
373			free(buf);
374		buf = xalloc(buflen);
375	}
376
377	bp = buf;
378	if (av == NULL)
379	{
380		memmove(bp, s, slen);
381		bp += slen;
382
383		/* assert(len > slen); */
384		len -= slen;
385	}
386	else
387	{
388		while (slen-- > 0 && (c = *s++) != '\0')
389		{
390			if (c != '%')
391			{
392  pushc:
393				if (--len <= 0)
394					break;
395				*bp++ = c;
396				continue;
397			}
398			if (slen-- <= 0 || (c = *s++) == '\0')
399				c = '%';
400			if (c == '%')
401				goto pushc;
402			if (!(isascii(c) && isdigit(c)))
403			{
404				*bp++ = '%';
405				--len;
406				goto pushc;
407			}
408			for (avp = av; --c >= '0' && *avp != NULL; avp++)
409				continue;
410			if (*avp == NULL)
411				continue;
412
413			/* transliterate argument into output string */
414			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
415				*bp++ = c;
416		}
417	}
418	if (map->map_app != NULL && len > 0)
419		(void) strlcpy(bp, map->map_app, len);
420	else
421		*bp = '\0';
422	if (tTd(39, 1))
423		dprintf("map_rewrite => %s\n", buf);
424	return buf;
425}
426/*
427**  INITMAPS -- rebuild alias maps
428**
429**	Parameters:
430**		none.
431**
432**	Returns:
433**		none.
434*/
435
436void
437initmaps()
438{
439#if XDEBUG
440	checkfd012("entering initmaps");
441#endif /* XDEBUG */
442	stabapply(map_init, 0);
443#if XDEBUG
444	checkfd012("exiting initmaps");
445#endif /* XDEBUG */
446}
447/*
448**  MAP_INIT -- rebuild a map
449**
450**	Parameters:
451**		s -- STAB entry: if map: try to rebuild
452**		unused -- unused variable
453**
454**	Returns:
455**		none.
456**
457**	Side Effects:
458**		will close already open rebuildable map.
459*/
460
461/* ARGSUSED1 */
462static void
463map_init(s, unused)
464	register STAB *s;
465	int unused;
466{
467	register MAP *map;
468
469	/* has to be a map */
470	if (s->s_type != ST_MAP)
471		return;
472
473	map = &s->s_map;
474	if (!bitset(MF_VALID, map->map_mflags))
475		return;
476
477	if (tTd(38, 2))
478		dprintf("map_init(%s:%s, %s)\n",
479			map->map_class->map_cname == NULL ? "NULL" :
480				map->map_class->map_cname,
481			map->map_mname == NULL ? "NULL" : map->map_mname,
482			map->map_file == NULL ? "NULL" : map->map_file);
483
484	if (!bitset(MF_ALIAS, map->map_mflags) ||
485	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
486	{
487		if (tTd(38, 3))
488			dprintf("\tnot rebuildable\n");
489		return;
490	}
491
492	/* if already open, close it (for nested open) */
493	if (bitset(MF_OPEN, map->map_mflags))
494	{
495		map->map_class->map_close(map);
496		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
497	}
498
499	(void) rebuildaliases(map, FALSE);
500	return;
501}
502/*
503**  OPENMAP -- open a map
504**
505**	Parameters:
506**		map -- map to open (it must not be open).
507**
508**	Returns:
509**		whether open succeeded.
510**
511*/
512
513bool
514openmap(map)
515	MAP *map;
516{
517	bool restore = FALSE;
518	bool savehold = HoldErrs;
519	bool savequick = QuickAbort;
520	int saveerrors = Errors;
521
522	if (!bitset(MF_VALID, map->map_mflags))
523		return FALSE;
524
525	/* better safe than sorry... */
526	if (bitset(MF_OPEN, map->map_mflags))
527		return TRUE;
528
529	/* Don't send a map open error out via SMTP */
530	if ((OnlyOneError || QuickAbort) &&
531	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
532	{
533		restore = TRUE;
534		HoldErrs = TRUE;
535		QuickAbort = FALSE;
536	}
537
538	errno = 0;
539	if (map->map_class->map_open(map, O_RDONLY))
540	{
541		if (tTd(38, 4))
542			dprintf("openmap()\t%s:%s %s: valid\n",
543				map->map_class->map_cname == NULL ? "NULL" :
544					map->map_class->map_cname,
545				map->map_mname == NULL ? "NULL" :
546					map->map_mname,
547				map->map_file == NULL ? "NULL" :
548					map->map_file);
549		map->map_mflags |= MF_OPEN;
550		map->map_pid = getpid();
551	}
552	else
553	{
554		if (tTd(38, 4))
555			dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
556				map->map_class->map_cname == NULL ? "NULL" :
557					map->map_class->map_cname,
558				map->map_mname == NULL ? "NULL" :
559					map->map_mname,
560				map->map_file == NULL ? "NULL" :
561					map->map_file,
562				errno == 0 ? "" : ": ",
563				errno == 0 ? "" : errstring(errno));
564		if (!bitset(MF_OPTIONAL, map->map_mflags))
565		{
566			extern MAPCLASS BogusMapClass;
567
568			map->map_class = &BogusMapClass;
569			map->map_mflags |= MF_OPEN;
570			map->map_pid = getpid();
571		}
572		else
573		{
574			/* don't try again */
575			map->map_mflags &= ~MF_VALID;
576		}
577	}
578
579	if (restore)
580	{
581		Errors = saveerrors;
582		HoldErrs = savehold;
583		QuickAbort = savequick;
584	}
585
586	return bitset(MF_OPEN, map->map_mflags);
587}
588/*
589**  CLOSEMAPS -- close all open maps opened by the current pid.
590**
591**	Parameters:
592**		none
593**
594**	Returns:
595**		none.
596*/
597
598void
599closemaps()
600{
601	stabapply(map_close, 0);
602}
603/*
604**  MAP_CLOSE -- close a map opened by the current pid.
605**
606**	Parameters:
607**		s -- STAB entry: if map: try to open
608**		second parameter is unused (required by stabapply())
609**
610**	Returns:
611**		none.
612*/
613
614/* ARGSUSED1 */
615static void
616map_close(s, unused)
617	register STAB *s;
618	int unused;
619{
620	MAP *map;
621
622	if (s->s_type != ST_MAP)
623		return;
624
625	map = &s->s_map;
626
627	if (!bitset(MF_VALID, map->map_mflags) ||
628	    !bitset(MF_OPEN, map->map_mflags) ||
629	    bitset(MF_SHARED, map->map_mflags) ||
630	    map->map_pid != getpid())
631		return;
632
633	if (tTd(38, 5))
634		dprintf("closemaps: closing %s (%s)\n",
635			map->map_mname == NULL ? "NULL" : map->map_mname,
636			map->map_file == NULL ? "NULL" : map->map_file);
637
638	map->map_class->map_close(map);
639	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
640}
641/*
642**  GETCANONNAME -- look up name using service switch
643**
644**	Parameters:
645**		host -- the host name to look up.
646**		hbsize -- the size of the host buffer.
647**		trymx -- if set, try MX records.
648**
649**	Returns:
650**		TRUE -- if the host was found.
651**		FALSE -- otherwise.
652*/
653
654bool
655getcanonname(host, hbsize, trymx)
656	char *host;
657	int hbsize;
658	bool trymx;
659{
660	int nmaps;
661	int mapno;
662	bool found = FALSE;
663	bool got_tempfail = FALSE;
664	auto int status;
665	char *maptype[MAXMAPSTACK];
666	short mapreturn[MAXMAPACTIONS];
667
668	nmaps = switch_map_find("hosts", maptype, mapreturn);
669	for (mapno = 0; mapno < nmaps; mapno++)
670	{
671		int i;
672
673		if (tTd(38, 20))
674			dprintf("getcanonname(%s), trying %s\n",
675				host, maptype[mapno]);
676		if (strcmp("files", maptype[mapno]) == 0)
677		{
678			found = text_getcanonname(host, hbsize, &status);
679		}
680#ifdef NIS
681		else if (strcmp("nis", maptype[mapno]) == 0)
682		{
683			found = nis_getcanonname(host, hbsize, &status);
684		}
685#endif /* NIS */
686#ifdef NISPLUS
687		else if (strcmp("nisplus", maptype[mapno]) == 0)
688		{
689			found = nisplus_getcanonname(host, hbsize, &status);
690		}
691#endif /* NISPLUS */
692#if NAMED_BIND
693		else if (strcmp("dns", maptype[mapno]) == 0)
694		{
695			found = dns_getcanonname(host, hbsize, trymx, &status);
696		}
697#endif /* NAMED_BIND */
698#if NETINFO
699		else if (strcmp("netinfo", maptype[mapno]) == 0)
700		{
701			found = ni_getcanonname(host, hbsize, &status);
702		}
703#endif /* NETINFO */
704		else
705		{
706			found = FALSE;
707			status = EX_UNAVAILABLE;
708		}
709
710		/*
711		**  Heuristic: if $m is not set, we are running during system
712		**  startup.  In this case, when a name is apparently found
713		**  but has no dot, treat is as not found.  This avoids
714		**  problems if /etc/hosts has no FQDN but is listed first
715		**  in the service switch.
716		*/
717
718		if (found &&
719		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
720			break;
721
722		/* see if we should continue */
723		if (status == EX_TEMPFAIL)
724		{
725			i = MA_TRYAGAIN;
726			got_tempfail = TRUE;
727		}
728		else if (status == EX_NOTFOUND)
729			i = MA_NOTFOUND;
730		else
731			i = MA_UNAVAIL;
732		if (bitset(1 << mapno, mapreturn[i]))
733			break;
734	}
735
736	if (found)
737	{
738		char *d;
739
740		if (tTd(38, 20))
741			dprintf("getcanonname(%s), found\n", host);
742
743		/*
744		**  If returned name is still single token, compensate
745		**  by tagging on $m.  This is because some sites set
746		**  up their DNS or NIS databases wrong.
747		*/
748
749		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
750		{
751			d = macvalue('m', CurEnv);
752			if (d != NULL &&
753			    hbsize > (int) (strlen(host) + strlen(d) + 1))
754			{
755				if (host[strlen(host) - 1] != '.')
756					(void) strlcat(host, ".", hbsize);
757				(void) strlcat(host, d, hbsize);
758			}
759			else
760				return FALSE;
761		}
762		return TRUE;
763	}
764
765	if (tTd(38, 20))
766		dprintf("getcanonname(%s), failed, status=%d\n", host, status);
767
768#if NAMED_BIND
769	if (got_tempfail)
770		h_errno = TRY_AGAIN;
771	else
772		h_errno = HOST_NOT_FOUND;
773#endif /* NAMED_BIND */
774
775	return FALSE;
776}
777/*
778**  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
779**
780**	Parameters:
781**		name -- the name against which to match.
782**		line -- the /etc/hosts line.
783**		cbuf -- the location to store the result.
784**		cbuflen -- the size of cbuf.
785**
786**	Returns:
787**		TRUE -- if the line matched the desired name.
788**		FALSE -- otherwise.
789*/
790
791static bool
792extract_canonname(name, line, cbuf, cbuflen)
793	char *name;
794	char *line;
795	char cbuf[];
796	int cbuflen;
797{
798	int i;
799	char *p;
800	bool found = FALSE;
801
802	cbuf[0] = '\0';
803	if (line[0] == '#')
804		return FALSE;
805
806	for (i = 1; ; i++)
807	{
808		char nbuf[MAXNAME + 1];
809
810		p = get_column(line, i, '\0', nbuf, sizeof nbuf);
811		if (p == NULL)
812			break;
813		if (*p == '\0')
814			continue;
815		if (cbuf[0] == '\0' ||
816		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
817		{
818			snprintf(cbuf, cbuflen, "%s", p);
819		}
820		if (strcasecmp(name, p) == 0)
821			found = TRUE;
822	}
823	if (found && strchr(cbuf, '.') == NULL)
824	{
825		/* try to add a domain on the end of the name */
826		char *domain = macvalue('m', CurEnv);
827
828		if (domain != NULL &&
829		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
830		{
831			p = &cbuf[i];
832			*p++ = '.';
833			(void) strlcpy(p, domain, cbuflen - i - 1);
834		}
835	}
836	return found;
837}
838/*
839**  NDBM modules
840*/
841
842#ifdef NDBM
843
844/*
845**  NDBM_MAP_OPEN -- DBM-style map open
846*/
847
848bool
849ndbm_map_open(map, mode)
850	MAP *map;
851	int mode;
852{
853	register DBM *dbm;
854	int save_errno;
855	int dfd;
856	int pfd;
857	long sff;
858	int ret;
859	int smode = S_IREAD;
860	char dirfile[MAXNAME + 1];
861	char pagfile[MAXNAME + 1];
862	struct stat st;
863	struct stat std, stp;
864
865	if (tTd(38, 2))
866		dprintf("ndbm_map_open(%s, %s, %d)\n",
867			map->map_mname, map->map_file, mode);
868	map->map_lockfd = -1;
869	mode &= O_ACCMODE;
870
871	/* do initial file and directory checks */
872	snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file);
873	snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file);
874	sff = SFF_ROOTOK|SFF_REGONLY;
875	if (mode == O_RDWR)
876	{
877		sff |= SFF_CREAT;
878		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
879			sff |= SFF_NOSLINK;
880		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
881			sff |= SFF_NOHLINK;
882		smode = S_IWRITE;
883	}
884	else
885	{
886		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
887			sff |= SFF_NOWLINK;
888	}
889	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
890		sff |= SFF_SAFEDIRPATH;
891	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
892			    sff, smode, &std);
893	if (ret == 0)
894		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
895			       sff, smode, &stp);
896
897# if !_FFR_REMOVE_AUTOREBUILD
898	if (ret == ENOENT && AutoRebuild &&
899	    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
900	    (bitset(MF_IMPL_NDBM, map->map_mflags) ||
901	     bitset(MF_ALIAS, map->map_mflags)) &&
902	    mode == O_RDONLY)
903	{
904		bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
905
906		/* may be able to rebuild */
907		map->map_mflags &= ~MF_IMPL_NDBM;
908		if (!rebuildaliases(map, TRUE))
909			return FALSE;
910		if (impl)
911			return impl_map_open(map, O_RDONLY);
912		else
913			return ndbm_map_open(map, O_RDONLY);
914	}
915# endif /* !_FFR_REMOVE_AUTOREBUILD */
916
917	if (ret != 0)
918	{
919		char *prob = "unsafe";
920
921		/* cannot open this map */
922		if (ret == ENOENT)
923			prob = "missing";
924		if (tTd(38, 2))
925			dprintf("\t%s map file: %d\n", prob, ret);
926		if (!bitset(MF_OPTIONAL, map->map_mflags))
927			syserr("dbm map \"%s\": %s map file %s",
928				map->map_mname, prob, map->map_file);
929		return FALSE;
930	}
931	if (std.st_mode == ST_MODE_NOFILE)
932		mode |= O_CREAT|O_EXCL;
933
934# if LOCK_ON_OPEN
935	if (mode == O_RDONLY)
936		mode |= O_SHLOCK;
937	else
938		mode |= O_TRUNC|O_EXLOCK;
939# else /* LOCK_ON_OPEN */
940	if ((mode & O_ACCMODE) == O_RDWR)
941	{
942#  if NOFTRUNCATE
943		/*
944		**  Warning: race condition.  Try to lock the file as
945		**  quickly as possible after opening it.
946		**	This may also have security problems on some systems,
947		**	but there isn't anything we can do about it.
948		*/
949
950		mode |= O_TRUNC;
951#  else /* NOFTRUNCATE */
952		/*
953		**  This ugly code opens the map without truncating it,
954		**  locks the file, then truncates it.  Necessary to
955		**  avoid race conditions.
956		*/
957
958		int dirfd;
959		int pagfd;
960		long sff = SFF_CREAT|SFF_OPENASROOT;
961
962		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
963			sff |= SFF_NOSLINK;
964		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
965			sff |= SFF_NOHLINK;
966
967		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
968		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
969
970		if (dirfd < 0 || pagfd < 0)
971		{
972			save_errno = errno;
973			if (dirfd >= 0)
974				(void) close(dirfd);
975			if (pagfd >= 0)
976				(void) close(pagfd);
977			errno = save_errno;
978			syserr("ndbm_map_open: cannot create database %s",
979				map->map_file);
980			return FALSE;
981		}
982		if (ftruncate(dirfd, (off_t) 0) < 0 ||
983		    ftruncate(pagfd, (off_t) 0) < 0)
984		{
985			save_errno = errno;
986			(void) close(dirfd);
987			(void) close(pagfd);
988			errno = save_errno;
989			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
990				map->map_file);
991			return FALSE;
992		}
993
994		/* if new file, get "before" bits for later filechanged check */
995		if (std.st_mode == ST_MODE_NOFILE &&
996		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
997		{
998			save_errno = errno;
999			(void) close(dirfd);
1000			(void) close(pagfd);
1001			errno = save_errno;
1002			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1003				map->map_file);
1004			return FALSE;
1005		}
1006
1007		/* have to save the lock for the duration (bletch) */
1008		map->map_lockfd = dirfd;
1009		(void) close(pagfd);
1010
1011		/* twiddle bits for dbm_open */
1012		mode &= ~(O_CREAT|O_EXCL);
1013#  endif /* NOFTRUNCATE */
1014	}
1015# endif /* LOCK_ON_OPEN */
1016
1017	/* open the database */
1018	dbm = dbm_open(map->map_file, mode, DBMMODE);
1019	if (dbm == NULL)
1020	{
1021		save_errno = errno;
1022		if (bitset(MF_ALIAS, map->map_mflags) &&
1023		    aliaswait(map, ".pag", FALSE))
1024			return TRUE;
1025# if !LOCK_ON_OPEN && !NOFTRUNCATE
1026		if (map->map_lockfd >= 0)
1027			(void) close(map->map_lockfd);
1028# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1029		errno = save_errno;
1030		if (!bitset(MF_OPTIONAL, map->map_mflags))
1031			syserr("Cannot open DBM database %s", map->map_file);
1032		return FALSE;
1033	}
1034	dfd = dbm_dirfno(dbm);
1035	pfd = dbm_pagfno(dbm);
1036	if (dfd == pfd)
1037	{
1038		/* heuristic: if files are linked, this is actually gdbm */
1039		dbm_close(dbm);
1040# if !LOCK_ON_OPEN && !NOFTRUNCATE
1041		if (map->map_lockfd >= 0)
1042			(void) close(map->map_lockfd);
1043# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1044		errno = 0;
1045		syserr("dbm map \"%s\": cannot support GDBM",
1046			map->map_mname);
1047		return FALSE;
1048	}
1049
1050	if (filechanged(dirfile, dfd, &std) ||
1051	    filechanged(pagfile, pfd, &stp))
1052	{
1053		save_errno = errno;
1054		dbm_close(dbm);
1055# if !LOCK_ON_OPEN && !NOFTRUNCATE
1056		if (map->map_lockfd >= 0)
1057			(void) close(map->map_lockfd);
1058# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1059		errno = save_errno;
1060		syserr("ndbm_map_open(%s): file changed after open",
1061			map->map_file);
1062		return FALSE;
1063	}
1064
1065	map->map_db1 = (ARBPTR_T) dbm;
1066
1067	/*
1068	**  Need to set map_mtime before the call to aliaswait()
1069	**  as aliaswait() will call map_lookup() which requires
1070	**  map_mtime to be set
1071	*/
1072
1073	if (fstat(dfd, &st) >= 0)
1074		map->map_mtime = st.st_mtime;
1075
1076	if (mode == O_RDONLY)
1077	{
1078# if LOCK_ON_OPEN
1079		if (dfd >= 0)
1080			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1081		if (pfd >= 0)
1082			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1083# endif /* LOCK_ON_OPEN */
1084		if (bitset(MF_ALIAS, map->map_mflags) &&
1085		    !aliaswait(map, ".pag", TRUE))
1086			return FALSE;
1087	}
1088	else
1089	{
1090		map->map_mflags |= MF_LOCKED;
1091		if (geteuid() == 0 && TrustedUid != 0)
1092		{
1093#  if HASFCHOWN
1094			if (fchown(dfd, TrustedUid, -1) < 0 ||
1095			    fchown(pfd, TrustedUid, -1) < 0)
1096			{
1097				int err = errno;
1098
1099				sm_syslog(LOG_ALERT, NOQID,
1100					  "ownership change on %s failed: %s",
1101					  map->map_file, errstring(err));
1102				message("050 ownership change on %s failed: %s",
1103					map->map_file, errstring(err));
1104			}
1105#  endif /* HASFCHOWN */
1106		}
1107	}
1108	return TRUE;
1109}
1110
1111
1112/*
1113**  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1114*/
1115
1116char *
1117ndbm_map_lookup(map, name, av, statp)
1118	MAP *map;
1119	char *name;
1120	char **av;
1121	int *statp;
1122{
1123	datum key, val;
1124	int fd;
1125	char keybuf[MAXNAME + 1];
1126	struct stat stbuf;
1127
1128	if (tTd(38, 20))
1129		dprintf("ndbm_map_lookup(%s, %s)\n",
1130			map->map_mname, name);
1131
1132	key.dptr = name;
1133	key.dsize = strlen(name);
1134	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1135	{
1136		if (key.dsize > sizeof keybuf - 1)
1137			key.dsize = sizeof keybuf - 1;
1138		memmove(keybuf, key.dptr, key.dsize);
1139		keybuf[key.dsize] = '\0';
1140		makelower(keybuf);
1141		key.dptr = keybuf;
1142	}
1143lockdbm:
1144	fd = dbm_dirfno((DBM *) map->map_db1);
1145	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1146		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
1147	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1148	{
1149		/* Reopen the database to sync the cache */
1150		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1151								 : O_RDONLY;
1152
1153		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1154			(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
1155		map->map_class->map_close(map);
1156		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1157		if (map->map_class->map_open(map, omode))
1158		{
1159			map->map_mflags |= MF_OPEN;
1160			map->map_pid = getpid();
1161			if ((omode && O_ACCMODE) == O_RDWR)
1162				map->map_mflags |= MF_WRITABLE;
1163			goto lockdbm;
1164		}
1165		else
1166		{
1167			if (!bitset(MF_OPTIONAL, map->map_mflags))
1168			{
1169				extern MAPCLASS BogusMapClass;
1170
1171				*statp = EX_TEMPFAIL;
1172				map->map_class = &BogusMapClass;
1173				map->map_mflags |= MF_OPEN;
1174				map->map_pid = getpid();
1175				syserr("Cannot reopen NDBM database %s",
1176					map->map_file);
1177			}
1178			return NULL;
1179		}
1180	}
1181	val.dptr = NULL;
1182	if (bitset(MF_TRY0NULL, map->map_mflags))
1183	{
1184		val = dbm_fetch((DBM *) map->map_db1, key);
1185		if (val.dptr != NULL)
1186			map->map_mflags &= ~MF_TRY1NULL;
1187	}
1188	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1189	{
1190		key.dsize++;
1191		val = dbm_fetch((DBM *) map->map_db1, key);
1192		if (val.dptr != NULL)
1193			map->map_mflags &= ~MF_TRY0NULL;
1194	}
1195	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1196		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
1197	if (val.dptr == NULL)
1198		return NULL;
1199	if (bitset(MF_MATCHONLY, map->map_mflags))
1200		return map_rewrite(map, name, strlen(name), NULL);
1201	else
1202		return map_rewrite(map, val.dptr, val.dsize, av);
1203}
1204
1205
1206/*
1207**  NDBM_MAP_STORE -- store a datum in the database
1208*/
1209
1210void
1211ndbm_map_store(map, lhs, rhs)
1212	register MAP *map;
1213	char *lhs;
1214	char *rhs;
1215{
1216	datum key;
1217	datum data;
1218	int status;
1219	char keybuf[MAXNAME + 1];
1220
1221	if (tTd(38, 12))
1222		dprintf("ndbm_map_store(%s, %s, %s)\n",
1223			map->map_mname, lhs, rhs);
1224
1225	key.dsize = strlen(lhs);
1226	key.dptr = lhs;
1227	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1228	{
1229		if (key.dsize > sizeof keybuf - 1)
1230			key.dsize = sizeof keybuf - 1;
1231		memmove(keybuf, key.dptr, key.dsize);
1232		keybuf[key.dsize] = '\0';
1233		makelower(keybuf);
1234		key.dptr = keybuf;
1235	}
1236
1237	data.dsize = strlen(rhs);
1238	data.dptr = rhs;
1239
1240	if (bitset(MF_INCLNULL, map->map_mflags))
1241	{
1242		key.dsize++;
1243		data.dsize++;
1244	}
1245
1246	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1247	if (status > 0)
1248	{
1249		if (!bitset(MF_APPEND, map->map_mflags))
1250			message("050 Warning: duplicate alias name %s", lhs);
1251		else
1252		{
1253			static char *buf = NULL;
1254			static int bufsiz = 0;
1255			auto int xstat;
1256			datum old;
1257
1258			old.dptr = ndbm_map_lookup(map, key.dptr,
1259						   (char **)NULL, &xstat);
1260			if (old.dptr != NULL && *(char *) old.dptr != '\0')
1261			{
1262				old.dsize = strlen(old.dptr);
1263				if (data.dsize + old.dsize + 2 > bufsiz)
1264				{
1265					if (buf != NULL)
1266						(void) free(buf);
1267					bufsiz = data.dsize + old.dsize + 2;
1268					buf = xalloc(bufsiz);
1269				}
1270				snprintf(buf, bufsiz, "%s,%s",
1271					data.dptr, old.dptr);
1272				data.dsize = data.dsize + old.dsize + 1;
1273				data.dptr = buf;
1274				if (tTd(38, 9))
1275					dprintf("ndbm_map_store append=%s\n",
1276						data.dptr);
1277			}
1278		}
1279		status = dbm_store((DBM *) map->map_db1,
1280				   key, data, DBM_REPLACE);
1281	}
1282	if (status != 0)
1283		syserr("readaliases: dbm put (%s): %d", lhs, status);
1284}
1285
1286
1287/*
1288**  NDBM_MAP_CLOSE -- close the database
1289*/
1290
1291void
1292ndbm_map_close(map)
1293	register MAP  *map;
1294{
1295	if (tTd(38, 9))
1296		dprintf("ndbm_map_close(%s, %s, %lx)\n",
1297			map->map_mname, map->map_file, map->map_mflags);
1298
1299	if (bitset(MF_WRITABLE, map->map_mflags))
1300	{
1301# ifdef NDBM_YP_COMPAT
1302		bool inclnull;
1303		char buf[MAXHOSTNAMELEN];
1304
1305		inclnull = bitset(MF_INCLNULL, map->map_mflags);
1306		map->map_mflags &= ~MF_INCLNULL;
1307
1308		if (strstr(map->map_file, "/yp/") != NULL)
1309		{
1310			long save_mflags = map->map_mflags;
1311
1312			map->map_mflags |= MF_NOFOLDCASE;
1313
1314			(void) snprintf(buf, sizeof buf, "%010ld", curtime());
1315			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1316
1317			(void) gethostname(buf, sizeof buf);
1318			ndbm_map_store(map, "YP_MASTER_NAME", buf);
1319
1320			map->map_mflags = save_mflags;
1321		}
1322
1323		if (inclnull)
1324			map->map_mflags |= MF_INCLNULL;
1325# endif /* NDBM_YP_COMPAT */
1326
1327		/* write out the distinguished alias */
1328		ndbm_map_store(map, "@", "@");
1329	}
1330	dbm_close((DBM *) map->map_db1);
1331
1332	/* release lock (if needed) */
1333# if !LOCK_ON_OPEN
1334	if (map->map_lockfd >= 0)
1335		(void) close(map->map_lockfd);
1336# endif /* !LOCK_ON_OPEN */
1337}
1338
1339#endif /* NDBM */
1340/*
1341**  NEWDB (Hash and BTree) Modules
1342*/
1343
1344#ifdef NEWDB
1345
1346/*
1347**  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1348**
1349**	These do rather bizarre locking.  If you can lock on open,
1350**	do that to avoid the condition of opening a database that
1351**	is being rebuilt.  If you don't, we'll try to fake it, but
1352**	there will be a race condition.  If opening for read-only,
1353**	we immediately release the lock to avoid freezing things up.
1354**	We really ought to hold the lock, but guarantee that we won't
1355**	be pokey about it.  That's hard to do.
1356*/
1357
1358/* these should be K line arguments */
1359# if DB_VERSION_MAJOR < 2
1360#  define db_cachesize	cachesize
1361#  define h_nelem	nelem
1362#  ifndef DB_CACHE_SIZE
1363#   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
1364#  endif /* ! DB_CACHE_SIZE */
1365#  ifndef DB_HASH_NELEM
1366#   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
1367#  endif /* ! DB_HASH_NELEM */
1368# endif /* DB_VERSION_MAJOR < 2 */
1369
1370bool
1371bt_map_open(map, mode)
1372	MAP *map;
1373	int mode;
1374{
1375# if DB_VERSION_MAJOR < 2
1376	BTREEINFO btinfo;
1377# endif /* DB_VERSION_MAJOR < 2 */
1378# if DB_VERSION_MAJOR == 2
1379	DB_INFO btinfo;
1380# endif /* DB_VERSION_MAJOR == 2 */
1381# if DB_VERSION_MAJOR > 2
1382	void *btinfo = NULL;
1383# endif /* DB_VERSION_MAJOR > 2 */
1384
1385	if (tTd(38, 2))
1386		dprintf("bt_map_open(%s, %s, %d)\n",
1387			map->map_mname, map->map_file, mode);
1388
1389# if DB_VERSION_MAJOR < 3
1390	memset(&btinfo, '\0', sizeof btinfo);
1391#  ifdef DB_CACHE_SIZE
1392	btinfo.db_cachesize = DB_CACHE_SIZE;
1393#  endif /* DB_CACHE_SIZE */
1394# endif /* DB_VERSION_MAJOR < 3 */
1395
1396	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1397}
1398
1399bool
1400hash_map_open(map, mode)
1401	MAP *map;
1402	int mode;
1403{
1404# if DB_VERSION_MAJOR < 2
1405	HASHINFO hinfo;
1406# endif /* DB_VERSION_MAJOR < 2 */
1407# if DB_VERSION_MAJOR == 2
1408	DB_INFO hinfo;
1409# endif /* DB_VERSION_MAJOR == 2 */
1410# if DB_VERSION_MAJOR > 2
1411	void *hinfo = NULL;
1412# endif /* DB_VERSION_MAJOR > 2 */
1413
1414	if (tTd(38, 2))
1415		dprintf("hash_map_open(%s, %s, %d)\n",
1416			map->map_mname, map->map_file, mode);
1417
1418# if DB_VERSION_MAJOR < 3
1419	memset(&hinfo, '\0', sizeof hinfo);
1420#  ifdef DB_HASH_NELEM
1421	hinfo.h_nelem = DB_HASH_NELEM;
1422#  endif /* DB_HASH_NELEM */
1423#  ifdef DB_CACHE_SIZE
1424	hinfo.db_cachesize = DB_CACHE_SIZE;
1425#  endif /* DB_CACHE_SIZE */
1426# endif /* DB_VERSION_MAJOR < 3 */
1427
1428	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1429}
1430
1431static bool
1432db_map_open(map, mode, mapclassname, dbtype, openinfo)
1433	MAP *map;
1434	int mode;
1435	char *mapclassname;
1436	DBTYPE dbtype;
1437# if DB_VERSION_MAJOR < 2
1438	const void *openinfo;
1439# endif /* DB_VERSION_MAJOR < 2 */
1440# if DB_VERSION_MAJOR == 2
1441	DB_INFO *openinfo;
1442# endif /* DB_VERSION_MAJOR == 2 */
1443# if DB_VERSION_MAJOR > 2
1444	void **openinfo;
1445# endif /* DB_VERSION_MAJOR > 2 */
1446{
1447	DB *db = NULL;
1448	int i;
1449	int omode;
1450	int smode = S_IREAD;
1451	int fd;
1452	long sff;
1453	int save_errno;
1454	struct stat st;
1455	char buf[MAXNAME + 1];
1456
1457	/* do initial file and directory checks */
1458	(void) strlcpy(buf, map->map_file, sizeof buf - 3);
1459	i = strlen(buf);
1460	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1461		(void) strlcat(buf, ".db", sizeof buf);
1462
1463	mode &= O_ACCMODE;
1464	omode = mode;
1465
1466	sff = SFF_ROOTOK|SFF_REGONLY;
1467	if (mode == O_RDWR)
1468	{
1469		sff |= SFF_CREAT;
1470		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1471			sff |= SFF_NOSLINK;
1472		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1473			sff |= SFF_NOHLINK;
1474		smode = S_IWRITE;
1475	}
1476	else
1477	{
1478		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1479			sff |= SFF_NOWLINK;
1480	}
1481	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1482		sff |= SFF_SAFEDIRPATH;
1483	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
1484
1485# if !_FFR_REMOVE_AUTOREBUILD
1486	if (i == ENOENT && AutoRebuild &&
1487	    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
1488	    (bitset(MF_IMPL_HASH, map->map_mflags) ||
1489	     bitset(MF_ALIAS, map->map_mflags)) &&
1490	    mode == O_RDONLY)
1491	{
1492		bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
1493
1494		/* may be able to rebuild */
1495		map->map_mflags &= ~MF_IMPL_HASH;
1496		if (!rebuildaliases(map, TRUE))
1497			return FALSE;
1498		if (impl)
1499			return impl_map_open(map, O_RDONLY);
1500		else
1501			return db_map_open(map, O_RDONLY, mapclassname,
1502					   dbtype, openinfo);
1503	}
1504# endif /* !_FFR_REMOVE_AUTOREBUILD */
1505
1506	if (i != 0)
1507	{
1508		char *prob = "unsafe";
1509
1510		/* cannot open this map */
1511		if (i == ENOENT)
1512			prob = "missing";
1513		if (tTd(38, 2))
1514			dprintf("\t%s map file: %s\n", prob, errstring(i));
1515		errno = i;
1516		if (!bitset(MF_OPTIONAL, map->map_mflags))
1517			syserr("%s map \"%s\": %s map file %s",
1518				mapclassname, map->map_mname, prob, buf);
1519		return FALSE;
1520	}
1521	if (st.st_mode == ST_MODE_NOFILE)
1522		omode |= O_CREAT|O_EXCL;
1523
1524	map->map_lockfd = -1;
1525
1526# if LOCK_ON_OPEN
1527	if (mode == O_RDWR)
1528		omode |= O_TRUNC|O_EXLOCK;
1529	else
1530		omode |= O_SHLOCK;
1531# else /* LOCK_ON_OPEN */
1532	/*
1533	**  Pre-lock the file to avoid race conditions.  In particular,
1534	**  since dbopen returns NULL if the file is zero length, we
1535	**  must have a locked instance around the dbopen.
1536	*/
1537
1538	fd = open(buf, omode, DBMMODE);
1539	if (fd < 0)
1540	{
1541		if (!bitset(MF_OPTIONAL, map->map_mflags))
1542			syserr("db_map_open: cannot pre-open database %s", buf);
1543		return FALSE;
1544	}
1545
1546	/* make sure no baddies slipped in just before the open... */
1547	if (filechanged(buf, fd, &st))
1548	{
1549		save_errno = errno;
1550		(void) close(fd);
1551		errno = save_errno;
1552		syserr("db_map_open(%s): file changed after pre-open", buf);
1553		return FALSE;
1554	}
1555
1556	/* if new file, get the "before" bits for later filechanged check */
1557	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
1558	{
1559		save_errno = errno;
1560		(void) close(fd);
1561		errno = save_errno;
1562		syserr("db_map_open(%s): cannot fstat pre-opened file",
1563			buf);
1564		return FALSE;
1565	}
1566
1567	/* actually lock the pre-opened file */
1568	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
1569		syserr("db_map_open: cannot lock %s", buf);
1570
1571	/* set up mode bits for dbopen */
1572	if (mode == O_RDWR)
1573		omode |= O_TRUNC;
1574	omode &= ~(O_EXCL|O_CREAT);
1575# endif /* LOCK_ON_OPEN */
1576
1577# if DB_VERSION_MAJOR < 2
1578	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
1579# else /* DB_VERSION_MAJOR < 2 */
1580	{
1581		int flags = 0;
1582#  if DB_VERSION_MAJOR > 2
1583		int ret;
1584#  endif /* DB_VERSION_MAJOR > 2 */
1585
1586		if (mode == O_RDONLY)
1587			flags |= DB_RDONLY;
1588		if (bitset(O_CREAT, omode))
1589			flags |= DB_CREATE;
1590		if (bitset(O_TRUNC, omode))
1591			flags |= DB_TRUNCATE;
1592
1593#  if !HASFLOCK && defined(DB_FCNTL_LOCKING)
1594		flags |= DB_FCNTL_LOCKING;
1595#  endif /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */
1596
1597#  if DB_VERSION_MAJOR > 2
1598		ret = db_create(&db, NULL, 0);
1599#  ifdef DB_CACHE_SIZE
1600		if (ret == 0 && db != NULL)
1601		{
1602			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
1603			if (ret != 0)
1604			{
1605				(void) db->close(db, 0);
1606				db = NULL;
1607			}
1608		}
1609#  endif /* DB_CACHE_SIZE */
1610#  ifdef DB_HASH_NELEM
1611		if (dbtype == DB_HASH && ret == 0 && db != NULL)
1612		{
1613			ret = db->set_h_nelem(db, DB_HASH_NELEM);
1614			if (ret != 0)
1615			{
1616				(void) db->close(db, 0);
1617				db = NULL;
1618			}
1619		}
1620#  endif /* DB_HASH_NELEM */
1621		if (ret == 0 && db != NULL)
1622		{
1623			ret = db->open(db, buf, NULL, dbtype, flags, DBMMODE);
1624			if (ret != 0)
1625			{
1626				(void) db->close(db, 0);
1627				db = NULL;
1628			}
1629		}
1630		errno = ret;
1631#  else /* DB_VERSION_MAJOR > 2 */
1632		errno = db_open(buf, dbtype, flags, DBMMODE,
1633				NULL, openinfo, &db);
1634#  endif /* DB_VERSION_MAJOR > 2 */
1635	}
1636# endif /* DB_VERSION_MAJOR < 2 */
1637	save_errno = errno;
1638
1639# if !LOCK_ON_OPEN
1640	if (mode == O_RDWR)
1641		map->map_lockfd = fd;
1642	else
1643		(void) close(fd);
1644# endif /* !LOCK_ON_OPEN */
1645
1646	if (db == NULL)
1647	{
1648		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1649		    aliaswait(map, ".db", FALSE))
1650			return TRUE;
1651# if !LOCK_ON_OPEN
1652		if (map->map_lockfd >= 0)
1653			(void) close(map->map_lockfd);
1654# endif /* !LOCK_ON_OPEN */
1655		errno = save_errno;
1656		if (!bitset(MF_OPTIONAL, map->map_mflags))
1657			syserr("Cannot open %s database %s",
1658				mapclassname, buf);
1659		return FALSE;
1660	}
1661
1662# if DB_VERSION_MAJOR < 2
1663	fd = db->fd(db);
1664# else /* DB_VERSION_MAJOR < 2 */
1665	fd = -1;
1666	errno = db->fd(db, &fd);
1667# endif /* DB_VERSION_MAJOR < 2 */
1668	if (filechanged(buf, fd, &st))
1669	{
1670		save_errno = errno;
1671# if DB_VERSION_MAJOR < 2
1672		(void) db->close(db);
1673# else /* DB_VERSION_MAJOR < 2 */
1674		errno = db->close(db, 0);
1675# endif /* DB_VERSION_MAJOR < 2 */
1676# if !LOCK_ON_OPEN
1677		if (map->map_lockfd >= 0)
1678			(void) close(map->map_lockfd);
1679# endif /* !LOCK_ON_OPEN */
1680		errno = save_errno;
1681		syserr("db_map_open(%s): file changed after open", buf);
1682		return FALSE;
1683	}
1684
1685	if (mode == O_RDWR)
1686		map->map_mflags |= MF_LOCKED;
1687# if LOCK_ON_OPEN
1688	if (fd >= 0 && mode == O_RDONLY)
1689	{
1690		(void) lockfile(fd, buf, NULL, LOCK_UN);
1691	}
1692# endif /* LOCK_ON_OPEN */
1693
1694	/* try to make sure that at least the database header is on disk */
1695	if (mode == O_RDWR)
1696	{
1697		(void) db->sync(db, 0);
1698		if (geteuid() == 0 && TrustedUid != 0)
1699		{
1700#  if HASFCHOWN
1701			if (fchown(fd, TrustedUid, -1) < 0)
1702			{
1703				int err = errno;
1704
1705				sm_syslog(LOG_ALERT, NOQID,
1706					  "ownership change on %s failed: %s",
1707					  buf, errstring(err));
1708				message("050 ownership change on %s failed: %s",
1709					buf, errstring(err));
1710			}
1711#  endif /* HASFCHOWN */
1712		}
1713	}
1714
1715	map->map_db2 = (ARBPTR_T) db;
1716
1717	/*
1718	**  Need to set map_mtime before the call to aliaswait()
1719	**  as aliaswait() will call map_lookup() which requires
1720	**  map_mtime to be set
1721	*/
1722
1723	if (fd >= 0 && fstat(fd, &st) >= 0)
1724		map->map_mtime = st.st_mtime;
1725
1726	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1727	    !aliaswait(map, ".db", TRUE))
1728		return FALSE;
1729	return TRUE;
1730}
1731
1732
1733/*
1734**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
1735*/
1736
1737char *
1738db_map_lookup(map, name, av, statp)
1739	MAP *map;
1740	char *name;
1741	char **av;
1742	int *statp;
1743{
1744	DBT key, val;
1745	register DB *db = (DB *) map->map_db2;
1746	int i;
1747	int st;
1748	int save_errno;
1749	int fd;
1750	struct stat stbuf;
1751	char keybuf[MAXNAME + 1];
1752	char buf[MAXNAME + 1];
1753
1754	memset(&key, '\0', sizeof key);
1755	memset(&val, '\0', sizeof val);
1756
1757	if (tTd(38, 20))
1758		dprintf("db_map_lookup(%s, %s)\n",
1759			map->map_mname, name);
1760
1761	i = strlen(map->map_file);
1762	if (i > MAXNAME)
1763		i = MAXNAME;
1764	(void) strlcpy(buf, map->map_file, i + 1);
1765	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
1766		buf[i - 3] = '\0';
1767
1768	key.size = strlen(name);
1769	if (key.size > sizeof keybuf - 1)
1770		key.size = sizeof keybuf - 1;
1771	key.data = keybuf;
1772	memmove(keybuf, name, key.size);
1773	keybuf[key.size] = '\0';
1774	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1775		makelower(keybuf);
1776  lockdb:
1777# if DB_VERSION_MAJOR < 2
1778	fd = db->fd(db);
1779# else /* DB_VERSION_MAJOR < 2 */
1780	fd = -1;
1781	errno = db->fd(db, &fd);
1782# endif /* DB_VERSION_MAJOR < 2 */
1783	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1784		(void) lockfile(fd, buf, ".db", LOCK_SH);
1785	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1786	{
1787		/* Reopen the database to sync the cache */
1788		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1789								 : O_RDONLY;
1790
1791		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1792			(void) lockfile(fd, buf, ".db", LOCK_UN);
1793		map->map_class->map_close(map);
1794		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1795		if (map->map_class->map_open(map, omode))
1796		{
1797			map->map_mflags |= MF_OPEN;
1798			map->map_pid = getpid();
1799			if ((omode && O_ACCMODE) == O_RDWR)
1800				map->map_mflags |= MF_WRITABLE;
1801			db = (DB *) map->map_db2;
1802			goto lockdb;
1803		}
1804		else
1805		{
1806			if (!bitset(MF_OPTIONAL, map->map_mflags))
1807			{
1808				extern MAPCLASS BogusMapClass;
1809
1810				*statp = EX_TEMPFAIL;
1811				map->map_class = &BogusMapClass;
1812				map->map_mflags |= MF_OPEN;
1813				map->map_pid = getpid();
1814				syserr("Cannot reopen DB database %s",
1815					map->map_file);
1816			}
1817			return NULL;
1818		}
1819	}
1820
1821	st = 1;
1822	if (bitset(MF_TRY0NULL, map->map_mflags))
1823	{
1824# if DB_VERSION_MAJOR < 2
1825		st = db->get(db, &key, &val, 0);
1826# else /* DB_VERSION_MAJOR < 2 */
1827		errno = db->get(db, NULL, &key, &val, 0);
1828		switch (errno)
1829		{
1830		  case DB_NOTFOUND:
1831		  case DB_KEYEMPTY:
1832			st = 1;
1833			break;
1834
1835		  case 0:
1836			st = 0;
1837			break;
1838
1839		  default:
1840			st = -1;
1841			break;
1842		}
1843# endif /* DB_VERSION_MAJOR < 2 */
1844		if (st == 0)
1845			map->map_mflags &= ~MF_TRY1NULL;
1846	}
1847	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
1848	{
1849		key.size++;
1850# if DB_VERSION_MAJOR < 2
1851		st = db->get(db, &key, &val, 0);
1852# else /* DB_VERSION_MAJOR < 2 */
1853		errno = db->get(db, NULL, &key, &val, 0);
1854		switch (errno)
1855		{
1856		  case DB_NOTFOUND:
1857		  case DB_KEYEMPTY:
1858			st = 1;
1859			break;
1860
1861		  case 0:
1862			st = 0;
1863			break;
1864
1865		  default:
1866			st = -1;
1867			break;
1868		}
1869# endif /* DB_VERSION_MAJOR < 2 */
1870		if (st == 0)
1871			map->map_mflags &= ~MF_TRY0NULL;
1872	}
1873	save_errno = errno;
1874	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1875		(void) lockfile(fd, buf, ".db", LOCK_UN);
1876	if (st != 0)
1877	{
1878		errno = save_errno;
1879		if (st < 0)
1880			syserr("db_map_lookup: get (%s)", name);
1881		return NULL;
1882	}
1883	if (bitset(MF_MATCHONLY, map->map_mflags))
1884		return map_rewrite(map, name, strlen(name), NULL);
1885	else
1886		return map_rewrite(map, val.data, val.size, av);
1887}
1888
1889
1890/*
1891**  DB_MAP_STORE -- store a datum in the NEWDB database
1892*/
1893
1894void
1895db_map_store(map, lhs, rhs)
1896	register MAP *map;
1897	char *lhs;
1898	char *rhs;
1899{
1900	int status;
1901	DBT key;
1902	DBT data;
1903	register DB *db = map->map_db2;
1904	char keybuf[MAXNAME + 1];
1905
1906	memset(&key, '\0', sizeof key);
1907	memset(&data, '\0', sizeof data);
1908
1909	if (tTd(38, 12))
1910		dprintf("db_map_store(%s, %s, %s)\n",
1911			map->map_mname, lhs, rhs);
1912
1913	key.size = strlen(lhs);
1914	key.data = lhs;
1915	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1916	{
1917		if (key.size > sizeof keybuf - 1)
1918			key.size = sizeof keybuf - 1;
1919		memmove(keybuf, key.data, key.size);
1920		keybuf[key.size] = '\0';
1921		makelower(keybuf);
1922		key.data = keybuf;
1923	}
1924
1925	data.size = strlen(rhs);
1926	data.data = rhs;
1927
1928	if (bitset(MF_INCLNULL, map->map_mflags))
1929	{
1930		key.size++;
1931		data.size++;
1932	}
1933
1934# if DB_VERSION_MAJOR < 2
1935	status = db->put(db, &key, &data, R_NOOVERWRITE);
1936# else /* DB_VERSION_MAJOR < 2 */
1937	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
1938	switch (errno)
1939	{
1940	  case DB_KEYEXIST:
1941		status = 1;
1942		break;
1943
1944	  case 0:
1945		status = 0;
1946		break;
1947
1948	  default:
1949		status = -1;
1950		break;
1951	}
1952# endif /* DB_VERSION_MAJOR < 2 */
1953	if (status > 0)
1954	{
1955		if (!bitset(MF_APPEND, map->map_mflags))
1956			message("050 Warning: duplicate alias name %s", lhs);
1957		else
1958		{
1959			static char *buf = NULL;
1960			static int bufsiz = 0;
1961			DBT old;
1962
1963			memset(&old, '\0', sizeof old);
1964
1965			old.data = db_map_lookup(map, key.data,
1966						 (char **)NULL, &status);
1967			if (old.data != NULL)
1968			{
1969				old.size = strlen(old.data);
1970				if (data.size + old.size + 2 > (size_t)bufsiz)
1971				{
1972					if (buf != NULL)
1973						(void) free(buf);
1974					bufsiz = data.size + old.size + 2;
1975					buf = xalloc(bufsiz);
1976				}
1977				snprintf(buf, bufsiz, "%s,%s",
1978					(char *) data.data, (char *) old.data);
1979				data.size = data.size + old.size + 1;
1980				data.data = buf;
1981				if (tTd(38, 9))
1982					dprintf("db_map_store append=%s\n",
1983						(char *) data.data);
1984			}
1985		}
1986# if DB_VERSION_MAJOR < 2
1987		status = db->put(db, &key, &data, 0);
1988# else /* DB_VERSION_MAJOR < 2 */
1989		status = errno = db->put(db, NULL, &key, &data, 0);
1990# endif /* DB_VERSION_MAJOR < 2 */
1991	}
1992	if (status != 0)
1993		syserr("readaliases: db put (%s)", lhs);
1994}
1995
1996
1997/*
1998**  DB_MAP_CLOSE -- add distinguished entries and close the database
1999*/
2000
2001void
2002db_map_close(map)
2003	MAP *map;
2004{
2005	register DB *db = map->map_db2;
2006
2007	if (tTd(38, 9))
2008		dprintf("db_map_close(%s, %s, %lx)\n",
2009			map->map_mname, map->map_file, map->map_mflags);
2010
2011	if (bitset(MF_WRITABLE, map->map_mflags))
2012	{
2013		/* write out the distinguished alias */
2014		db_map_store(map, "@", "@");
2015	}
2016
2017	(void) db->sync(db, 0);
2018
2019# if !LOCK_ON_OPEN
2020	if (map->map_lockfd >= 0)
2021		(void) close(map->map_lockfd);
2022# endif /* !LOCK_ON_OPEN */
2023
2024# if DB_VERSION_MAJOR < 2
2025	if (db->close(db) != 0)
2026# else /* DB_VERSION_MAJOR < 2 */
2027	/*
2028	**  Berkeley DB can use internal shared memory
2029	**  locking for its memory pool.  Closing a map
2030	**  opened by another process will interfere
2031	**  with the shared memory and locks of the parent
2032	**  process leaving things in a bad state.
2033	*/
2034
2035	/*
2036	**  If this map was not opened by the current
2037	**  process, do not close the map but recover
2038	**  the file descriptor.
2039	*/
2040	if (map->map_pid != getpid())
2041	{
2042		int fd = -1;
2043
2044		errno = db->fd(db, &fd);
2045		if (fd >= 0)
2046			(void) close(fd);
2047		return;
2048	}
2049
2050	if ((errno = db->close(db, 0)) != 0)
2051# endif /* DB_VERSION_MAJOR < 2 */
2052		syserr("db_map_close(%s, %s, %lx): db close failure",
2053			map->map_mname, map->map_file, map->map_mflags);
2054}
2055#endif /* NEWDB */
2056/*
2057**  NIS Modules
2058*/
2059
2060#ifdef NIS
2061
2062# ifndef YPERR_BUSY
2063#  define YPERR_BUSY	16
2064# endif /* ! YPERR_BUSY */
2065
2066/*
2067**  NIS_MAP_OPEN -- open DBM map
2068*/
2069
2070bool
2071nis_map_open(map, mode)
2072	MAP *map;
2073	int mode;
2074{
2075	int yperr;
2076	register char *p;
2077	auto char *vp;
2078	auto int vsize;
2079
2080	if (tTd(38, 2))
2081		dprintf("nis_map_open(%s, %s, %d)\n",
2082			map->map_mname, map->map_file, mode);
2083
2084	mode &= O_ACCMODE;
2085	if (mode != O_RDONLY)
2086	{
2087		/* issue a pseudo-error message */
2088# ifdef ENOSYS
2089		errno = ENOSYS;
2090# else /* ENOSYS */
2091#  ifdef EFTYPE
2092		errno = EFTYPE;
2093#  else /* EFTYPE */
2094		errno = ENXIO;
2095#  endif /* EFTYPE */
2096# endif /* ENOSYS */
2097		return FALSE;
2098	}
2099
2100	p = strchr(map->map_file, '@');
2101	if (p != NULL)
2102	{
2103		*p++ = '\0';
2104		if (*p != '\0')
2105			map->map_domain = p;
2106	}
2107
2108	if (*map->map_file == '\0')
2109		map->map_file = "mail.aliases";
2110
2111	if (map->map_domain == NULL)
2112	{
2113		yperr = yp_get_default_domain(&map->map_domain);
2114		if (yperr != 0)
2115		{
2116			if (!bitset(MF_OPTIONAL, map->map_mflags))
2117				syserr("421 4.3.5 NIS map %s specified, but NIS not running",
2118				       map->map_file);
2119			return FALSE;
2120		}
2121	}
2122
2123	/* check to see if this map actually exists */
2124	vp = NULL;
2125	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2126			&vp, &vsize);
2127	if (tTd(38, 10))
2128		dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2129			map->map_domain, map->map_file, yperr_string(yperr));
2130	if (vp != NULL)
2131		free(vp);
2132
2133	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2134	{
2135		/*
2136		**  We ought to be calling aliaswait() here if this is an
2137		**  alias file, but powerful HP-UX NIS servers  apparently
2138		**  don't insert the @:@ token into the alias map when it
2139		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
2140		*/
2141
2142# if 0
2143		if (!bitset(MF_ALIAS, map->map_mflags) ||
2144		    aliaswait(map, NULL, TRUE))
2145# endif /* 0 */
2146			return TRUE;
2147	}
2148
2149	if (!bitset(MF_OPTIONAL, map->map_mflags))
2150	{
2151		syserr("421 4.0.0 Cannot bind to map %s in domain %s: %s",
2152			map->map_file, map->map_domain, yperr_string(yperr));
2153	}
2154
2155	return FALSE;
2156}
2157
2158
2159/*
2160**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2161*/
2162
2163/* ARGSUSED3 */
2164char *
2165nis_map_lookup(map, name, av, statp)
2166	MAP *map;
2167	char *name;
2168	char **av;
2169	int *statp;
2170{
2171	char *vp;
2172	auto int vsize;
2173	int buflen;
2174	int yperr;
2175	char keybuf[MAXNAME + 1];
2176
2177	if (tTd(38, 20))
2178		dprintf("nis_map_lookup(%s, %s)\n",
2179			map->map_mname, name);
2180
2181	buflen = strlen(name);
2182	if (buflen > sizeof keybuf - 1)
2183		buflen = sizeof keybuf - 1;
2184	memmove(keybuf, name, buflen);
2185	keybuf[buflen] = '\0';
2186	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2187		makelower(keybuf);
2188	yperr = YPERR_KEY;
2189	vp = NULL;
2190	if (bitset(MF_TRY0NULL, map->map_mflags))
2191	{
2192		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2193			     &vp, &vsize);
2194		if (yperr == 0)
2195			map->map_mflags &= ~MF_TRY1NULL;
2196	}
2197	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2198	{
2199		if (vp != NULL)
2200		{
2201			free(vp);
2202			vp = NULL;
2203		}
2204		buflen++;
2205		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2206			     &vp, &vsize);
2207		if (yperr == 0)
2208			map->map_mflags &= ~MF_TRY0NULL;
2209	}
2210	if (yperr != 0)
2211	{
2212		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2213			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2214		if (vp != NULL)
2215			free(vp);
2216		return NULL;
2217	}
2218	if (bitset(MF_MATCHONLY, map->map_mflags))
2219		return map_rewrite(map, name, strlen(name), NULL);
2220	else
2221	{
2222		char *ret;
2223
2224		ret = map_rewrite(map, vp, vsize, av);
2225		if (vp != NULL)
2226			free(vp);
2227		return ret;
2228	}
2229}
2230
2231
2232/*
2233**  NIS_GETCANONNAME -- look up canonical name in NIS
2234*/
2235
2236static bool
2237nis_getcanonname(name, hbsize, statp)
2238	char *name;
2239	int hbsize;
2240	int *statp;
2241{
2242	char *vp;
2243	auto int vsize;
2244	int keylen;
2245	int yperr;
2246	static bool try0null = TRUE;
2247	static bool try1null = TRUE;
2248	static char *yp_domain = NULL;
2249	char host_record[MAXLINE];
2250	char cbuf[MAXNAME];
2251	char nbuf[MAXNAME + 1];
2252
2253	if (tTd(38, 20))
2254		dprintf("nis_getcanonname(%s)\n", name);
2255
2256	if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2257	{
2258		*statp = EX_UNAVAILABLE;
2259		return FALSE;
2260	}
2261	shorten_hostname(nbuf);
2262	keylen = strlen(nbuf);
2263
2264	if (yp_domain == NULL)
2265		(void) yp_get_default_domain(&yp_domain);
2266	makelower(nbuf);
2267	yperr = YPERR_KEY;
2268	vp = NULL;
2269	if (try0null)
2270	{
2271		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2272			     &vp, &vsize);
2273		if (yperr == 0)
2274			try1null = FALSE;
2275	}
2276	if (yperr == YPERR_KEY && try1null)
2277	{
2278		if (vp != NULL)
2279		{
2280			free(vp);
2281			vp = NULL;
2282		}
2283		keylen++;
2284		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2285			     &vp, &vsize);
2286		if (yperr == 0)
2287			try0null = FALSE;
2288	}
2289	if (yperr != 0)
2290	{
2291		if (yperr == YPERR_KEY)
2292			*statp = EX_NOHOST;
2293		else if (yperr == YPERR_BUSY)
2294			*statp = EX_TEMPFAIL;
2295		else
2296			*statp = EX_UNAVAILABLE;
2297		if (vp != NULL)
2298			free(vp);
2299		return FALSE;
2300	}
2301	(void) strlcpy(host_record, vp, sizeof host_record);
2302	free(vp);
2303	if (tTd(38, 44))
2304		dprintf("got record `%s'\n", host_record);
2305	if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
2306	{
2307		/* this should not happen, but.... */
2308		*statp = EX_NOHOST;
2309		return FALSE;
2310	}
2311	if (hbsize < strlen(cbuf))
2312	{
2313		*statp = EX_UNAVAILABLE;
2314		return FALSE;
2315	}
2316	(void) strlcpy(name, cbuf, hbsize);
2317	*statp = EX_OK;
2318	return TRUE;
2319}
2320
2321#endif /* NIS */
2322/*
2323**  NISPLUS Modules
2324**
2325**	This code donated by Sun Microsystems.
2326*/
2327
2328#ifdef NISPLUS
2329
2330# undef NIS		/* symbol conflict in nis.h */
2331# undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2332# include <rpcsvc/nis.h>
2333# include <rpcsvc/nislib.h>
2334
2335# define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2336# define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2337# define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2338# define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
2339
2340/*
2341**  NISPLUS_MAP_OPEN -- open nisplus table
2342*/
2343
2344bool
2345nisplus_map_open(map, mode)
2346	MAP *map;
2347	int mode;
2348{
2349	nis_result *res = NULL;
2350	int retry_cnt, max_col, i;
2351	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2352
2353	if (tTd(38, 2))
2354		dprintf("nisplus_map_open(%s, %s, %d)\n",
2355			map->map_mname, map->map_file, mode);
2356
2357	mode &= O_ACCMODE;
2358	if (mode != O_RDONLY)
2359	{
2360		errno = EPERM;
2361		return FALSE;
2362	}
2363
2364	if (*map->map_file == '\0')
2365		map->map_file = "mail_aliases.org_dir";
2366
2367	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2368	{
2369		/* set default NISPLUS Domain to $m */
2370		map->map_domain = newstr(nisplus_default_domain());
2371		if (tTd(38, 2))
2372			dprintf("nisplus_map_open(%s): using domain %s\n",
2373				map->map_file, map->map_domain);
2374	}
2375	if (!PARTIAL_NAME(map->map_file))
2376	{
2377		map->map_domain = newstr("");
2378		snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
2379	}
2380	else
2381	{
2382		/* check to see if this map actually exists */
2383		snprintf(qbuf, sizeof qbuf, "%s.%s",
2384			map->map_file, map->map_domain);
2385	}
2386
2387	retry_cnt = 0;
2388	while (res == NULL || res->status != NIS_SUCCESS)
2389	{
2390		res = nis_lookup(qbuf, FOLLOW_LINKS);
2391		switch (res->status)
2392		{
2393		  case NIS_SUCCESS:
2394			break;
2395
2396		  case NIS_TRYAGAIN:
2397		  case NIS_RPCERROR:
2398		  case NIS_NAMEUNREACHABLE:
2399			if (retry_cnt++ > 4)
2400			{
2401				errno = EAGAIN;
2402				return FALSE;
2403			}
2404			/* try not to overwhelm hosed server */
2405			sleep(2);
2406			break;
2407
2408		  default:		/* all other nisplus errors */
2409# if 0
2410			if (!bitset(MF_OPTIONAL, map->map_mflags))
2411				syserr("421 4.0.0 Cannot find table %s.%s: %s",
2412					map->map_file, map->map_domain,
2413					nis_sperrno(res->status));
2414# endif /* 0 */
2415			errno = EAGAIN;
2416			return FALSE;
2417		}
2418	}
2419
2420	if (NIS_RES_NUMOBJ(res) != 1 ||
2421	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2422	{
2423		if (tTd(38, 10))
2424			dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2425# if 0
2426		if (!bitset(MF_OPTIONAL, map->map_mflags))
2427			syserr("421 4.0.0 %s.%s: %s is not a table",
2428				map->map_file, map->map_domain,
2429				nis_sperrno(res->status));
2430# endif /* 0 */
2431		errno = EBADF;
2432		return FALSE;
2433	}
2434	/* default key column is column 0 */
2435	if (map->map_keycolnm == NULL)
2436		map->map_keycolnm = newstr(COL_NAME(res,0));
2437
2438	max_col = COL_MAX(res);
2439
2440	/* verify the key column exist */
2441	for (i = 0; i< max_col; i++)
2442	{
2443		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2444			break;
2445	}
2446	if (i == max_col)
2447	{
2448		if (tTd(38, 2))
2449			dprintf("nisplus_map_open(%s): can not find key column %s\n",
2450				map->map_file, map->map_keycolnm);
2451		errno = ENOENT;
2452		return FALSE;
2453	}
2454
2455	/* default value column is the last column */
2456	if (map->map_valcolnm == NULL)
2457	{
2458		map->map_valcolno = max_col - 1;
2459		return TRUE;
2460	}
2461
2462	for (i = 0; i< max_col; i++)
2463	{
2464		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2465		{
2466			map->map_valcolno = i;
2467			return TRUE;
2468		}
2469	}
2470
2471	if (tTd(38, 2))
2472		dprintf("nisplus_map_open(%s): can not find column %s\n",
2473			map->map_file, map->map_keycolnm);
2474	errno = ENOENT;
2475	return FALSE;
2476}
2477
2478
2479/*
2480**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2481*/
2482
2483char *
2484nisplus_map_lookup(map, name, av, statp)
2485	MAP *map;
2486	char *name;
2487	char **av;
2488	int *statp;
2489{
2490	char *p;
2491	auto int vsize;
2492	char *skp;
2493	int skleft;
2494	char search_key[MAXNAME + 4];
2495	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2496	nis_result *result;
2497
2498	if (tTd(38, 20))
2499		dprintf("nisplus_map_lookup(%s, %s)\n",
2500			map->map_mname, name);
2501
2502	if (!bitset(MF_OPEN, map->map_mflags))
2503	{
2504		if (nisplus_map_open(map, O_RDONLY))
2505		{
2506			map->map_mflags |= MF_OPEN;
2507			map->map_pid = getpid();
2508		}
2509		else
2510		{
2511			*statp = EX_UNAVAILABLE;
2512			return NULL;
2513		}
2514	}
2515
2516	/*
2517	**  Copy the name to the key buffer, escaping double quote characters
2518	**  by doubling them and quoting "]" and "," to avoid having the
2519	**  NIS+ parser choke on them.
2520	*/
2521
2522	skleft = sizeof search_key - 4;
2523	skp = search_key;
2524	for (p = name; *p != '\0' && skleft > 0; p++)
2525	{
2526		switch (*p)
2527		{
2528		  case ']':
2529		  case ',':
2530			/* quote the character */
2531			*skp++ = '"';
2532			*skp++ = *p;
2533			*skp++ = '"';
2534			skleft -= 3;
2535			break;
2536
2537		  case '"':
2538			/* double the quote */
2539			*skp++ = '"';
2540			skleft--;
2541			/* FALLTHROUGH */
2542
2543		  default:
2544			*skp++ = *p;
2545			skleft--;
2546			break;
2547		}
2548	}
2549	*skp = '\0';
2550	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2551		makelower(search_key);
2552
2553	/* construct the query */
2554	if (PARTIAL_NAME(map->map_file))
2555		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
2556			map->map_keycolnm, search_key, map->map_file,
2557			map->map_domain);
2558	else
2559		snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
2560			map->map_keycolnm, search_key, map->map_file);
2561
2562	if (tTd(38, 20))
2563		dprintf("qbuf=%s\n", qbuf);
2564	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
2565	if (result->status == NIS_SUCCESS)
2566	{
2567		int count;
2568		char *str;
2569
2570		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2571		{
2572			if (LogLevel > 10)
2573				sm_syslog(LOG_WARNING, CurEnv->e_id,
2574					  "%s: lookup error, expected 1 entry, got %d",
2575					  map->map_file, count);
2576
2577			/* ignore second entry */
2578			if (tTd(38, 20))
2579				dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
2580					name, count);
2581		}
2582
2583		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
2584		/* set the length of the result */
2585		if (p == NULL)
2586			p = "";
2587		vsize = strlen(p);
2588		if (tTd(38, 20))
2589			dprintf("nisplus_map_lookup(%s), found %s\n",
2590				name, p);
2591		if (bitset(MF_MATCHONLY, map->map_mflags))
2592			str = map_rewrite(map, name, strlen(name), NULL);
2593		else
2594			str = map_rewrite(map, p, vsize, av);
2595		nis_freeresult(result);
2596		*statp = EX_OK;
2597		return str;
2598	}
2599	else
2600	{
2601		if (result->status == NIS_NOTFOUND)
2602			*statp = EX_NOTFOUND;
2603		else if (result->status == NIS_TRYAGAIN)
2604			*statp = EX_TEMPFAIL;
2605		else
2606		{
2607			*statp = EX_UNAVAILABLE;
2608			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2609		}
2610	}
2611	if (tTd(38, 20))
2612		dprintf("nisplus_map_lookup(%s), failed\n", name);
2613	nis_freeresult(result);
2614	return NULL;
2615}
2616
2617
2618
2619/*
2620**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
2621*/
2622
2623static bool
2624nisplus_getcanonname(name, hbsize, statp)
2625	char *name;
2626	int hbsize;
2627	int *statp;
2628{
2629	char *vp;
2630	auto int vsize;
2631	nis_result *result;
2632	char *p;
2633	char nbuf[MAXNAME + 1];
2634	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2635
2636	if (strlen(name) >= sizeof nbuf)
2637	{
2638		*statp = EX_UNAVAILABLE;
2639		return FALSE;
2640	}
2641	(void) strlcpy(nbuf, name, sizeof nbuf);
2642	shorten_hostname(nbuf);
2643
2644	p = strchr(nbuf, '.');
2645	if (p == NULL)
2646	{
2647		/* single token */
2648		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
2649	}
2650	else if (p[1] != '\0')
2651	{
2652		/* multi token -- take only first token in nbuf */
2653		*p = '\0';
2654		snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
2655			nbuf, &p[1]);
2656	}
2657	else
2658	{
2659		*statp = EX_NOHOST;
2660		return FALSE;
2661	}
2662
2663	if (tTd(38, 20))
2664		dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n",
2665			name, qbuf);
2666
2667	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
2668		NULL, NULL);
2669
2670	if (result->status == NIS_SUCCESS)
2671	{
2672		int count;
2673		char *domain;
2674
2675		if ((count = NIS_RES_NUMOBJ(result)) != 1)
2676		{
2677			if (LogLevel > 10)
2678				sm_syslog(LOG_WARNING, CurEnv->e_id,
2679					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
2680					  count);
2681
2682			/* ignore second entry */
2683			if (tTd(38, 20))
2684				dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
2685					name, count);
2686		}
2687
2688		if (tTd(38, 20))
2689			dprintf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
2690				name, (NIS_RES_OBJECT(result))->zo_domain);
2691
2692
2693		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
2694		vsize = strlen(vp);
2695		if (tTd(38, 20))
2696			dprintf("nisplus_getcanonname(%s), found %s\n",
2697				name, vp);
2698		if (strchr(vp, '.') != NULL)
2699		{
2700			domain = "";
2701		}
2702		else
2703		{
2704			domain = macvalue('m', CurEnv);
2705			if (domain == NULL)
2706				domain = "";
2707		}
2708		if (hbsize > vsize + (int) strlen(domain) + 1)
2709		{
2710			if (domain[0] == '\0')
2711				(void) strlcpy(name, vp, hbsize);
2712			else
2713				snprintf(name, hbsize, "%s.%s", vp, domain);
2714			*statp = EX_OK;
2715		}
2716		else
2717			*statp = EX_NOHOST;
2718		nis_freeresult(result);
2719		return TRUE;
2720	}
2721	else
2722	{
2723		if (result->status == NIS_NOTFOUND)
2724			*statp = EX_NOHOST;
2725		else if (result->status == NIS_TRYAGAIN)
2726			*statp = EX_TEMPFAIL;
2727		else
2728			*statp = EX_UNAVAILABLE;
2729	}
2730	if (tTd(38, 20))
2731		dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
2732			name, result->status, *statp);
2733	nis_freeresult(result);
2734	return FALSE;
2735}
2736
2737char *
2738nisplus_default_domain()
2739{
2740	static char default_domain[MAXNAME + 1] = "";
2741	char *p;
2742
2743	if (default_domain[0] != '\0')
2744		return default_domain;
2745
2746	p = nis_local_directory();
2747	snprintf(default_domain, sizeof default_domain, "%s", p);
2748	return default_domain;
2749}
2750
2751#endif /* NISPLUS */
2752/*
2753**  LDAP Modules
2754*/
2755
2756/*
2757**  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
2758*/
2759
2760#if defined(LDAPMAP) || defined(PH_MAP)
2761
2762# ifdef PH_MAP
2763#  define ph_map_dequote ldapmap_dequote
2764# endif /* PH_MAP */
2765
2766char *
2767ldapmap_dequote(str)
2768	char *str;
2769{
2770	char *p;
2771	char *start;
2772
2773	if (str == NULL)
2774		return NULL;
2775
2776	p = str;
2777	if (*p == '"')
2778	{
2779		/* Should probably swallow initial whitespace here */
2780		start = ++p;
2781	}
2782	else
2783		return str;
2784	while (*p != '"' && *p != '\0')
2785		p++;
2786	if (*p != '\0')
2787		*p = '\0';
2788	return start;
2789}
2790#endif /* defined(LDAPMAP) || defined(PH_MAP) */
2791
2792#ifdef LDAPMAP
2793
2794LDAPMAP_STRUCT *LDAPDefaults = NULL;
2795
2796/*
2797**  LDAPMAP_OPEN -- open LDAP map
2798**
2799**	Connect to the LDAP server.  Re-use existing connections since a
2800**	single server connection to a host (with the same host, port,
2801**	bind DN, and secret) can answer queries for multiple maps.
2802*/
2803
2804bool
2805ldapmap_open(map, mode)
2806	MAP *map;
2807	int mode;
2808{
2809	LDAPMAP_STRUCT *lmap;
2810	STAB *s;
2811
2812	if (tTd(38, 2))
2813		dprintf("ldapmap_open(%s, %d)\n", map->map_mname, mode);
2814
2815	mode &= O_ACCMODE;
2816
2817	/* sendmail doesn't have the ability to write to LDAP (yet) */
2818	if (mode != O_RDONLY)
2819	{
2820		/* issue a pseudo-error message */
2821# ifdef ENOSYS
2822		errno = ENOSYS;
2823# else /* ENOSYS */
2824#  ifdef EFTYPE
2825		errno = EFTYPE;
2826#  else /* EFTYPE */
2827		errno = ENXIO;
2828#  endif /* EFTYPE */
2829# endif /* ENOSYS */
2830		return FALSE;
2831	}
2832
2833	/* Comma separate if used as an alias file */
2834	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
2835		map->map_coldelim = ',';
2836
2837	lmap = (LDAPMAP_STRUCT *) map->map_db1;
2838
2839	s = ldapmap_findconn(lmap);
2840	if (s->s_ldap != NULL)
2841	{
2842		/* Already have a connection open to this LDAP server */
2843		lmap->ldap_ld = s->s_ldap;
2844		map->map_mflags |= MF_SHARED;
2845		return TRUE;
2846	}
2847
2848	/* No connection yet, connect */
2849	if (!ldapmap_start(map))
2850		return FALSE;
2851
2852	/* Save connection for reuse */
2853	s->s_ldap = lmap->ldap_ld;
2854	return TRUE;
2855}
2856
2857/*
2858**  LDAPMAP_START -- actually connect to an LDAP server
2859**
2860**	Parameters:
2861**		map -- the map being opened.
2862**
2863**	Returns:
2864**		TRUE if connection is successful, FALSE otherwise.
2865**
2866**	Side Effects:
2867**		Populates lmap->ldap_ld.
2868*/
2869
2870static jmp_buf	LDAPTimeout;
2871
2872static bool
2873ldapmap_start(map)
2874	MAP *map;
2875{
2876	register int bind_result;
2877	int save_errno;
2878	register EVENT *ev = NULL;
2879	LDAPMAP_STRUCT *lmap;
2880	LDAP *ld;
2881
2882	if (tTd(38, 2))
2883		dprintf("ldapmap_start(%s)\n", map->map_mname);
2884
2885	lmap = (LDAPMAP_STRUCT *) map->map_db1;
2886
2887	if (tTd(38,9))
2888		dprintf("ldapmap_start(%s, %d)\n",
2889			lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host,
2890			lmap->ldap_port);
2891
2892# if USE_LDAP_INIT
2893	ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
2894# else /* USE_LDAP_INIT */
2895	/*
2896	**  If using ldap_open(), the actual connection to the server
2897	**  happens now so we need the timeout here.  For ldap_init(),
2898	**  the connection happens at bind time.
2899	*/
2900
2901	/* set the timeout */
2902	if (lmap->ldap_timeout.tv_sec != 0)
2903	{
2904		if (setjmp(LDAPTimeout) != 0)
2905		{
2906			if (LogLevel > 1)
2907				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2908					  "timeout conning to LDAP server %.100s",
2909					  lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
2910			return FALSE;
2911		}
2912		ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
2913	}
2914
2915	ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
2916	save_errno = errno;
2917
2918	/* clear the event if it has not sprung */
2919	if (ev != NULL)
2920		clrevent(ev);
2921# endif /* USE_LDAP_INIT */
2922
2923	errno = save_errno;
2924	if (ld == NULL)
2925	{
2926		if (!bitset(MF_OPTIONAL, map->map_mflags))
2927		{
2928			if (bitset(MF_NODEFER, map->map_mflags))
2929				syserr("%s failed to %s in map %s",
2930# if USE_LDAP_INIT
2931				       "ldap_init",
2932# else /* USE_LDAP_INIT */
2933				       "ldap_open",
2934# endif /* USE_LDAP_INIT */
2935				       lmap->ldap_host == NULL ? "localhost"
2936							       : lmap->ldap_host,
2937				       map->map_mname);
2938			else
2939				syserr("421 4.0.0 %s failed to %s in map %s",
2940# if USE_LDAP_INIT
2941				       "ldap_init",
2942# else /* USE_LDAP_INIT */
2943				       "ldap_open",
2944# endif /* USE_LDAP_INIT */
2945				       lmap->ldap_host == NULL ? "localhost"
2946							       : lmap->ldap_host,
2947				       map->map_mname);
2948		}
2949		return FALSE;
2950	}
2951
2952	ldapmap_setopts(ld, lmap);
2953
2954# if USE_LDAP_INIT
2955	/*
2956	**  If using ldap_init(), the actual connection to the server
2957	**  happens at ldap_bind_s() so we need the timeout here.
2958	*/
2959
2960	/* set the timeout */
2961	if (lmap->ldap_timeout.tv_sec != 0)
2962	{
2963		if (setjmp(LDAPTimeout) != 0)
2964		{
2965			if (LogLevel > 1)
2966				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2967					  "timeout conning to LDAP server %.100s",
2968					  lmap->ldap_host == NULL ? "localhost"
2969								  : lmap->ldap_host);
2970			return FALSE;
2971		}
2972		ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
2973	}
2974# endif /* USE_LDAP_INIT */
2975
2976# ifdef LDAP_AUTH_KRBV4
2977	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
2978	    lmap->ldap_secret != NULL)
2979	{
2980		/*
2981		**  Need to put ticket in environment here instead of
2982		**  during parseargs as there may be different tickets
2983		**  for different LDAP connections.
2984		*/
2985
2986		(void) putenv(lmap->ldap_secret);
2987	}
2988# endif /* LDAP_AUTH_KRBV4 */
2989
2990	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
2991				  lmap->ldap_secret, lmap->ldap_method);
2992
2993# if USE_LDAP_INIT
2994	/* clear the event if it has not sprung */
2995	if (ev != NULL)
2996		clrevent(ev);
2997# endif /* USE_LDAP_INIT */
2998
2999	if (bind_result != LDAP_SUCCESS)
3000	{
3001		errno = bind_result + E_LDAPBASE;
3002		if (!bitset(MF_OPTIONAL, map->map_mflags))
3003		{
3004			syserr("421 4.0.0 Cannot bind to map %s in ldap server %s",
3005			       map->map_mname,
3006			       lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
3007		}
3008		return FALSE;
3009	}
3010
3011	/* We need to cast ld into the map structure */
3012	lmap->ldap_ld = ld;
3013	return TRUE;
3014}
3015
3016/* ARGSUSED */
3017static void
3018ldaptimeout(sig_no)
3019	int sig_no;
3020{
3021	longjmp(LDAPTimeout, 1);
3022}
3023
3024/*
3025**  LDAPMAP_CLOSE -- close ldap map
3026*/
3027
3028void
3029ldapmap_close(map)
3030	MAP *map;
3031{
3032	LDAPMAP_STRUCT *lmap;
3033	STAB *s;
3034
3035	if (tTd(38, 2))
3036		dprintf("ldapmap_close(%s)\n", map->map_mname);
3037
3038	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3039
3040	/* Check if already closed */
3041	if (lmap->ldap_ld == NULL)
3042		return;
3043
3044	s = ldapmap_findconn(lmap);
3045
3046	/* Check if already closed */
3047	if (s->s_ldap == NULL)
3048		return;
3049
3050	/* If same as saved connection, stored connection is going away */
3051	if (s->s_ldap == lmap->ldap_ld)
3052		s->s_ldap = NULL;
3053
3054	if (lmap->ldap_ld != NULL)
3055	{
3056		ldap_unbind(lmap->ldap_ld);
3057		lmap->ldap_ld = NULL;
3058	}
3059}
3060
3061# ifdef SUNET_ID
3062/*
3063**  SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
3064**  This only makes sense at Stanford University.
3065*/
3066
3067char *
3068sunet_id_hash(str)
3069	char *str;
3070{
3071	char *p, *p_last;
3072
3073	p = str;
3074	p_last = p;
3075	while (*p != '\0')
3076	{
3077		if (islower(*p) || isdigit(*p))
3078		{
3079			*p_last = *p;
3080			p_last++;
3081		}
3082		else if (isupper(*p))
3083		{
3084			*p_last = tolower(*p);
3085			p_last++;
3086		}
3087		++p;
3088	}
3089	if (*p_last != '\0')
3090		*p_last = '\0';
3091	return str;
3092}
3093# endif /* SUNET_ID */
3094
3095/*
3096**  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3097*/
3098
3099char *
3100ldapmap_lookup(map, name, av, statp)
3101	MAP *map;
3102	char *name;
3103	char **av;
3104	int *statp;
3105{
3106	int i;
3107	int entries = 0;
3108	int msgid;
3109	int ret;
3110	int vsize;
3111	char *fp, *vp;
3112	char *p, *q;
3113	char *result = NULL;
3114	LDAPMAP_STRUCT *lmap = NULL;
3115	char keybuf[MAXNAME + 1];
3116	char filter[LDAPMAP_MAX_FILTER + 1];
3117
3118	if (tTd(38, 20))
3119		dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3120
3121	/* Get ldap struct pointer from map */
3122	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3123	ldapmap_setopts(lmap->ldap_ld, lmap);
3124
3125	(void) strlcpy(keybuf, name, sizeof keybuf);
3126
3127	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3128	{
3129# ifdef SUNET_ID
3130		sunet_id_hash(keybuf);
3131# else /* SUNET_ID */
3132		makelower(keybuf);
3133# endif /* SUNET_ID */
3134	}
3135
3136	/* substitute keybuf into filter, perhaps multiple times */
3137	memset(filter, '\0', sizeof filter);
3138	fp = filter;
3139	p = lmap->ldap_filter;
3140	while ((q = strchr(p, '%')) != NULL)
3141	{
3142		if (q[1] == 's')
3143		{
3144			snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
3145				 q - p, p, keybuf);
3146			fp += strlen(fp);
3147			p = q + 2;
3148		}
3149		else if (q[1] == '0')
3150		{
3151			char *k = keybuf;
3152
3153			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
3154				 q - p, p);
3155			fp += strlen(fp);
3156			p = q + 2;
3157
3158			/* Properly escape LDAP special characters */
3159			while (SPACELEFT(filter, fp) > 0 &&
3160			       *k != '\0')
3161			{
3162				if (*k == '*' || *k == '(' ||
3163				    *k == ')' || *k == '\\')
3164				{
3165					(void) strlcat(fp,
3166						       (*k == '*' ? "\\2A" :
3167							(*k == '(' ? "\\28" :
3168							 (*k == ')' ? "\\29" :
3169							  (*k == '\\' ? "\\5C" :
3170							   "\00")))),
3171						SPACELEFT(filter, fp));
3172					fp += strlen(fp);
3173					k++;
3174				}
3175				else
3176					*fp++ = *k++;
3177			}
3178		}
3179		else
3180		{
3181			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
3182				 q - p + 1, p);
3183			p = q + (q[1] == '%' ? 2 : 1);
3184			fp += strlen(fp);
3185		}
3186	}
3187	snprintf(fp, SPACELEFT(filter, fp), "%s", p);
3188	if (tTd(38, 20))
3189		dprintf("ldap search filter=%s\n", filter);
3190
3191	lmap->ldap_res = NULL;
3192	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope,
3193			    filter,
3194			    (lmap->ldap_attr[0] == NULL ? NULL :
3195			     lmap->ldap_attr),
3196			    lmap->ldap_attrsonly);
3197	if (msgid == -1)
3198	{
3199		errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3200		if (!bitset(MF_OPTIONAL, map->map_mflags))
3201		{
3202			if (bitset(MF_NODEFER, map->map_mflags))
3203				syserr("Error in ldap_search_st using %s in map %s",
3204				       filter, map->map_mname);
3205			else
3206				syserr("421 4.0.0 Error in ldap_search_st using %s in map %s",
3207				       filter, map->map_mname);
3208		}
3209		*statp = EX_TEMPFAIL;
3210		return NULL;
3211	}
3212
3213	*statp = EX_NOTFOUND;
3214	vp = NULL;
3215
3216	/* Get results (all if MF_NOREWRITE, otherwise one by one) */
3217	while ((ret = ldap_result(lmap->ldap_ld, msgid,
3218				  bitset(MF_NOREWRITE, map->map_mflags),
3219				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
3220				   &(lmap->ldap_timeout)),
3221				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
3222	{
3223		LDAPMessage *entry;
3224
3225		if (bitset(MF_SINGLEMATCH, map->map_mflags))
3226		{
3227			entries += ldap_count_entries(lmap->ldap_ld,
3228						      lmap->ldap_res);
3229			if (entries > 1)
3230			{
3231				*statp = EX_NOTFOUND;
3232				if (lmap->ldap_res != NULL)
3233				{
3234					ldap_msgfree(lmap->ldap_res);
3235					lmap->ldap_res = NULL;
3236				}
3237				(void) ldap_abandon(lmap->ldap_ld, msgid);
3238				if (vp != NULL)
3239					free(vp);
3240				if (tTd(38, 25))
3241					dprintf("ldap search found multiple on a single match query\n");
3242				return NULL;
3243			}
3244		}
3245
3246		/* If we don't want multiple values and we have one, break */
3247		if (map->map_coldelim == '\0' && vp != NULL)
3248			break;
3249
3250		/* Cycle through all entries */
3251		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
3252		     entry != NULL;
3253		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
3254		{
3255			BerElement *ber;
3256			char *attr;
3257			char **vals = NULL;
3258
3259			/*
3260			**  If matching only and found an entry,
3261			**  no need to spin through attributes
3262			*/
3263
3264			if (*statp == EX_OK &&
3265			    bitset(MF_MATCHONLY, map->map_mflags))
3266				continue;
3267
3268# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3269			/*
3270			**  Reset value to prevent lingering
3271			**  LDAP_DECODING_ERROR due to
3272			**  OpenLDAP 1.X's hack (see below)
3273			*/
3274
3275			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3276# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3277
3278			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
3279							 &ber);
3280			     attr != NULL;
3281			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
3282							ber))
3283			{
3284				char *tmp, *vp_tmp;
3285
3286				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3287				{
3288					vals = ldap_get_values(lmap->ldap_ld,
3289							       entry,
3290							       attr);
3291					if (vals == NULL)
3292					{
3293						errno = ldapmap_geterrno(lmap->ldap_ld);
3294						if (errno == LDAP_SUCCESS)
3295							continue;
3296
3297						/* Must be an error */
3298						errno += E_LDAPBASE;
3299						if (!bitset(MF_OPTIONAL,
3300							    map->map_mflags))
3301						{
3302							if (bitset(MF_NODEFER,
3303								   map->map_mflags))
3304								syserr("Error getting LDAP values in map %s",
3305								       map->map_mname);
3306							else
3307								syserr("421 4.0.0 Error getting LDAP values in map %s",
3308								       map->map_mname);
3309						}
3310						*statp = EX_TEMPFAIL;
3311# if USING_NETSCAPE_LDAP
3312						ldap_mem_free(attr);
3313# endif /* USING_NETSCAPE_LDAP */
3314						if (lmap->ldap_res != NULL)
3315						{
3316							ldap_msgfree(lmap->ldap_res);
3317							lmap->ldap_res = NULL;
3318						}
3319						(void) ldap_abandon(lmap->ldap_ld,
3320								    msgid);
3321						if (vp != NULL)
3322							free(vp);
3323						return NULL;
3324					}
3325				}
3326
3327				*statp = EX_OK;
3328
3329# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3330				/*
3331				**  Reset value to prevent lingering
3332				**  LDAP_DECODING_ERROR due to
3333				**  OpenLDAP 1.X's hack (see below)
3334				*/
3335
3336				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3337# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3338
3339				/*
3340				**  If matching only,
3341				**  no need to spin through entries
3342				*/
3343
3344				if (bitset(MF_MATCHONLY, map->map_mflags))
3345					continue;
3346
3347				/*
3348				**  If we don't want multiple values,
3349				**  return first found.
3350				*/
3351
3352				if (map->map_coldelim == '\0')
3353				{
3354					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3355					{
3356						vp = newstr(attr);
3357# if USING_NETSCAPE_LDAP
3358						ldap_mem_free(attr);
3359# endif /* USING_NETSCAPE_LDAP */
3360						break;
3361					}
3362
3363					if (vals[0] == NULL)
3364					{
3365						ldap_value_free(vals);
3366# if USING_NETSCAPE_LDAP
3367						ldap_mem_free(attr);
3368# endif /* USING_NETSCAPE_LDAP */
3369						continue;
3370					}
3371
3372					vp = newstr(vals[0]);
3373					ldap_value_free(vals);
3374# if USING_NETSCAPE_LDAP
3375					ldap_mem_free(attr);
3376# endif /* USING_NETSCAPE_LDAP */
3377					break;
3378				}
3379
3380				/* attributes only */
3381				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3382				{
3383					if (vp == NULL)
3384						vp = newstr(attr);
3385					else
3386					{
3387						vsize = strlen(vp) +
3388							strlen(attr) + 2;
3389						tmp = xalloc(vsize);
3390						snprintf(tmp, vsize, "%s%c%s",
3391							 vp, map->map_coldelim,
3392							 attr);
3393						free(vp);
3394						vp = tmp;
3395					}
3396# if USING_NETSCAPE_LDAP
3397					ldap_mem_free(attr);
3398# endif /* USING_NETSCAPE_LDAP */
3399					continue;
3400				}
3401
3402				/*
3403				**  If there is more than one,
3404				**  munge then into a map_coldelim
3405				**  separated string
3406				*/
3407
3408				vsize = 0;
3409				for (i = 0; vals[i] != NULL; i++)
3410					vsize += strlen(vals[i]) + 1;
3411				vp_tmp = xalloc(vsize);
3412				*vp_tmp = '\0';
3413
3414				p = vp_tmp;
3415				for (i = 0; vals[i] != NULL; i++)
3416				{
3417					p += strlcpy(p, vals[i],
3418						     vsize - (p - vp_tmp));
3419					if (p >= vp_tmp + vsize)
3420						syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3421					if (vals[i + 1] != NULL)
3422						*p++ = map->map_coldelim;
3423				}
3424
3425				ldap_value_free(vals);
3426# if USING_NETSCAPE_LDAP
3427				ldap_mem_free(attr);
3428# endif /* USING_NETSCAPE_LDAP */
3429				if (vp == NULL)
3430				{
3431					vp = vp_tmp;
3432					continue;
3433				}
3434				vsize = strlen(vp) + strlen(vp_tmp) + 2;
3435				tmp = xalloc(vsize);
3436				snprintf(tmp, vsize, "%s%c%s",
3437					 vp, map->map_coldelim, vp_tmp);
3438
3439				free(vp);
3440				free(vp_tmp);
3441				vp = tmp;
3442			}
3443			errno = ldapmap_geterrno(lmap->ldap_ld);
3444
3445			/*
3446			**  We check errno != LDAP_DECODING_ERROR since
3447			**  OpenLDAP 1.X has a very ugly *undocumented*
3448			**  hack of returning this error code from
3449			**  ldap_next_attribute() if the library freed the
3450			**  ber attribute.  See:
3451			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
3452			*/
3453
3454			if (errno != LDAP_SUCCESS &&
3455			    errno != LDAP_DECODING_ERROR)
3456			{
3457				/* Must be an error */
3458				errno += E_LDAPBASE;
3459				if (!bitset(MF_OPTIONAL, map->map_mflags))
3460				{
3461					if (bitset(MF_NODEFER, map->map_mflags))
3462						syserr("Error getting LDAP attributes in map %s",
3463						       map->map_mname);
3464					else
3465						syserr("421 4.0.0 Error getting LDAP attributes in map %s",
3466						       map->map_mname);
3467				}
3468				*statp = EX_TEMPFAIL;
3469				if (lmap->ldap_res != NULL)
3470				{
3471					ldap_msgfree(lmap->ldap_res);
3472					lmap->ldap_res = NULL;
3473				}
3474				(void) ldap_abandon(lmap->ldap_ld, msgid);
3475				if (vp != NULL)
3476					free(vp);
3477				return NULL;
3478			}
3479
3480			/* We don't want multiple values and we have one */
3481			if (map->map_coldelim == '\0' && vp != NULL)
3482				break;
3483		}
3484		errno = ldapmap_geterrno(lmap->ldap_ld);
3485		if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR)
3486		{
3487			/* Must be an error */
3488			errno += E_LDAPBASE;
3489			if (!bitset(MF_OPTIONAL, map->map_mflags))
3490			{
3491				if (bitset(MF_NODEFER, map->map_mflags))
3492					syserr("Error getting LDAP entries in map %s",
3493					       map->map_mname);
3494				else
3495					syserr("421 4.0.0 Error getting LDAP entries in map %s",
3496					       map->map_mname);
3497			}
3498			*statp = EX_TEMPFAIL;
3499			if (lmap->ldap_res != NULL)
3500			{
3501				ldap_msgfree(lmap->ldap_res);
3502				lmap->ldap_res = NULL;
3503			}
3504			(void) ldap_abandon(lmap->ldap_ld, msgid);
3505			if (vp != NULL)
3506				free(vp);
3507			return NULL;
3508		}
3509		ldap_msgfree(lmap->ldap_res);
3510		lmap->ldap_res = NULL;
3511	}
3512
3513	/*
3514	**  If grabbing all results at once for MF_NOREWRITE and
3515	**  only want a single match, make sure that's all we have
3516	*/
3517
3518	if (ret == LDAP_RES_SEARCH_RESULT &&
3519	    bitset(MF_NOREWRITE|MF_SINGLEMATCH, map->map_mflags))
3520	{
3521		entries += ldap_count_entries(lmap->ldap_ld, lmap->ldap_res);
3522		if (entries > 1)
3523		{
3524			*statp = EX_NOTFOUND;
3525			if (lmap->ldap_res != NULL)
3526			{
3527				ldap_msgfree(lmap->ldap_res);
3528				lmap->ldap_res = NULL;
3529			}
3530			if (vp != NULL)
3531				free(vp);
3532			return NULL;
3533		}
3534		*statp = EX_OK;
3535	}
3536
3537	if (ret == 0)
3538		errno = ETIMEDOUT;
3539	else
3540		errno = ldapmap_geterrno(lmap->ldap_ld);
3541	if (errno != LDAP_SUCCESS)
3542	{
3543		/* Must be an error */
3544		if (ret != 0)
3545			errno += E_LDAPBASE;
3546		if (!bitset(MF_OPTIONAL, map->map_mflags))
3547		{
3548			if (bitset(MF_NODEFER, map->map_mflags))
3549				syserr("Error getting LDAP results in map %s",
3550				       map->map_mname);
3551			else
3552				syserr("421 4.0.0 Error getting LDAP results in map %s",
3553				       map->map_mname);
3554		}
3555		*statp = EX_TEMPFAIL;
3556		if (vp != NULL)
3557			free(vp);
3558		return NULL;
3559	}
3560
3561	/* Did we match anything? */
3562	if (vp == NULL)
3563		return NULL;
3564
3565	/*
3566	**  If MF_NOREWRITE, we are special map which doesn't
3567	**  actually return a map value.  Instead, we don't free
3568	**  ldap_res and let the calling function process the LDAP
3569	**  results.  The caller should ldap_msgfree(lmap->ldap_res).
3570	*/
3571
3572	if (bitset(MF_NOREWRITE, map->map_mflags))
3573	{
3574		/* vp != NULL due to test above */
3575		free(vp);
3576		return "";
3577	}
3578
3579	if (*statp == EX_OK)
3580	{
3581		/* vp != NULL due to test above */
3582		if (LogLevel > 9)
3583			sm_syslog(LOG_INFO, CurEnv->e_id,
3584				  "ldap %.100s => %s", name, vp);
3585		if (bitset(MF_MATCHONLY, map->map_mflags))
3586			result = map_rewrite(map, name, strlen(name), NULL);
3587		else
3588			result = map_rewrite(map, vp, strlen(vp), av);
3589		free(vp);
3590	}
3591	return result;
3592}
3593
3594/*
3595**  LDAPMAP_FINDCONN -- find an LDAP connection to the server
3596**
3597**	Cache LDAP connections based on the host, port, bind DN,
3598**	and secret so we don't have multiple connections open to
3599**	the same server for different maps.
3600**
3601**	Parameters:
3602**		lmap -- LDAP map information
3603**
3604**	Returns:
3605**		Symbol table entry for the LDAP connection.
3606**
3607*/
3608
3609static STAB *
3610ldapmap_findconn(lmap)
3611	LDAPMAP_STRUCT *lmap;
3612{
3613	int len;
3614	char *nbuf;
3615	STAB *s;
3616
3617	len = (lmap->ldap_host == NULL ? strlen("localhost") :
3618					 strlen(lmap->ldap_host)) + 1 + 8 + 1 +
3619		(lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) +
3620		1 +
3621		(lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) +
3622		1;
3623	nbuf = xalloc(len);
3624	snprintf(nbuf, len, "%s%c%d%c%s%c%s",
3625		 (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host),
3626		 CONDELSE,
3627		 lmap->ldap_port,
3628		 CONDELSE,
3629		 (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn),
3630		 CONDELSE,
3631		 (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret));
3632	s = stab(nbuf, ST_LDAP, ST_ENTER);
3633	free(nbuf);
3634	return s;
3635}
3636/*
3637**  LDAPMAP_SETOPTS -- set LDAP options
3638**
3639**	Parameters:
3640**		ld -- LDAP session handle
3641**		lmap -- LDAP map information
3642**
3643**	Returns:
3644**		None.
3645**
3646*/
3647
3648static void
3649ldapmap_setopts(ld, lmap)
3650	LDAP *ld;
3651	LDAPMAP_STRUCT *lmap;
3652{
3653# if USE_LDAP_SET_OPTION
3654	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
3655	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
3656		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
3657	else
3658		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
3659	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
3660	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
3661# else /* USE_LDAP_SET_OPTION */
3662	/* From here on in we can use ldap internal timelimits */
3663	ld->ld_deref = lmap->ldap_deref;
3664	ld->ld_options = lmap->ldap_options;
3665	ld->ld_sizelimit = lmap->ldap_sizelimit;
3666	ld->ld_timelimit = lmap->ldap_timelimit;
3667# endif /* USE_LDAP_SET_OPTION */
3668}
3669/*
3670**  LDAPMAP_GETERRNO -- get ldap errno value
3671**
3672**	Parameters:
3673**		ld -- LDAP session handle
3674**
3675**	Returns:
3676**		LDAP errno.
3677**
3678*/
3679
3680static int
3681ldapmap_geterrno(ld)
3682	LDAP *ld;
3683{
3684	int err = LDAP_SUCCESS;
3685
3686# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
3687	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
3688# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
3689#  ifdef LDAP_OPT_SIZELIMIT
3690	err = ldap_get_lderrno(ld, NULL, NULL);
3691#  else /* LDAP_OPT_SIZELIMIT */
3692	err = ld->ld_errno;
3693
3694	/*
3695	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
3696	**  OpenLDAP 1.X's hack (see above)
3697	*/
3698
3699	ld->ld_errno = LDAP_SUCCESS;
3700#  endif /* LDAP_OPT_SIZELIMIT */
3701# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
3702	return err;
3703}
3704
3705/*
3706**  LDAPX_MAP_PARSEARGS -- print warning about use of ldapx map.
3707*/
3708
3709bool
3710ldapx_map_parseargs(map, args)
3711	MAP *map;
3712	char *args;
3713{
3714	printf("Warning: The \"ldapx\" map class is deprecated and will be removed in a future\n");
3715	printf("         version.  Use the \"ldap\" map class instead for map \"%s\".\n",
3716	       map->map_mname);
3717	return ldapmap_parseargs(map, args);
3718}
3719
3720/*
3721**  LDAPMAP_PARSEARGS -- parse ldap map definition args.
3722*/
3723
3724struct lamvalues LDAPAuthMethods[] =
3725{
3726	{	"none",		LDAP_AUTH_NONE		},
3727	{	"simple",	LDAP_AUTH_SIMPLE	},
3728# ifdef LDAP_AUTH_KRBV4
3729	{	"krbv4",	LDAP_AUTH_KRBV4		},
3730# endif /* LDAP_AUTH_KRBV4 */
3731	{	NULL,		0			}
3732};
3733
3734struct ladvalues LDAPAliasDereference[] =
3735{
3736	{	"never",	LDAP_DEREF_NEVER	},
3737	{	"always",	LDAP_DEREF_ALWAYS	},
3738	{	"search",	LDAP_DEREF_SEARCHING	},
3739	{	"find",		LDAP_DEREF_FINDING	},
3740	{	NULL,		0			}
3741};
3742
3743struct lssvalues LDAPSearchScope[] =
3744{
3745	{	"base",		LDAP_SCOPE_BASE		},
3746	{	"one",		LDAP_SCOPE_ONELEVEL	},
3747	{	"sub",		LDAP_SCOPE_SUBTREE	},
3748	{	NULL,		0			}
3749};
3750
3751bool
3752ldapmap_parseargs(map, args)
3753	MAP *map;
3754	char *args;
3755{
3756	bool secretread = TRUE;
3757	int i;
3758	register char *p = args;
3759	LDAPMAP_STRUCT *lmap;
3760	struct lamvalues *lam;
3761	struct ladvalues *lad;
3762	struct lssvalues *lss;
3763	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3764
3765	/* Get ldap struct pointer from map */
3766	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3767
3768	/* Check if setting the initial LDAP defaults */
3769	if (lmap == NULL || lmap != LDAPDefaults)
3770	{
3771		/* We need to alloc an LDAPMAP_STRUCT struct */
3772		lmap = (LDAPMAP_STRUCT *) xalloc(sizeof *lmap);
3773		if (LDAPDefaults == NULL)
3774			ldapmap_clear(lmap);
3775		else
3776			STRUCTCOPY(*LDAPDefaults, *lmap);
3777	}
3778
3779	/* there is no check whether there is really an argument */
3780	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3781	map->map_spacesub = SpaceSub;	/* default value */
3782	for (;;)
3783	{
3784		while (isascii(*p) && isspace(*p))
3785			p++;
3786		if (*p != '-')
3787			break;
3788		switch (*++p)
3789		{
3790		  case 'N':
3791			map->map_mflags |= MF_INCLNULL;
3792			map->map_mflags &= ~MF_TRY0NULL;
3793			break;
3794
3795		  case 'O':
3796			map->map_mflags &= ~MF_TRY1NULL;
3797			break;
3798
3799		  case 'o':
3800			map->map_mflags |= MF_OPTIONAL;
3801			break;
3802
3803		  case 'f':
3804			map->map_mflags |= MF_NOFOLDCASE;
3805			break;
3806
3807		  case 'm':
3808			map->map_mflags |= MF_MATCHONLY;
3809			break;
3810
3811		  case 'A':
3812			map->map_mflags |= MF_APPEND;
3813			break;
3814
3815		  case 'q':
3816			map->map_mflags |= MF_KEEPQUOTES;
3817			break;
3818
3819		  case 'a':
3820			map->map_app = ++p;
3821			break;
3822
3823		  case 'T':
3824			map->map_tapp = ++p;
3825			break;
3826
3827		  case 't':
3828			map->map_mflags |= MF_NODEFER;
3829			break;
3830
3831		  case 'S':
3832			map->map_spacesub = *++p;
3833			break;
3834
3835		  case 'D':
3836			map->map_mflags |= MF_DEFER;
3837			break;
3838
3839		  case 'z':
3840			if (*++p != '\\')
3841				map->map_coldelim = *p;
3842			else
3843			{
3844				switch (*++p)
3845				{
3846				  case 'n':
3847					map->map_coldelim = '\n';
3848					break;
3849
3850				  case 't':
3851					map->map_coldelim = '\t';
3852					break;
3853
3854				  default:
3855					map->map_coldelim = '\\';
3856				}
3857			}
3858			break;
3859
3860			/* Start of ldapmap specific args */
3861		  case 'k':		/* search field */
3862			while (isascii(*++p) && isspace(*p))
3863				continue;
3864			lmap->ldap_filter = p;
3865			break;
3866
3867		  case 'v':		/* attr to return */
3868			while (isascii(*++p) && isspace(*p))
3869				continue;
3870			lmap->ldap_attr[0] = p;
3871			lmap->ldap_attr[1] = NULL;
3872			break;
3873
3874		  case '1':
3875			map->map_mflags |= MF_SINGLEMATCH;
3876			break;
3877
3878			/* args stolen from ldapsearch.c */
3879		  case 'R':		/* don't auto chase referrals */
3880# ifdef LDAP_REFERRALS
3881			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3882# else /* LDAP_REFERRALS */
3883			syserr("compile with -DLDAP_REFERRALS for referral support\n");
3884# endif /* LDAP_REFERRALS */
3885			break;
3886
3887		  case 'n':		/* retrieve attribute names only */
3888			lmap->ldap_attrsonly = LDAPMAP_TRUE;
3889			break;
3890
3891		  case 'r':		/* alias dereferencing */
3892			while (isascii(*++p) && isspace(*p))
3893				continue;
3894
3895			if (strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3896				p += 11;
3897
3898			for (lad = LDAPAliasDereference;
3899			     lad != NULL && lad->lad_name != NULL; lad++)
3900			{
3901				if (strncasecmp(p, lad->lad_name,
3902						strlen(lad->lad_name)) == 0)
3903					break;
3904			}
3905			if (lad->lad_name != NULL)
3906				lmap->ldap_deref = lad->lad_code;
3907			else
3908			{
3909				/* bad config line */
3910				if (!bitset(MCF_OPTFILE,
3911					    map->map_class->map_cflags))
3912				{
3913					char *ptr;
3914
3915					if ((ptr = strchr(p, ' ')) != NULL)
3916						*ptr = '\0';
3917					syserr("Deref must be [never|always|search|find] not %s in map %s",
3918						p, map->map_mname);
3919					if (ptr != NULL)
3920						*ptr = ' ';
3921					return FALSE;
3922				}
3923			}
3924			break;
3925
3926		  case 's':		/* search scope */
3927			while (isascii(*++p) && isspace(*p))
3928				continue;
3929
3930			if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3931				p += 11;
3932
3933			for (lss = LDAPSearchScope;
3934			     lss != NULL && lss->lss_name != NULL; lss++)
3935			{
3936				if (strncasecmp(p, lss->lss_name,
3937						strlen(lss->lss_name)) == 0)
3938					break;
3939			}
3940			if (lss->lss_name != NULL)
3941				lmap->ldap_scope = lss->lss_code;
3942			else
3943			{
3944				/* bad config line */
3945				if (!bitset(MCF_OPTFILE,
3946					    map->map_class->map_cflags))
3947				{
3948					char *ptr;
3949
3950					if ((ptr = strchr(p, ' ')) != NULL)
3951						*ptr = '\0';
3952					syserr("Scope must be [base|one|sub] not %s in map %s",
3953						p, map->map_mname);
3954					if (ptr != NULL)
3955						*ptr = ' ';
3956					return FALSE;
3957				}
3958			}
3959			break;
3960
3961		  case 'h':		/* ldap host */
3962			while (isascii(*++p) && isspace(*p))
3963				continue;
3964			lmap->ldap_host = p;
3965			break;
3966
3967		  case 'b':		/* search base */
3968			while (isascii(*++p) && isspace(*p))
3969				continue;
3970			lmap->ldap_base = p;
3971			break;
3972
3973		  case 'p':		/* ldap port */
3974			while (isascii(*++p) && isspace(*p))
3975				continue;
3976			lmap->ldap_port = atoi(p);
3977			break;
3978
3979		  case 'l':		/* time limit */
3980			while (isascii(*++p) && isspace(*p))
3981				continue;
3982			lmap->ldap_timelimit = atoi(p);
3983			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
3984			break;
3985
3986		  case 'Z':
3987			while (isascii(*++p) && isspace(*p))
3988				continue;
3989			lmap->ldap_sizelimit = atoi(p);
3990			break;
3991
3992		  case 'd':		/* Dn to bind to server as */
3993			while (isascii(*++p) && isspace(*p))
3994				continue;
3995			lmap->ldap_binddn = p;
3996			break;
3997
3998		  case 'M':		/* Method for binding */
3999			while (isascii(*++p) && isspace(*p))
4000				continue;
4001
4002			if (strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4003				p += 10;
4004
4005			for (lam = LDAPAuthMethods;
4006			     lam != NULL && lam->lam_name != NULL; lam++)
4007			{
4008				if (strncasecmp(p, lam->lam_name,
4009						strlen(lam->lam_name)) == 0)
4010					break;
4011			}
4012			if (lam->lam_name != NULL)
4013				lmap->ldap_method = lam->lam_code;
4014			else
4015			{
4016				/* bad config line */
4017				if (!bitset(MCF_OPTFILE,
4018					    map->map_class->map_cflags))
4019				{
4020					char *ptr;
4021
4022					if ((ptr = strchr(p, ' ')) != NULL)
4023						*ptr = '\0';
4024					syserr("Method for binding must be [none|simple|krbv4] not %s in map %s",
4025						p, map->map_mname);
4026					if (ptr != NULL)
4027						*ptr = ' ';
4028					return FALSE;
4029				}
4030			}
4031
4032			break;
4033
4034			/*
4035			**  This is a string that is dependent on the
4036			**  method used defined above.
4037			*/
4038
4039		  case 'P':		/* Secret password for binding */
4040			 while (isascii(*++p) && isspace(*p))
4041				continue;
4042			lmap->ldap_secret = p;
4043			secretread = FALSE;
4044			break;
4045
4046		  default:
4047			syserr("Illegal option %c map %s", *p, map->map_mname);
4048			break;
4049		}
4050
4051		/* need to account for quoted strings here */
4052		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4053		{
4054			if (*p == '"')
4055			{
4056				while (*++p != '"' && *p != '\0')
4057					continue;
4058				if (*p != '\0')
4059					p++;
4060			}
4061			else
4062				p++;
4063		}
4064
4065		if (*p != '\0')
4066			*p++ = '\0';
4067	}
4068
4069	if (map->map_app != NULL)
4070		map->map_app = newstr(ldapmap_dequote(map->map_app));
4071	if (map->map_tapp != NULL)
4072		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4073
4074	/*
4075	**  We need to swallow up all the stuff into a struct
4076	**  and dump it into map->map_dbptr1
4077	*/
4078
4079	if (lmap->ldap_host != NULL &&
4080	    (LDAPDefaults == NULL ||
4081	     LDAPDefaults == lmap ||
4082	     LDAPDefaults->ldap_host != lmap->ldap_host))
4083		lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4084	map->map_domain = lmap->ldap_host;
4085
4086	if (lmap->ldap_binddn != NULL &&
4087	    (LDAPDefaults == NULL ||
4088	     LDAPDefaults == lmap ||
4089	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4090		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4091
4092	if (lmap->ldap_secret != NULL &&
4093	    (LDAPDefaults == NULL ||
4094	     LDAPDefaults == lmap ||
4095	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4096	{
4097		FILE *sfd;
4098		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4099
4100		if (DontLockReadFiles)
4101			sff |= SFF_NOLOCK;
4102
4103		/* need to use method to map secret to passwd string */
4104		switch (lmap->ldap_method)
4105		{
4106		  case LDAP_AUTH_NONE:
4107			/* Do nothing */
4108			break;
4109
4110		  case LDAP_AUTH_SIMPLE:
4111
4112			/*
4113			**  Secret is the name of a file with
4114			**  the first line as the password.
4115			*/
4116
4117			/* Already read in the secret? */
4118			if (secretread)
4119				break;
4120
4121			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4122					O_RDONLY, 0, sff);
4123			if (sfd == NULL)
4124			{
4125				syserr("LDAP map: cannot open secret %s",
4126				       ldapmap_dequote(lmap->ldap_secret));
4127				return FALSE;
4128			}
4129			lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD,
4130						   sfd, 0, "ldapmap_parseargs");
4131			(void) fclose(sfd);
4132			if (lmap->ldap_secret != NULL &&
4133			    strlen(m_tmp) > 0)
4134			{
4135				/* chomp newline */
4136				if (m_tmp[strlen(m_tmp) - 1] == '\n')
4137					m_tmp[strlen(m_tmp) - 1] = '\0';
4138
4139				lmap->ldap_secret = m_tmp;
4140			}
4141			break;
4142
4143# ifdef LDAP_AUTH_KRBV4
4144		  case LDAP_AUTH_KRBV4:
4145
4146			/*
4147			**  Secret is where the ticket file is
4148			**  stashed
4149			*/
4150
4151			snprintf(m_tmp, MAXPATHLEN + LDAPMAP_MAX_PASSWD,
4152				 "KRBTKFILE=%s",
4153				 ldapmap_dequote(lmap->ldap_secret));
4154			lmap->ldap_secret = m_tmp;
4155			break;
4156# endif /* LDAP_AUTH_KRBV4 */
4157
4158		  default:	       /* Should NEVER get here */
4159			syserr("LDAP map: Illegal value in lmap method");
4160			return FALSE;
4161			break;
4162		}
4163	}
4164
4165	if (lmap->ldap_secret != NULL &&
4166	    (LDAPDefaults == NULL ||
4167	     LDAPDefaults == lmap ||
4168	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4169		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4170
4171	if (lmap->ldap_base != NULL &&
4172	    (LDAPDefaults == NULL ||
4173	     LDAPDefaults == lmap ||
4174	     LDAPDefaults->ldap_base != lmap->ldap_base))
4175		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4176
4177	/*
4178	**  Save the server from extra work.  If request is for a single
4179	**  match, tell the server to only return enough records to
4180	**  determine if there is a single match or not.  This can not
4181	**  be one since the server would only return one and we wouldn't
4182	**  know if there were others available.
4183	*/
4184
4185	if (bitset(MF_SINGLEMATCH, map->map_mflags))
4186		lmap->ldap_sizelimit = 2;
4187
4188	/* If setting defaults, don't process ldap_filter and ldap_attr */
4189	if (lmap == LDAPDefaults)
4190		return TRUE;
4191
4192	if (lmap->ldap_filter != NULL)
4193		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4194	else
4195	{
4196		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4197		{
4198			syserr("No filter given in map %s", map->map_mname);
4199			return FALSE;
4200		}
4201	}
4202
4203	if (lmap->ldap_attr[0] != NULL)
4204	{
4205		i = 0;
4206		p = ldapmap_dequote(lmap->ldap_attr[0]);
4207		lmap->ldap_attr[0] = NULL;
4208
4209		while (p != NULL)
4210		{
4211			char *v;
4212
4213			while (isascii(*p) && isspace(*p))
4214				p++;
4215			if (*p == '\0')
4216				break;
4217			v = p;
4218			p = strchr(v, ',');
4219			if (p != NULL)
4220				*p++ = '\0';
4221
4222			if (i == LDAPMAP_MAX_ATTR)
4223			{
4224				syserr("Too many return attributes in %s (max %d)",
4225				       map->map_mname, LDAPMAP_MAX_ATTR);
4226				return FALSE;
4227			}
4228			if (*v != '\0')
4229				lmap->ldap_attr[i++] = newstr(v);
4230		}
4231		lmap->ldap_attr[i] = NULL;
4232	}
4233
4234	map->map_db1 = (ARBPTR_T) lmap;
4235	return TRUE;
4236}
4237
4238/*
4239**  LDAPMAP_CLEAR -- set default values for LDAPMAP_STRUCT
4240**
4241**	Parameters:
4242**		lmap -- pointer to LDAPMAP_STRUCT to clear
4243**
4244**	Returns:
4245**		None.
4246**
4247*/
4248
4249static void
4250ldapmap_clear(lmap)
4251	LDAPMAP_STRUCT *lmap;
4252{
4253	lmap->ldap_host = NULL;
4254	lmap->ldap_port = LDAP_PORT;
4255	lmap->ldap_deref = LDAP_DEREF_NEVER;
4256	lmap->ldap_timelimit = LDAP_NO_LIMIT;
4257	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
4258# ifdef LDAP_REFERRALS
4259	lmap->ldap_options = LDAP_OPT_REFERRALS;
4260# else /* LDAP_REFERRALS */
4261	lmap->ldap_options = 0;
4262# endif /* LDAP_REFERRALS */
4263	lmap->ldap_binddn = NULL;
4264	lmap->ldap_secret = NULL;
4265	lmap->ldap_method = LDAP_AUTH_SIMPLE;
4266	lmap->ldap_base = NULL;
4267	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
4268	lmap->ldap_attrsonly = LDAPMAP_FALSE;
4269	lmap->ldap_timeout.tv_sec = 0;
4270	lmap->ldap_timeout.tv_usec = 0;
4271	lmap->ldap_ld = NULL;
4272	lmap->ldap_filter = NULL;
4273	lmap->ldap_attr[0] = NULL;
4274	lmap->ldap_res = NULL;
4275}
4276/*
4277**  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4278**
4279**	Parameters:
4280**		spec -- map argument string from LDAPDefaults option
4281**
4282**	Returns:
4283**		None.
4284**
4285*/
4286
4287void
4288ldapmap_set_defaults(spec)
4289	char *spec;
4290{
4291	MAP map;
4292
4293	/* Allocate and set the default values */
4294	if (LDAPDefaults == NULL)
4295		LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4296	ldapmap_clear(LDAPDefaults);
4297
4298	memset(&map, '\0', sizeof map);
4299	map.map_db1 = (ARBPTR_T) LDAPDefaults;
4300
4301	(void) ldapmap_parseargs(&map, spec);
4302
4303	/* These should never be set in LDAPDefaults */
4304	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4305	    map.map_spacesub != SpaceSub ||
4306	    map.map_app != NULL ||
4307	    map.map_tapp != NULL)
4308	{
4309		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4310		if (map.map_app != NULL)
4311		{
4312			free(map.map_app);
4313			map.map_app = NULL;
4314		}
4315		if (map.map_tapp != NULL)
4316		{
4317			free(map.map_tapp);
4318			map.map_tapp = NULL;
4319		}
4320	}
4321
4322	if (LDAPDefaults->ldap_filter != NULL)
4323	{
4324		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4325		/* don't free, it isn't malloc'ed in parseargs */
4326		LDAPDefaults->ldap_filter = NULL;
4327	}
4328
4329	if (LDAPDefaults->ldap_attr[0] != NULL)
4330	{
4331		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4332		/* don't free, they aren't malloc'ed in parseargs */
4333		LDAPDefaults->ldap_attr[0] = NULL;
4334	}
4335}
4336#endif /* LDAPMAP */
4337/*
4338**  PH map
4339*/
4340
4341#ifdef PH_MAP
4342
4343/*
4344**  Support for the CCSO Nameserver (ph/qi).
4345**  This code is intended to replace the so-called "ph mailer".
4346**  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
4347*/
4348
4349# include <qiapi.h>
4350# include <qicode.h>
4351
4352/*
4353**  PH_MAP_PARSEARGS -- parse ph map definition args.
4354*/
4355
4356bool
4357ph_map_parseargs(map, args)
4358	MAP *map;
4359	char *args;
4360{
4361	int i;
4362	register int done;
4363	PH_MAP_STRUCT *pmap = NULL;
4364	register char *p = args;
4365
4366	pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4367
4368	/* defaults */
4369	pmap->ph_servers = NULL;
4370	pmap->ph_field_list = NULL;
4371	pmap->ph_to_server = NULL;
4372	pmap->ph_from_server = NULL;
4373	pmap->ph_sockfd = -1;
4374	pmap->ph_timeout = 0;
4375
4376	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4377	for (;;)
4378	{
4379		while (isascii(*p) && isspace(*p))
4380			p++;
4381		if (*p != '-')
4382			break;
4383		switch (*++p)
4384		{
4385		  case 'N':
4386			map->map_mflags |= MF_INCLNULL;
4387			map->map_mflags &= ~MF_TRY0NULL;
4388			break;
4389
4390		  case 'O':
4391			map->map_mflags &= ~MF_TRY1NULL;
4392			break;
4393
4394		  case 'o':
4395			map->map_mflags |= MF_OPTIONAL;
4396			break;
4397
4398		  case 'f':
4399			map->map_mflags |= MF_NOFOLDCASE;
4400			break;
4401
4402		  case 'm':
4403			map->map_mflags |= MF_MATCHONLY;
4404			break;
4405
4406		  case 'A':
4407			map->map_mflags |= MF_APPEND;
4408			break;
4409
4410		  case 'q':
4411			map->map_mflags |= MF_KEEPQUOTES;
4412			break;
4413
4414		  case 't':
4415			map->map_mflags |= MF_NODEFER;
4416			break;
4417
4418		  case 'a':
4419			map->map_app = ++p;
4420			break;
4421
4422		  case 'T':
4423			map->map_tapp = ++p;
4424			break;
4425
4426#if _FFR_PHMAP_TIMEOUT
4427		  case 'l':
4428			while (isascii(*++p) && isspace(*p))
4429				continue;
4430			pmap->ph_timeout = atoi(p);
4431			break;
4432#endif /* _FFR_PHMAP_TIMEOUT */
4433
4434		  case 'S':
4435			map->map_spacesub = *++p;
4436			break;
4437
4438		  case 'D':
4439			map->map_mflags |= MF_DEFER;
4440			break;
4441
4442		  case 'h':		/* PH server list */
4443			while (isascii(*++p) && isspace(*p))
4444				continue;
4445			pmap->ph_servers = p;
4446			break;
4447
4448		  case 'v':		/* fields to search for */
4449			while (isascii(*++p) && isspace(*p))
4450				continue;
4451			pmap->ph_field_list = p;
4452			break;
4453
4454		  default:
4455			syserr("ph_map_parseargs: unknown option -%c\n", *p);
4456		}
4457
4458		/* try to account for quoted strings */
4459		done = isascii(*p) && isspace(*p);
4460		while (*p != '\0' && !done)
4461		{
4462			if (*p == '"')
4463			{
4464				while (*++p != '"' && *p != '\0')
4465					continue;
4466				if (*p != '\0')
4467					p++;
4468			}
4469			else
4470				p++;
4471			done = isascii(*p) && isspace(*p);
4472		}
4473
4474		if (*p != '\0')
4475			*p++ = '\0';
4476	}
4477
4478	if (map->map_app != NULL)
4479		map->map_app = newstr(ph_map_dequote(map->map_app));
4480	if (map->map_tapp != NULL)
4481		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4482
4483	if (pmap->ph_field_list != NULL)
4484		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4485	else
4486		pmap->ph_field_list = DEFAULT_PH_MAP_FIELDS;
4487
4488	if (pmap->ph_servers != NULL)
4489		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4490	else
4491	{
4492		syserr("ph_map_parseargs: -h flag is required");
4493		return FALSE;
4494	}
4495
4496	map->map_db1 = (ARBPTR_T) pmap;
4497	return TRUE;
4498}
4499
4500#if _FFR_PHMAP_TIMEOUT
4501/*
4502**  PH_MAP_CLOSE -- close the connection to the ph server
4503*/
4504
4505static void
4506ph_map_safeclose(map)
4507	MAP *map;
4508{
4509	int save_errno = errno;
4510	PH_MAP_STRUCT *pmap;
4511
4512	pmap = (PH_MAP_STRUCT *)map->map_db1;
4513
4514	if (pmap->ph_sockfd != -1)
4515	{
4516		(void) close(pmap->ph_sockfd);
4517		pmap->ph_sockfd = -1;
4518	}
4519	if (pmap->ph_from_server != NULL)
4520	{
4521		(void) fclose(pmap->ph_from_server);
4522		pmap->ph_from_server = NULL;
4523	}
4524	if (pmap->ph_to_server != NULL)
4525	{
4526		(void) fclose(pmap->ph_to_server);
4527		pmap->ph_to_server = NULL;
4528	}
4529	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4530	errno = save_errno;
4531}
4532
4533void
4534ph_map_close(map)
4535	MAP *map;
4536{
4537	PH_MAP_STRUCT *pmap;
4538
4539	pmap = (PH_MAP_STRUCT *)map->map_db1;
4540	(void) fprintf(pmap->ph_to_server, "quit\n");
4541	(void) fflush(pmap->ph_to_server);
4542	ph_map_safeclose(map);
4543}
4544
4545static jmp_buf  PHTimeout;
4546
4547/* ARGSUSED */
4548static void
4549ph_timeout_func(sig_no)
4550	int sig_no;
4551{
4552	longjmp(PHTimeout, 1);
4553}
4554#else /* _FFR_PHMAP_TIMEOUT */
4555/*
4556**  PH_MAP_CLOSE -- close the connection to the ph server
4557*/
4558
4559void
4560ph_map_close(map)
4561	MAP *map;
4562{
4563	PH_MAP_STRUCT *pmap;
4564
4565	pmap = (PH_MAP_STRUCT *)map->map_db1;
4566	CloseQi(pmap->ph_to_server, pmap->ph_from_server);
4567	pmap->ph_to_server = NULL;
4568	pmap->ph_from_server = NULL;
4569}
4570#endif /* _FFR_PHMAP_TIMEOUT */
4571
4572/*
4573**  PH_MAP_OPEN -- sub for opening PH map
4574*/
4575bool
4576ph_map_open(map, mode)
4577	MAP *map;
4578	int mode;
4579{
4580#if !_FFR_PHMAP_TIMEOUT
4581	int save_errno = 0;
4582#endif /* !_FFR_PHMAP_TIMEOUT */
4583	int j;
4584	char *hostlist, *tmp;
4585	QIR *server_data = NULL;
4586	PH_MAP_STRUCT *pmap;
4587#if _FFR_PHMAP_TIMEOUT
4588	register EVENT *ev = NULL;
4589#endif /* _FFR_PHMAP_TIMEOUT */
4590
4591	if (tTd(38, 2))
4592		dprintf("ph_map_open(%s)\n", map->map_mname);
4593
4594	mode &= O_ACCMODE;
4595	if (mode != O_RDONLY)
4596	{
4597		/* issue a pseudo-error message */
4598# ifdef ENOSYS
4599		errno = ENOSYS;
4600# else /* ENOSYS */
4601#  ifdef EFTYPE
4602		errno = EFTYPE;
4603#  else /* EFTYPE */
4604		errno = ENXIO;
4605#  endif /* EFTYPE */
4606# endif /* ENOSYS */
4607		return FALSE;
4608	}
4609
4610	pmap = (PH_MAP_STRUCT *)map->map_db1;
4611
4612	hostlist = newstr(pmap->ph_servers);
4613	tmp = strtok(hostlist, " ");
4614	do
4615	{
4616#if _FFR_PHMAP_TIMEOUT
4617		if (pmap->ph_timeout != 0)
4618		{
4619			if (setjmp(PHTimeout) != 0)
4620			{
4621				ev = NULL;
4622				if (LogLevel > 1)
4623					sm_syslog(LOG_NOTICE, CurEnv->e_id,
4624						  "timeout connecting to PH server %.100s",
4625						  tmp);
4626# ifdef ETIMEDOUT
4627				errno = ETIMEDOUT;
4628# else /* ETIMEDOUT */
4629				errno = 0;
4630# endif /* ETIMEDOUT */
4631				goto ph_map_open_abort;
4632			}
4633			ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
4634		}
4635		if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) &&
4636		    !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server),
4637				&(pmap->ph_from_server)) &&
4638		    fprintf(pmap->ph_to_server, "id sendmail+phmap\n") >= 0 &&
4639		    fflush(pmap->ph_to_server) == 0 &&
4640		    (server_data = ReadQi(pmap->ph_from_server, &j)) != NULL &&
4641		    server_data->code == 200)
4642		{
4643			if (ev != NULL)
4644				clrevent(ev);
4645			FreeQIR(server_data);
4646#else /* _FFR_PHMAP_TIMEOUT */
4647		if (OpenQi(tmp, &(pmap->ph_to_server),
4648			   &(pmap->ph_from_server)) >= 0)
4649		{
4650			if (fprintf(pmap->ph_to_server,
4651				    "id sendmail+phmap\n") < 0 ||
4652			    fflush(pmap->ph_to_server) < 0 ||
4653			    (server_data = ReadQi(pmap->ph_from_server,
4654						  &j)) == NULL ||
4655			    server_data->code != 200)
4656			{
4657				save_errno = errno;
4658				CloseQi(pmap->ph_to_server,
4659					pmap->ph_from_server);
4660				continue;
4661			}
4662			if (server_data != NULL)
4663				FreeQIR(server_data);
4664#endif /* _FFR_PHMAP_TIMEOUT */
4665			free(hostlist);
4666			return TRUE;
4667		}
4668#if _FFR_PHMAP_TIMEOUT
4669  ph_map_open_abort:
4670		if (ev != NULL)
4671			clrevent(ev);
4672		ph_map_safeclose(map);
4673		if (server_data != NULL)
4674		{
4675			FreeQIR(server_data);
4676			server_data = NULL;
4677		}
4678#else /* _FFR_PHMAP_TIMEOUT */
4679		save_errno = errno;
4680#endif /* _FFR_PHMAP_TIMEOUT */
4681	} while (tmp = strtok(NULL, " "));
4682
4683#if !_FFR_PHMAP_TIMEOUT
4684	errno = save_errno;
4685#endif /* !_FFR_PHMAP_TIMEOUT */
4686	if (!bitset(MF_OPTIONAL, map->map_mflags))
4687	{
4688		if (errno == 0 && !bitset(MF_NODEFER,map->map_mflags))
4689			errno = EAGAIN;
4690		syserr("ph_map_open: cannot connect to PH server");
4691	}
4692	else if (LogLevel > 1)
4693		sm_syslog(LOG_NOTICE, CurEnv->e_id,
4694			  "ph_map_open: cannot connect to PH server");
4695	free(hostlist);
4696	return FALSE;
4697}
4698
4699/*
4700**  PH_MAP_LOOKUP -- look up key from ph server
4701*/
4702
4703#if _FFR_PHMAP_TIMEOUT
4704# define MAX_PH_FIELDS	20
4705#endif /* _FFR_PHMAP_TIMEOUT */
4706
4707char *
4708ph_map_lookup(map, key, args, pstat)
4709	MAP *map;
4710	char *key;
4711	char **args;
4712	int *pstat;
4713{
4714	int j;
4715	size_t sz;
4716	char *tmp, *tmp2;
4717	char *message = NULL, *field = NULL, *fmtkey;
4718	QIR *server_data = NULL;
4719	QIR *qirp;
4720	char keybuf[MAXKEY + 1], fieldbuf[101];
4721#if _FFR_PHMAP_TIMEOUT
4722	QIR *hold_data[MAX_PH_FIELDS];
4723	int hold_data_idx = 0;
4724	register EVENT *ev = NULL;
4725#endif /* _FFR_PHMAP_TIMEOUT */
4726	PH_MAP_STRUCT *pmap;
4727
4728	pmap = (PH_MAP_STRUCT *)map->map_db1;
4729
4730	*pstat = EX_OK;
4731
4732#if _FFR_PHMAP_TIMEOUT
4733	if (pmap->ph_timeout != 0)
4734	{
4735		if (setjmp(PHTimeout) != 0)
4736		{
4737			ev = NULL;
4738			if (LogLevel > 1)
4739				sm_syslog(LOG_NOTICE, CurEnv->e_id,
4740					  "timeout during PH lookup of %.100s",
4741					  key);
4742# ifdef ETIMEDOUT
4743			errno = ETIMEDOUT;
4744# else /* ETIMEDOUT */
4745			errno = 0;
4746# endif /* ETIMEDOUT */
4747			*pstat = EX_TEMPFAIL;
4748			goto ph_map_lookup_abort;
4749		}
4750		ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
4751	}
4752
4753#endif /* _FFR_PHMAP_TIMEOUT */
4754	/* check all relevant fields */
4755	tmp = pmap->ph_field_list;
4756	do
4757	{
4758#if _FFR_PHMAP_TIMEOUT
4759		server_data = NULL;
4760#endif /* _FFR_PHMAP_TIMEOUT */
4761		while (isascii(*tmp) && isspace(*tmp))
4762			tmp++;
4763		if (*tmp == '\0')
4764			break;
4765		sz = strcspn(tmp, " ") + 1;
4766		if (sz > sizeof fieldbuf)
4767			sz = sizeof fieldbuf;
4768		(void) strlcpy(fieldbuf, tmp, sz);
4769		field = fieldbuf;
4770		tmp += sz;
4771
4772		(void) strlcpy(keybuf, key, sizeof keybuf);
4773		fmtkey = keybuf;
4774		if (strcmp(field, "alias") == 0)
4775		{
4776			/*
4777			**  for alias lookups, replace any punctuation
4778			**  characters with '-'
4779			*/
4780
4781			for (tmp2 = fmtkey; *tmp2 !=  '\0'; tmp2++)
4782			{
4783				if (isascii(*tmp2) && ispunct(*tmp2))
4784					*tmp2 = '-';
4785			}
4786			tmp2 = field;
4787		}
4788		else if (strcmp(field,"spacedname") == 0)
4789		{
4790			/*
4791			**  for "spaced" name lookups, replace any
4792			**  punctuation characters with a space
4793			*/
4794
4795			for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++)
4796			{
4797				if (isascii(*tmp2) && ispunct(*tmp2) &&
4798				    *tmp2 != '*')
4799					*tmp2 = ' ';
4800			}
4801			tmp2 = &(field[6]);
4802		}
4803		else
4804			tmp2 = field;
4805
4806		if (LogLevel > 9)
4807			sm_syslog(LOG_NOTICE, CurEnv->e_id,
4808				  "ph_map_lookup: query %s=\"%s\" return email",
4809				  tmp2, fmtkey);
4810		if (tTd(38, 20))
4811			dprintf("ph_map_lookup: query %s=\"%s\" return email\n",
4812				tmp2, fmtkey);
4813
4814		j = 0;
4815
4816		if (fprintf(pmap->ph_to_server, "query %s=%s return email\n",
4817			    tmp2, fmtkey) < 0)
4818			message = "qi query command failed";
4819		else if (fflush(pmap->ph_to_server) < 0)
4820			message = "qi fflush failed";
4821		else if ((server_data = ReadQi(pmap->ph_from_server,
4822					       &j)) == NULL)
4823			message = "ReadQi() returned NULL";
4824
4825#if _FFR_PHMAP_TIMEOUT
4826		if ((hold_data[hold_data_idx] = server_data) != NULL)
4827		{
4828			/* save pointer for later free() */
4829			hold_data_idx++;
4830		}
4831#endif /* _FFR_PHMAP_TIMEOUT */
4832
4833		if (server_data == NULL ||
4834		    (server_data->code >= 400 &&
4835		     server_data->code < 500))
4836		{
4837			/* temporary failure */
4838			*pstat = EX_TEMPFAIL;
4839#if _FFR_PHMAP_TIMEOUT
4840			break;
4841#else /* _FFR_PHMAP_TIMEOUT */
4842			if (server_data != NULL)
4843			{
4844				FreeQIR(server_data);
4845				server_data = NULL;
4846			}
4847			return NULL;
4848#endif /* _FFR_PHMAP_TIMEOUT */
4849		}
4850
4851		/*
4852		**  if we found a single match, break out.
4853		**  otherwise, try the next field.
4854		*/
4855
4856		if (j == 1)
4857			break;
4858
4859		/*
4860		**  check for a single response which is an error:
4861		**  ReadQi() doesn't set j on error responses,
4862		**  but we should stop here instead of moving on if
4863		**  it happens (e.g., alias found but email field empty)
4864		*/
4865
4866		for (qirp = server_data;
4867		     qirp != NULL && qirp->code < 0;
4868		     qirp++)
4869		{
4870			if (tTd(38, 20))
4871				dprintf("ph_map_lookup: QIR: %d:%d:%d:%s\n",
4872					qirp->code, qirp->subcode, qirp->field,
4873					(qirp->message ? qirp->message
4874					 : "[NULL]"));
4875			if (qirp->code <= -500)
4876			{
4877				j = 0;
4878				goto ph_map_lookup_abort;
4879			}
4880		}
4881
4882#if _FFR_PHMAP_TIMEOUT
4883	} while (*tmp != '\0' && hold_data_idx < MAX_PH_FIELDS);
4884#else /* _FFR_PHMAP_TIMEOUT */
4885	} while (*tmp != '\0');
4886#endif /* _FFR_PHMAP_TIMEOUT */
4887
4888  ph_map_lookup_abort:
4889#if _FFR_PHMAP_TIMEOUT
4890	if (ev != NULL)
4891		clrevent(ev);
4892
4893	/*
4894	**  Return EX_TEMPFAIL if the timer popped
4895	**  or we got a temporary PH error
4896	*/
4897
4898	if (*pstat == EX_TEMPFAIL)
4899		ph_map_safeclose(map);
4900
4901	/* if we didn't find a single match, bail out */
4902	if (*pstat == EX_OK && j != 1)
4903		*pstat = EX_UNAVAILABLE;
4904
4905	if (*pstat == EX_OK)
4906	{
4907		/*
4908		** skip leading whitespace and chop at first address
4909		*/
4910
4911		for (tmp = server_data->message;
4912		     isascii(*tmp) && isspace(*tmp);
4913		     tmp++)
4914			continue;
4915
4916		for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
4917		{
4918			if (isascii(*tmp2) && isspace(*tmp2))
4919			{
4920				*tmp2 = '\0';
4921				break;
4922			}
4923		}
4924
4925		if (tTd(38,20))
4926			dprintf("ph_map_lookup: %s => %s\n", key, tmp);
4927
4928		if (bitset(MF_MATCHONLY, map->map_mflags))
4929			message = map_rewrite(map, key, strlen(key), NULL);
4930		else
4931			message = map_rewrite(map, tmp, strlen(tmp), args);
4932	}
4933
4934	/*
4935	**  Deferred free() of returned server_data values
4936	**  the deferral is to avoid the risk of a free() being
4937	**  interrupted by the event timer.  By now the timeout event
4938	**  has been cleared and none of the data is still in use.
4939	*/
4940
4941	while (--hold_data_idx >= 0)
4942	{
4943		if (hold_data[hold_data_idx] != NULL)
4944			FreeQIR(hold_data[hold_data_idx]);
4945	}
4946
4947	if (*pstat == EX_OK)
4948		return message;
4949
4950	return NULL;
4951#else /* _FFR_PHMAP_TIMEOUT */
4952	/* if we didn't find a single match, bail out */
4953	if (j != 1)
4954	{
4955		*pstat = EX_UNAVAILABLE;
4956		if (server_data != NULL)
4957		{
4958			FreeQIR(server_data);
4959			server_data = NULL;
4960		}
4961		return NULL;
4962	}
4963
4964	/*
4965	** skip leading whitespace and chop at first address
4966	*/
4967
4968	for (tmp = server_data->message;
4969	     isascii(*tmp) && isspace(*tmp);
4970	     tmp++)
4971		continue;
4972
4973	for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
4974	{
4975		if (isascii(*tmp2) && isspace(*tmp2))
4976		{
4977			*tmp2 = '\0';
4978			break;
4979		}
4980	}
4981
4982	if (tTd(38,20))
4983		dprintf("ph_map_lookup: %s => %s\n", key, tmp);
4984
4985	if (bitset(MF_MATCHONLY, map->map_mflags))
4986		message = map_rewrite(map, key, strlen(key), NULL);
4987	else
4988		message = map_rewrite(map, tmp, strlen(tmp), args);
4989	if (server_data != NULL)
4990	{
4991		FreeQIR(server_data);
4992		server_data = NULL;
4993	}
4994	return message;
4995#endif /* _FFR_PHMAP_TIMEOUT */
4996}
4997#endif /* PH_MAP */
4998/*
4999**  syslog map
5000*/
5001
5002#define map_prio	map_lockfd	/* overload field */
5003
5004/*
5005**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5006*/
5007
5008bool
5009syslog_map_parseargs(map, args)
5010	MAP *map;
5011	char *args;
5012{
5013	char *p = args;
5014	char *priority = NULL;
5015
5016	/* there is no check whether there is really an argument */
5017	while (*p != '\0')
5018	{
5019		while (isascii(*p) && isspace(*p))
5020			p++;
5021		if (*p != '-')
5022			break;
5023		++p;
5024		if (*p == 'D')
5025		{
5026			map->map_mflags |= MF_DEFER;
5027			++p;
5028		}
5029		else if (*p == 'S')
5030		{
5031			map->map_spacesub = *++p;
5032			if (*p != '\0')
5033				p++;
5034		}
5035		else if (*p == 'L')
5036		{
5037			while (*++p != '\0' && isascii(*p) && isspace(*p))
5038				continue;
5039			if (*p == '\0')
5040				break;
5041			priority = p;
5042			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5043				p++;
5044			if (*p != '\0')
5045				*p++ = '\0';
5046		}
5047		else
5048		{
5049			syserr("Illegal option %c map syslog", *p);
5050			++p;
5051		}
5052	}
5053
5054	if (priority == NULL)
5055		map->map_prio = LOG_INFO;
5056	else
5057	{
5058		if (strncasecmp("LOG_", priority, 4) == 0)
5059			priority += 4;
5060
5061#ifdef LOG_EMERG
5062		if (strcasecmp("EMERG", priority) == 0)
5063			map->map_prio = LOG_EMERG;
5064		else
5065#endif /* LOG_EMERG */
5066#ifdef LOG_ALERT
5067		if (strcasecmp("ALERT", priority) == 0)
5068			map->map_prio = LOG_ALERT;
5069		else
5070#endif /* LOG_ALERT */
5071#ifdef LOG_CRIT
5072		if (strcasecmp("CRIT", priority) == 0)
5073			map->map_prio = LOG_CRIT;
5074		else
5075#endif /* LOG_CRIT */
5076#ifdef LOG_ERR
5077		if (strcasecmp("ERR", priority) == 0)
5078			map->map_prio = LOG_ERR;
5079		else
5080#endif /* LOG_ERR */
5081#ifdef LOG_WARNING
5082		if (strcasecmp("WARNING", priority) == 0)
5083			map->map_prio = LOG_WARNING;
5084		else
5085#endif /* LOG_WARNING */
5086#ifdef LOG_NOTICE
5087		if (strcasecmp("NOTICE", priority) == 0)
5088			map->map_prio = LOG_NOTICE;
5089		else
5090#endif /* LOG_NOTICE */
5091#ifdef LOG_INFO
5092		if (strcasecmp("INFO", priority) == 0)
5093			map->map_prio = LOG_INFO;
5094		else
5095#endif /* LOG_INFO */
5096#ifdef LOG_DEBUG
5097		if (strcasecmp("DEBUG", priority) == 0)
5098			map->map_prio = LOG_DEBUG;
5099		else
5100#endif /* LOG_DEBUG */
5101		{
5102			syserr("syslog_map_parseargs: Unknown priority %s\n",
5103			       priority);
5104			return FALSE;
5105		}
5106	}
5107	return TRUE;
5108}
5109
5110/*
5111**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
5112*/
5113
5114char *
5115syslog_map_lookup(map, string, args, statp)
5116	MAP *map;
5117	char *string;
5118	char **args;
5119	int *statp;
5120{
5121	char *ptr = map_rewrite(map, string, strlen(string), args);
5122
5123	if (ptr != NULL)
5124	{
5125		if (tTd(38, 20))
5126			dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5127				map->map_mname, map->map_prio, ptr);
5128
5129		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5130	}
5131
5132	*statp = EX_OK;
5133	return "";
5134}
5135
5136/*
5137**  HESIOD Modules
5138*/
5139
5140#ifdef HESIOD
5141
5142bool
5143hes_map_open(map, mode)
5144	MAP *map;
5145	int mode;
5146{
5147	if (tTd(38, 2))
5148		dprintf("hes_map_open(%s, %s, %d)\n",
5149			map->map_mname, map->map_file, mode);
5150
5151	if (mode != O_RDONLY)
5152	{
5153		/* issue a pseudo-error message */
5154# ifdef ENOSYS
5155		errno = ENOSYS;
5156# else /* ENOSYS */
5157#  ifdef EFTYPE
5158		errno = EFTYPE;
5159#  else /* EFTYPE */
5160		errno = ENXIO;
5161#  endif /* EFTYPE */
5162# endif /* ENOSYS */
5163		return FALSE;
5164	}
5165
5166# ifdef HESIOD_INIT
5167	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5168		return TRUE;
5169
5170	if (!bitset(MF_OPTIONAL, map->map_mflags))
5171		syserr("421 4.0.0 cannot initialize Hesiod map (%s)",
5172			errstring(errno));
5173	return FALSE;
5174# else /* HESIOD_INIT */
5175	if (hes_error() == HES_ER_UNINIT)
5176		hes_init();
5177	switch (hes_error())
5178	{
5179	  case HES_ER_OK:
5180	  case HES_ER_NOTFOUND:
5181		return TRUE;
5182	}
5183
5184	if (!bitset(MF_OPTIONAL, map->map_mflags))
5185		syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error());
5186
5187	return FALSE;
5188# endif /* HESIOD_INIT */
5189}
5190
5191char *
5192hes_map_lookup(map, name, av, statp)
5193	MAP *map;
5194	char *name;
5195	char **av;
5196	int *statp;
5197{
5198	char **hp;
5199
5200	if (tTd(38, 20))
5201		dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5202
5203	if (name[0] == '\\')
5204	{
5205		char *np;
5206		int nl;
5207		char nbuf[MAXNAME];
5208
5209		nl = strlen(name);
5210		if (nl < sizeof nbuf - 1)
5211			np = nbuf;
5212		else
5213			np = xalloc(strlen(name) + 2);
5214		np[0] = '\\';
5215		(void) strlcpy(&np[1], name, (sizeof nbuf) - 1);
5216# ifdef HESIOD_INIT
5217		hp = hesiod_resolve(HesiodContext, np, map->map_file);
5218# else /* HESIOD_INIT */
5219		hp = hes_resolve(np, map->map_file);
5220# endif /* HESIOD_INIT */
5221		if (np != nbuf)
5222			free(np);
5223	}
5224	else
5225	{
5226# ifdef HESIOD_INIT
5227		hp = hesiod_resolve(HesiodContext, name, map->map_file);
5228# else /* HESIOD_INIT */
5229		hp = hes_resolve(name, map->map_file);
5230# endif /* HESIOD_INIT */
5231	}
5232# ifdef HESIOD_INIT
5233	if (hp == NULL)
5234		return NULL;
5235	if (*hp == NULL)
5236	{
5237		hesiod_free_list(HesiodContext, hp);
5238		switch (errno)
5239		{
5240		  case ENOENT:
5241			  *statp = EX_NOTFOUND;
5242			  break;
5243		  case ECONNREFUSED:
5244		  case EMSGSIZE:
5245			  *statp = EX_TEMPFAIL;
5246			  break;
5247		  case ENOMEM:
5248		  default:
5249			  *statp = EX_UNAVAILABLE;
5250			  break;
5251		}
5252		return NULL;
5253	}
5254# else /* HESIOD_INIT */
5255	if (hp == NULL || hp[0] == NULL)
5256	{
5257		switch (hes_error())
5258		{
5259		  case HES_ER_OK:
5260			*statp = EX_OK;
5261			break;
5262
5263		  case HES_ER_NOTFOUND:
5264			*statp = EX_NOTFOUND;
5265			break;
5266
5267		  case HES_ER_CONFIG:
5268			*statp = EX_UNAVAILABLE;
5269			break;
5270
5271		  case HES_ER_NET:
5272			*statp = EX_TEMPFAIL;
5273			break;
5274		}
5275		return NULL;
5276	}
5277# endif /* HESIOD_INIT */
5278
5279	if (bitset(MF_MATCHONLY, map->map_mflags))
5280		return map_rewrite(map, name, strlen(name), NULL);
5281	else
5282		return map_rewrite(map, hp[0], strlen(hp[0]), av);
5283}
5284
5285#endif /* HESIOD */
5286/*
5287**  NeXT NETINFO Modules
5288*/
5289
5290#if NETINFO
5291
5292# define NETINFO_DEFAULT_DIR		"/aliases"
5293# define NETINFO_DEFAULT_PROPERTY	"members"
5294
5295/*
5296**  NI_MAP_OPEN -- open NetInfo Aliases
5297*/
5298
5299bool
5300ni_map_open(map, mode)
5301	MAP *map;
5302	int mode;
5303{
5304	if (tTd(38, 2))
5305		dprintf("ni_map_open(%s, %s, %d)\n",
5306			map->map_mname, map->map_file, mode);
5307	mode &= O_ACCMODE;
5308
5309	if (*map->map_file == '\0')
5310		map->map_file = NETINFO_DEFAULT_DIR;
5311
5312	if (map->map_valcolnm == NULL)
5313		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5314
5315	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
5316		map->map_coldelim = ',';
5317
5318	return TRUE;
5319}
5320
5321
5322/*
5323**  NI_MAP_LOOKUP -- look up a datum in NetInfo
5324*/
5325
5326char *
5327ni_map_lookup(map, name, av, statp)
5328	MAP *map;
5329	char *name;
5330	char **av;
5331	int *statp;
5332{
5333	char *res;
5334	char *propval;
5335
5336	if (tTd(38, 20))
5337		dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5338
5339	propval = ni_propval(map->map_file, map->map_keycolnm, name,
5340			     map->map_valcolnm, map->map_coldelim);
5341
5342	if (propval == NULL)
5343		return NULL;
5344
5345	if (bitset(MF_MATCHONLY, map->map_mflags))
5346		res = map_rewrite(map, name, strlen(name), NULL);
5347	else
5348		res = map_rewrite(map, propval, strlen(propval), av);
5349	free(propval);
5350	return res;
5351}
5352
5353
5354static bool
5355ni_getcanonname(name, hbsize, statp)
5356	char *name;
5357	int hbsize;
5358	int *statp;
5359{
5360	char *vptr;
5361	char *ptr;
5362	char nbuf[MAXNAME + 1];
5363
5364	if (tTd(38, 20))
5365		dprintf("ni_getcanonname(%s)\n", name);
5366
5367	if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5368	{
5369		*statp = EX_UNAVAILABLE;
5370		return FALSE;
5371	}
5372	shorten_hostname(nbuf);
5373
5374	/* we only accept single token search key */
5375	if (strchr(nbuf, '.'))
5376	{
5377		*statp = EX_NOHOST;
5378		return FALSE;
5379	}
5380
5381	/* Do the search */
5382	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5383
5384	if (vptr == NULL)
5385	{
5386		*statp = EX_NOHOST;
5387		return FALSE;
5388	}
5389
5390	/* Only want the first machine name */
5391	if ((ptr = strchr(vptr, '\n')) != NULL)
5392		*ptr = '\0';
5393
5394	if (hbsize >= strlen(vptr))
5395	{
5396		(void) strlcpy(name, vptr, hbsize);
5397		free(vptr);
5398		*statp = EX_OK;
5399		return TRUE;
5400	}
5401	*statp = EX_UNAVAILABLE;
5402	free(vptr);
5403	return FALSE;
5404}
5405
5406
5407/*
5408**  NI_PROPVAL -- NetInfo property value lookup routine
5409**
5410**	Parameters:
5411**		keydir -- the NetInfo directory name in which to search
5412**			for the key.
5413**		keyprop -- the name of the property in which to find the
5414**			property we are interested.  Defaults to "name".
5415**		keyval -- the value for which we are really searching.
5416**		valprop -- the property name for the value in which we
5417**			are interested.
5418**		sepchar -- if non-nil, this can be multiple-valued, and
5419**			we should return a string separated by this
5420**			character.
5421**
5422**	Returns:
5423**		NULL -- if:
5424**			1. the directory is not found
5425**			2. the property name is not found
5426**			3. the property contains multiple values
5427**			4. some error occurred
5428**		else -- the value of the lookup.
5429**
5430**	Example:
5431**		To search for an alias value, use:
5432**		  ni_propval("/aliases", "name", aliasname, "members", ',')
5433**
5434**	Notes:
5435**		Caller should free the return value of ni_proval
5436*/
5437
5438# include <netinfo/ni.h>
5439
5440# define LOCAL_NETINFO_DOMAIN	"."
5441# define PARENT_NETINFO_DOMAIN	".."
5442# define MAX_NI_LEVELS		256
5443
5444char *
5445ni_propval(keydir, keyprop, keyval, valprop, sepchar)
5446	char *keydir;
5447	char *keyprop;
5448	char *keyval;
5449	char *valprop;
5450	int sepchar;
5451{
5452	char *propval = NULL;
5453	int i;
5454	int j, alen, l;
5455	void *ni = NULL;
5456	void *lastni = NULL;
5457	ni_status nis;
5458	ni_id nid;
5459	ni_namelist ninl;
5460	register char *p;
5461	char keybuf[1024];
5462
5463	/*
5464	**  Create the full key from the two parts.
5465	**
5466	**	Note that directory can end with, e.g., "name=" to specify
5467	**	an alternate search property.
5468	*/
5469
5470	i = strlen(keydir) + strlen(keyval) + 2;
5471	if (keyprop != NULL)
5472		i += strlen(keyprop) + 1;
5473	if (i >= sizeof keybuf)
5474		return NULL;
5475	(void) strlcpy(keybuf, keydir, sizeof keybuf);
5476	(void) strlcat(keybuf, "/", sizeof keybuf);
5477	if (keyprop != NULL)
5478	{
5479		(void) strlcat(keybuf, keyprop, sizeof keybuf);
5480		(void) strlcat(keybuf, "=", sizeof keybuf);
5481	}
5482	(void) strlcat(keybuf, keyval, sizeof keybuf);
5483
5484	if (tTd(38, 21))
5485		dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
5486			keydir, keyprop, keyval, valprop, sepchar, keybuf);
5487	/*
5488	**  If the passed directory and property name are found
5489	**  in one of netinfo domains we need to search (starting
5490	**  from the local domain moving all the way back to the
5491	**  root domain) set propval to the property's value
5492	**  and return it.
5493	*/
5494
5495	for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
5496	{
5497		if (i == 0)
5498		{
5499			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
5500			if (tTd(38, 20))
5501				dprintf("ni_open(LOCAL) = %d\n", nis);
5502		}
5503		else
5504		{
5505			if (lastni != NULL)
5506				ni_free(lastni);
5507			lastni = ni;
5508			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
5509			if (tTd(38, 20))
5510				dprintf("ni_open(PARENT) = %d\n", nis);
5511		}
5512
5513		/*
5514		**  Don't bother if we didn't get a handle on a
5515		**  proper domain.  This is not necessarily an error.
5516		**  We would get a positive ni_status if, for instance
5517		**  we never found the directory or property and tried
5518		**  to open the parent of the root domain!
5519		*/
5520
5521		if (nis != 0)
5522			break;
5523
5524		/*
5525		**  Find the path to the server information.
5526		*/
5527
5528		if (ni_pathsearch(ni, &nid, keybuf) != 0)
5529			continue;
5530
5531		/*
5532		**  Find associated value information.
5533		*/
5534
5535		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
5536			continue;
5537
5538		if (tTd(38, 20))
5539			dprintf("ni_lookupprop: len=%d\n",
5540				ninl.ni_namelist_len);
5541
5542		/*
5543		**  See if we have an acceptable number of values.
5544		*/
5545
5546		if (ninl.ni_namelist_len <= 0)
5547			continue;
5548
5549		if (sepchar == '\0' && ninl.ni_namelist_len > 1)
5550		{
5551			ni_namelist_free(&ninl);
5552			continue;
5553		}
5554
5555		/*
5556		**  Calculate number of bytes needed and build result
5557		*/
5558
5559		alen = 1;
5560		for (j = 0; j < ninl.ni_namelist_len; j++)
5561			alen += strlen(ninl.ni_namelist_val[j]) + 1;
5562		propval = p = xalloc(alen);
5563		for (j = 0; j < ninl.ni_namelist_len; j++)
5564		{
5565			(void) strlcpy(p, ninl.ni_namelist_val[j], alen);
5566			l = strlen(p);
5567			p += l;
5568			*p++ = sepchar;
5569			alen -= l + 1;
5570		}
5571		*--p = '\0';
5572
5573		ni_namelist_free(&ninl);
5574	}
5575
5576	/*
5577	**  Clean up.
5578	*/
5579
5580	if (ni != NULL)
5581		ni_free(ni);
5582	if (lastni != NULL && ni != lastni)
5583		ni_free(lastni);
5584	if (tTd(38, 20))
5585		dprintf("ni_propval returns: '%s'\n", propval);
5586
5587	return propval;
5588}
5589
5590#endif /* NETINFO */
5591/*
5592**  TEXT (unindexed text file) Modules
5593**
5594**	This code donated by Sun Microsystems.
5595*/
5596
5597#define map_sff		map_lockfd	/* overload field */
5598
5599
5600/*
5601**  TEXT_MAP_OPEN -- open text table
5602*/
5603
5604bool
5605text_map_open(map, mode)
5606	MAP *map;
5607	int mode;
5608{
5609	long sff;
5610	int i;
5611
5612	if (tTd(38, 2))
5613		dprintf("text_map_open(%s, %s, %d)\n",
5614			map->map_mname, map->map_file, mode);
5615
5616	mode &= O_ACCMODE;
5617	if (mode != O_RDONLY)
5618	{
5619		errno = EPERM;
5620		return FALSE;
5621	}
5622
5623	if (*map->map_file == '\0')
5624	{
5625		syserr("text map \"%s\": file name required",
5626			map->map_mname);
5627		return FALSE;
5628	}
5629
5630	if (map->map_file[0] != '/')
5631	{
5632		syserr("text map \"%s\": file name must be fully qualified",
5633			map->map_mname);
5634		return FALSE;
5635	}
5636
5637	sff = SFF_ROOTOK|SFF_REGONLY;
5638	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5639		sff |= SFF_NOWLINK;
5640	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5641		sff |= SFF_SAFEDIRPATH;
5642	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5643			  sff, S_IRUSR, NULL)) != 0)
5644	{
5645		int save_errno = errno;
5646
5647		/* cannot open this map */
5648		if (tTd(38, 2))
5649			dprintf("\tunsafe map file: %d\n", i);
5650		errno = save_errno;
5651		if (!bitset(MF_OPTIONAL, map->map_mflags))
5652			syserr("text map \"%s\": unsafe map file %s",
5653				map->map_mname, map->map_file);
5654		return FALSE;
5655	}
5656
5657	if (map->map_keycolnm == NULL)
5658		map->map_keycolno = 0;
5659	else
5660	{
5661		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5662		{
5663			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5664				map->map_mname, map->map_file,
5665				map->map_keycolnm);
5666			return FALSE;
5667		}
5668		map->map_keycolno = atoi(map->map_keycolnm);
5669	}
5670
5671	if (map->map_valcolnm == NULL)
5672		map->map_valcolno = 0;
5673	else
5674	{
5675		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5676		{
5677			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5678					map->map_mname, map->map_file,
5679					map->map_valcolnm);
5680			return FALSE;
5681		}
5682		map->map_valcolno = atoi(map->map_valcolnm);
5683	}
5684
5685	if (tTd(38, 2))
5686	{
5687		dprintf("text_map_open(%s, %s): delimiter = ",
5688			map->map_mname, map->map_file);
5689		if (map->map_coldelim == '\0')
5690			dprintf("(white space)\n");
5691		else
5692			dprintf("%c\n", map->map_coldelim);
5693	}
5694
5695	map->map_sff = sff;
5696	return TRUE;
5697}
5698
5699
5700/*
5701**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5702*/
5703
5704char *
5705text_map_lookup(map, name, av, statp)
5706	MAP *map;
5707	char *name;
5708	char **av;
5709	int *statp;
5710{
5711	char *vp;
5712	auto int vsize;
5713	int buflen;
5714	FILE *f;
5715	char delim;
5716	int key_idx;
5717	bool found_it;
5718	long sff = map->map_sff;
5719	char search_key[MAXNAME + 1];
5720	char linebuf[MAXLINE];
5721	char buf[MAXNAME + 1];
5722
5723	found_it = FALSE;
5724	if (tTd(38, 20))
5725		dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
5726
5727	buflen = strlen(name);
5728	if (buflen > sizeof search_key - 1)
5729		buflen = sizeof search_key - 1;
5730	memmove(search_key, name, buflen);
5731	search_key[buflen] = '\0';
5732	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5733		makelower(search_key);
5734
5735	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5736	if (f == NULL)
5737	{
5738		map->map_mflags &= ~(MF_VALID|MF_OPEN);
5739		*statp = EX_UNAVAILABLE;
5740		return NULL;
5741	}
5742	key_idx = map->map_keycolno;
5743	delim = map->map_coldelim;
5744	while (fgets(linebuf, MAXLINE, f) != NULL)
5745	{
5746		char *p;
5747
5748		/* skip comment line */
5749		if (linebuf[0] == '#')
5750			continue;
5751		p = strchr(linebuf, '\n');
5752		if (p != NULL)
5753			*p = '\0';
5754		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5755		if (p != NULL && strcasecmp(search_key, p) == 0)
5756		{
5757			found_it = TRUE;
5758			break;
5759		}
5760	}
5761	(void) fclose(f);
5762	if (!found_it)
5763	{
5764		*statp = EX_NOTFOUND;
5765		return NULL;
5766	}
5767	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5768	if (vp == NULL)
5769	{
5770		*statp = EX_NOTFOUND;
5771		return NULL;
5772	}
5773	vsize = strlen(vp);
5774	*statp = EX_OK;
5775	if (bitset(MF_MATCHONLY, map->map_mflags))
5776		return map_rewrite(map, name, strlen(name), NULL);
5777	else
5778		return map_rewrite(map, vp, vsize, av);
5779}
5780
5781/*
5782**  TEXT_GETCANONNAME -- look up canonical name in hosts file
5783*/
5784
5785static bool
5786text_getcanonname(name, hbsize, statp)
5787	char *name;
5788	int hbsize;
5789	int *statp;
5790{
5791	bool found;
5792	FILE *f;
5793	char linebuf[MAXLINE];
5794	char cbuf[MAXNAME + 1];
5795	char nbuf[MAXNAME + 1];
5796
5797	if (tTd(38, 20))
5798		dprintf("text_getcanonname(%s)\n", name);
5799
5800	if (strlen(name) >= (SIZE_T) sizeof nbuf)
5801	{
5802		*statp = EX_UNAVAILABLE;
5803		return FALSE;
5804	}
5805	(void) strlcpy(nbuf, name, sizeof nbuf);
5806	shorten_hostname(nbuf);
5807
5808	f = fopen(HostsFile, "r");
5809	if (f == NULL)
5810	{
5811		*statp = EX_UNAVAILABLE;
5812		return FALSE;
5813	}
5814	found = FALSE;
5815	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
5816	{
5817		char *p = strpbrk(linebuf, "#\n");
5818
5819		if (p != NULL)
5820			*p = '\0';
5821		if (linebuf[0] != '\0')
5822			found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
5823	}
5824	(void) fclose(f);
5825	if (!found)
5826	{
5827		*statp = EX_NOHOST;
5828		return FALSE;
5829	}
5830
5831	if ((SIZE_T) hbsize >= strlen(cbuf))
5832	{
5833		(void) strlcpy(name, cbuf, hbsize);
5834		*statp = EX_OK;
5835		return TRUE;
5836	}
5837	*statp = EX_UNAVAILABLE;
5838	return FALSE;
5839}
5840/*
5841**  STAB (Symbol Table) Modules
5842*/
5843
5844
5845/*
5846**  STAB_MAP_LOOKUP -- look up alias in symbol table
5847*/
5848
5849/* ARGSUSED2 */
5850char *
5851stab_map_lookup(map, name, av, pstat)
5852	register MAP *map;
5853	char *name;
5854	char **av;
5855	int *pstat;
5856{
5857	register STAB *s;
5858
5859	if (tTd(38, 20))
5860		dprintf("stab_lookup(%s, %s)\n",
5861			map->map_mname, name);
5862
5863	s = stab(name, ST_ALIAS, ST_FIND);
5864	if (s != NULL)
5865		return s->s_alias;
5866	return NULL;
5867}
5868
5869
5870/*
5871**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5872*/
5873
5874void
5875stab_map_store(map, lhs, rhs)
5876	register MAP *map;
5877	char *lhs;
5878	char *rhs;
5879{
5880	register STAB *s;
5881
5882	s = stab(lhs, ST_ALIAS, ST_ENTER);
5883	s->s_alias = newstr(rhs);
5884}
5885
5886
5887/*
5888**  STAB_MAP_OPEN -- initialize (reads data file)
5889**
5890**	This is a wierd case -- it is only intended as a fallback for
5891**	aliases.  For this reason, opens for write (only during a
5892**	"newaliases") always fails, and opens for read open the
5893**	actual underlying text file instead of the database.
5894*/
5895
5896bool
5897stab_map_open(map, mode)
5898	register MAP *map;
5899	int mode;
5900{
5901	FILE *af;
5902	long sff;
5903	struct stat st;
5904
5905	if (tTd(38, 2))
5906		dprintf("stab_map_open(%s, %s, %d)\n",
5907			map->map_mname, map->map_file, mode);
5908
5909	mode &= O_ACCMODE;
5910	if (mode != O_RDONLY)
5911	{
5912		errno = EPERM;
5913		return FALSE;
5914	}
5915
5916	sff = SFF_ROOTOK|SFF_REGONLY;
5917	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5918		sff |= SFF_NOWLINK;
5919	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5920		sff |= SFF_SAFEDIRPATH;
5921	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5922	if (af == NULL)
5923		return FALSE;
5924	readaliases(map, af, FALSE, FALSE);
5925
5926	if (fstat(fileno(af), &st) >= 0)
5927		map->map_mtime = st.st_mtime;
5928	(void) fclose(af);
5929
5930	return TRUE;
5931}
5932/*
5933**  Implicit Modules
5934**
5935**	Tries several types.  For back compatibility of aliases.
5936*/
5937
5938
5939/*
5940**  IMPL_MAP_LOOKUP -- lookup in best open database
5941*/
5942
5943char *
5944impl_map_lookup(map, name, av, pstat)
5945	MAP *map;
5946	char *name;
5947	char **av;
5948	int *pstat;
5949{
5950	if (tTd(38, 20))
5951		dprintf("impl_map_lookup(%s, %s)\n",
5952			map->map_mname, name);
5953
5954#ifdef NEWDB
5955	if (bitset(MF_IMPL_HASH, map->map_mflags))
5956		return db_map_lookup(map, name, av, pstat);
5957#endif /* NEWDB */
5958#ifdef NDBM
5959	if (bitset(MF_IMPL_NDBM, map->map_mflags))
5960		return ndbm_map_lookup(map, name, av, pstat);
5961#endif /* NDBM */
5962	return stab_map_lookup(map, name, av, pstat);
5963}
5964
5965/*
5966**  IMPL_MAP_STORE -- store in open databases
5967*/
5968
5969void
5970impl_map_store(map, lhs, rhs)
5971	MAP *map;
5972	char *lhs;
5973	char *rhs;
5974{
5975	if (tTd(38, 12))
5976		dprintf("impl_map_store(%s, %s, %s)\n",
5977			map->map_mname, lhs, rhs);
5978#ifdef NEWDB
5979	if (bitset(MF_IMPL_HASH, map->map_mflags))
5980		db_map_store(map, lhs, rhs);
5981#endif /* NEWDB */
5982#ifdef NDBM
5983	if (bitset(MF_IMPL_NDBM, map->map_mflags))
5984		ndbm_map_store(map, lhs, rhs);
5985#endif /* NDBM */
5986	stab_map_store(map, lhs, rhs);
5987}
5988
5989/*
5990**  IMPL_MAP_OPEN -- implicit database open
5991*/
5992
5993bool
5994impl_map_open(map, mode)
5995	MAP *map;
5996	int mode;
5997{
5998	if (tTd(38, 2))
5999		dprintf("impl_map_open(%s, %s, %d)\n",
6000			map->map_mname, map->map_file, mode);
6001
6002	mode &= O_ACCMODE;
6003#ifdef NEWDB
6004	map->map_mflags |= MF_IMPL_HASH;
6005	if (hash_map_open(map, mode))
6006	{
6007# ifdef NDBM_YP_COMPAT
6008		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6009# endif /* NDBM_YP_COMPAT */
6010			return TRUE;
6011	}
6012	else
6013		map->map_mflags &= ~MF_IMPL_HASH;
6014#endif /* NEWDB */
6015#ifdef NDBM
6016	map->map_mflags |= MF_IMPL_NDBM;
6017	if (ndbm_map_open(map, mode))
6018	{
6019		return TRUE;
6020	}
6021	else
6022		map->map_mflags &= ~MF_IMPL_NDBM;
6023#endif /* NDBM */
6024
6025#if defined(NEWDB) || defined(NDBM)
6026	if (Verbose)
6027		message("WARNING: cannot open alias database %s%s",
6028			map->map_file,
6029			mode == O_RDONLY ? "; reading text version" : "");
6030#else /* defined(NEWDB) || defined(NDBM) */
6031	if (mode != O_RDONLY)
6032		usrerr("Cannot rebuild aliases: no database format defined");
6033#endif /* defined(NEWDB) || defined(NDBM) */
6034
6035	if (mode == O_RDONLY)
6036		return stab_map_open(map, mode);
6037	else
6038		return FALSE;
6039}
6040
6041
6042/*
6043**  IMPL_MAP_CLOSE -- close any open database(s)
6044*/
6045
6046void
6047impl_map_close(map)
6048	MAP *map;
6049{
6050	if (tTd(38, 9))
6051		dprintf("impl_map_close(%s, %s, %lx)\n",
6052			map->map_mname, map->map_file, map->map_mflags);
6053#ifdef NEWDB
6054	if (bitset(MF_IMPL_HASH, map->map_mflags))
6055	{
6056		db_map_close(map);
6057		map->map_mflags &= ~MF_IMPL_HASH;
6058	}
6059#endif /* NEWDB */
6060
6061#ifdef NDBM
6062	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6063	{
6064		ndbm_map_close(map);
6065		map->map_mflags &= ~MF_IMPL_NDBM;
6066	}
6067#endif /* NDBM */
6068}
6069/*
6070**  User map class.
6071**
6072**	Provides access to the system password file.
6073*/
6074
6075/*
6076**  USER_MAP_OPEN -- open user map
6077**
6078**	Really just binds field names to field numbers.
6079*/
6080
6081bool
6082user_map_open(map, mode)
6083	MAP *map;
6084	int mode;
6085{
6086	if (tTd(38, 2))
6087		dprintf("user_map_open(%s, %d)\n",
6088			map->map_mname, mode);
6089
6090	mode &= O_ACCMODE;
6091	if (mode != O_RDONLY)
6092	{
6093		/* issue a pseudo-error message */
6094#ifdef ENOSYS
6095		errno = ENOSYS;
6096#else /* ENOSYS */
6097# ifdef EFTYPE
6098		errno = EFTYPE;
6099# else /* EFTYPE */
6100		errno = ENXIO;
6101# endif /* EFTYPE */
6102#endif /* ENOSYS */
6103		return FALSE;
6104	}
6105	if (map->map_valcolnm == NULL)
6106		/* EMPTY */
6107		/* nothing */ ;
6108	else if (strcasecmp(map->map_valcolnm, "name") == 0)
6109		map->map_valcolno = 1;
6110	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
6111		map->map_valcolno = 2;
6112	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
6113		map->map_valcolno = 3;
6114	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
6115		map->map_valcolno = 4;
6116	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
6117		map->map_valcolno = 5;
6118	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
6119		map->map_valcolno = 6;
6120	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
6121		map->map_valcolno = 7;
6122	else
6123	{
6124		syserr("User map %s: unknown column name %s",
6125			map->map_mname, map->map_valcolnm);
6126		return FALSE;
6127	}
6128	return TRUE;
6129}
6130
6131
6132/*
6133**  USER_MAP_LOOKUP -- look up a user in the passwd file.
6134*/
6135
6136/* ARGSUSED3 */
6137char *
6138user_map_lookup(map, key, av, statp)
6139	MAP *map;
6140	char *key;
6141	char **av;
6142	int *statp;
6143{
6144	struct passwd *pw;
6145	auto bool fuzzy;
6146
6147	if (tTd(38, 20))
6148		dprintf("user_map_lookup(%s, %s)\n",
6149			map->map_mname, key);
6150
6151	pw = finduser(key, &fuzzy);
6152	if (pw == NULL)
6153		return NULL;
6154	if (bitset(MF_MATCHONLY, map->map_mflags))
6155		return map_rewrite(map, key, strlen(key), NULL);
6156	else
6157	{
6158		char *rwval = NULL;
6159		char buf[30];
6160
6161		switch (map->map_valcolno)
6162		{
6163		  case 0:
6164		  case 1:
6165			rwval = pw->pw_name;
6166			break;
6167
6168		  case 2:
6169			rwval = pw->pw_passwd;
6170			break;
6171
6172		  case 3:
6173			snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid);
6174			rwval = buf;
6175			break;
6176
6177		  case 4:
6178			snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid);
6179			rwval = buf;
6180			break;
6181
6182		  case 5:
6183			rwval = pw->pw_gecos;
6184			break;
6185
6186		  case 6:
6187			rwval = pw->pw_dir;
6188			break;
6189
6190		  case 7:
6191			rwval = pw->pw_shell;
6192			break;
6193		}
6194		return map_rewrite(map, rwval, strlen(rwval), av);
6195	}
6196}
6197/*
6198**  Program map type.
6199**
6200**	This provides access to arbitrary programs.  It should be used
6201**	only very sparingly, since there is no way to bound the cost
6202**	of invoking an arbitrary program.
6203*/
6204
6205char *
6206prog_map_lookup(map, name, av, statp)
6207	MAP *map;
6208	char *name;
6209	char **av;
6210	int *statp;
6211{
6212	int i;
6213	int save_errno;
6214	int fd;
6215	int status;
6216	auto pid_t pid;
6217	register char *p;
6218	char *rval;
6219	char *argv[MAXPV + 1];
6220	char buf[MAXLINE];
6221
6222	if (tTd(38, 20))
6223		dprintf("prog_map_lookup(%s, %s) %s\n",
6224			map->map_mname, name, map->map_file);
6225
6226	i = 0;
6227	argv[i++] = map->map_file;
6228	if (map->map_rebuild != NULL)
6229	{
6230		snprintf(buf, sizeof buf, "%s", map->map_rebuild);
6231		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6232		{
6233			if (i >= MAXPV - 1)
6234				break;
6235			argv[i++] = p;
6236		}
6237	}
6238	argv[i++] = name;
6239	argv[i] = NULL;
6240	if (tTd(38, 21))
6241	{
6242		dprintf("prog_open:");
6243		for (i = 0; argv[i] != NULL; i++)
6244			dprintf(" %s", argv[i]);
6245		dprintf("\n");
6246	}
6247	(void) blocksignal(SIGCHLD);
6248	pid = prog_open(argv, &fd, CurEnv);
6249	if (pid < 0)
6250	{
6251		if (!bitset(MF_OPTIONAL, map->map_mflags))
6252			syserr("prog_map_lookup(%s) failed (%s) -- closing",
6253				map->map_mname, errstring(errno));
6254		else if (tTd(38, 9))
6255			dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6256				map->map_mname, errstring(errno));
6257		map->map_mflags &= ~(MF_VALID|MF_OPEN);
6258		*statp = EX_OSFILE;
6259		return NULL;
6260	}
6261	i = read(fd, buf, sizeof buf - 1);
6262	if (i < 0)
6263	{
6264		syserr("prog_map_lookup(%s): read error %s\n",
6265			map->map_mname, errstring(errno));
6266		rval = NULL;
6267	}
6268	else if (i == 0)
6269	{
6270		if (tTd(38, 20))
6271			dprintf("prog_map_lookup(%s): empty answer\n",
6272				map->map_mname);
6273		rval = NULL;
6274	}
6275	else
6276	{
6277		buf[i] = '\0';
6278		p = strchr(buf, '\n');
6279		if (p != NULL)
6280			*p = '\0';
6281
6282		/* collect the return value */
6283		if (bitset(MF_MATCHONLY, map->map_mflags))
6284			rval = map_rewrite(map, name, strlen(name), NULL);
6285		else
6286			rval = map_rewrite(map, buf, strlen(buf), NULL);
6287
6288		/* now flush any additional output */
6289		while ((i = read(fd, buf, sizeof buf)) > 0)
6290			continue;
6291	}
6292
6293	/* wait for the process to terminate */
6294	(void) close(fd);
6295	status = waitfor(pid);
6296	save_errno = errno;
6297	(void) releasesignal(SIGCHLD);
6298	errno = save_errno;
6299
6300	if (status == -1)
6301	{
6302		syserr("prog_map_lookup(%s): wait error %s\n",
6303			map->map_mname, errstring(errno));
6304		*statp = EX_SOFTWARE;
6305		rval = NULL;
6306	}
6307	else if (WIFEXITED(status))
6308	{
6309		if ((*statp = WEXITSTATUS(status)) != EX_OK)
6310			rval = NULL;
6311	}
6312	else
6313	{
6314		syserr("prog_map_lookup(%s): child died on signal %d",
6315			map->map_mname, status);
6316		*statp = EX_UNAVAILABLE;
6317		rval = NULL;
6318	}
6319	return rval;
6320}
6321/*
6322**  Sequenced map type.
6323**
6324**	Tries each map in order until something matches, much like
6325**	implicit.  Stores go to the first map in the list that can
6326**	support storing.
6327**
6328**	This is slightly unusual in that there are two interfaces.
6329**	The "sequence" interface lets you stack maps arbitrarily.
6330**	The "switch" interface builds a sequence map by looking
6331**	at a system-dependent configuration file such as
6332**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6333**
6334**	We don't need an explicit open, since all maps are
6335**	opened during startup, including underlying maps.
6336*/
6337
6338/*
6339**  SEQ_MAP_PARSE -- Sequenced map parsing
6340*/
6341
6342bool
6343seq_map_parse(map, ap)
6344	MAP *map;
6345	char *ap;
6346{
6347	int maxmap;
6348
6349	if (tTd(38, 2))
6350		dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6351	maxmap = 0;
6352	while (*ap != '\0')
6353	{
6354		register char *p;
6355		STAB *s;
6356
6357		/* find beginning of map name */
6358		while (isascii(*ap) && isspace(*ap))
6359			ap++;
6360		for (p = ap;
6361		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6362		     p++)
6363			continue;
6364		if (*p != '\0')
6365			*p++ = '\0';
6366		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6367			p++;
6368		if (*ap == '\0')
6369		{
6370			ap = p;
6371			continue;
6372		}
6373		s = stab(ap, ST_MAP, ST_FIND);
6374		if (s == NULL)
6375		{
6376			syserr("Sequence map %s: unknown member map %s",
6377				map->map_mname, ap);
6378		}
6379		else if (maxmap == MAXMAPSTACK)
6380		{
6381			syserr("Sequence map %s: too many member maps (%d max)",
6382				map->map_mname, MAXMAPSTACK);
6383			maxmap++;
6384		}
6385		else if (maxmap < MAXMAPSTACK)
6386		{
6387			map->map_stack[maxmap++] = &s->s_map;
6388		}
6389		ap = p;
6390	}
6391	return TRUE;
6392}
6393
6394
6395/*
6396**  SWITCH_MAP_OPEN -- open a switched map
6397**
6398**	This looks at the system-dependent configuration and builds
6399**	a sequence map that does the same thing.
6400**
6401**	Every system must define a switch_map_find routine in conf.c
6402**	that will return the list of service types associated with a
6403**	given service class.
6404*/
6405
6406bool
6407switch_map_open(map, mode)
6408	MAP *map;
6409	int mode;
6410{
6411	int mapno;
6412	int nmaps;
6413	char *maptype[MAXMAPSTACK];
6414
6415	if (tTd(38, 2))
6416		dprintf("switch_map_open(%s, %s, %d)\n",
6417			map->map_mname, map->map_file, mode);
6418
6419	mode &= O_ACCMODE;
6420	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6421	if (tTd(38, 19))
6422	{
6423		dprintf("\tswitch_map_find => %d\n", nmaps);
6424		for (mapno = 0; mapno < nmaps; mapno++)
6425			dprintf("\t\t%s\n", maptype[mapno]);
6426	}
6427	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6428		return FALSE;
6429
6430	for (mapno = 0; mapno < nmaps; mapno++)
6431	{
6432		register STAB *s;
6433		char nbuf[MAXNAME + 1];
6434
6435		if (maptype[mapno] == NULL)
6436			continue;
6437		(void) snprintf(nbuf, sizeof nbuf, "%s.%s",
6438			map->map_mname, maptype[mapno]);
6439		s = stab(nbuf, ST_MAP, ST_FIND);
6440		if (s == NULL)
6441		{
6442			syserr("Switch map %s: unknown member map %s",
6443				map->map_mname, nbuf);
6444		}
6445		else
6446		{
6447			map->map_stack[mapno] = &s->s_map;
6448			if (tTd(38, 4))
6449				dprintf("\tmap_stack[%d] = %s:%s\n",
6450					mapno, s->s_map.map_class->map_cname,
6451					nbuf);
6452		}
6453	}
6454	return TRUE;
6455}
6456
6457
6458/*
6459**  SEQ_MAP_CLOSE -- close all underlying maps
6460*/
6461
6462void
6463seq_map_close(map)
6464	MAP *map;
6465{
6466	int mapno;
6467
6468	if (tTd(38, 9))
6469		dprintf("seq_map_close(%s)\n", map->map_mname);
6470
6471	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6472	{
6473		MAP *mm = map->map_stack[mapno];
6474
6475		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6476			continue;
6477		mm->map_class->map_close(mm);
6478		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
6479	}
6480}
6481
6482
6483/*
6484**  SEQ_MAP_LOOKUP -- sequenced map lookup
6485*/
6486
6487char *
6488seq_map_lookup(map, key, args, pstat)
6489	MAP *map;
6490	char *key;
6491	char **args;
6492	int *pstat;
6493{
6494	int mapno;
6495	int mapbit = 0x01;
6496	bool tempfail = FALSE;
6497
6498	if (tTd(38, 20))
6499		dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6500
6501	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6502	{
6503		MAP *mm = map->map_stack[mapno];
6504		char *rv;
6505
6506		if (mm == NULL)
6507			continue;
6508		if (!bitset(MF_OPEN, mm->map_mflags) &&
6509		    !openmap(mm))
6510		{
6511			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6512			{
6513				*pstat = EX_UNAVAILABLE;
6514				return NULL;
6515			}
6516			continue;
6517		}
6518		*pstat = EX_OK;
6519		rv = mm->map_class->map_lookup(mm, key, args, pstat);
6520		if (rv != NULL)
6521			return rv;
6522		if (*pstat == EX_TEMPFAIL)
6523		{
6524			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6525				return NULL;
6526			tempfail = TRUE;
6527		}
6528		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6529			break;
6530	}
6531	if (tempfail)
6532		*pstat = EX_TEMPFAIL;
6533	else if (*pstat == EX_OK)
6534		*pstat = EX_NOTFOUND;
6535	return NULL;
6536}
6537
6538
6539/*
6540**  SEQ_MAP_STORE -- sequenced map store
6541*/
6542
6543void
6544seq_map_store(map, key, val)
6545	MAP *map;
6546	char *key;
6547	char *val;
6548{
6549	int mapno;
6550
6551	if (tTd(38, 12))
6552		dprintf("seq_map_store(%s, %s, %s)\n",
6553			map->map_mname, key, val);
6554
6555	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6556	{
6557		MAP *mm = map->map_stack[mapno];
6558
6559		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6560			continue;
6561
6562		mm->map_class->map_store(mm, key, val);
6563		return;
6564	}
6565	syserr("seq_map_store(%s, %s, %s): no writable map",
6566		map->map_mname, key, val);
6567}
6568/*
6569**  NULL stubs
6570*/
6571
6572/* ARGSUSED */
6573bool
6574null_map_open(map, mode)
6575	MAP *map;
6576	int mode;
6577{
6578	return TRUE;
6579}
6580
6581/* ARGSUSED */
6582void
6583null_map_close(map)
6584	MAP *map;
6585{
6586	return;
6587}
6588
6589char *
6590null_map_lookup(map, key, args, pstat)
6591	MAP *map;
6592	char *key;
6593	char **args;
6594	int *pstat;
6595{
6596	*pstat = EX_NOTFOUND;
6597	return NULL;
6598}
6599
6600/* ARGSUSED */
6601void
6602null_map_store(map, key, val)
6603	MAP *map;
6604	char *key;
6605	char *val;
6606{
6607	return;
6608}
6609
6610
6611/*
6612**  BOGUS stubs
6613*/
6614
6615char *
6616bogus_map_lookup(map, key, args, pstat)
6617	MAP *map;
6618	char *key;
6619	char **args;
6620	int *pstat;
6621{
6622	*pstat = EX_TEMPFAIL;
6623	return NULL;
6624}
6625
6626MAPCLASS	BogusMapClass =
6627{
6628	"bogus-map",		NULL,		0,
6629	NULL,		bogus_map_lookup,	null_map_store,
6630	null_map_open,	null_map_close,
6631};
6632/*
6633**  MACRO modules
6634*/
6635
6636char *
6637macro_map_lookup(map, name, av, statp)
6638	MAP *map;
6639	char *name;
6640	char **av;
6641	int *statp;
6642{
6643	int mid;
6644
6645	if (tTd(38, 20))
6646		dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6647			name == NULL ? "NULL" : name);
6648
6649	if (name == NULL ||
6650	    *name == '\0' ||
6651	    (mid = macid(name, NULL)) == '\0')
6652	{
6653		*statp = EX_CONFIG;
6654		return NULL;
6655	}
6656
6657	if (av[1] == NULL)
6658		define(mid, NULL, CurEnv);
6659	else
6660		define(mid, newstr(av[1]), CurEnv);
6661
6662	*statp = EX_OK;
6663	return "";
6664}
6665/*
6666**  REGEX modules
6667*/
6668
6669#ifdef MAP_REGEX
6670
6671# include <regex.h>
6672
6673# define DEFAULT_DELIM	CONDELSE
6674
6675# define END_OF_FIELDS	-1
6676
6677# define ERRBUF_SIZE	80
6678# define MAX_MATCH	32
6679
6680# define xnalloc(s)	memset(xalloc(s), '\0', s);
6681
6682struct regex_map
6683{
6684	regex_t	regex_pattern_buf;	/* xalloc it */
6685	int	*regex_subfields;	/* move to type MAP */
6686	char	*regex_delim;		/* move to type MAP */
6687};
6688
6689static int
6690parse_fields(s, ibuf, blen, nr_substrings)
6691	char *s;
6692	int *ibuf;		/* array */
6693	int blen;		/* number of elements in ibuf */
6694	int nr_substrings;	/* number of substrings in the pattern */
6695{
6696	register char *cp;
6697	int i = 0;
6698	bool lastone = FALSE;
6699
6700	blen--;		/* for terminating END_OF_FIELDS */
6701	cp = s;
6702	do
6703	{
6704		for (;; cp++)
6705		{
6706			if (*cp == ',')
6707			{
6708				*cp = '\0';
6709				break;
6710			}
6711			if (*cp == '\0')
6712			{
6713				lastone = TRUE;
6714				break;
6715			}
6716		}
6717		if (i < blen)
6718		{
6719			int val = atoi(s);
6720
6721			if (val < 0 || val >= nr_substrings)
6722			{
6723				syserr("field (%d) out of range, only %d substrings in pattern",
6724				       val, nr_substrings);
6725				return -1;
6726			}
6727			ibuf[i++] = val;
6728		}
6729		else
6730		{
6731			syserr("too many fields, %d max\n", blen);
6732			return -1;
6733		}
6734		s = ++cp;
6735	} while (!lastone);
6736	ibuf[i] = END_OF_FIELDS;
6737	return i;
6738}
6739
6740bool
6741regex_map_init(map, ap)
6742	MAP *map;
6743	char *ap;
6744{
6745	int regerr;
6746	struct regex_map *map_p;
6747	register char *p;
6748	char *sub_param = NULL;
6749	int pflags;
6750	static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
6751
6752	if (tTd(38, 2))
6753		dprintf("regex_map_init: mapname '%s', args '%s'\n",
6754			map->map_mname, ap);
6755
6756	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6757
6758	p = ap;
6759
6760	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6761
6762	for (;;)
6763	{
6764		while (isascii(*p) && isspace(*p))
6765			p++;
6766		if (*p != '-')
6767			break;
6768		switch (*++p)
6769		{
6770		  case 'n':	/* not */
6771			map->map_mflags |= MF_REGEX_NOT;
6772			break;
6773
6774		  case 'f':	/* case sensitive */
6775			map->map_mflags |= MF_NOFOLDCASE;
6776			pflags &= ~REG_ICASE;
6777			break;
6778
6779		  case 'b':	/* basic regular expressions */
6780			pflags &= ~REG_EXTENDED;
6781			break;
6782
6783		  case 's':	/* substring match () syntax */
6784			sub_param = ++p;
6785			pflags &= ~REG_NOSUB;
6786			break;
6787
6788		  case 'd':	/* delimiter */
6789			map_p->regex_delim = ++p;
6790			break;
6791
6792		  case 'a':	/* map append */
6793			map->map_app = ++p;
6794			break;
6795
6796		  case 'm':	/* matchonly */
6797			map->map_mflags |= MF_MATCHONLY;
6798			break;
6799
6800		  case 'S':
6801			map->map_spacesub = *++p;
6802			break;
6803
6804		  case 'D':
6805			map->map_mflags |= MF_DEFER;
6806			break;
6807
6808		}
6809		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6810			p++;
6811		if (*p != '\0')
6812			*p++ = '\0';
6813	}
6814	if (tTd(38, 3))
6815		dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6816
6817	if ((regerr = regcomp(&(map_p->regex_pattern_buf), p, pflags)) != 0)
6818	{
6819		/* Errorhandling */
6820		char errbuf[ERRBUF_SIZE];
6821
6822		(void) regerror(regerr, &(map_p->regex_pattern_buf),
6823			 errbuf, ERRBUF_SIZE);
6824		syserr("pattern-compile-error: %s\n", errbuf);
6825		free(map_p);
6826		return FALSE;
6827	}
6828
6829	if (map->map_app != NULL)
6830		map->map_app = newstr(map->map_app);
6831	if (map_p->regex_delim != NULL)
6832		map_p->regex_delim = newstr(map_p->regex_delim);
6833	else
6834		map_p->regex_delim = defdstr;
6835
6836	if (!bitset(REG_NOSUB, pflags))
6837	{
6838		/* substring matching */
6839		int substrings;
6840		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6841
6842		substrings = map_p->regex_pattern_buf.re_nsub + 1;
6843
6844		if (tTd(38, 3))
6845			dprintf("regex_map_init: nr of substrings %d\n",
6846				substrings);
6847
6848		if (substrings >= MAX_MATCH)
6849		{
6850			syserr("too many substrings, %d max\n", MAX_MATCH);
6851			free(map_p);
6852			return FALSE;
6853		}
6854		if (sub_param != NULL && sub_param[0] != '\0')
6855		{
6856			/* optional parameter -sfields */
6857			if (parse_fields(sub_param, fields,
6858					 MAX_MATCH + 1, substrings) == -1)
6859				return FALSE;
6860		}
6861		else
6862		{
6863			/* set default fields */
6864			int i;
6865
6866			for (i = 0; i < substrings; i++)
6867				fields[i] = i;
6868			fields[i] = END_OF_FIELDS;
6869		}
6870		map_p->regex_subfields = fields;
6871		if (tTd(38, 3))
6872		{
6873			int *ip;
6874
6875			dprintf("regex_map_init: subfields");
6876			for (ip = fields; *ip != END_OF_FIELDS; ip++)
6877				dprintf(" %d", *ip);
6878			dprintf("\n");
6879		}
6880	}
6881	map->map_db1 = (ARBPTR_T)map_p;	/* dirty hack */
6882
6883	return TRUE;
6884}
6885
6886static char *
6887regex_map_rewrite(map, s, slen, av)
6888	MAP *map;
6889	const char *s;
6890	size_t slen;
6891	char **av;
6892{
6893	if (bitset(MF_MATCHONLY, map->map_mflags))
6894		return map_rewrite(map, av[0], strlen(av[0]), NULL);
6895	else
6896		return map_rewrite(map, s, slen, NULL);
6897}
6898
6899char *
6900regex_map_lookup(map, name, av, statp)
6901	MAP *map;
6902	char *name;
6903	char **av;
6904	int *statp;
6905{
6906	int reg_res;
6907	struct regex_map *map_p;
6908	regmatch_t pmatch[MAX_MATCH];
6909
6910	if (tTd(38, 20))
6911	{
6912		char **cpp;
6913
6914		dprintf("regex_map_lookup: key '%s'\n", name);
6915		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6916			dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6917	}
6918
6919	map_p = (struct regex_map *)(map->map_db1);
6920	reg_res = regexec(&(map_p->regex_pattern_buf),
6921			  name, MAX_MATCH, pmatch, 0);
6922
6923	if (bitset(MF_REGEX_NOT, map->map_mflags))
6924	{
6925		/* option -n */
6926		if (reg_res == REG_NOMATCH)
6927			return regex_map_rewrite(map, "", (size_t)0, av);
6928		else
6929			return NULL;
6930	}
6931	if (reg_res == REG_NOMATCH)
6932		return NULL;
6933
6934	if (map_p->regex_subfields != NULL)
6935	{
6936		/* option -s */
6937		static char retbuf[MAXNAME];
6938		int fields[MAX_MATCH + 1];
6939		bool first = TRUE;
6940		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6941		bool quotemode = FALSE, bslashmode = FALSE;
6942		register char *dp, *sp;
6943		char *endp, *ldp;
6944		int *ip;
6945
6946		dp = retbuf;
6947		ldp = retbuf + sizeof(retbuf) - 1;
6948
6949		if (av[1] != NULL)
6950		{
6951			if (parse_fields(av[1], fields, MAX_MATCH + 1,
6952					 (int) map_p->regex_pattern_buf.re_nsub + 1) == -1)
6953			{
6954				*statp = EX_CONFIG;
6955				return NULL;
6956			}
6957			ip = fields;
6958		}
6959		else
6960			ip = map_p->regex_subfields;
6961
6962		for ( ; *ip != END_OF_FIELDS; ip++)
6963		{
6964			if (!first)
6965			{
6966				for (sp = map_p->regex_delim; *sp; sp++)
6967				{
6968					if (dp < ldp)
6969						*dp++ = *sp;
6970				}
6971			}
6972			else
6973				first = FALSE;
6974
6975
6976			if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
6977				continue;
6978
6979			sp = name + pmatch[*ip].rm_so;
6980			endp = name + pmatch[*ip].rm_eo;
6981			for (; endp > sp; sp++)
6982			{
6983				if (dp < ldp)
6984				{
6985					if (bslashmode)
6986					{
6987						*dp++ = *sp;
6988						bslashmode = FALSE;
6989					}
6990					else if (quotemode && *sp != '"' &&
6991						*sp != '\\')
6992					{
6993						*dp++ = *sp;
6994					}
6995					else switch(*dp++ = *sp)
6996					{
6997						case '\\':
6998						bslashmode = TRUE;
6999						break;
7000
7001						case '(':
7002						cmntcnt++;
7003						break;
7004
7005						case ')':
7006						cmntcnt--;
7007						break;
7008
7009						case '<':
7010						anglecnt++;
7011						break;
7012
7013						case '>':
7014						anglecnt--;
7015						break;
7016
7017						case ' ':
7018						spacecnt++;
7019						break;
7020
7021						case '"':
7022						quotemode = !quotemode;
7023						break;
7024					}
7025				}
7026			}
7027		}
7028		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7029		    bslashmode || spacecnt != 0)
7030		{
7031			sm_syslog(LOG_WARNING, NOQID,
7032				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
7033				  map->map_mname, name);
7034			return NULL;
7035		}
7036
7037		*dp = '\0';
7038
7039		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7040	}
7041	return regex_map_rewrite(map, "", (size_t)0, av);
7042}
7043#endif /* MAP_REGEX */
7044/*
7045**  NSD modules
7046*/
7047#ifdef MAP_NSD
7048
7049# include <ndbm.h>
7050# define _DATUM_DEFINED
7051# include <ns_api.h>
7052
7053typedef struct ns_map_list
7054{
7055	ns_map_t *map;
7056	char *mapname;
7057	struct ns_map_list *next;
7058} ns_map_list_t;
7059
7060static ns_map_t *
7061ns_map_t_find(mapname)
7062	char *mapname;
7063{
7064	static ns_map_list_t *ns_maps = NULL;
7065	ns_map_list_t *ns_map;
7066
7067	/* walk the list of maps looking for the correctly named map */
7068	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7069	{
7070		if (strcmp(ns_map->mapname, mapname) == 0)
7071			break;
7072	}
7073
7074	/* if we are looking at a NULL ns_map_list_t, then create a new one */
7075	if (ns_map == NULL)
7076	{
7077		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
7078		ns_map->mapname = newstr(mapname);
7079		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
7080		ns_map->next = ns_maps;
7081		ns_maps = ns_map;
7082	}
7083	return ns_map->map;
7084}
7085
7086char *
7087nsd_map_lookup(map, name, av, statp)
7088	MAP *map;
7089	char *name;
7090	char **av;
7091	int *statp;
7092{
7093	int buflen;
7094	char *p;
7095	ns_map_t *ns_map;
7096	char keybuf[MAXNAME + 1];
7097	char buf[MAXLINE];
7098
7099	if (tTd(38, 20))
7100		dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7101
7102	buflen = strlen(name);
7103	if (buflen > sizeof keybuf - 1)
7104		buflen = sizeof keybuf - 1;
7105	memmove(keybuf, name, buflen);
7106	keybuf[buflen] = '\0';
7107	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7108		makelower(keybuf);
7109
7110	ns_map = ns_map_t_find(map->map_file);
7111	if (ns_map == NULL)
7112	{
7113		if (tTd(38, 20))
7114			dprintf("nsd_map_t_find failed\n");
7115		return NULL;
7116	}
7117
7118	if (ns_lookup(ns_map, NULL, map->map_file,
7119		      keybuf, NULL, buf, MAXLINE) == NULL)
7120		return NULL;
7121
7122	/* Null out trailing \n */
7123	if ((p = strchr(buf, '\n')) != NULL)
7124		*p = '\0';
7125
7126	return map_rewrite(map, buf, strlen(buf), av);
7127}
7128#endif /* MAP_NSD */
7129
7130char *
7131arith_map_lookup(map, name, av, statp)
7132	MAP *map;
7133	char *name;
7134	char **av;
7135	int *statp;
7136{
7137	long r;
7138	long v[2];
7139	bool res = FALSE;
7140	bool boolres;
7141	static char result[16];
7142	char **cpp;
7143
7144	if (tTd(38, 2))
7145	{
7146		dprintf("arith_map_lookup: key '%s'\n", name);
7147		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7148			dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7149	}
7150	r = 0;
7151	boolres = FALSE;
7152	cpp = av;
7153	*statp = EX_OK;
7154
7155	/*
7156	**  read arguments for arith map
7157	**  - no check is made whether they are really numbers
7158	**  - just ignores args after the second
7159	*/
7160	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7161		v[r++] = strtol(*cpp, NULL, 0);
7162
7163	/* operator and (at least) two operands given? */
7164	if (name != NULL && r == 2)
7165	{
7166		switch(*name)
7167		{
7168#if _FFR_ARITH
7169		  case '|':
7170			r = v[0] | v[1];
7171			break;
7172
7173		  case '&':
7174			r = v[0] & v[1];
7175			break;
7176
7177		  case '%':
7178			if (v[1] == 0)
7179				return NULL;
7180			r = v[0] % v[1];
7181			break;
7182#endif /* _FFR_ARITH */
7183
7184		  case '+':
7185			r = v[0] + v[1];
7186			break;
7187
7188		  case '-':
7189			r = v[0] - v[1];
7190			break;
7191
7192		  case '*':
7193			r = v[0] * v[1];
7194			break;
7195
7196		  case '/':
7197			if (v[1] == 0)
7198				return NULL;
7199			r = v[0] / v[1];
7200			break;
7201
7202		  case 'l':
7203			res = v[0] < v[1];
7204			boolres = TRUE;
7205			break;
7206
7207		  case '=':
7208			res = v[0] == v[1];
7209			boolres = TRUE;
7210			break;
7211
7212		  default:
7213			/* XXX */
7214			*statp = EX_CONFIG;
7215			if (LogLevel > 10)
7216				sm_syslog(LOG_WARNING, NOQID,
7217					  "arith_map: unknown operator %c",
7218					  isprint(*name) ? *name : '?');
7219			return NULL;
7220		}
7221		if (boolres)
7222			snprintf(result, sizeof result, res ? "TRUE" : "FALSE");
7223		else
7224			snprintf(result, sizeof result, "%ld", r);
7225		return result;
7226	}
7227	*statp = EX_CONFIG;
7228	return NULL;
7229}
7230