map.c revision 71345
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.34 2000/12/18 18:00:43 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			MapOpenErr = TRUE;
572		}
573		else
574		{
575			/* don't try again */
576			map->map_mflags &= ~MF_VALID;
577		}
578	}
579
580	if (restore)
581	{
582		Errors = saveerrors;
583		HoldErrs = savehold;
584		QuickAbort = savequick;
585	}
586
587	return bitset(MF_OPEN, map->map_mflags);
588}
589/*
590**  CLOSEMAPS -- close all open maps opened by the current pid.
591**
592**	Parameters:
593**		none
594**
595**	Returns:
596**		none.
597*/
598
599void
600closemaps()
601{
602	stabapply(map_close, 0);
603}
604/*
605**  MAP_CLOSE -- close a map opened by the current pid.
606**
607**	Parameters:
608**		s -- STAB entry: if map: try to open
609**		second parameter is unused (required by stabapply())
610**
611**	Returns:
612**		none.
613*/
614
615/* ARGSUSED1 */
616static void
617map_close(s, unused)
618	register STAB *s;
619	int unused;
620{
621	MAP *map;
622
623	if (s->s_type != ST_MAP)
624		return;
625
626	map = &s->s_map;
627
628	if (!bitset(MF_VALID, map->map_mflags) ||
629	    !bitset(MF_OPEN, 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): ", 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		if (tTd(38, 2))
2845			dprintf("using cached connection\n");
2846		return TRUE;
2847	}
2848
2849	if (tTd(38, 2))
2850		dprintf("opening new connection\n");
2851
2852	/* No connection yet, connect */
2853	if (!ldapmap_start(map))
2854		return FALSE;
2855
2856	/* Save connection for reuse */
2857	s->s_ldap = lmap->ldap_ld;
2858	return TRUE;
2859}
2860
2861/*
2862**  LDAPMAP_START -- actually connect to an LDAP server
2863**
2864**	Parameters:
2865**		map -- the map being opened.
2866**
2867**	Returns:
2868**		TRUE if connection is successful, FALSE otherwise.
2869**
2870**	Side Effects:
2871**		Populates lmap->ldap_ld.
2872*/
2873
2874static jmp_buf	LDAPTimeout;
2875
2876static bool
2877ldapmap_start(map)
2878	MAP *map;
2879{
2880	register int bind_result;
2881	int save_errno;
2882	register EVENT *ev = NULL;
2883	LDAPMAP_STRUCT *lmap;
2884	LDAP *ld;
2885
2886	if (tTd(38, 2))
2887		dprintf("ldapmap_start(%s)\n", map->map_mname);
2888
2889	lmap = (LDAPMAP_STRUCT *) map->map_db1;
2890
2891	if (tTd(38,9))
2892		dprintf("ldapmap_start(%s, %d)\n",
2893			lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host,
2894			lmap->ldap_port);
2895
2896# if USE_LDAP_INIT
2897	ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
2898	save_errno = errno;
2899# else /* USE_LDAP_INIT */
2900	/*
2901	**  If using ldap_open(), the actual connection to the server
2902	**  happens now so we need the timeout here.  For ldap_init(),
2903	**  the connection happens at bind time.
2904	*/
2905
2906	/* set the timeout */
2907	if (lmap->ldap_timeout.tv_sec != 0)
2908	{
2909		if (setjmp(LDAPTimeout) != 0)
2910		{
2911			if (LogLevel > 1)
2912				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2913					  "timeout conning to LDAP server %.100s",
2914					  lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
2915			return FALSE;
2916		}
2917		ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
2918	}
2919
2920	ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
2921	save_errno = errno;
2922
2923	/* clear the event if it has not sprung */
2924	if (ev != NULL)
2925		clrevent(ev);
2926# endif /* USE_LDAP_INIT */
2927
2928	errno = save_errno;
2929	if (ld == NULL)
2930	{
2931		if (!bitset(MF_OPTIONAL, map->map_mflags))
2932		{
2933			if (bitset(MF_NODEFER, map->map_mflags))
2934				syserr("%s failed to %s in map %s",
2935# if USE_LDAP_INIT
2936				       "ldap_init",
2937# else /* USE_LDAP_INIT */
2938				       "ldap_open",
2939# endif /* USE_LDAP_INIT */
2940				       lmap->ldap_host == NULL ? "localhost"
2941							       : lmap->ldap_host,
2942				       map->map_mname);
2943			else
2944				syserr("421 4.0.0 %s failed to %s in map %s",
2945# if USE_LDAP_INIT
2946				       "ldap_init",
2947# else /* USE_LDAP_INIT */
2948				       "ldap_open",
2949# endif /* USE_LDAP_INIT */
2950				       lmap->ldap_host == NULL ? "localhost"
2951							       : lmap->ldap_host,
2952				       map->map_mname);
2953		}
2954		return FALSE;
2955	}
2956
2957	ldapmap_setopts(ld, lmap);
2958
2959# if USE_LDAP_INIT
2960	/*
2961	**  If using ldap_init(), the actual connection to the server
2962	**  happens at ldap_bind_s() so we need the timeout here.
2963	*/
2964
2965	/* set the timeout */
2966	if (lmap->ldap_timeout.tv_sec != 0)
2967	{
2968		if (setjmp(LDAPTimeout) != 0)
2969		{
2970			if (LogLevel > 1)
2971				sm_syslog(LOG_NOTICE, CurEnv->e_id,
2972					  "timeout conning to LDAP server %.100s",
2973					  lmap->ldap_host == NULL ? "localhost"
2974								  : lmap->ldap_host);
2975			return FALSE;
2976		}
2977		ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
2978	}
2979# endif /* USE_LDAP_INIT */
2980
2981# ifdef LDAP_AUTH_KRBV4
2982	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
2983	    lmap->ldap_secret != NULL)
2984	{
2985		/*
2986		**  Need to put ticket in environment here instead of
2987		**  during parseargs as there may be different tickets
2988		**  for different LDAP connections.
2989		*/
2990
2991		(void) putenv(lmap->ldap_secret);
2992	}
2993# endif /* LDAP_AUTH_KRBV4 */
2994
2995	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
2996				  lmap->ldap_secret, lmap->ldap_method);
2997
2998# if USE_LDAP_INIT
2999	/* clear the event if it has not sprung */
3000	if (ev != NULL)
3001		clrevent(ev);
3002# endif /* USE_LDAP_INIT */
3003
3004	if (bind_result != LDAP_SUCCESS)
3005	{
3006		errno = bind_result + E_LDAPBASE;
3007		if (!bitset(MF_OPTIONAL, map->map_mflags))
3008		{
3009			syserr("421 4.0.0 Cannot bind to map %s in ldap server %s",
3010			       map->map_mname,
3011			       lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
3012		}
3013		return FALSE;
3014	}
3015
3016	/* We need to cast ld into the map structure */
3017	lmap->ldap_ld = ld;
3018	return TRUE;
3019}
3020
3021/* ARGSUSED */
3022static void
3023ldaptimeout(sig_no)
3024	int sig_no;
3025{
3026	longjmp(LDAPTimeout, 1);
3027}
3028
3029/*
3030**  LDAPMAP_CLOSE -- close ldap map
3031*/
3032
3033void
3034ldapmap_close(map)
3035	MAP *map;
3036{
3037	LDAPMAP_STRUCT *lmap;
3038	STAB *s;
3039
3040	if (tTd(38, 2))
3041		dprintf("ldapmap_close(%s)\n", map->map_mname);
3042
3043	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3044
3045	/* Check if already closed */
3046	if (lmap->ldap_ld == NULL)
3047		return;
3048
3049	s = ldapmap_findconn(lmap);
3050
3051	/* Check if already closed */
3052	if (s->s_ldap == NULL)
3053		return;
3054
3055	/* If same as saved connection, stored connection is going away */
3056	if (s->s_ldap == lmap->ldap_ld)
3057		s->s_ldap = NULL;
3058
3059	if (lmap->ldap_ld != NULL)
3060	{
3061		ldap_unbind(lmap->ldap_ld);
3062		lmap->ldap_ld = NULL;
3063	}
3064}
3065
3066# ifdef SUNET_ID
3067/*
3068**  SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
3069**  This only makes sense at Stanford University.
3070*/
3071
3072char *
3073sunet_id_hash(str)
3074	char *str;
3075{
3076	char *p, *p_last;
3077
3078	p = str;
3079	p_last = p;
3080	while (*p != '\0')
3081	{
3082		if (islower(*p) || isdigit(*p))
3083		{
3084			*p_last = *p;
3085			p_last++;
3086		}
3087		else if (isupper(*p))
3088		{
3089			*p_last = tolower(*p);
3090			p_last++;
3091		}
3092		++p;
3093	}
3094	if (*p_last != '\0')
3095		*p_last = '\0';
3096	return str;
3097}
3098# endif /* SUNET_ID */
3099
3100/*
3101**  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3102*/
3103
3104char *
3105ldapmap_lookup(map, name, av, statp)
3106	MAP *map;
3107	char *name;
3108	char **av;
3109	int *statp;
3110{
3111	int i;
3112	int entries = 0;
3113	int msgid;
3114	int ret;
3115	int vsize;
3116	char *fp, *vp;
3117	char *p, *q;
3118	char *result = NULL;
3119	LDAPMAP_STRUCT *lmap = NULL;
3120	char keybuf[MAXNAME + 1];
3121	char filter[LDAPMAP_MAX_FILTER + 1];
3122
3123	if (tTd(38, 20))
3124		dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3125
3126	/* Get ldap struct pointer from map */
3127	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3128	ldapmap_setopts(lmap->ldap_ld, lmap);
3129
3130	(void) strlcpy(keybuf, name, sizeof keybuf);
3131
3132	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3133	{
3134# ifdef SUNET_ID
3135		sunet_id_hash(keybuf);
3136# else /* SUNET_ID */
3137		makelower(keybuf);
3138# endif /* SUNET_ID */
3139	}
3140
3141	/* substitute keybuf into filter, perhaps multiple times */
3142	memset(filter, '\0', sizeof filter);
3143	fp = filter;
3144	p = lmap->ldap_filter;
3145	while ((q = strchr(p, '%')) != NULL)
3146	{
3147		if (q[1] == 's')
3148		{
3149			snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
3150				 (int) (q - p), p, keybuf);
3151			fp += strlen(fp);
3152			p = q + 2;
3153		}
3154		else if (q[1] == '0')
3155		{
3156			char *k = keybuf;
3157
3158			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
3159				 (int) (q - p), p);
3160			fp += strlen(fp);
3161			p = q + 2;
3162
3163			/* Properly escape LDAP special characters */
3164			while (SPACELEFT(filter, fp) > 0 &&
3165			       *k != '\0')
3166			{
3167				if (*k == '*' || *k == '(' ||
3168				    *k == ')' || *k == '\\')
3169				{
3170					(void) strlcat(fp,
3171						       (*k == '*' ? "\\2A" :
3172							(*k == '(' ? "\\28" :
3173							 (*k == ')' ? "\\29" :
3174							  (*k == '\\' ? "\\5C" :
3175							   "\00")))),
3176						SPACELEFT(filter, fp));
3177					fp += strlen(fp);
3178					k++;
3179				}
3180				else
3181					*fp++ = *k++;
3182			}
3183		}
3184		else
3185		{
3186			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
3187				 (int) (q - p + 1), p);
3188			p = q + (q[1] == '%' ? 2 : 1);
3189			fp += strlen(fp);
3190		}
3191	}
3192	snprintf(fp, SPACELEFT(filter, fp), "%s", p);
3193	if (tTd(38, 20))
3194		dprintf("ldap search filter=%s\n", filter);
3195
3196	lmap->ldap_res = NULL;
3197	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope,
3198			    filter,
3199			    (lmap->ldap_attr[0] == NULL ? NULL :
3200			     lmap->ldap_attr),
3201			    lmap->ldap_attrsonly);
3202	if (msgid == -1)
3203	{
3204		errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3205		if (!bitset(MF_OPTIONAL, map->map_mflags))
3206		{
3207			if (bitset(MF_NODEFER, map->map_mflags))
3208				syserr("Error in ldap_search using %s in map %s",
3209				       filter, map->map_mname);
3210			else
3211				syserr("421 4.0.0 Error in ldap_search using %s in map %s",
3212				       filter, map->map_mname);
3213		}
3214		*statp = EX_TEMPFAIL;
3215#ifdef LDAP_SERVER_DOWN
3216		if (errno == LDAP_SERVER_DOWN)
3217		{
3218			int save_errno = errno;
3219
3220			/* server disappeared, try reopen on next search */
3221			map->map_class->map_close(map);
3222			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3223			errno = save_errno;
3224		}
3225#endif /* LDAP_SERVER_DOWN */
3226		return NULL;
3227	}
3228
3229	*statp = EX_NOTFOUND;
3230	vp = NULL;
3231
3232	/* Get results (all if MF_NOREWRITE, otherwise one by one) */
3233	while ((ret = ldap_result(lmap->ldap_ld, msgid,
3234				  bitset(MF_NOREWRITE, map->map_mflags),
3235				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
3236				   &(lmap->ldap_timeout)),
3237				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
3238	{
3239		LDAPMessage *entry;
3240
3241		if (bitset(MF_SINGLEMATCH, map->map_mflags))
3242		{
3243			entries += ldap_count_entries(lmap->ldap_ld,
3244						      lmap->ldap_res);
3245			if (entries > 1)
3246			{
3247				*statp = EX_NOTFOUND;
3248				if (lmap->ldap_res != NULL)
3249				{
3250					ldap_msgfree(lmap->ldap_res);
3251					lmap->ldap_res = NULL;
3252				}
3253				(void) ldap_abandon(lmap->ldap_ld, msgid);
3254				if (vp != NULL)
3255					free(vp);
3256				if (tTd(38, 25))
3257					dprintf("ldap search found multiple on a single match query\n");
3258				return NULL;
3259			}
3260		}
3261
3262		/* If we don't want multiple values and we have one, break */
3263		if (map->map_coldelim == '\0' && vp != NULL)
3264			break;
3265
3266		/* Cycle through all entries */
3267		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
3268		     entry != NULL;
3269		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
3270		{
3271			BerElement *ber;
3272			char *attr;
3273			char **vals = NULL;
3274
3275			/*
3276			**  If matching only and found an entry,
3277			**  no need to spin through attributes
3278			*/
3279
3280			if (*statp == EX_OK &&
3281			    bitset(MF_MATCHONLY, map->map_mflags))
3282				continue;
3283
3284# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3285			/*
3286			**  Reset value to prevent lingering
3287			**  LDAP_DECODING_ERROR due to
3288			**  OpenLDAP 1.X's hack (see below)
3289			*/
3290
3291			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3292# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3293
3294			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
3295							 &ber);
3296			     attr != NULL;
3297			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
3298							ber))
3299			{
3300				char *tmp, *vp_tmp;
3301
3302				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3303				{
3304					vals = ldap_get_values(lmap->ldap_ld,
3305							       entry,
3306							       attr);
3307					if (vals == NULL)
3308					{
3309						errno = ldapmap_geterrno(lmap->ldap_ld);
3310						if (errno == LDAP_SUCCESS)
3311							continue;
3312
3313						/* Must be an error */
3314						errno += E_LDAPBASE;
3315						if (!bitset(MF_OPTIONAL,
3316							    map->map_mflags))
3317						{
3318							if (bitset(MF_NODEFER,
3319								   map->map_mflags))
3320								syserr("Error getting LDAP values in map %s",
3321								       map->map_mname);
3322							else
3323								syserr("421 4.0.0 Error getting LDAP values in map %s",
3324								       map->map_mname);
3325						}
3326						*statp = EX_TEMPFAIL;
3327# if USING_NETSCAPE_LDAP
3328						ldap_memfree(attr);
3329# endif /* USING_NETSCAPE_LDAP */
3330						if (lmap->ldap_res != NULL)
3331						{
3332							ldap_msgfree(lmap->ldap_res);
3333							lmap->ldap_res = NULL;
3334						}
3335						(void) ldap_abandon(lmap->ldap_ld,
3336								    msgid);
3337						if (vp != NULL)
3338							free(vp);
3339						return NULL;
3340					}
3341				}
3342
3343				*statp = EX_OK;
3344
3345# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3346				/*
3347				**  Reset value to prevent lingering
3348				**  LDAP_DECODING_ERROR due to
3349				**  OpenLDAP 1.X's hack (see below)
3350				*/
3351
3352				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3353# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3354
3355				/*
3356				**  If matching only,
3357				**  no need to spin through entries
3358				*/
3359
3360				if (bitset(MF_MATCHONLY, map->map_mflags))
3361					continue;
3362
3363				/*
3364				**  If we don't want multiple values,
3365				**  return first found.
3366				*/
3367
3368				if (map->map_coldelim == '\0')
3369				{
3370					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3371					{
3372						vp = newstr(attr);
3373# if USING_NETSCAPE_LDAP
3374						ldap_memfree(attr);
3375# endif /* USING_NETSCAPE_LDAP */
3376						break;
3377					}
3378
3379					if (vals[0] == NULL)
3380					{
3381						ldap_value_free(vals);
3382# if USING_NETSCAPE_LDAP
3383						ldap_memfree(attr);
3384# endif /* USING_NETSCAPE_LDAP */
3385						continue;
3386					}
3387
3388					vp = newstr(vals[0]);
3389					ldap_value_free(vals);
3390# if USING_NETSCAPE_LDAP
3391					ldap_memfree(attr);
3392# endif /* USING_NETSCAPE_LDAP */
3393					break;
3394				}
3395
3396				/* attributes only */
3397				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3398				{
3399					if (vp == NULL)
3400						vp = newstr(attr);
3401					else
3402					{
3403						vsize = strlen(vp) +
3404							strlen(attr) + 2;
3405						tmp = xalloc(vsize);
3406						snprintf(tmp, vsize, "%s%c%s",
3407							 vp, map->map_coldelim,
3408							 attr);
3409						free(vp);
3410						vp = tmp;
3411					}
3412# if USING_NETSCAPE_LDAP
3413					ldap_memfree(attr);
3414# endif /* USING_NETSCAPE_LDAP */
3415					continue;
3416				}
3417
3418				/*
3419				**  If there is more than one,
3420				**  munge then into a map_coldelim
3421				**  separated string
3422				*/
3423
3424				vsize = 0;
3425				for (i = 0; vals[i] != NULL; i++)
3426					vsize += strlen(vals[i]) + 1;
3427				vp_tmp = xalloc(vsize);
3428				*vp_tmp = '\0';
3429
3430				p = vp_tmp;
3431				for (i = 0; vals[i] != NULL; i++)
3432				{
3433					p += strlcpy(p, vals[i],
3434						     vsize - (p - vp_tmp));
3435					if (p >= vp_tmp + vsize)
3436						syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3437					if (vals[i + 1] != NULL)
3438						*p++ = map->map_coldelim;
3439				}
3440
3441				ldap_value_free(vals);
3442# if USING_NETSCAPE_LDAP
3443				ldap_memfree(attr);
3444# endif /* USING_NETSCAPE_LDAP */
3445				if (vp == NULL)
3446				{
3447					vp = vp_tmp;
3448					continue;
3449				}
3450				vsize = strlen(vp) + strlen(vp_tmp) + 2;
3451				tmp = xalloc(vsize);
3452				snprintf(tmp, vsize, "%s%c%s",
3453					 vp, map->map_coldelim, vp_tmp);
3454
3455				free(vp);
3456				free(vp_tmp);
3457				vp = tmp;
3458			}
3459			errno = ldapmap_geterrno(lmap->ldap_ld);
3460
3461			/*
3462			**  We check errno != LDAP_DECODING_ERROR since
3463			**  OpenLDAP 1.X has a very ugly *undocumented*
3464			**  hack of returning this error code from
3465			**  ldap_next_attribute() if the library freed the
3466			**  ber attribute.  See:
3467			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
3468			*/
3469
3470			if (errno != LDAP_SUCCESS &&
3471			    errno != LDAP_DECODING_ERROR)
3472			{
3473				/* Must be an error */
3474				errno += E_LDAPBASE;
3475				if (!bitset(MF_OPTIONAL, map->map_mflags))
3476				{
3477					if (bitset(MF_NODEFER, map->map_mflags))
3478						syserr("Error getting LDAP attributes in map %s",
3479						       map->map_mname);
3480					else
3481						syserr("421 4.0.0 Error getting LDAP attributes in map %s",
3482						       map->map_mname);
3483				}
3484				*statp = EX_TEMPFAIL;
3485				if (lmap->ldap_res != NULL)
3486				{
3487					ldap_msgfree(lmap->ldap_res);
3488					lmap->ldap_res = NULL;
3489				}
3490				(void) ldap_abandon(lmap->ldap_ld, msgid);
3491				if (vp != NULL)
3492					free(vp);
3493				return NULL;
3494			}
3495
3496			/* We don't want multiple values and we have one */
3497			if (map->map_coldelim == '\0' && vp != NULL)
3498				break;
3499		}
3500		errno = ldapmap_geterrno(lmap->ldap_ld);
3501		if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR)
3502		{
3503			/* Must be an error */
3504			errno += E_LDAPBASE;
3505			if (!bitset(MF_OPTIONAL, map->map_mflags))
3506			{
3507				if (bitset(MF_NODEFER, map->map_mflags))
3508					syserr("Error getting LDAP entries in map %s",
3509					       map->map_mname);
3510				else
3511					syserr("421 4.0.0 Error getting LDAP entries in map %s",
3512					       map->map_mname);
3513			}
3514			*statp = EX_TEMPFAIL;
3515			if (lmap->ldap_res != NULL)
3516			{
3517				ldap_msgfree(lmap->ldap_res);
3518				lmap->ldap_res = NULL;
3519			}
3520			(void) ldap_abandon(lmap->ldap_ld, msgid);
3521			if (vp != NULL)
3522				free(vp);
3523			return NULL;
3524		}
3525		ldap_msgfree(lmap->ldap_res);
3526		lmap->ldap_res = NULL;
3527	}
3528
3529	/*
3530	**  If grabbing all results at once for MF_NOREWRITE and
3531	**  only want a single match, make sure that's all we have
3532	*/
3533
3534	if (ret == LDAP_RES_SEARCH_RESULT &&
3535	    bitset(MF_NOREWRITE|MF_SINGLEMATCH, map->map_mflags))
3536	{
3537		entries += ldap_count_entries(lmap->ldap_ld, lmap->ldap_res);
3538		if (entries > 1)
3539		{
3540			*statp = EX_NOTFOUND;
3541			if (lmap->ldap_res != NULL)
3542			{
3543				ldap_msgfree(lmap->ldap_res);
3544				lmap->ldap_res = NULL;
3545			}
3546			if (vp != NULL)
3547				free(vp);
3548			return NULL;
3549		}
3550		*statp = EX_OK;
3551	}
3552
3553	if (ret == 0)
3554		errno = ETIMEDOUT;
3555	else
3556		errno = ldapmap_geterrno(lmap->ldap_ld);
3557	if (errno != LDAP_SUCCESS)
3558	{
3559		/* Must be an error */
3560		if (ret != 0)
3561			errno += E_LDAPBASE;
3562		if (!bitset(MF_OPTIONAL, map->map_mflags))
3563		{
3564			if (bitset(MF_NODEFER, map->map_mflags))
3565				syserr("Error getting LDAP results in map %s",
3566				       map->map_mname);
3567			else
3568				syserr("421 4.0.0 Error getting LDAP results in map %s",
3569				       map->map_mname);
3570		}
3571		*statp = EX_TEMPFAIL;
3572		if (vp != NULL)
3573			free(vp);
3574		return NULL;
3575	}
3576
3577	/* Did we match anything? */
3578	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3579		return NULL;
3580
3581	/*
3582	**  If MF_NOREWRITE, we are special map which doesn't
3583	**  actually return a map value.  Instead, we don't free
3584	**  ldap_res and let the calling function process the LDAP
3585	**  results.  The caller should ldap_msgfree(lmap->ldap_res).
3586	*/
3587
3588	if (bitset(MF_NOREWRITE, map->map_mflags))
3589	{
3590		if (vp != NULL)
3591			free(vp);
3592		return "";
3593	}
3594
3595	if (*statp == EX_OK)
3596	{
3597		if (LogLevel > 9)
3598			sm_syslog(LOG_INFO, CurEnv->e_id,
3599				  "ldap %.100s => %s", name,
3600				  vp == NULL ? "<NULL>" : vp);
3601		if (bitset(MF_MATCHONLY, map->map_mflags))
3602			result = map_rewrite(map, name, strlen(name), NULL);
3603		else
3604		{
3605			/* vp != NULL according to test above */
3606			result = map_rewrite(map, vp, strlen(vp), av);
3607		}
3608		if (vp != NULL)
3609			free(vp);
3610	}
3611	return result;
3612}
3613
3614/*
3615**  LDAPMAP_FINDCONN -- find an LDAP connection to the server
3616**
3617**	Cache LDAP connections based on the host, port, bind DN,
3618**	secret, and PID so we don't have multiple connections open to
3619**	the same server for different maps.  Need a separate connection
3620**	per PID since a parent process may close the map before the
3621**	child is done with it.
3622**
3623**	Parameters:
3624**		lmap -- LDAP map information
3625**
3626**	Returns:
3627**		Symbol table entry for the LDAP connection.
3628**
3629*/
3630
3631static STAB *
3632ldapmap_findconn(lmap)
3633	LDAPMAP_STRUCT *lmap;
3634{
3635	int len;
3636	char *nbuf;
3637	STAB *s;
3638
3639	len = (lmap->ldap_host == NULL ? strlen("localhost") :
3640					 strlen(lmap->ldap_host)) + 1 + 8 + 1 +
3641		(lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) +
3642		1 +
3643		(lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) +
3644		8 + 1;
3645	nbuf = xalloc(len);
3646	snprintf(nbuf, len, "%s%c%d%c%s%c%s%d",
3647		 (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host),
3648		 CONDELSE,
3649		 lmap->ldap_port,
3650		 CONDELSE,
3651		 (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn),
3652		 CONDELSE,
3653		 (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret),
3654		 getpid());
3655	s = stab(nbuf, ST_LDAP, ST_ENTER);
3656	free(nbuf);
3657	return s;
3658}
3659/*
3660**  LDAPMAP_SETOPTS -- set LDAP options
3661**
3662**	Parameters:
3663**		ld -- LDAP session handle
3664**		lmap -- LDAP map information
3665**
3666**	Returns:
3667**		None.
3668**
3669*/
3670
3671static void
3672ldapmap_setopts(ld, lmap)
3673	LDAP *ld;
3674	LDAPMAP_STRUCT *lmap;
3675{
3676# if USE_LDAP_SET_OPTION
3677	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
3678	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
3679		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
3680	else
3681		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
3682	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
3683	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
3684# else /* USE_LDAP_SET_OPTION */
3685	/* From here on in we can use ldap internal timelimits */
3686	ld->ld_deref = lmap->ldap_deref;
3687	ld->ld_options = lmap->ldap_options;
3688	ld->ld_sizelimit = lmap->ldap_sizelimit;
3689	ld->ld_timelimit = lmap->ldap_timelimit;
3690# endif /* USE_LDAP_SET_OPTION */
3691}
3692/*
3693**  LDAPMAP_GETERRNO -- get ldap errno value
3694**
3695**	Parameters:
3696**		ld -- LDAP session handle
3697**
3698**	Returns:
3699**		LDAP errno.
3700**
3701*/
3702
3703static int
3704ldapmap_geterrno(ld)
3705	LDAP *ld;
3706{
3707	int err = LDAP_SUCCESS;
3708
3709# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
3710	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
3711# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
3712#  ifdef LDAP_OPT_SIZELIMIT
3713	err = ldap_get_lderrno(ld, NULL, NULL);
3714#  else /* LDAP_OPT_SIZELIMIT */
3715	err = ld->ld_errno;
3716
3717	/*
3718	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
3719	**  OpenLDAP 1.X's hack (see above)
3720	*/
3721
3722	ld->ld_errno = LDAP_SUCCESS;
3723#  endif /* LDAP_OPT_SIZELIMIT */
3724# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
3725	return err;
3726}
3727
3728/*
3729**  LDAPX_MAP_PARSEARGS -- print warning about use of ldapx map.
3730*/
3731
3732bool
3733ldapx_map_parseargs(map, args)
3734	MAP *map;
3735	char *args;
3736{
3737	printf("Warning: The \"ldapx\" map class is deprecated and will be removed in a future\n");
3738	printf("         version.  Use the \"ldap\" map class instead for map \"%s\".\n",
3739	       map->map_mname);
3740	return ldapmap_parseargs(map, args);
3741}
3742
3743/*
3744**  LDAPMAP_PARSEARGS -- parse ldap map definition args.
3745*/
3746
3747struct lamvalues LDAPAuthMethods[] =
3748{
3749	{	"none",		LDAP_AUTH_NONE		},
3750	{	"simple",	LDAP_AUTH_SIMPLE	},
3751# ifdef LDAP_AUTH_KRBV4
3752	{	"krbv4",	LDAP_AUTH_KRBV4		},
3753# endif /* LDAP_AUTH_KRBV4 */
3754	{	NULL,		0			}
3755};
3756
3757struct ladvalues LDAPAliasDereference[] =
3758{
3759	{	"never",	LDAP_DEREF_NEVER	},
3760	{	"always",	LDAP_DEREF_ALWAYS	},
3761	{	"search",	LDAP_DEREF_SEARCHING	},
3762	{	"find",		LDAP_DEREF_FINDING	},
3763	{	NULL,		0			}
3764};
3765
3766struct lssvalues LDAPSearchScope[] =
3767{
3768	{	"base",		LDAP_SCOPE_BASE		},
3769	{	"one",		LDAP_SCOPE_ONELEVEL	},
3770	{	"sub",		LDAP_SCOPE_SUBTREE	},
3771	{	NULL,		0			}
3772};
3773
3774bool
3775ldapmap_parseargs(map, args)
3776	MAP *map;
3777	char *args;
3778{
3779	bool secretread = TRUE;
3780	int i;
3781	register char *p = args;
3782	LDAPMAP_STRUCT *lmap;
3783	struct lamvalues *lam;
3784	struct ladvalues *lad;
3785	struct lssvalues *lss;
3786	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3787
3788	/* Get ldap struct pointer from map */
3789	lmap = (LDAPMAP_STRUCT *) map->map_db1;
3790
3791	/* Check if setting the initial LDAP defaults */
3792	if (lmap == NULL || lmap != LDAPDefaults)
3793	{
3794		/* We need to alloc an LDAPMAP_STRUCT struct */
3795		lmap = (LDAPMAP_STRUCT *) xalloc(sizeof *lmap);
3796		if (LDAPDefaults == NULL)
3797			ldapmap_clear(lmap);
3798		else
3799			STRUCTCOPY(*LDAPDefaults, *lmap);
3800	}
3801
3802	/* there is no check whether there is really an argument */
3803	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3804	map->map_spacesub = SpaceSub;	/* default value */
3805	for (;;)
3806	{
3807		while (isascii(*p) && isspace(*p))
3808			p++;
3809		if (*p != '-')
3810			break;
3811		switch (*++p)
3812		{
3813		  case 'N':
3814			map->map_mflags |= MF_INCLNULL;
3815			map->map_mflags &= ~MF_TRY0NULL;
3816			break;
3817
3818		  case 'O':
3819			map->map_mflags &= ~MF_TRY1NULL;
3820			break;
3821
3822		  case 'o':
3823			map->map_mflags |= MF_OPTIONAL;
3824			break;
3825
3826		  case 'f':
3827			map->map_mflags |= MF_NOFOLDCASE;
3828			break;
3829
3830		  case 'm':
3831			map->map_mflags |= MF_MATCHONLY;
3832			break;
3833
3834		  case 'A':
3835			map->map_mflags |= MF_APPEND;
3836			break;
3837
3838		  case 'q':
3839			map->map_mflags |= MF_KEEPQUOTES;
3840			break;
3841
3842		  case 'a':
3843			map->map_app = ++p;
3844			break;
3845
3846		  case 'T':
3847			map->map_tapp = ++p;
3848			break;
3849
3850		  case 't':
3851			map->map_mflags |= MF_NODEFER;
3852			break;
3853
3854		  case 'S':
3855			map->map_spacesub = *++p;
3856			break;
3857
3858		  case 'D':
3859			map->map_mflags |= MF_DEFER;
3860			break;
3861
3862		  case 'z':
3863			if (*++p != '\\')
3864				map->map_coldelim = *p;
3865			else
3866			{
3867				switch (*++p)
3868				{
3869				  case 'n':
3870					map->map_coldelim = '\n';
3871					break;
3872
3873				  case 't':
3874					map->map_coldelim = '\t';
3875					break;
3876
3877				  default:
3878					map->map_coldelim = '\\';
3879				}
3880			}
3881			break;
3882
3883			/* Start of ldapmap specific args */
3884		  case 'k':		/* search field */
3885			while (isascii(*++p) && isspace(*p))
3886				continue;
3887			lmap->ldap_filter = p;
3888			break;
3889
3890		  case 'v':		/* attr to return */
3891			while (isascii(*++p) && isspace(*p))
3892				continue;
3893			lmap->ldap_attr[0] = p;
3894			lmap->ldap_attr[1] = NULL;
3895			break;
3896
3897		  case '1':
3898			map->map_mflags |= MF_SINGLEMATCH;
3899			break;
3900
3901			/* args stolen from ldapsearch.c */
3902		  case 'R':		/* don't auto chase referrals */
3903# ifdef LDAP_REFERRALS
3904			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3905# else /* LDAP_REFERRALS */
3906			syserr("compile with -DLDAP_REFERRALS for referral support\n");
3907# endif /* LDAP_REFERRALS */
3908			break;
3909
3910		  case 'n':		/* retrieve attribute names only */
3911			lmap->ldap_attrsonly = LDAPMAP_TRUE;
3912			break;
3913
3914		  case 'r':		/* alias dereferencing */
3915			while (isascii(*++p) && isspace(*p))
3916				continue;
3917
3918			if (strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3919				p += 11;
3920
3921			for (lad = LDAPAliasDereference;
3922			     lad != NULL && lad->lad_name != NULL; lad++)
3923			{
3924				if (strncasecmp(p, lad->lad_name,
3925						strlen(lad->lad_name)) == 0)
3926					break;
3927			}
3928			if (lad->lad_name != NULL)
3929				lmap->ldap_deref = lad->lad_code;
3930			else
3931			{
3932				/* bad config line */
3933				if (!bitset(MCF_OPTFILE,
3934					    map->map_class->map_cflags))
3935				{
3936					char *ptr;
3937
3938					if ((ptr = strchr(p, ' ')) != NULL)
3939						*ptr = '\0';
3940					syserr("Deref must be [never|always|search|find] not %s in map %s",
3941						p, map->map_mname);
3942					if (ptr != NULL)
3943						*ptr = ' ';
3944					return FALSE;
3945				}
3946			}
3947			break;
3948
3949		  case 's':		/* search scope */
3950			while (isascii(*++p) && isspace(*p))
3951				continue;
3952
3953			if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3954				p += 11;
3955
3956			for (lss = LDAPSearchScope;
3957			     lss != NULL && lss->lss_name != NULL; lss++)
3958			{
3959				if (strncasecmp(p, lss->lss_name,
3960						strlen(lss->lss_name)) == 0)
3961					break;
3962			}
3963			if (lss->lss_name != NULL)
3964				lmap->ldap_scope = lss->lss_code;
3965			else
3966			{
3967				/* bad config line */
3968				if (!bitset(MCF_OPTFILE,
3969					    map->map_class->map_cflags))
3970				{
3971					char *ptr;
3972
3973					if ((ptr = strchr(p, ' ')) != NULL)
3974						*ptr = '\0';
3975					syserr("Scope must be [base|one|sub] not %s in map %s",
3976						p, map->map_mname);
3977					if (ptr != NULL)
3978						*ptr = ' ';
3979					return FALSE;
3980				}
3981			}
3982			break;
3983
3984		  case 'h':		/* ldap host */
3985			while (isascii(*++p) && isspace(*p))
3986				continue;
3987			lmap->ldap_host = p;
3988			break;
3989
3990		  case 'b':		/* search base */
3991			while (isascii(*++p) && isspace(*p))
3992				continue;
3993			lmap->ldap_base = p;
3994			break;
3995
3996		  case 'p':		/* ldap port */
3997			while (isascii(*++p) && isspace(*p))
3998				continue;
3999			lmap->ldap_port = atoi(p);
4000			break;
4001
4002		  case 'l':		/* time limit */
4003			while (isascii(*++p) && isspace(*p))
4004				continue;
4005			lmap->ldap_timelimit = atoi(p);
4006			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4007			break;
4008
4009		  case 'Z':
4010			while (isascii(*++p) && isspace(*p))
4011				continue;
4012			lmap->ldap_sizelimit = atoi(p);
4013			break;
4014
4015		  case 'd':		/* Dn to bind to server as */
4016			while (isascii(*++p) && isspace(*p))
4017				continue;
4018			lmap->ldap_binddn = p;
4019			break;
4020
4021		  case 'M':		/* Method for binding */
4022			while (isascii(*++p) && isspace(*p))
4023				continue;
4024
4025			if (strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4026				p += 10;
4027
4028			for (lam = LDAPAuthMethods;
4029			     lam != NULL && lam->lam_name != NULL; lam++)
4030			{
4031				if (strncasecmp(p, lam->lam_name,
4032						strlen(lam->lam_name)) == 0)
4033					break;
4034			}
4035			if (lam->lam_name != NULL)
4036				lmap->ldap_method = lam->lam_code;
4037			else
4038			{
4039				/* bad config line */
4040				if (!bitset(MCF_OPTFILE,
4041					    map->map_class->map_cflags))
4042				{
4043					char *ptr;
4044
4045					if ((ptr = strchr(p, ' ')) != NULL)
4046						*ptr = '\0';
4047					syserr("Method for binding must be [none|simple|krbv4] not %s in map %s",
4048						p, map->map_mname);
4049					if (ptr != NULL)
4050						*ptr = ' ';
4051					return FALSE;
4052				}
4053			}
4054
4055			break;
4056
4057			/*
4058			**  This is a string that is dependent on the
4059			**  method used defined above.
4060			*/
4061
4062		  case 'P':		/* Secret password for binding */
4063			 while (isascii(*++p) && isspace(*p))
4064				continue;
4065			lmap->ldap_secret = p;
4066			secretread = FALSE;
4067			break;
4068
4069		  default:
4070			syserr("Illegal option %c map %s", *p, map->map_mname);
4071			break;
4072		}
4073
4074		/* need to account for quoted strings here */
4075		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4076		{
4077			if (*p == '"')
4078			{
4079				while (*++p != '"' && *p != '\0')
4080					continue;
4081				if (*p != '\0')
4082					p++;
4083			}
4084			else
4085				p++;
4086		}
4087
4088		if (*p != '\0')
4089			*p++ = '\0';
4090	}
4091
4092	if (map->map_app != NULL)
4093		map->map_app = newstr(ldapmap_dequote(map->map_app));
4094	if (map->map_tapp != NULL)
4095		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4096
4097	/*
4098	**  We need to swallow up all the stuff into a struct
4099	**  and dump it into map->map_dbptr1
4100	*/
4101
4102	if (lmap->ldap_host != NULL &&
4103	    (LDAPDefaults == NULL ||
4104	     LDAPDefaults == lmap ||
4105	     LDAPDefaults->ldap_host != lmap->ldap_host))
4106		lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4107	map->map_domain = lmap->ldap_host;
4108
4109	if (lmap->ldap_binddn != NULL &&
4110	    (LDAPDefaults == NULL ||
4111	     LDAPDefaults == lmap ||
4112	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4113		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4114
4115	if (lmap->ldap_secret != NULL &&
4116	    (LDAPDefaults == NULL ||
4117	     LDAPDefaults == lmap ||
4118	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4119	{
4120		FILE *sfd;
4121		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4122
4123		if (DontLockReadFiles)
4124			sff |= SFF_NOLOCK;
4125
4126		/* need to use method to map secret to passwd string */
4127		switch (lmap->ldap_method)
4128		{
4129		  case LDAP_AUTH_NONE:
4130			/* Do nothing */
4131			break;
4132
4133		  case LDAP_AUTH_SIMPLE:
4134
4135			/*
4136			**  Secret is the name of a file with
4137			**  the first line as the password.
4138			*/
4139
4140			/* Already read in the secret? */
4141			if (secretread)
4142				break;
4143
4144			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4145					O_RDONLY, 0, sff);
4146			if (sfd == NULL)
4147			{
4148				syserr("LDAP map: cannot open secret %s",
4149				       ldapmap_dequote(lmap->ldap_secret));
4150				return FALSE;
4151			}
4152			lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD,
4153						   sfd, TimeOuts.to_fileopen,
4154						   "ldapmap_parseargs");
4155			(void) fclose(sfd);
4156			if (lmap->ldap_secret != NULL &&
4157			    strlen(m_tmp) > 0)
4158			{
4159				/* chomp newline */
4160				if (m_tmp[strlen(m_tmp) - 1] == '\n')
4161					m_tmp[strlen(m_tmp) - 1] = '\0';
4162
4163				lmap->ldap_secret = m_tmp;
4164			}
4165			break;
4166
4167# ifdef LDAP_AUTH_KRBV4
4168		  case LDAP_AUTH_KRBV4:
4169
4170			/*
4171			**  Secret is where the ticket file is
4172			**  stashed
4173			*/
4174
4175			snprintf(m_tmp, MAXPATHLEN + LDAPMAP_MAX_PASSWD,
4176				 "KRBTKFILE=%s",
4177				 ldapmap_dequote(lmap->ldap_secret));
4178			lmap->ldap_secret = m_tmp;
4179			break;
4180# endif /* LDAP_AUTH_KRBV4 */
4181
4182		  default:	       /* Should NEVER get here */
4183			syserr("LDAP map: Illegal value in lmap method");
4184			return FALSE;
4185			break;
4186		}
4187	}
4188
4189	if (lmap->ldap_secret != NULL &&
4190	    (LDAPDefaults == NULL ||
4191	     LDAPDefaults == lmap ||
4192	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
4193		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4194
4195	if (lmap->ldap_base != NULL &&
4196	    (LDAPDefaults == NULL ||
4197	     LDAPDefaults == lmap ||
4198	     LDAPDefaults->ldap_base != lmap->ldap_base))
4199		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4200
4201	/*
4202	**  Save the server from extra work.  If request is for a single
4203	**  match, tell the server to only return enough records to
4204	**  determine if there is a single match or not.  This can not
4205	**  be one since the server would only return one and we wouldn't
4206	**  know if there were others available.
4207	*/
4208
4209	if (bitset(MF_SINGLEMATCH, map->map_mflags))
4210		lmap->ldap_sizelimit = 2;
4211
4212	/* If setting defaults, don't process ldap_filter and ldap_attr */
4213	if (lmap == LDAPDefaults)
4214		return TRUE;
4215
4216	if (lmap->ldap_filter != NULL)
4217		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4218	else
4219	{
4220		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4221		{
4222			syserr("No filter given in map %s", map->map_mname);
4223			return FALSE;
4224		}
4225	}
4226
4227	if (lmap->ldap_attr[0] != NULL)
4228	{
4229		i = 0;
4230		p = ldapmap_dequote(lmap->ldap_attr[0]);
4231		lmap->ldap_attr[0] = NULL;
4232
4233		while (p != NULL)
4234		{
4235			char *v;
4236
4237			while (isascii(*p) && isspace(*p))
4238				p++;
4239			if (*p == '\0')
4240				break;
4241			v = p;
4242			p = strchr(v, ',');
4243			if (p != NULL)
4244				*p++ = '\0';
4245
4246			if (i >= LDAPMAP_MAX_ATTR)
4247			{
4248				syserr("Too many return attributes in %s (max %d)",
4249				       map->map_mname, LDAPMAP_MAX_ATTR);
4250				return FALSE;
4251			}
4252			if (*v != '\0')
4253				lmap->ldap_attr[i++] = newstr(v);
4254		}
4255		lmap->ldap_attr[i] = NULL;
4256	}
4257
4258	map->map_db1 = (ARBPTR_T) lmap;
4259	return TRUE;
4260}
4261
4262/*
4263**  LDAPMAP_CLEAR -- set default values for LDAPMAP_STRUCT
4264**
4265**	Parameters:
4266**		lmap -- pointer to LDAPMAP_STRUCT to clear
4267**
4268**	Returns:
4269**		None.
4270**
4271*/
4272
4273static void
4274ldapmap_clear(lmap)
4275	LDAPMAP_STRUCT *lmap;
4276{
4277	lmap->ldap_host = NULL;
4278	lmap->ldap_port = LDAP_PORT;
4279	lmap->ldap_deref = LDAP_DEREF_NEVER;
4280	lmap->ldap_timelimit = LDAP_NO_LIMIT;
4281	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
4282# ifdef LDAP_REFERRALS
4283	lmap->ldap_options = LDAP_OPT_REFERRALS;
4284# else /* LDAP_REFERRALS */
4285	lmap->ldap_options = 0;
4286# endif /* LDAP_REFERRALS */
4287	lmap->ldap_binddn = NULL;
4288	lmap->ldap_secret = NULL;
4289	lmap->ldap_method = LDAP_AUTH_SIMPLE;
4290	lmap->ldap_base = NULL;
4291	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
4292	lmap->ldap_attrsonly = LDAPMAP_FALSE;
4293	lmap->ldap_timeout.tv_sec = 0;
4294	lmap->ldap_timeout.tv_usec = 0;
4295	lmap->ldap_ld = NULL;
4296	lmap->ldap_filter = NULL;
4297	lmap->ldap_attr[0] = NULL;
4298	lmap->ldap_res = NULL;
4299}
4300/*
4301**  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4302**
4303**	Parameters:
4304**		spec -- map argument string from LDAPDefaults option
4305**
4306**	Returns:
4307**		None.
4308**
4309*/
4310
4311void
4312ldapmap_set_defaults(spec)
4313	char *spec;
4314{
4315	MAP map;
4316
4317	/* Allocate and set the default values */
4318	if (LDAPDefaults == NULL)
4319		LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4320	ldapmap_clear(LDAPDefaults);
4321
4322	memset(&map, '\0', sizeof map);
4323	map.map_db1 = (ARBPTR_T) LDAPDefaults;
4324
4325	(void) ldapmap_parseargs(&map, spec);
4326
4327	/* These should never be set in LDAPDefaults */
4328	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4329	    map.map_spacesub != SpaceSub ||
4330	    map.map_app != NULL ||
4331	    map.map_tapp != NULL)
4332	{
4333		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4334		if (map.map_app != NULL)
4335		{
4336			free(map.map_app);
4337			map.map_app = NULL;
4338		}
4339		if (map.map_tapp != NULL)
4340		{
4341			free(map.map_tapp);
4342			map.map_tapp = NULL;
4343		}
4344	}
4345
4346	if (LDAPDefaults->ldap_filter != NULL)
4347	{
4348		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4349		/* don't free, it isn't malloc'ed in parseargs */
4350		LDAPDefaults->ldap_filter = NULL;
4351	}
4352
4353	if (LDAPDefaults->ldap_attr[0] != NULL)
4354	{
4355		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4356		/* don't free, they aren't malloc'ed in parseargs */
4357		LDAPDefaults->ldap_attr[0] = NULL;
4358	}
4359}
4360#endif /* LDAPMAP */
4361/*
4362**  PH map
4363*/
4364
4365#ifdef PH_MAP
4366
4367/*
4368**  Support for the CCSO Nameserver (ph/qi).
4369**  This code is intended to replace the so-called "ph mailer".
4370**  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
4371*/
4372
4373# include <qiapi.h>
4374# include <qicode.h>
4375
4376/*
4377**  PH_MAP_PARSEARGS -- parse ph map definition args.
4378*/
4379
4380bool
4381ph_map_parseargs(map, args)
4382	MAP *map;
4383	char *args;
4384{
4385	int i;
4386	register int done;
4387	PH_MAP_STRUCT *pmap = NULL;
4388	register char *p = args;
4389
4390	pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4391
4392	/* defaults */
4393	pmap->ph_servers = NULL;
4394	pmap->ph_field_list = NULL;
4395	pmap->ph_to_server = NULL;
4396	pmap->ph_from_server = NULL;
4397	pmap->ph_sockfd = -1;
4398	pmap->ph_timeout = 0;
4399
4400	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4401	for (;;)
4402	{
4403		while (isascii(*p) && isspace(*p))
4404			p++;
4405		if (*p != '-')
4406			break;
4407		switch (*++p)
4408		{
4409		  case 'N':
4410			map->map_mflags |= MF_INCLNULL;
4411			map->map_mflags &= ~MF_TRY0NULL;
4412			break;
4413
4414		  case 'O':
4415			map->map_mflags &= ~MF_TRY1NULL;
4416			break;
4417
4418		  case 'o':
4419			map->map_mflags |= MF_OPTIONAL;
4420			break;
4421
4422		  case 'f':
4423			map->map_mflags |= MF_NOFOLDCASE;
4424			break;
4425
4426		  case 'm':
4427			map->map_mflags |= MF_MATCHONLY;
4428			break;
4429
4430		  case 'A':
4431			map->map_mflags |= MF_APPEND;
4432			break;
4433
4434		  case 'q':
4435			map->map_mflags |= MF_KEEPQUOTES;
4436			break;
4437
4438		  case 't':
4439			map->map_mflags |= MF_NODEFER;
4440			break;
4441
4442		  case 'a':
4443			map->map_app = ++p;
4444			break;
4445
4446		  case 'T':
4447			map->map_tapp = ++p;
4448			break;
4449
4450#if _FFR_PHMAP_TIMEOUT
4451		  case 'l':
4452			while (isascii(*++p) && isspace(*p))
4453				continue;
4454			pmap->ph_timeout = atoi(p);
4455			break;
4456#endif /* _FFR_PHMAP_TIMEOUT */
4457
4458		  case 'S':
4459			map->map_spacesub = *++p;
4460			break;
4461
4462		  case 'D':
4463			map->map_mflags |= MF_DEFER;
4464			break;
4465
4466		  case 'h':		/* PH server list */
4467			while (isascii(*++p) && isspace(*p))
4468				continue;
4469			pmap->ph_servers = p;
4470			break;
4471
4472		  case 'v':		/* fields to search for */
4473			while (isascii(*++p) && isspace(*p))
4474				continue;
4475			pmap->ph_field_list = p;
4476			break;
4477
4478		  default:
4479			syserr("ph_map_parseargs: unknown option -%c\n", *p);
4480		}
4481
4482		/* try to account for quoted strings */
4483		done = isascii(*p) && isspace(*p);
4484		while (*p != '\0' && !done)
4485		{
4486			if (*p == '"')
4487			{
4488				while (*++p != '"' && *p != '\0')
4489					continue;
4490				if (*p != '\0')
4491					p++;
4492			}
4493			else
4494				p++;
4495			done = isascii(*p) && isspace(*p);
4496		}
4497
4498		if (*p != '\0')
4499			*p++ = '\0';
4500	}
4501
4502	if (map->map_app != NULL)
4503		map->map_app = newstr(ph_map_dequote(map->map_app));
4504	if (map->map_tapp != NULL)
4505		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4506
4507	if (pmap->ph_field_list != NULL)
4508		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4509	else
4510		pmap->ph_field_list = DEFAULT_PH_MAP_FIELDS;
4511
4512	if (pmap->ph_servers != NULL)
4513		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4514	else
4515	{
4516		syserr("ph_map_parseargs: -h flag is required");
4517		return FALSE;
4518	}
4519
4520	map->map_db1 = (ARBPTR_T) pmap;
4521	return TRUE;
4522}
4523
4524#if _FFR_PHMAP_TIMEOUT
4525/*
4526**  PH_MAP_CLOSE -- close the connection to the ph server
4527*/
4528
4529static void
4530ph_map_safeclose(map)
4531	MAP *map;
4532{
4533	int save_errno = errno;
4534	PH_MAP_STRUCT *pmap;
4535
4536	pmap = (PH_MAP_STRUCT *)map->map_db1;
4537
4538	if (pmap->ph_sockfd != -1)
4539	{
4540		(void) close(pmap->ph_sockfd);
4541		pmap->ph_sockfd = -1;
4542	}
4543	if (pmap->ph_from_server != NULL)
4544	{
4545		(void) fclose(pmap->ph_from_server);
4546		pmap->ph_from_server = NULL;
4547	}
4548	if (pmap->ph_to_server != NULL)
4549	{
4550		(void) fclose(pmap->ph_to_server);
4551		pmap->ph_to_server = NULL;
4552	}
4553	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4554	errno = save_errno;
4555}
4556
4557void
4558ph_map_close(map)
4559	MAP *map;
4560{
4561	PH_MAP_STRUCT *pmap;
4562
4563	pmap = (PH_MAP_STRUCT *)map->map_db1;
4564	(void) fprintf(pmap->ph_to_server, "quit\n");
4565	(void) fflush(pmap->ph_to_server);
4566	ph_map_safeclose(map);
4567}
4568
4569static jmp_buf  PHTimeout;
4570
4571/* ARGSUSED */
4572static void
4573ph_timeout_func(sig_no)
4574	int sig_no;
4575{
4576	longjmp(PHTimeout, 1);
4577}
4578#else /* _FFR_PHMAP_TIMEOUT */
4579/*
4580**  PH_MAP_CLOSE -- close the connection to the ph server
4581*/
4582
4583void
4584ph_map_close(map)
4585	MAP *map;
4586{
4587	PH_MAP_STRUCT *pmap;
4588
4589	pmap = (PH_MAP_STRUCT *)map->map_db1;
4590	CloseQi(pmap->ph_to_server, pmap->ph_from_server);
4591	pmap->ph_to_server = NULL;
4592	pmap->ph_from_server = NULL;
4593}
4594#endif /* _FFR_PHMAP_TIMEOUT */
4595
4596/*
4597**  PH_MAP_OPEN -- sub for opening PH map
4598*/
4599bool
4600ph_map_open(map, mode)
4601	MAP *map;
4602	int mode;
4603{
4604#if !_FFR_PHMAP_TIMEOUT
4605	int save_errno = 0;
4606#endif /* !_FFR_PHMAP_TIMEOUT */
4607	int j;
4608	char *hostlist, *tmp;
4609	QIR *server_data = NULL;
4610	PH_MAP_STRUCT *pmap;
4611#if _FFR_PHMAP_TIMEOUT
4612	register EVENT *ev = NULL;
4613#endif /* _FFR_PHMAP_TIMEOUT */
4614
4615	if (tTd(38, 2))
4616		dprintf("ph_map_open(%s)\n", map->map_mname);
4617
4618	mode &= O_ACCMODE;
4619	if (mode != O_RDONLY)
4620	{
4621		/* issue a pseudo-error message */
4622# ifdef ENOSYS
4623		errno = ENOSYS;
4624# else /* ENOSYS */
4625#  ifdef EFTYPE
4626		errno = EFTYPE;
4627#  else /* EFTYPE */
4628		errno = ENXIO;
4629#  endif /* EFTYPE */
4630# endif /* ENOSYS */
4631		return FALSE;
4632	}
4633
4634	if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4635	    bitset(MF_DEFER, map->map_mflags))
4636	{
4637		if (tTd(9, 1))
4638			dprintf("ph_map_open(%s) => DEFERRED\n",
4639				map->map_mname);
4640
4641		/*
4642		** Unset MF_DEFER here so that map_lookup() returns
4643		** a temporary failure using the bogus map and
4644		** map->map_tapp instead of the default permanent error.
4645		*/
4646
4647		map->map_mflags &= ~MF_DEFER;
4648		return FALSE;
4649	}
4650
4651	pmap = (PH_MAP_STRUCT *)map->map_db1;
4652
4653	hostlist = newstr(pmap->ph_servers);
4654	tmp = strtok(hostlist, " ");
4655	do
4656	{
4657#if _FFR_PHMAP_TIMEOUT
4658		if (pmap->ph_timeout != 0)
4659		{
4660			if (setjmp(PHTimeout) != 0)
4661			{
4662				ev = NULL;
4663				if (LogLevel > 1)
4664					sm_syslog(LOG_NOTICE, CurEnv->e_id,
4665						  "timeout connecting to PH server %.100s",
4666						  tmp);
4667# ifdef ETIMEDOUT
4668				errno = ETIMEDOUT;
4669# else /* ETIMEDOUT */
4670				errno = EAGAIN;
4671# endif /* ETIMEDOUT */
4672				goto ph_map_open_abort;
4673			}
4674			ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
4675		}
4676		if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) &&
4677		    !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server),
4678				&(pmap->ph_from_server)) &&
4679		    fprintf(pmap->ph_to_server, "id sendmail+phmap\n") >= 0 &&
4680		    fflush(pmap->ph_to_server) == 0 &&
4681		    (server_data = ReadQi(pmap->ph_from_server, &j)) != NULL &&
4682		    server_data->code == 200)
4683		{
4684			if (ev != NULL)
4685				clrevent(ev);
4686			FreeQIR(server_data);
4687#else /* _FFR_PHMAP_TIMEOUT */
4688		if (OpenQi(tmp, &(pmap->ph_to_server),
4689			   &(pmap->ph_from_server)) >= 0)
4690		{
4691			if (fprintf(pmap->ph_to_server,
4692				    "id sendmail+phmap\n") < 0 ||
4693			    fflush(pmap->ph_to_server) < 0 ||
4694			    (server_data = ReadQi(pmap->ph_from_server,
4695						  &j)) == NULL ||
4696			    server_data->code != 200)
4697			{
4698				save_errno = errno;
4699				CloseQi(pmap->ph_to_server,
4700					pmap->ph_from_server);
4701				continue;
4702			}
4703			if (server_data != NULL)
4704				FreeQIR(server_data);
4705#endif /* _FFR_PHMAP_TIMEOUT */
4706			free(hostlist);
4707			return TRUE;
4708		}
4709#if _FFR_PHMAP_TIMEOUT
4710  ph_map_open_abort:
4711		if (ev != NULL)
4712			clrevent(ev);
4713		ph_map_safeclose(map);
4714		if (server_data != NULL)
4715		{
4716			FreeQIR(server_data);
4717			server_data = NULL;
4718		}
4719#else /* _FFR_PHMAP_TIMEOUT */
4720		save_errno = errno;
4721#endif /* _FFR_PHMAP_TIMEOUT */
4722	} while (tmp = strtok(NULL, " "));
4723
4724#if !_FFR_PHMAP_TIMEOUT
4725	errno = save_errno;
4726#endif /* !_FFR_PHMAP_TIMEOUT */
4727	if (bitset(MF_NODEFER, map->map_mflags))
4728	{
4729		if (errno == 0)
4730			errno = EAGAIN;
4731		syserr("ph_map_open: %s: cannot connect to PH server",
4732		       map->map_mname);
4733	}
4734	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
4735		sm_syslog(LOG_NOTICE, CurEnv->e_id,
4736			  "ph_map_open: %s: cannot connect to PH server",
4737			  map->map_mname);
4738	free(hostlist);
4739	return FALSE;
4740}
4741
4742/*
4743**  PH_MAP_LOOKUP -- look up key from ph server
4744*/
4745
4746#if _FFR_PHMAP_TIMEOUT
4747# define MAX_PH_FIELDS	20
4748#endif /* _FFR_PHMAP_TIMEOUT */
4749
4750char *
4751ph_map_lookup(map, key, args, pstat)
4752	MAP *map;
4753	char *key;
4754	char **args;
4755	int *pstat;
4756{
4757	int j;
4758	size_t sz;
4759	char *tmp, *tmp2;
4760	char *message = NULL, *field = NULL, *fmtkey;
4761	QIR *server_data = NULL;
4762	QIR *qirp;
4763	char keybuf[MAXKEY + 1], fieldbuf[101];
4764#if _FFR_PHMAP_TIMEOUT
4765	QIR *hold_data[MAX_PH_FIELDS];
4766	int hold_data_idx = 0;
4767	register EVENT *ev = NULL;
4768#endif /* _FFR_PHMAP_TIMEOUT */
4769	PH_MAP_STRUCT *pmap;
4770
4771	pmap = (PH_MAP_STRUCT *)map->map_db1;
4772
4773	*pstat = EX_OK;
4774
4775#if _FFR_PHMAP_TIMEOUT
4776	if (pmap->ph_timeout != 0)
4777	{
4778		if (setjmp(PHTimeout) != 0)
4779		{
4780			ev = NULL;
4781			if (LogLevel > 1)
4782				sm_syslog(LOG_NOTICE, CurEnv->e_id,
4783					  "timeout during PH lookup of %.100s",
4784					  key);
4785# ifdef ETIMEDOUT
4786			errno = ETIMEDOUT;
4787# else /* ETIMEDOUT */
4788			errno = 0;
4789# endif /* ETIMEDOUT */
4790			*pstat = EX_TEMPFAIL;
4791			goto ph_map_lookup_abort;
4792		}
4793		ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
4794	}
4795
4796#endif /* _FFR_PHMAP_TIMEOUT */
4797	/* check all relevant fields */
4798	tmp = pmap->ph_field_list;
4799	do
4800	{
4801#if _FFR_PHMAP_TIMEOUT
4802		server_data = NULL;
4803#endif /* _FFR_PHMAP_TIMEOUT */
4804		while (isascii(*tmp) && isspace(*tmp))
4805			tmp++;
4806		if (*tmp == '\0')
4807			break;
4808		sz = strcspn(tmp, " ") + 1;
4809		if (sz > sizeof fieldbuf)
4810			sz = sizeof fieldbuf;
4811		(void) strlcpy(fieldbuf, tmp, sz);
4812		field = fieldbuf;
4813		tmp += sz;
4814
4815		(void) strlcpy(keybuf, key, sizeof keybuf);
4816		fmtkey = keybuf;
4817		if (strcmp(field, "alias") == 0)
4818		{
4819			/*
4820			**  for alias lookups, replace any punctuation
4821			**  characters with '-'
4822			*/
4823
4824			for (tmp2 = fmtkey; *tmp2 !=  '\0'; tmp2++)
4825			{
4826				if (isascii(*tmp2) && ispunct(*tmp2))
4827					*tmp2 = '-';
4828			}
4829			tmp2 = field;
4830		}
4831		else if (strcmp(field,"spacedname") == 0)
4832		{
4833			/*
4834			**  for "spaced" name lookups, replace any
4835			**  punctuation characters with a space
4836			*/
4837
4838			for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++)
4839			{
4840				if (isascii(*tmp2) && ispunct(*tmp2) &&
4841				    *tmp2 != '*')
4842					*tmp2 = ' ';
4843			}
4844			tmp2 = &(field[6]);
4845		}
4846		else
4847			tmp2 = field;
4848
4849		if (LogLevel > 9)
4850			sm_syslog(LOG_NOTICE, CurEnv->e_id,
4851				  "ph_map_lookup: query %s=\"%s\" return email",
4852				  tmp2, fmtkey);
4853		if (tTd(38, 20))
4854			dprintf("ph_map_lookup: query %s=\"%s\" return email\n",
4855				tmp2, fmtkey);
4856
4857		j = 0;
4858
4859		if (fprintf(pmap->ph_to_server, "query %s=%s return email\n",
4860			    tmp2, fmtkey) < 0)
4861			message = "qi query command failed";
4862		else if (fflush(pmap->ph_to_server) < 0)
4863			message = "qi fflush failed";
4864		else if ((server_data = ReadQi(pmap->ph_from_server,
4865					       &j)) == NULL)
4866			message = "ReadQi() returned NULL";
4867
4868#if _FFR_PHMAP_TIMEOUT
4869		if ((hold_data[hold_data_idx] = server_data) != NULL)
4870		{
4871			/* save pointer for later free() */
4872			hold_data_idx++;
4873		}
4874#endif /* _FFR_PHMAP_TIMEOUT */
4875
4876		if (server_data == NULL ||
4877		    (server_data->code >= 400 &&
4878		     server_data->code < 500))
4879		{
4880			/* temporary failure */
4881			*pstat = EX_TEMPFAIL;
4882#if _FFR_PHMAP_TIMEOUT
4883			break;
4884#else /* _FFR_PHMAP_TIMEOUT */
4885			if (server_data != NULL)
4886			{
4887				FreeQIR(server_data);
4888				server_data = NULL;
4889			}
4890			return NULL;
4891#endif /* _FFR_PHMAP_TIMEOUT */
4892		}
4893
4894		/*
4895		**  if we found a single match, break out.
4896		**  otherwise, try the next field.
4897		*/
4898
4899		if (j == 1)
4900			break;
4901
4902		/*
4903		**  check for a single response which is an error:
4904		**  ReadQi() doesn't set j on error responses,
4905		**  but we should stop here instead of moving on if
4906		**  it happens (e.g., alias found but email field empty)
4907		*/
4908
4909		for (qirp = server_data;
4910		     qirp != NULL && qirp->code < 0;
4911		     qirp++)
4912		{
4913			if (tTd(38, 20))
4914				dprintf("ph_map_lookup: QIR: %d:%d:%d:%s\n",
4915					qirp->code, qirp->subcode, qirp->field,
4916					(qirp->message ? qirp->message
4917					 : "[NULL]"));
4918			if (qirp->code <= -500)
4919			{
4920				j = 0;
4921				goto ph_map_lookup_abort;
4922			}
4923		}
4924
4925#if _FFR_PHMAP_TIMEOUT
4926	} while (*tmp != '\0' && hold_data_idx < MAX_PH_FIELDS);
4927#else /* _FFR_PHMAP_TIMEOUT */
4928	} while (*tmp != '\0');
4929#endif /* _FFR_PHMAP_TIMEOUT */
4930
4931  ph_map_lookup_abort:
4932#if _FFR_PHMAP_TIMEOUT
4933	if (ev != NULL)
4934		clrevent(ev);
4935
4936	/*
4937	**  Return EX_TEMPFAIL if the timer popped
4938	**  or we got a temporary PH error
4939	*/
4940
4941	if (*pstat == EX_TEMPFAIL)
4942		ph_map_safeclose(map);
4943
4944	/* if we didn't find a single match, bail out */
4945	if (*pstat == EX_OK && j != 1)
4946		*pstat = EX_UNAVAILABLE;
4947
4948	if (*pstat == EX_OK)
4949	{
4950		/*
4951		** skip leading whitespace and chop at first address
4952		*/
4953
4954		for (tmp = server_data->message;
4955		     isascii(*tmp) && isspace(*tmp);
4956		     tmp++)
4957			continue;
4958
4959		for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
4960		{
4961			if (isascii(*tmp2) && isspace(*tmp2))
4962			{
4963				*tmp2 = '\0';
4964				break;
4965			}
4966		}
4967
4968		if (tTd(38,20))
4969			dprintf("ph_map_lookup: %s => %s\n", key, tmp);
4970
4971		if (bitset(MF_MATCHONLY, map->map_mflags))
4972			message = map_rewrite(map, key, strlen(key), NULL);
4973		else
4974			message = map_rewrite(map, tmp, strlen(tmp), args);
4975	}
4976
4977	/*
4978	**  Deferred free() of returned server_data values
4979	**  the deferral is to avoid the risk of a free() being
4980	**  interrupted by the event timer.  By now the timeout event
4981	**  has been cleared and none of the data is still in use.
4982	*/
4983
4984	while (--hold_data_idx >= 0)
4985	{
4986		if (hold_data[hold_data_idx] != NULL)
4987			FreeQIR(hold_data[hold_data_idx]);
4988	}
4989
4990	if (*pstat == EX_OK)
4991		return message;
4992
4993	return NULL;
4994#else /* _FFR_PHMAP_TIMEOUT */
4995	/* if we didn't find a single match, bail out */
4996	if (j != 1)
4997	{
4998		*pstat = EX_UNAVAILABLE;
4999		if (server_data != NULL)
5000		{
5001			FreeQIR(server_data);
5002			server_data = NULL;
5003		}
5004		return NULL;
5005	}
5006
5007	/*
5008	** skip leading whitespace and chop at first address
5009	*/
5010
5011	for (tmp = server_data->message;
5012	     isascii(*tmp) && isspace(*tmp);
5013	     tmp++)
5014		continue;
5015
5016	for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
5017	{
5018		if (isascii(*tmp2) && isspace(*tmp2))
5019		{
5020			*tmp2 = '\0';
5021			break;
5022		}
5023	}
5024
5025	if (tTd(38,20))
5026		dprintf("ph_map_lookup: %s => %s\n", key, tmp);
5027
5028	if (bitset(MF_MATCHONLY, map->map_mflags))
5029		message = map_rewrite(map, key, strlen(key), NULL);
5030	else
5031		message = map_rewrite(map, tmp, strlen(tmp), args);
5032	if (server_data != NULL)
5033	{
5034		FreeQIR(server_data);
5035		server_data = NULL;
5036	}
5037	return message;
5038#endif /* _FFR_PHMAP_TIMEOUT */
5039}
5040#endif /* PH_MAP */
5041/*
5042**  syslog map
5043*/
5044
5045#define map_prio	map_lockfd	/* overload field */
5046
5047/*
5048**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5049*/
5050
5051bool
5052syslog_map_parseargs(map, args)
5053	MAP *map;
5054	char *args;
5055{
5056	char *p = args;
5057	char *priority = NULL;
5058
5059	/* there is no check whether there is really an argument */
5060	while (*p != '\0')
5061	{
5062		while (isascii(*p) && isspace(*p))
5063			p++;
5064		if (*p != '-')
5065			break;
5066		++p;
5067		if (*p == 'D')
5068		{
5069			map->map_mflags |= MF_DEFER;
5070			++p;
5071		}
5072		else if (*p == 'S')
5073		{
5074			map->map_spacesub = *++p;
5075			if (*p != '\0')
5076				p++;
5077		}
5078		else if (*p == 'L')
5079		{
5080			while (*++p != '\0' && isascii(*p) && isspace(*p))
5081				continue;
5082			if (*p == '\0')
5083				break;
5084			priority = p;
5085			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5086				p++;
5087			if (*p != '\0')
5088				*p++ = '\0';
5089		}
5090		else
5091		{
5092			syserr("Illegal option %c map syslog", *p);
5093			++p;
5094		}
5095	}
5096
5097	if (priority == NULL)
5098		map->map_prio = LOG_INFO;
5099	else
5100	{
5101		if (strncasecmp("LOG_", priority, 4) == 0)
5102			priority += 4;
5103
5104#ifdef LOG_EMERG
5105		if (strcasecmp("EMERG", priority) == 0)
5106			map->map_prio = LOG_EMERG;
5107		else
5108#endif /* LOG_EMERG */
5109#ifdef LOG_ALERT
5110		if (strcasecmp("ALERT", priority) == 0)
5111			map->map_prio = LOG_ALERT;
5112		else
5113#endif /* LOG_ALERT */
5114#ifdef LOG_CRIT
5115		if (strcasecmp("CRIT", priority) == 0)
5116			map->map_prio = LOG_CRIT;
5117		else
5118#endif /* LOG_CRIT */
5119#ifdef LOG_ERR
5120		if (strcasecmp("ERR", priority) == 0)
5121			map->map_prio = LOG_ERR;
5122		else
5123#endif /* LOG_ERR */
5124#ifdef LOG_WARNING
5125		if (strcasecmp("WARNING", priority) == 0)
5126			map->map_prio = LOG_WARNING;
5127		else
5128#endif /* LOG_WARNING */
5129#ifdef LOG_NOTICE
5130		if (strcasecmp("NOTICE", priority) == 0)
5131			map->map_prio = LOG_NOTICE;
5132		else
5133#endif /* LOG_NOTICE */
5134#ifdef LOG_INFO
5135		if (strcasecmp("INFO", priority) == 0)
5136			map->map_prio = LOG_INFO;
5137		else
5138#endif /* LOG_INFO */
5139#ifdef LOG_DEBUG
5140		if (strcasecmp("DEBUG", priority) == 0)
5141			map->map_prio = LOG_DEBUG;
5142		else
5143#endif /* LOG_DEBUG */
5144		{
5145			syserr("syslog_map_parseargs: Unknown priority %s\n",
5146			       priority);
5147			return FALSE;
5148		}
5149	}
5150	return TRUE;
5151}
5152
5153/*
5154**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
5155*/
5156
5157char *
5158syslog_map_lookup(map, string, args, statp)
5159	MAP *map;
5160	char *string;
5161	char **args;
5162	int *statp;
5163{
5164	char *ptr = map_rewrite(map, string, strlen(string), args);
5165
5166	if (ptr != NULL)
5167	{
5168		if (tTd(38, 20))
5169			dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5170				map->map_mname, map->map_prio, ptr);
5171
5172		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5173	}
5174
5175	*statp = EX_OK;
5176	return "";
5177}
5178
5179/*
5180**  HESIOD Modules
5181*/
5182
5183#ifdef HESIOD
5184
5185bool
5186hes_map_open(map, mode)
5187	MAP *map;
5188	int mode;
5189{
5190	if (tTd(38, 2))
5191		dprintf("hes_map_open(%s, %s, %d)\n",
5192			map->map_mname, map->map_file, mode);
5193
5194	if (mode != O_RDONLY)
5195	{
5196		/* issue a pseudo-error message */
5197# ifdef ENOSYS
5198		errno = ENOSYS;
5199# else /* ENOSYS */
5200#  ifdef EFTYPE
5201		errno = EFTYPE;
5202#  else /* EFTYPE */
5203		errno = ENXIO;
5204#  endif /* EFTYPE */
5205# endif /* ENOSYS */
5206		return FALSE;
5207	}
5208
5209# ifdef HESIOD_INIT
5210	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5211		return TRUE;
5212
5213	if (!bitset(MF_OPTIONAL, map->map_mflags))
5214		syserr("421 4.0.0 cannot initialize Hesiod map (%s)",
5215			errstring(errno));
5216	return FALSE;
5217# else /* HESIOD_INIT */
5218	if (hes_error() == HES_ER_UNINIT)
5219		hes_init();
5220	switch (hes_error())
5221	{
5222	  case HES_ER_OK:
5223	  case HES_ER_NOTFOUND:
5224		return TRUE;
5225	}
5226
5227	if (!bitset(MF_OPTIONAL, map->map_mflags))
5228		syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error());
5229
5230	return FALSE;
5231# endif /* HESIOD_INIT */
5232}
5233
5234char *
5235hes_map_lookup(map, name, av, statp)
5236	MAP *map;
5237	char *name;
5238	char **av;
5239	int *statp;
5240{
5241	char **hp;
5242
5243	if (tTd(38, 20))
5244		dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5245
5246	if (name[0] == '\\')
5247	{
5248		char *np;
5249		int nl;
5250		char nbuf[MAXNAME];
5251
5252		nl = strlen(name);
5253		if (nl < sizeof nbuf - 1)
5254			np = nbuf;
5255		else
5256			np = xalloc(strlen(name) + 2);
5257		np[0] = '\\';
5258		(void) strlcpy(&np[1], name, (sizeof nbuf) - 1);
5259# ifdef HESIOD_INIT
5260		hp = hesiod_resolve(HesiodContext, np, map->map_file);
5261# else /* HESIOD_INIT */
5262		hp = hes_resolve(np, map->map_file);
5263# endif /* HESIOD_INIT */
5264		if (np != nbuf)
5265			free(np);
5266	}
5267	else
5268	{
5269# ifdef HESIOD_INIT
5270		hp = hesiod_resolve(HesiodContext, name, map->map_file);
5271# else /* HESIOD_INIT */
5272		hp = hes_resolve(name, map->map_file);
5273# endif /* HESIOD_INIT */
5274	}
5275# ifdef HESIOD_INIT
5276	if (hp == NULL)
5277		return NULL;
5278	if (*hp == NULL)
5279	{
5280		hesiod_free_list(HesiodContext, hp);
5281		switch (errno)
5282		{
5283		  case ENOENT:
5284			  *statp = EX_NOTFOUND;
5285			  break;
5286		  case ECONNREFUSED:
5287		  case EMSGSIZE:
5288			  *statp = EX_TEMPFAIL;
5289			  break;
5290		  case ENOMEM:
5291		  default:
5292			  *statp = EX_UNAVAILABLE;
5293			  break;
5294		}
5295		return NULL;
5296	}
5297# else /* HESIOD_INIT */
5298	if (hp == NULL || hp[0] == NULL)
5299	{
5300		switch (hes_error())
5301		{
5302		  case HES_ER_OK:
5303			*statp = EX_OK;
5304			break;
5305
5306		  case HES_ER_NOTFOUND:
5307			*statp = EX_NOTFOUND;
5308			break;
5309
5310		  case HES_ER_CONFIG:
5311			*statp = EX_UNAVAILABLE;
5312			break;
5313
5314		  case HES_ER_NET:
5315			*statp = EX_TEMPFAIL;
5316			break;
5317		}
5318		return NULL;
5319	}
5320# endif /* HESIOD_INIT */
5321
5322	if (bitset(MF_MATCHONLY, map->map_mflags))
5323		return map_rewrite(map, name, strlen(name), NULL);
5324	else
5325		return map_rewrite(map, hp[0], strlen(hp[0]), av);
5326}
5327
5328#endif /* HESIOD */
5329/*
5330**  NeXT NETINFO Modules
5331*/
5332
5333#if NETINFO
5334
5335# define NETINFO_DEFAULT_DIR		"/aliases"
5336# define NETINFO_DEFAULT_PROPERTY	"members"
5337
5338/*
5339**  NI_MAP_OPEN -- open NetInfo Aliases
5340*/
5341
5342bool
5343ni_map_open(map, mode)
5344	MAP *map;
5345	int mode;
5346{
5347	if (tTd(38, 2))
5348		dprintf("ni_map_open(%s, %s, %d)\n",
5349			map->map_mname, map->map_file, mode);
5350	mode &= O_ACCMODE;
5351
5352	if (*map->map_file == '\0')
5353		map->map_file = NETINFO_DEFAULT_DIR;
5354
5355	if (map->map_valcolnm == NULL)
5356		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5357
5358	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
5359		map->map_coldelim = ',';
5360
5361	return TRUE;
5362}
5363
5364
5365/*
5366**  NI_MAP_LOOKUP -- look up a datum in NetInfo
5367*/
5368
5369char *
5370ni_map_lookup(map, name, av, statp)
5371	MAP *map;
5372	char *name;
5373	char **av;
5374	int *statp;
5375{
5376	char *res;
5377	char *propval;
5378
5379	if (tTd(38, 20))
5380		dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5381
5382	propval = ni_propval(map->map_file, map->map_keycolnm, name,
5383			     map->map_valcolnm, map->map_coldelim);
5384
5385	if (propval == NULL)
5386		return NULL;
5387
5388	if (bitset(MF_MATCHONLY, map->map_mflags))
5389		res = map_rewrite(map, name, strlen(name), NULL);
5390	else
5391		res = map_rewrite(map, propval, strlen(propval), av);
5392	free(propval);
5393	return res;
5394}
5395
5396
5397static bool
5398ni_getcanonname(name, hbsize, statp)
5399	char *name;
5400	int hbsize;
5401	int *statp;
5402{
5403	char *vptr;
5404	char *ptr;
5405	char nbuf[MAXNAME + 1];
5406
5407	if (tTd(38, 20))
5408		dprintf("ni_getcanonname(%s)\n", name);
5409
5410	if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5411	{
5412		*statp = EX_UNAVAILABLE;
5413		return FALSE;
5414	}
5415	shorten_hostname(nbuf);
5416
5417	/* we only accept single token search key */
5418	if (strchr(nbuf, '.'))
5419	{
5420		*statp = EX_NOHOST;
5421		return FALSE;
5422	}
5423
5424	/* Do the search */
5425	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5426
5427	if (vptr == NULL)
5428	{
5429		*statp = EX_NOHOST;
5430		return FALSE;
5431	}
5432
5433	/* Only want the first machine name */
5434	if ((ptr = strchr(vptr, '\n')) != NULL)
5435		*ptr = '\0';
5436
5437	if (hbsize >= strlen(vptr))
5438	{
5439		(void) strlcpy(name, vptr, hbsize);
5440		free(vptr);
5441		*statp = EX_OK;
5442		return TRUE;
5443	}
5444	*statp = EX_UNAVAILABLE;
5445	free(vptr);
5446	return FALSE;
5447}
5448
5449
5450/*
5451**  NI_PROPVAL -- NetInfo property value lookup routine
5452**
5453**	Parameters:
5454**		keydir -- the NetInfo directory name in which to search
5455**			for the key.
5456**		keyprop -- the name of the property in which to find the
5457**			property we are interested.  Defaults to "name".
5458**		keyval -- the value for which we are really searching.
5459**		valprop -- the property name for the value in which we
5460**			are interested.
5461**		sepchar -- if non-nil, this can be multiple-valued, and
5462**			we should return a string separated by this
5463**			character.
5464**
5465**	Returns:
5466**		NULL -- if:
5467**			1. the directory is not found
5468**			2. the property name is not found
5469**			3. the property contains multiple values
5470**			4. some error occurred
5471**		else -- the value of the lookup.
5472**
5473**	Example:
5474**		To search for an alias value, use:
5475**		  ni_propval("/aliases", "name", aliasname, "members", ',')
5476**
5477**	Notes:
5478**		Caller should free the return value of ni_proval
5479*/
5480
5481# include <netinfo/ni.h>
5482
5483# define LOCAL_NETINFO_DOMAIN	"."
5484# define PARENT_NETINFO_DOMAIN	".."
5485# define MAX_NI_LEVELS		256
5486
5487char *
5488ni_propval(keydir, keyprop, keyval, valprop, sepchar)
5489	char *keydir;
5490	char *keyprop;
5491	char *keyval;
5492	char *valprop;
5493	int sepchar;
5494{
5495	char *propval = NULL;
5496	int i;
5497	int j, alen, l;
5498	void *ni = NULL;
5499	void *lastni = NULL;
5500	ni_status nis;
5501	ni_id nid;
5502	ni_namelist ninl;
5503	register char *p;
5504	char keybuf[1024];
5505
5506	/*
5507	**  Create the full key from the two parts.
5508	**
5509	**	Note that directory can end with, e.g., "name=" to specify
5510	**	an alternate search property.
5511	*/
5512
5513	i = strlen(keydir) + strlen(keyval) + 2;
5514	if (keyprop != NULL)
5515		i += strlen(keyprop) + 1;
5516	if (i >= sizeof keybuf)
5517		return NULL;
5518	(void) strlcpy(keybuf, keydir, sizeof keybuf);
5519	(void) strlcat(keybuf, "/", sizeof keybuf);
5520	if (keyprop != NULL)
5521	{
5522		(void) strlcat(keybuf, keyprop, sizeof keybuf);
5523		(void) strlcat(keybuf, "=", sizeof keybuf);
5524	}
5525	(void) strlcat(keybuf, keyval, sizeof keybuf);
5526
5527	if (tTd(38, 21))
5528		dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
5529			keydir, keyprop, keyval, valprop, sepchar, keybuf);
5530	/*
5531	**  If the passed directory and property name are found
5532	**  in one of netinfo domains we need to search (starting
5533	**  from the local domain moving all the way back to the
5534	**  root domain) set propval to the property's value
5535	**  and return it.
5536	*/
5537
5538	for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
5539	{
5540		if (i == 0)
5541		{
5542			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
5543			if (tTd(38, 20))
5544				dprintf("ni_open(LOCAL) = %d\n", nis);
5545		}
5546		else
5547		{
5548			if (lastni != NULL)
5549				ni_free(lastni);
5550			lastni = ni;
5551			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
5552			if (tTd(38, 20))
5553				dprintf("ni_open(PARENT) = %d\n", nis);
5554		}
5555
5556		/*
5557		**  Don't bother if we didn't get a handle on a
5558		**  proper domain.  This is not necessarily an error.
5559		**  We would get a positive ni_status if, for instance
5560		**  we never found the directory or property and tried
5561		**  to open the parent of the root domain!
5562		*/
5563
5564		if (nis != 0)
5565			break;
5566
5567		/*
5568		**  Find the path to the server information.
5569		*/
5570
5571		if (ni_pathsearch(ni, &nid, keybuf) != 0)
5572			continue;
5573
5574		/*
5575		**  Find associated value information.
5576		*/
5577
5578		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
5579			continue;
5580
5581		if (tTd(38, 20))
5582			dprintf("ni_lookupprop: len=%d\n",
5583				ninl.ni_namelist_len);
5584
5585		/*
5586		**  See if we have an acceptable number of values.
5587		*/
5588
5589		if (ninl.ni_namelist_len <= 0)
5590			continue;
5591
5592		if (sepchar == '\0' && ninl.ni_namelist_len > 1)
5593		{
5594			ni_namelist_free(&ninl);
5595			continue;
5596		}
5597
5598		/*
5599		**  Calculate number of bytes needed and build result
5600		*/
5601
5602		alen = 1;
5603		for (j = 0; j < ninl.ni_namelist_len; j++)
5604			alen += strlen(ninl.ni_namelist_val[j]) + 1;
5605		propval = p = xalloc(alen);
5606		for (j = 0; j < ninl.ni_namelist_len; j++)
5607		{
5608			(void) strlcpy(p, ninl.ni_namelist_val[j], alen);
5609			l = strlen(p);
5610			p += l;
5611			*p++ = sepchar;
5612			alen -= l + 1;
5613		}
5614		*--p = '\0';
5615
5616		ni_namelist_free(&ninl);
5617	}
5618
5619	/*
5620	**  Clean up.
5621	*/
5622
5623	if (ni != NULL)
5624		ni_free(ni);
5625	if (lastni != NULL && ni != lastni)
5626		ni_free(lastni);
5627	if (tTd(38, 20))
5628		dprintf("ni_propval returns: '%s'\n", propval);
5629
5630	return propval;
5631}
5632
5633#endif /* NETINFO */
5634/*
5635**  TEXT (unindexed text file) Modules
5636**
5637**	This code donated by Sun Microsystems.
5638*/
5639
5640#define map_sff		map_lockfd	/* overload field */
5641
5642
5643/*
5644**  TEXT_MAP_OPEN -- open text table
5645*/
5646
5647bool
5648text_map_open(map, mode)
5649	MAP *map;
5650	int mode;
5651{
5652	long sff;
5653	int i;
5654
5655	if (tTd(38, 2))
5656		dprintf("text_map_open(%s, %s, %d)\n",
5657			map->map_mname, map->map_file, mode);
5658
5659	mode &= O_ACCMODE;
5660	if (mode != O_RDONLY)
5661	{
5662		errno = EPERM;
5663		return FALSE;
5664	}
5665
5666	if (*map->map_file == '\0')
5667	{
5668		syserr("text map \"%s\": file name required",
5669			map->map_mname);
5670		return FALSE;
5671	}
5672
5673	if (map->map_file[0] != '/')
5674	{
5675		syserr("text map \"%s\": file name must be fully qualified",
5676			map->map_mname);
5677		return FALSE;
5678	}
5679
5680	sff = SFF_ROOTOK|SFF_REGONLY;
5681	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5682		sff |= SFF_NOWLINK;
5683	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5684		sff |= SFF_SAFEDIRPATH;
5685	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5686			  sff, S_IRUSR, NULL)) != 0)
5687	{
5688		int save_errno = errno;
5689
5690		/* cannot open this map */
5691		if (tTd(38, 2))
5692			dprintf("\tunsafe map file: %d\n", i);
5693		errno = save_errno;
5694		if (!bitset(MF_OPTIONAL, map->map_mflags))
5695			syserr("text map \"%s\": unsafe map file %s",
5696				map->map_mname, map->map_file);
5697		return FALSE;
5698	}
5699
5700	if (map->map_keycolnm == NULL)
5701		map->map_keycolno = 0;
5702	else
5703	{
5704		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5705		{
5706			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5707				map->map_mname, map->map_file,
5708				map->map_keycolnm);
5709			return FALSE;
5710		}
5711		map->map_keycolno = atoi(map->map_keycolnm);
5712	}
5713
5714	if (map->map_valcolnm == NULL)
5715		map->map_valcolno = 0;
5716	else
5717	{
5718		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5719		{
5720			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5721					map->map_mname, map->map_file,
5722					map->map_valcolnm);
5723			return FALSE;
5724		}
5725		map->map_valcolno = atoi(map->map_valcolnm);
5726	}
5727
5728	if (tTd(38, 2))
5729	{
5730		dprintf("text_map_open(%s, %s): delimiter = ",
5731			map->map_mname, map->map_file);
5732		if (map->map_coldelim == '\0')
5733			dprintf("(white space)\n");
5734		else
5735			dprintf("%c\n", map->map_coldelim);
5736	}
5737
5738	map->map_sff = sff;
5739	return TRUE;
5740}
5741
5742
5743/*
5744**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5745*/
5746
5747char *
5748text_map_lookup(map, name, av, statp)
5749	MAP *map;
5750	char *name;
5751	char **av;
5752	int *statp;
5753{
5754	char *vp;
5755	auto int vsize;
5756	int buflen;
5757	FILE *f;
5758	char delim;
5759	int key_idx;
5760	bool found_it;
5761	long sff = map->map_sff;
5762	char search_key[MAXNAME + 1];
5763	char linebuf[MAXLINE];
5764	char buf[MAXNAME + 1];
5765
5766	found_it = FALSE;
5767	if (tTd(38, 20))
5768		dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
5769
5770	buflen = strlen(name);
5771	if (buflen > sizeof search_key - 1)
5772		buflen = sizeof search_key - 1;
5773	memmove(search_key, name, buflen);
5774	search_key[buflen] = '\0';
5775	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5776		makelower(search_key);
5777
5778	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5779	if (f == NULL)
5780	{
5781		map->map_mflags &= ~(MF_VALID|MF_OPEN);
5782		*statp = EX_UNAVAILABLE;
5783		return NULL;
5784	}
5785	key_idx = map->map_keycolno;
5786	delim = map->map_coldelim;
5787	while (fgets(linebuf, MAXLINE, f) != NULL)
5788	{
5789		char *p;
5790
5791		/* skip comment line */
5792		if (linebuf[0] == '#')
5793			continue;
5794		p = strchr(linebuf, '\n');
5795		if (p != NULL)
5796			*p = '\0';
5797		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5798		if (p != NULL && strcasecmp(search_key, p) == 0)
5799		{
5800			found_it = TRUE;
5801			break;
5802		}
5803	}
5804	(void) fclose(f);
5805	if (!found_it)
5806	{
5807		*statp = EX_NOTFOUND;
5808		return NULL;
5809	}
5810	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5811	if (vp == NULL)
5812	{
5813		*statp = EX_NOTFOUND;
5814		return NULL;
5815	}
5816	vsize = strlen(vp);
5817	*statp = EX_OK;
5818	if (bitset(MF_MATCHONLY, map->map_mflags))
5819		return map_rewrite(map, name, strlen(name), NULL);
5820	else
5821		return map_rewrite(map, vp, vsize, av);
5822}
5823
5824/*
5825**  TEXT_GETCANONNAME -- look up canonical name in hosts file
5826*/
5827
5828static bool
5829text_getcanonname(name, hbsize, statp)
5830	char *name;
5831	int hbsize;
5832	int *statp;
5833{
5834	bool found;
5835	FILE *f;
5836	char linebuf[MAXLINE];
5837	char cbuf[MAXNAME + 1];
5838	char nbuf[MAXNAME + 1];
5839
5840	if (tTd(38, 20))
5841		dprintf("text_getcanonname(%s)\n", name);
5842
5843	if (strlen(name) >= (SIZE_T) sizeof nbuf)
5844	{
5845		*statp = EX_UNAVAILABLE;
5846		return FALSE;
5847	}
5848	(void) strlcpy(nbuf, name, sizeof nbuf);
5849	shorten_hostname(nbuf);
5850
5851	f = fopen(HostsFile, "r");
5852	if (f == NULL)
5853	{
5854		*statp = EX_UNAVAILABLE;
5855		return FALSE;
5856	}
5857	found = FALSE;
5858	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
5859	{
5860		char *p = strpbrk(linebuf, "#\n");
5861
5862		if (p != NULL)
5863			*p = '\0';
5864		if (linebuf[0] != '\0')
5865			found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
5866	}
5867	(void) fclose(f);
5868	if (!found)
5869	{
5870		*statp = EX_NOHOST;
5871		return FALSE;
5872	}
5873
5874	if ((SIZE_T) hbsize >= strlen(cbuf))
5875	{
5876		(void) strlcpy(name, cbuf, hbsize);
5877		*statp = EX_OK;
5878		return TRUE;
5879	}
5880	*statp = EX_UNAVAILABLE;
5881	return FALSE;
5882}
5883/*
5884**  STAB (Symbol Table) Modules
5885*/
5886
5887
5888/*
5889**  STAB_MAP_LOOKUP -- look up alias in symbol table
5890*/
5891
5892/* ARGSUSED2 */
5893char *
5894stab_map_lookup(map, name, av, pstat)
5895	register MAP *map;
5896	char *name;
5897	char **av;
5898	int *pstat;
5899{
5900	register STAB *s;
5901
5902	if (tTd(38, 20))
5903		dprintf("stab_lookup(%s, %s)\n",
5904			map->map_mname, name);
5905
5906	s = stab(name, ST_ALIAS, ST_FIND);
5907	if (s != NULL)
5908		return s->s_alias;
5909	return NULL;
5910}
5911
5912
5913/*
5914**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5915*/
5916
5917void
5918stab_map_store(map, lhs, rhs)
5919	register MAP *map;
5920	char *lhs;
5921	char *rhs;
5922{
5923	register STAB *s;
5924
5925	s = stab(lhs, ST_ALIAS, ST_ENTER);
5926	s->s_alias = newstr(rhs);
5927}
5928
5929
5930/*
5931**  STAB_MAP_OPEN -- initialize (reads data file)
5932**
5933**	This is a wierd case -- it is only intended as a fallback for
5934**	aliases.  For this reason, opens for write (only during a
5935**	"newaliases") always fails, and opens for read open the
5936**	actual underlying text file instead of the database.
5937*/
5938
5939bool
5940stab_map_open(map, mode)
5941	register MAP *map;
5942	int mode;
5943{
5944	FILE *af;
5945	long sff;
5946	struct stat st;
5947
5948	if (tTd(38, 2))
5949		dprintf("stab_map_open(%s, %s, %d)\n",
5950			map->map_mname, map->map_file, mode);
5951
5952	mode &= O_ACCMODE;
5953	if (mode != O_RDONLY)
5954	{
5955		errno = EPERM;
5956		return FALSE;
5957	}
5958
5959	sff = SFF_ROOTOK|SFF_REGONLY;
5960	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5961		sff |= SFF_NOWLINK;
5962	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5963		sff |= SFF_SAFEDIRPATH;
5964	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5965	if (af == NULL)
5966		return FALSE;
5967	readaliases(map, af, FALSE, FALSE);
5968
5969	if (fstat(fileno(af), &st) >= 0)
5970		map->map_mtime = st.st_mtime;
5971	(void) fclose(af);
5972
5973	return TRUE;
5974}
5975/*
5976**  Implicit Modules
5977**
5978**	Tries several types.  For back compatibility of aliases.
5979*/
5980
5981
5982/*
5983**  IMPL_MAP_LOOKUP -- lookup in best open database
5984*/
5985
5986char *
5987impl_map_lookup(map, name, av, pstat)
5988	MAP *map;
5989	char *name;
5990	char **av;
5991	int *pstat;
5992{
5993	if (tTd(38, 20))
5994		dprintf("impl_map_lookup(%s, %s)\n",
5995			map->map_mname, name);
5996
5997#ifdef NEWDB
5998	if (bitset(MF_IMPL_HASH, map->map_mflags))
5999		return db_map_lookup(map, name, av, pstat);
6000#endif /* NEWDB */
6001#ifdef NDBM
6002	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6003		return ndbm_map_lookup(map, name, av, pstat);
6004#endif /* NDBM */
6005	return stab_map_lookup(map, name, av, pstat);
6006}
6007
6008/*
6009**  IMPL_MAP_STORE -- store in open databases
6010*/
6011
6012void
6013impl_map_store(map, lhs, rhs)
6014	MAP *map;
6015	char *lhs;
6016	char *rhs;
6017{
6018	if (tTd(38, 12))
6019		dprintf("impl_map_store(%s, %s, %s)\n",
6020			map->map_mname, lhs, rhs);
6021#ifdef NEWDB
6022	if (bitset(MF_IMPL_HASH, map->map_mflags))
6023		db_map_store(map, lhs, rhs);
6024#endif /* NEWDB */
6025#ifdef NDBM
6026	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6027		ndbm_map_store(map, lhs, rhs);
6028#endif /* NDBM */
6029	stab_map_store(map, lhs, rhs);
6030}
6031
6032/*
6033**  IMPL_MAP_OPEN -- implicit database open
6034*/
6035
6036bool
6037impl_map_open(map, mode)
6038	MAP *map;
6039	int mode;
6040{
6041	if (tTd(38, 2))
6042		dprintf("impl_map_open(%s, %s, %d)\n",
6043			map->map_mname, map->map_file, mode);
6044
6045	mode &= O_ACCMODE;
6046#ifdef NEWDB
6047	map->map_mflags |= MF_IMPL_HASH;
6048	if (hash_map_open(map, mode))
6049	{
6050# ifdef NDBM_YP_COMPAT
6051		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6052# endif /* NDBM_YP_COMPAT */
6053			return TRUE;
6054	}
6055	else
6056		map->map_mflags &= ~MF_IMPL_HASH;
6057#endif /* NEWDB */
6058#ifdef NDBM
6059	map->map_mflags |= MF_IMPL_NDBM;
6060	if (ndbm_map_open(map, mode))
6061	{
6062		return TRUE;
6063	}
6064	else
6065		map->map_mflags &= ~MF_IMPL_NDBM;
6066#endif /* NDBM */
6067
6068#if defined(NEWDB) || defined(NDBM)
6069	if (Verbose)
6070		message("WARNING: cannot open alias database %s%s",
6071			map->map_file,
6072			mode == O_RDONLY ? "; reading text version" : "");
6073#else /* defined(NEWDB) || defined(NDBM) */
6074	if (mode != O_RDONLY)
6075		usrerr("Cannot rebuild aliases: no database format defined");
6076#endif /* defined(NEWDB) || defined(NDBM) */
6077
6078	if (mode == O_RDONLY)
6079		return stab_map_open(map, mode);
6080	else
6081		return FALSE;
6082}
6083
6084
6085/*
6086**  IMPL_MAP_CLOSE -- close any open database(s)
6087*/
6088
6089void
6090impl_map_close(map)
6091	MAP *map;
6092{
6093	if (tTd(38, 9))
6094		dprintf("impl_map_close(%s, %s, %lx)\n",
6095			map->map_mname, map->map_file, map->map_mflags);
6096#ifdef NEWDB
6097	if (bitset(MF_IMPL_HASH, map->map_mflags))
6098	{
6099		db_map_close(map);
6100		map->map_mflags &= ~MF_IMPL_HASH;
6101	}
6102#endif /* NEWDB */
6103
6104#ifdef NDBM
6105	if (bitset(MF_IMPL_NDBM, map->map_mflags))
6106	{
6107		ndbm_map_close(map);
6108		map->map_mflags &= ~MF_IMPL_NDBM;
6109	}
6110#endif /* NDBM */
6111}
6112/*
6113**  User map class.
6114**
6115**	Provides access to the system password file.
6116*/
6117
6118/*
6119**  USER_MAP_OPEN -- open user map
6120**
6121**	Really just binds field names to field numbers.
6122*/
6123
6124bool
6125user_map_open(map, mode)
6126	MAP *map;
6127	int mode;
6128{
6129	if (tTd(38, 2))
6130		dprintf("user_map_open(%s, %d)\n",
6131			map->map_mname, mode);
6132
6133	mode &= O_ACCMODE;
6134	if (mode != O_RDONLY)
6135	{
6136		/* issue a pseudo-error message */
6137#ifdef ENOSYS
6138		errno = ENOSYS;
6139#else /* ENOSYS */
6140# ifdef EFTYPE
6141		errno = EFTYPE;
6142# else /* EFTYPE */
6143		errno = ENXIO;
6144# endif /* EFTYPE */
6145#endif /* ENOSYS */
6146		return FALSE;
6147	}
6148	if (map->map_valcolnm == NULL)
6149		/* EMPTY */
6150		/* nothing */ ;
6151	else if (strcasecmp(map->map_valcolnm, "name") == 0)
6152		map->map_valcolno = 1;
6153	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
6154		map->map_valcolno = 2;
6155	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
6156		map->map_valcolno = 3;
6157	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
6158		map->map_valcolno = 4;
6159	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
6160		map->map_valcolno = 5;
6161	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
6162		map->map_valcolno = 6;
6163	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
6164		map->map_valcolno = 7;
6165	else
6166	{
6167		syserr("User map %s: unknown column name %s",
6168			map->map_mname, map->map_valcolnm);
6169		return FALSE;
6170	}
6171	return TRUE;
6172}
6173
6174
6175/*
6176**  USER_MAP_LOOKUP -- look up a user in the passwd file.
6177*/
6178
6179/* ARGSUSED3 */
6180char *
6181user_map_lookup(map, key, av, statp)
6182	MAP *map;
6183	char *key;
6184	char **av;
6185	int *statp;
6186{
6187	struct passwd *pw;
6188	auto bool fuzzy;
6189
6190	if (tTd(38, 20))
6191		dprintf("user_map_lookup(%s, %s)\n",
6192			map->map_mname, key);
6193
6194	pw = finduser(key, &fuzzy);
6195	if (pw == NULL)
6196		return NULL;
6197	if (bitset(MF_MATCHONLY, map->map_mflags))
6198		return map_rewrite(map, key, strlen(key), NULL);
6199	else
6200	{
6201		char *rwval = NULL;
6202		char buf[30];
6203
6204		switch (map->map_valcolno)
6205		{
6206		  case 0:
6207		  case 1:
6208			rwval = pw->pw_name;
6209			break;
6210
6211		  case 2:
6212			rwval = pw->pw_passwd;
6213			break;
6214
6215		  case 3:
6216			snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid);
6217			rwval = buf;
6218			break;
6219
6220		  case 4:
6221			snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid);
6222			rwval = buf;
6223			break;
6224
6225		  case 5:
6226			rwval = pw->pw_gecos;
6227			break;
6228
6229		  case 6:
6230			rwval = pw->pw_dir;
6231			break;
6232
6233		  case 7:
6234			rwval = pw->pw_shell;
6235			break;
6236		}
6237		return map_rewrite(map, rwval, strlen(rwval), av);
6238	}
6239}
6240/*
6241**  Program map type.
6242**
6243**	This provides access to arbitrary programs.  It should be used
6244**	only very sparingly, since there is no way to bound the cost
6245**	of invoking an arbitrary program.
6246*/
6247
6248char *
6249prog_map_lookup(map, name, av, statp)
6250	MAP *map;
6251	char *name;
6252	char **av;
6253	int *statp;
6254{
6255	int i;
6256	int save_errno;
6257	int fd;
6258	int status;
6259	auto pid_t pid;
6260	register char *p;
6261	char *rval;
6262	char *argv[MAXPV + 1];
6263	char buf[MAXLINE];
6264
6265	if (tTd(38, 20))
6266		dprintf("prog_map_lookup(%s, %s) %s\n",
6267			map->map_mname, name, map->map_file);
6268
6269	i = 0;
6270	argv[i++] = map->map_file;
6271	if (map->map_rebuild != NULL)
6272	{
6273		snprintf(buf, sizeof buf, "%s", map->map_rebuild);
6274		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6275		{
6276			if (i >= MAXPV - 1)
6277				break;
6278			argv[i++] = p;
6279		}
6280	}
6281	argv[i++] = name;
6282	argv[i] = NULL;
6283	if (tTd(38, 21))
6284	{
6285		dprintf("prog_open:");
6286		for (i = 0; argv[i] != NULL; i++)
6287			dprintf(" %s", argv[i]);
6288		dprintf("\n");
6289	}
6290	(void) blocksignal(SIGCHLD);
6291	pid = prog_open(argv, &fd, CurEnv);
6292	if (pid < 0)
6293	{
6294		if (!bitset(MF_OPTIONAL, map->map_mflags))
6295			syserr("prog_map_lookup(%s) failed (%s) -- closing",
6296				map->map_mname, errstring(errno));
6297		else if (tTd(38, 9))
6298			dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6299				map->map_mname, errstring(errno));
6300		map->map_mflags &= ~(MF_VALID|MF_OPEN);
6301		*statp = EX_OSFILE;
6302		return NULL;
6303	}
6304	i = read(fd, buf, sizeof buf - 1);
6305	if (i < 0)
6306	{
6307		syserr("prog_map_lookup(%s): read error %s\n",
6308			map->map_mname, errstring(errno));
6309		rval = NULL;
6310	}
6311	else if (i == 0)
6312	{
6313		if (tTd(38, 20))
6314			dprintf("prog_map_lookup(%s): empty answer\n",
6315				map->map_mname);
6316		rval = NULL;
6317	}
6318	else
6319	{
6320		buf[i] = '\0';
6321		p = strchr(buf, '\n');
6322		if (p != NULL)
6323			*p = '\0';
6324
6325		/* collect the return value */
6326		if (bitset(MF_MATCHONLY, map->map_mflags))
6327			rval = map_rewrite(map, name, strlen(name), NULL);
6328		else
6329			rval = map_rewrite(map, buf, strlen(buf), NULL);
6330
6331		/* now flush any additional output */
6332		while ((i = read(fd, buf, sizeof buf)) > 0)
6333			continue;
6334	}
6335
6336	/* wait for the process to terminate */
6337	(void) close(fd);
6338	status = waitfor(pid);
6339	save_errno = errno;
6340	(void) releasesignal(SIGCHLD);
6341	errno = save_errno;
6342
6343	if (status == -1)
6344	{
6345		syserr("prog_map_lookup(%s): wait error %s\n",
6346			map->map_mname, errstring(errno));
6347		*statp = EX_SOFTWARE;
6348		rval = NULL;
6349	}
6350	else if (WIFEXITED(status))
6351	{
6352		if ((*statp = WEXITSTATUS(status)) != EX_OK)
6353			rval = NULL;
6354	}
6355	else
6356	{
6357		syserr("prog_map_lookup(%s): child died on signal %d",
6358			map->map_mname, status);
6359		*statp = EX_UNAVAILABLE;
6360		rval = NULL;
6361	}
6362	return rval;
6363}
6364/*
6365**  Sequenced map type.
6366**
6367**	Tries each map in order until something matches, much like
6368**	implicit.  Stores go to the first map in the list that can
6369**	support storing.
6370**
6371**	This is slightly unusual in that there are two interfaces.
6372**	The "sequence" interface lets you stack maps arbitrarily.
6373**	The "switch" interface builds a sequence map by looking
6374**	at a system-dependent configuration file such as
6375**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6376**
6377**	We don't need an explicit open, since all maps are
6378**	opened during startup, including underlying maps.
6379*/
6380
6381/*
6382**  SEQ_MAP_PARSE -- Sequenced map parsing
6383*/
6384
6385bool
6386seq_map_parse(map, ap)
6387	MAP *map;
6388	char *ap;
6389{
6390	int maxmap;
6391
6392	if (tTd(38, 2))
6393		dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6394	maxmap = 0;
6395	while (*ap != '\0')
6396	{
6397		register char *p;
6398		STAB *s;
6399
6400		/* find beginning of map name */
6401		while (isascii(*ap) && isspace(*ap))
6402			ap++;
6403		for (p = ap;
6404		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6405		     p++)
6406			continue;
6407		if (*p != '\0')
6408			*p++ = '\0';
6409		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6410			p++;
6411		if (*ap == '\0')
6412		{
6413			ap = p;
6414			continue;
6415		}
6416		s = stab(ap, ST_MAP, ST_FIND);
6417		if (s == NULL)
6418		{
6419			syserr("Sequence map %s: unknown member map %s",
6420				map->map_mname, ap);
6421		}
6422		else if (maxmap == MAXMAPSTACK)
6423		{
6424			syserr("Sequence map %s: too many member maps (%d max)",
6425				map->map_mname, MAXMAPSTACK);
6426			maxmap++;
6427		}
6428		else if (maxmap < MAXMAPSTACK)
6429		{
6430			map->map_stack[maxmap++] = &s->s_map;
6431		}
6432		ap = p;
6433	}
6434	return TRUE;
6435}
6436
6437
6438/*
6439**  SWITCH_MAP_OPEN -- open a switched map
6440**
6441**	This looks at the system-dependent configuration and builds
6442**	a sequence map that does the same thing.
6443**
6444**	Every system must define a switch_map_find routine in conf.c
6445**	that will return the list of service types associated with a
6446**	given service class.
6447*/
6448
6449bool
6450switch_map_open(map, mode)
6451	MAP *map;
6452	int mode;
6453{
6454	int mapno;
6455	int nmaps;
6456	char *maptype[MAXMAPSTACK];
6457
6458	if (tTd(38, 2))
6459		dprintf("switch_map_open(%s, %s, %d)\n",
6460			map->map_mname, map->map_file, mode);
6461
6462	mode &= O_ACCMODE;
6463	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6464	if (tTd(38, 19))
6465	{
6466		dprintf("\tswitch_map_find => %d\n", nmaps);
6467		for (mapno = 0; mapno < nmaps; mapno++)
6468			dprintf("\t\t%s\n", maptype[mapno]);
6469	}
6470	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6471		return FALSE;
6472
6473	for (mapno = 0; mapno < nmaps; mapno++)
6474	{
6475		register STAB *s;
6476		char nbuf[MAXNAME + 1];
6477
6478		if (maptype[mapno] == NULL)
6479			continue;
6480		(void) snprintf(nbuf, sizeof nbuf, "%s.%s",
6481			map->map_mname, maptype[mapno]);
6482		s = stab(nbuf, ST_MAP, ST_FIND);
6483		if (s == NULL)
6484		{
6485			syserr("Switch map %s: unknown member map %s",
6486				map->map_mname, nbuf);
6487		}
6488		else
6489		{
6490			map->map_stack[mapno] = &s->s_map;
6491			if (tTd(38, 4))
6492				dprintf("\tmap_stack[%d] = %s:%s\n",
6493					mapno, s->s_map.map_class->map_cname,
6494					nbuf);
6495		}
6496	}
6497	return TRUE;
6498}
6499
6500
6501/*
6502**  SEQ_MAP_CLOSE -- close all underlying maps
6503*/
6504
6505void
6506seq_map_close(map)
6507	MAP *map;
6508{
6509	int mapno;
6510
6511	if (tTd(38, 9))
6512		dprintf("seq_map_close(%s)\n", map->map_mname);
6513
6514	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6515	{
6516		MAP *mm = map->map_stack[mapno];
6517
6518		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6519			continue;
6520		mm->map_class->map_close(mm);
6521		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
6522	}
6523}
6524
6525
6526/*
6527**  SEQ_MAP_LOOKUP -- sequenced map lookup
6528*/
6529
6530char *
6531seq_map_lookup(map, key, args, pstat)
6532	MAP *map;
6533	char *key;
6534	char **args;
6535	int *pstat;
6536{
6537	int mapno;
6538	int mapbit = 0x01;
6539	bool tempfail = FALSE;
6540
6541	if (tTd(38, 20))
6542		dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6543
6544	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6545	{
6546		MAP *mm = map->map_stack[mapno];
6547		char *rv;
6548
6549		if (mm == NULL)
6550			continue;
6551		if (!bitset(MF_OPEN, mm->map_mflags) &&
6552		    !openmap(mm))
6553		{
6554			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6555			{
6556				*pstat = EX_UNAVAILABLE;
6557				return NULL;
6558			}
6559			continue;
6560		}
6561		*pstat = EX_OK;
6562		rv = mm->map_class->map_lookup(mm, key, args, pstat);
6563		if (rv != NULL)
6564			return rv;
6565		if (*pstat == EX_TEMPFAIL)
6566		{
6567			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6568				return NULL;
6569			tempfail = TRUE;
6570		}
6571		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6572			break;
6573	}
6574	if (tempfail)
6575		*pstat = EX_TEMPFAIL;
6576	else if (*pstat == EX_OK)
6577		*pstat = EX_NOTFOUND;
6578	return NULL;
6579}
6580
6581
6582/*
6583**  SEQ_MAP_STORE -- sequenced map store
6584*/
6585
6586void
6587seq_map_store(map, key, val)
6588	MAP *map;
6589	char *key;
6590	char *val;
6591{
6592	int mapno;
6593
6594	if (tTd(38, 12))
6595		dprintf("seq_map_store(%s, %s, %s)\n",
6596			map->map_mname, key, val);
6597
6598	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6599	{
6600		MAP *mm = map->map_stack[mapno];
6601
6602		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6603			continue;
6604
6605		mm->map_class->map_store(mm, key, val);
6606		return;
6607	}
6608	syserr("seq_map_store(%s, %s, %s): no writable map",
6609		map->map_mname, key, val);
6610}
6611/*
6612**  NULL stubs
6613*/
6614
6615/* ARGSUSED */
6616bool
6617null_map_open(map, mode)
6618	MAP *map;
6619	int mode;
6620{
6621	return TRUE;
6622}
6623
6624/* ARGSUSED */
6625void
6626null_map_close(map)
6627	MAP *map;
6628{
6629	return;
6630}
6631
6632char *
6633null_map_lookup(map, key, args, pstat)
6634	MAP *map;
6635	char *key;
6636	char **args;
6637	int *pstat;
6638{
6639	*pstat = EX_NOTFOUND;
6640	return NULL;
6641}
6642
6643/* ARGSUSED */
6644void
6645null_map_store(map, key, val)
6646	MAP *map;
6647	char *key;
6648	char *val;
6649{
6650	return;
6651}
6652
6653
6654/*
6655**  BOGUS stubs
6656*/
6657
6658char *
6659bogus_map_lookup(map, key, args, pstat)
6660	MAP *map;
6661	char *key;
6662	char **args;
6663	int *pstat;
6664{
6665	*pstat = EX_TEMPFAIL;
6666	return NULL;
6667}
6668
6669MAPCLASS	BogusMapClass =
6670{
6671	"bogus-map",		NULL,		0,
6672	NULL,		bogus_map_lookup,	null_map_store,
6673	null_map_open,	null_map_close,
6674};
6675/*
6676**  MACRO modules
6677*/
6678
6679char *
6680macro_map_lookup(map, name, av, statp)
6681	MAP *map;
6682	char *name;
6683	char **av;
6684	int *statp;
6685{
6686	int mid;
6687
6688	if (tTd(38, 20))
6689		dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6690			name == NULL ? "NULL" : name);
6691
6692	if (name == NULL ||
6693	    *name == '\0' ||
6694	    (mid = macid(name, NULL)) == '\0')
6695	{
6696		*statp = EX_CONFIG;
6697		return NULL;
6698	}
6699
6700	if (av[1] == NULL)
6701		define(mid, NULL, CurEnv);
6702	else
6703		define(mid, newstr(av[1]), CurEnv);
6704
6705	*statp = EX_OK;
6706	return "";
6707}
6708/*
6709**  REGEX modules
6710*/
6711
6712#ifdef MAP_REGEX
6713
6714# include <regex.h>
6715
6716# define DEFAULT_DELIM	CONDELSE
6717
6718# define END_OF_FIELDS	-1
6719
6720# define ERRBUF_SIZE	80
6721# define MAX_MATCH	32
6722
6723# define xnalloc(s)	memset(xalloc(s), '\0', s);
6724
6725struct regex_map
6726{
6727	regex_t	*regex_pattern_buf;	/* xalloc it */
6728	int	*regex_subfields;	/* move to type MAP */
6729	char	*regex_delim;		/* move to type MAP */
6730};
6731
6732static int
6733parse_fields(s, ibuf, blen, nr_substrings)
6734	char *s;
6735	int *ibuf;		/* array */
6736	int blen;		/* number of elements in ibuf */
6737	int nr_substrings;	/* number of substrings in the pattern */
6738{
6739	register char *cp;
6740	int i = 0;
6741	bool lastone = FALSE;
6742
6743	blen--;		/* for terminating END_OF_FIELDS */
6744	cp = s;
6745	do
6746	{
6747		for (;; cp++)
6748		{
6749			if (*cp == ',')
6750			{
6751				*cp = '\0';
6752				break;
6753			}
6754			if (*cp == '\0')
6755			{
6756				lastone = TRUE;
6757				break;
6758			}
6759		}
6760		if (i < blen)
6761		{
6762			int val = atoi(s);
6763
6764			if (val < 0 || val >= nr_substrings)
6765			{
6766				syserr("field (%d) out of range, only %d substrings in pattern",
6767				       val, nr_substrings);
6768				return -1;
6769			}
6770			ibuf[i++] = val;
6771		}
6772		else
6773		{
6774			syserr("too many fields, %d max\n", blen);
6775			return -1;
6776		}
6777		s = ++cp;
6778	} while (!lastone);
6779	ibuf[i] = END_OF_FIELDS;
6780	return i;
6781}
6782
6783bool
6784regex_map_init(map, ap)
6785	MAP *map;
6786	char *ap;
6787{
6788	int regerr;
6789	struct regex_map *map_p;
6790	register char *p;
6791	char *sub_param = NULL;
6792	int pflags;
6793	static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
6794
6795	if (tTd(38, 2))
6796		dprintf("regex_map_init: mapname '%s', args '%s'\n",
6797			map->map_mname, ap);
6798
6799	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6800
6801	p = ap;
6802
6803	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6804	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6805
6806	for (;;)
6807	{
6808		while (isascii(*p) && isspace(*p))
6809			p++;
6810		if (*p != '-')
6811			break;
6812		switch (*++p)
6813		{
6814		  case 'n':	/* not */
6815			map->map_mflags |= MF_REGEX_NOT;
6816			break;
6817
6818		  case 'f':	/* case sensitive */
6819			map->map_mflags |= MF_NOFOLDCASE;
6820			pflags &= ~REG_ICASE;
6821			break;
6822
6823		  case 'b':	/* basic regular expressions */
6824			pflags &= ~REG_EXTENDED;
6825			break;
6826
6827		  case 's':	/* substring match () syntax */
6828			sub_param = ++p;
6829			pflags &= ~REG_NOSUB;
6830			break;
6831
6832		  case 'd':	/* delimiter */
6833			map_p->regex_delim = ++p;
6834			break;
6835
6836		  case 'a':	/* map append */
6837			map->map_app = ++p;
6838			break;
6839
6840		  case 'm':	/* matchonly */
6841			map->map_mflags |= MF_MATCHONLY;
6842			break;
6843
6844		  case 'S':
6845			map->map_spacesub = *++p;
6846			break;
6847
6848		  case 'D':
6849			map->map_mflags |= MF_DEFER;
6850			break;
6851
6852		}
6853		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6854			p++;
6855		if (*p != '\0')
6856			*p++ = '\0';
6857	}
6858	if (tTd(38, 3))
6859		dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6860
6861	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6862	{
6863		/* Errorhandling */
6864		char errbuf[ERRBUF_SIZE];
6865
6866		(void) regerror(regerr, map_p->regex_pattern_buf,
6867			 errbuf, ERRBUF_SIZE);
6868		syserr("pattern-compile-error: %s\n", errbuf);
6869		free(map_p->regex_pattern_buf);
6870		free(map_p);
6871		return FALSE;
6872	}
6873
6874	if (map->map_app != NULL)
6875		map->map_app = newstr(map->map_app);
6876	if (map_p->regex_delim != NULL)
6877		map_p->regex_delim = newstr(map_p->regex_delim);
6878	else
6879		map_p->regex_delim = defdstr;
6880
6881	if (!bitset(REG_NOSUB, pflags))
6882	{
6883		/* substring matching */
6884		int substrings;
6885		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6886
6887		substrings = map_p->regex_pattern_buf->re_nsub + 1;
6888
6889		if (tTd(38, 3))
6890			dprintf("regex_map_init: nr of substrings %d\n",
6891				substrings);
6892
6893		if (substrings >= MAX_MATCH)
6894		{
6895			syserr("too many substrings, %d max\n", MAX_MATCH);
6896			free(map_p->regex_pattern_buf);
6897			free(map_p);
6898			return FALSE;
6899		}
6900		if (sub_param != NULL && sub_param[0] != '\0')
6901		{
6902			/* optional parameter -sfields */
6903			if (parse_fields(sub_param, fields,
6904					 MAX_MATCH + 1, substrings) == -1)
6905				return FALSE;
6906		}
6907		else
6908		{
6909			/* set default fields */
6910			int i;
6911
6912			for (i = 0; i < substrings; i++)
6913				fields[i] = i;
6914			fields[i] = END_OF_FIELDS;
6915		}
6916		map_p->regex_subfields = fields;
6917		if (tTd(38, 3))
6918		{
6919			int *ip;
6920
6921			dprintf("regex_map_init: subfields");
6922			for (ip = fields; *ip != END_OF_FIELDS; ip++)
6923				dprintf(" %d", *ip);
6924			dprintf("\n");
6925		}
6926	}
6927	map->map_db1 = (ARBPTR_T)map_p;	/* dirty hack */
6928
6929	return TRUE;
6930}
6931
6932static char *
6933regex_map_rewrite(map, s, slen, av)
6934	MAP *map;
6935	const char *s;
6936	size_t slen;
6937	char **av;
6938{
6939	if (bitset(MF_MATCHONLY, map->map_mflags))
6940		return map_rewrite(map, av[0], strlen(av[0]), NULL);
6941	else
6942		return map_rewrite(map, s, slen, NULL);
6943}
6944
6945char *
6946regex_map_lookup(map, name, av, statp)
6947	MAP *map;
6948	char *name;
6949	char **av;
6950	int *statp;
6951{
6952	int reg_res;
6953	struct regex_map *map_p;
6954	regmatch_t pmatch[MAX_MATCH];
6955
6956	if (tTd(38, 20))
6957	{
6958		char **cpp;
6959
6960		dprintf("regex_map_lookup: key '%s'\n", name);
6961		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6962			dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6963	}
6964
6965	map_p = (struct regex_map *)(map->map_db1);
6966	reg_res = regexec(map_p->regex_pattern_buf,
6967			  name, MAX_MATCH, pmatch, 0);
6968
6969	if (bitset(MF_REGEX_NOT, map->map_mflags))
6970	{
6971		/* option -n */
6972		if (reg_res == REG_NOMATCH)
6973			return regex_map_rewrite(map, "", (size_t)0, av);
6974		else
6975			return NULL;
6976	}
6977	if (reg_res == REG_NOMATCH)
6978		return NULL;
6979
6980	if (map_p->regex_subfields != NULL)
6981	{
6982		/* option -s */
6983		static char retbuf[MAXNAME];
6984		int fields[MAX_MATCH + 1];
6985		bool first = TRUE;
6986		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6987		bool quotemode = FALSE, bslashmode = FALSE;
6988		register char *dp, *sp;
6989		char *endp, *ldp;
6990		int *ip;
6991
6992		dp = retbuf;
6993		ldp = retbuf + sizeof(retbuf) - 1;
6994
6995		if (av[1] != NULL)
6996		{
6997			if (parse_fields(av[1], fields, MAX_MATCH + 1,
6998					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
6999			{
7000				*statp = EX_CONFIG;
7001				return NULL;
7002			}
7003			ip = fields;
7004		}
7005		else
7006			ip = map_p->regex_subfields;
7007
7008		for ( ; *ip != END_OF_FIELDS; ip++)
7009		{
7010			if (!first)
7011			{
7012				for (sp = map_p->regex_delim; *sp; sp++)
7013				{
7014					if (dp < ldp)
7015						*dp++ = *sp;
7016				}
7017			}
7018			else
7019				first = FALSE;
7020
7021
7022			if (*ip >= MAX_MATCH ||
7023			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7024				continue;
7025
7026			sp = name + pmatch[*ip].rm_so;
7027			endp = name + pmatch[*ip].rm_eo;
7028			for (; endp > sp; sp++)
7029			{
7030				if (dp < ldp)
7031				{
7032					if (bslashmode)
7033					{
7034						*dp++ = *sp;
7035						bslashmode = FALSE;
7036					}
7037					else if (quotemode && *sp != '"' &&
7038						*sp != '\\')
7039					{
7040						*dp++ = *sp;
7041					}
7042					else switch(*dp++ = *sp)
7043					{
7044						case '\\':
7045						bslashmode = TRUE;
7046						break;
7047
7048						case '(':
7049						cmntcnt++;
7050						break;
7051
7052						case ')':
7053						cmntcnt--;
7054						break;
7055
7056						case '<':
7057						anglecnt++;
7058						break;
7059
7060						case '>':
7061						anglecnt--;
7062						break;
7063
7064						case ' ':
7065						spacecnt++;
7066						break;
7067
7068						case '"':
7069						quotemode = !quotemode;
7070						break;
7071					}
7072				}
7073			}
7074		}
7075		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7076		    bslashmode || spacecnt != 0)
7077		{
7078			sm_syslog(LOG_WARNING, NOQID,
7079				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
7080				  map->map_mname, name);
7081			return NULL;
7082		}
7083
7084		*dp = '\0';
7085
7086		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7087	}
7088	return regex_map_rewrite(map, "", (size_t)0, av);
7089}
7090#endif /* MAP_REGEX */
7091/*
7092**  NSD modules
7093*/
7094#ifdef MAP_NSD
7095
7096# include <ndbm.h>
7097# define _DATUM_DEFINED
7098# include <ns_api.h>
7099
7100typedef struct ns_map_list
7101{
7102	ns_map_t *map;
7103	char *mapname;
7104	struct ns_map_list *next;
7105} ns_map_list_t;
7106
7107static ns_map_t *
7108ns_map_t_find(mapname)
7109	char *mapname;
7110{
7111	static ns_map_list_t *ns_maps = NULL;
7112	ns_map_list_t *ns_map;
7113
7114	/* walk the list of maps looking for the correctly named map */
7115	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7116	{
7117		if (strcmp(ns_map->mapname, mapname) == 0)
7118			break;
7119	}
7120
7121	/* if we are looking at a NULL ns_map_list_t, then create a new one */
7122	if (ns_map == NULL)
7123	{
7124		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
7125		ns_map->mapname = newstr(mapname);
7126		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
7127		ns_map->next = ns_maps;
7128		ns_maps = ns_map;
7129	}
7130	return ns_map->map;
7131}
7132
7133char *
7134nsd_map_lookup(map, name, av, statp)
7135	MAP *map;
7136	char *name;
7137	char **av;
7138	int *statp;
7139{
7140	int buflen, r;
7141	char *p;
7142	ns_map_t *ns_map;
7143	char keybuf[MAXNAME + 1];
7144	char buf[MAXLINE];
7145
7146	if (tTd(38, 20))
7147		dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7148
7149	buflen = strlen(name);
7150	if (buflen > sizeof keybuf - 1)
7151		buflen = sizeof keybuf - 1;
7152	memmove(keybuf, name, buflen);
7153	keybuf[buflen] = '\0';
7154	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7155		makelower(keybuf);
7156
7157	ns_map = ns_map_t_find(map->map_file);
7158	if (ns_map == NULL)
7159	{
7160		if (tTd(38, 20))
7161			dprintf("nsd_map_t_find failed\n");
7162		*statp = EX_UNAVAILABLE;
7163		return NULL;
7164	}
7165	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, buf, MAXLINE);
7166	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7167	{
7168		*statp = EX_TEMPFAIL;
7169		return NULL;
7170	}
7171	if (r == NS_BADREQ || r == NS_NOPERM)
7172	{
7173		*statp = EX_CONFIG;
7174		return NULL;
7175	}
7176	if (r != NS_SUCCESS)
7177	{
7178		*statp = EX_NOTFOUND;
7179		return NULL;
7180	}
7181
7182	*statp = EX_OK;
7183
7184	/* Null out trailing \n */
7185	if ((p = strchr(buf, '\n')) != NULL)
7186		*p = '\0';
7187
7188	return map_rewrite(map, buf, strlen(buf), av);
7189}
7190#endif /* MAP_NSD */
7191
7192char *
7193arith_map_lookup(map, name, av, statp)
7194	MAP *map;
7195	char *name;
7196	char **av;
7197	int *statp;
7198{
7199	long r;
7200	long v[2];
7201	bool res = FALSE;
7202	bool boolres;
7203	static char result[16];
7204	char **cpp;
7205
7206	if (tTd(38, 2))
7207	{
7208		dprintf("arith_map_lookup: key '%s'\n", name);
7209		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7210			dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7211	}
7212	r = 0;
7213	boolres = FALSE;
7214	cpp = av;
7215	*statp = EX_OK;
7216
7217	/*
7218	**  read arguments for arith map
7219	**  - no check is made whether they are really numbers
7220	**  - just ignores args after the second
7221	*/
7222	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7223		v[r++] = strtol(*cpp, NULL, 0);
7224
7225	/* operator and (at least) two operands given? */
7226	if (name != NULL && r == 2)
7227	{
7228		switch(*name)
7229		{
7230#if _FFR_ARITH
7231		  case '|':
7232			r = v[0] | v[1];
7233			break;
7234
7235		  case '&':
7236			r = v[0] & v[1];
7237			break;
7238
7239		  case '%':
7240			if (v[1] == 0)
7241				return NULL;
7242			r = v[0] % v[1];
7243			break;
7244#endif /* _FFR_ARITH */
7245
7246		  case '+':
7247			r = v[0] + v[1];
7248			break;
7249
7250		  case '-':
7251			r = v[0] - v[1];
7252			break;
7253
7254		  case '*':
7255			r = v[0] * v[1];
7256			break;
7257
7258		  case '/':
7259			if (v[1] == 0)
7260				return NULL;
7261			r = v[0] / v[1];
7262			break;
7263
7264		  case 'l':
7265			res = v[0] < v[1];
7266			boolres = TRUE;
7267			break;
7268
7269		  case '=':
7270			res = v[0] == v[1];
7271			boolres = TRUE;
7272			break;
7273
7274		  default:
7275			/* XXX */
7276			*statp = EX_CONFIG;
7277			if (LogLevel > 10)
7278				sm_syslog(LOG_WARNING, NOQID,
7279					  "arith_map: unknown operator %c",
7280					  isprint(*name) ? *name : '?');
7281			return NULL;
7282		}
7283		if (boolres)
7284			snprintf(result, sizeof result, res ? "TRUE" : "FALSE");
7285		else
7286			snprintf(result, sizeof result, "%ld", r);
7287		return result;
7288	}
7289	*statp = EX_CONFIG;
7290	return NULL;
7291}
7292