source: trunk/third/sendmail/src/map.c @ 12554

Revision 12554, 102.1 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1992, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)map.c       8.261 (Berkeley) 2/2/1999";
15#endif /* not lint */
16
17#include "sendmail.h"
18
19#ifdef NDBM
20# include <ndbm.h>
21# ifdef R_FIRST
22  ERROR README: You are running the Berkeley DB version of ndbm.h.  See
23  ERROR README: the README file about tweaking Berkeley DB so it can
24  ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
25  ERROR README: and use -DNEWDB instead.
26# endif
27#endif
28#ifdef NEWDB
29# include <db.h>
30# ifndef DB_VERSION_MAJOR
31#  define DB_VERSION_MAJOR 1
32# endif
33#endif
34#ifdef NIS
35  struct dom_binding;   /* forward reference needed on IRIX */
36# include <rpcsvc/ypclnt.h>
37# ifdef NDBM
38#  define NDBM_YP_COMPAT        /* create YP-compatible NDBM files */
39# endif
40#endif
41
42/*
43**  MAP.C -- implementations for various map classes.
44**
45**      Each map class implements a series of functions:
46**
47**      bool map_parse(MAP *map, char *args)
48**              Parse the arguments from the config file.  Return TRUE
49**              if they were ok, FALSE otherwise.  Fill in map with the
50**              values.
51**
52**      char *map_lookup(MAP *map, char *key, char **args, int *pstat)
53**              Look up the key in the given map.  If found, do any
54**              rewriting the map wants (including "args" if desired)
55**              and return the value.  Set *pstat to the appropriate status
56**              on error and return NULL.  Args will be NULL if called
57**              from the alias routines, although this should probably
58**              not be relied upon.  It is suggested you call map_rewrite
59**              to return the results -- it takes care of null termination
60**              and uses a dynamically expanded buffer as needed.
61**
62**      void map_store(MAP *map, char *key, char *value)
63**              Store the key:value pair in the map.
64**
65**      bool map_open(MAP *map, int mode)
66**              Open the map for the indicated mode.  Mode should
67**              be either O_RDONLY or O_RDWR.  Return TRUE if it
68**              was opened successfully, FALSE otherwise.  If the open
69**              failed an the MF_OPTIONAL flag is not set, it should
70**              also print an error.  If the MF_ALIAS bit is set
71**              and this map class understands the @:@ convention, it
72**              should call aliaswait() before returning.
73**
74**      void map_close(MAP *map)
75**              Close the map.
76**
77**      This file also includes the implementation for getcanonname.
78**      It is currently implemented in a pretty ad-hoc manner; it ought
79**      to be more properly integrated into the map structure.
80*/
81
82#define DBMMODE         0644
83
84#ifndef EX_NOTFOUND
85# define EX_NOTFOUND    EX_NOHOST
86#endif
87
88extern bool     aliaswait __P((MAP *, char *, int));
89extern bool     extract_canonname __P((char *, char *, char[], int));
90
91#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
92# define LOCK_ON_OPEN   1       /* we can open/create a locked file */
93#else
94# define LOCK_ON_OPEN   0       /* no such luck -- bend over backwards */
95#endif
96
97#ifndef O_ACCMODE
98# define O_ACCMODE      (O_RDONLY|O_WRONLY|O_RDWR)
99#endif
100/*
101**  MAP_PARSEARGS -- parse config line arguments for database lookup
102**
103**      This is a generic version of the map_parse method.
104**
105**      Parameters:
106**              map -- the map being initialized.
107**              ap -- a pointer to the args on the config line.
108**
109**      Returns:
110**              TRUE -- if everything parsed OK.
111**              FALSE -- otherwise.
112**
113**      Side Effects:
114**              null terminates the filename; stores it in map
115*/
116
117bool
118map_parseargs(map, ap)
119        MAP *map;
120        char *ap;
121{
122        register char *p = ap;
123
124        map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
125        for (;;)
126        {
127                while (isascii(*p) && isspace(*p))
128                        p++;
129                if (*p != '-')
130                        break;
131                switch (*++p)
132                {
133                  case 'N':
134                        map->map_mflags |= MF_INCLNULL;
135                        map->map_mflags &= ~MF_TRY0NULL;
136                        break;
137
138                  case 'O':
139                        map->map_mflags &= ~MF_TRY1NULL;
140                        break;
141
142                  case 'o':
143                        map->map_mflags |= MF_OPTIONAL;
144                        break;
145
146                  case 'f':
147                        map->map_mflags |= MF_NOFOLDCASE;
148                        break;
149
150                  case 'm':
151                        map->map_mflags |= MF_MATCHONLY;
152                        break;
153
154                  case 'A':
155                        map->map_mflags |= MF_APPEND;
156                        break;
157
158                  case 'q':
159                        map->map_mflags |= MF_KEEPQUOTES;
160                        break;
161
162                  case 'a':
163                        map->map_app = ++p;
164                        break;
165
166                  case 'T':
167                        map->map_tapp = ++p;
168                        break;
169
170                  case 'k':
171                        while (isascii(*++p) && isspace(*p))
172                                continue;
173                        map->map_keycolnm = p;
174                        break;
175
176                  case 'v':
177                        while (isascii(*++p) && isspace(*p))
178                                continue;
179                        map->map_valcolnm = p;
180                        break;
181
182                  case 'z':
183                        if (*++p != '\\')
184                                map->map_coldelim = *p;
185                        else
186                        {
187                                switch (*++p)
188                                {
189                                  case 'n':
190                                        map->map_coldelim = '\n';
191                                        break;
192
193                                  case 't':
194                                        map->map_coldelim = '\t';
195                                        break;
196
197                                  default:
198                                        map->map_coldelim = '\\';
199                                }
200                        }
201                        break;
202
203                  case 't':
204                        map->map_mflags |= MF_NODEFER;
205                        break;
206
207#ifdef RESERVED_FOR_SUN
208                  case 'd':
209                        map->map_mflags |= MF_DOMAIN_WIDE;
210                        break;
211
212                  case 's':
213                        /* info type */
214                        break;
215#endif
216                }
217                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
218                        p++;
219                if (*p != '\0')
220                        *p++ = '\0';
221        }
222        if (map->map_app != NULL)
223                map->map_app = newstr(map->map_app);
224        if (map->map_tapp != NULL)
225                map->map_tapp = newstr(map->map_tapp);
226        if (map->map_keycolnm != NULL)
227                map->map_keycolnm = newstr(map->map_keycolnm);
228        if (map->map_valcolnm != NULL)
229                map->map_valcolnm = newstr(map->map_valcolnm);
230
231        if (*p != '\0')
232        {
233                map->map_file = p;
234                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
235                        p++;
236                if (*p != '\0')
237                        *p++ = '\0';
238                map->map_file = newstr(map->map_file);
239        }
240
241        while (*p != '\0' && isascii(*p) && isspace(*p))
242                p++;
243        if (*p != '\0')
244                map->map_rebuild = newstr(p);
245
246        if (map->map_file == NULL &&
247            !bitset(MCF_OPTFILE, map->map_class->map_cflags))
248        {
249                syserr("No file name for %s map %s",
250                        map->map_class->map_cname, map->map_mname);
251                return FALSE;
252        }
253        return TRUE;
254}
255/*
256**  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
257**
258**      It also adds the map_app string.  It can be used as a utility
259**      in the map_lookup method.
260**
261**      Parameters:
262**              map -- the map that causes this.
263**              s -- the string to rewrite, NOT necessarily null terminated.
264**              slen -- the length of s.
265**              av -- arguments to interpolate into buf.
266**
267**      Returns:
268**              Pointer to rewritten result.  This is static data that
269**              should be copied if it is to be saved!
270**
271**      Side Effects:
272**              none.
273*/
274
275char *
276map_rewrite(map, s, slen, av)
277        register MAP *map;
278        register const char *s;
279        size_t slen;
280        char **av;
281{
282        register char *bp;
283        register char c;
284        char **avp;
285        register char *ap;
286        size_t l;
287        size_t len;
288        static size_t buflen = 0;
289        static char *buf = NULL;
290
291        if (tTd(39, 1))
292        {
293                printf("map_rewrite(%.*s), av =", (int)slen, s);
294                if (av == NULL)
295                        printf(" (nullv)");
296                else
297                {
298                        for (avp = av; *avp != NULL; avp++)
299                                printf("\n\t%s", *avp);
300                }
301                printf("\n");
302        }
303
304        /* count expected size of output (can safely overestimate) */
305        l = len = slen;
306        if (av != NULL)
307        {
308                const char *sp = s;
309
310                while (l-- > 0 && (c = *sp++) != '\0')
311                {
312                        if (c != '%')
313                                continue;
314                        if (l-- <= 0)
315                                break;
316                        c = *sp++;
317                        if (!(isascii(c) && isdigit(c)))
318                                continue;
319                        for (avp = av; --c >= '0' && *avp != NULL; avp++)
320                                continue;
321                        if (*avp == NULL)
322                                continue;
323                        len += strlen(*avp);
324                }
325        }
326        if (map->map_app != NULL)
327                len += strlen(map->map_app);
328        if (buflen < ++len)
329        {
330                /* need to malloc additional space */
331                buflen = len;
332                if (buf != NULL)
333                        free(buf);
334                buf = xalloc(buflen);
335        }
336
337        bp = buf;
338        if (av == NULL)
339        {
340                bcopy(s, bp, slen);
341                bp += slen;
342        }
343        else
344        {
345                while (slen-- > 0 && (c = *s++) != '\0')
346                {
347                        if (c != '%')
348                        {
349  pushc:
350                                *bp++ = c;
351                                continue;
352                        }
353                        if (slen-- <= 0 || (c = *s++) == '\0')
354                                c = '%';
355                        if (c == '%')
356                                goto pushc;
357                        if (!(isascii(c) && isdigit(c)))
358                        {
359                                *bp++ = '%';
360                                goto pushc;
361                        }
362                        for (avp = av; --c >= '0' && *avp != NULL; avp++)
363                                continue;
364                        if (*avp == NULL)
365                                continue;
366
367                        /* transliterate argument into output string */
368                        for (ap = *avp; (c = *ap++) != '\0'; )
369                                *bp++ = c;
370                }
371        }
372        if (map->map_app != NULL)
373                strcpy(bp, map->map_app);
374        else
375                *bp = '\0';
376        if (tTd(39, 1))
377                printf("map_rewrite => %s\n", buf);
378        return buf;
379}
380/*
381**  INITMAPS -- initialize for aliasing
382**
383**      Parameters:
384**              rebuild -- if TRUE, this rebuilds the cached versions.
385**              e -- current envelope.
386**
387**      Returns:
388**              none.
389**
390**      Side Effects:
391**              initializes aliases:
392**              if alias database:  opens the database.
393**              if no database available: reads aliases into the symbol table.
394*/
395
396void
397initmaps(rebuild, e)
398        bool rebuild;
399        register ENVELOPE *e;
400{
401        extern void map_init __P((STAB *, int));
402
403#if XDEBUG
404        checkfd012("entering initmaps");
405#endif
406        CurEnv = e;
407
408        stabapply(map_init, 0);
409        stabapply(map_init, rebuild ? 2 : 1);
410#if XDEBUG
411        checkfd012("exiting initmaps");
412#endif
413}
414
415void
416map_init(s, pass)
417        register STAB *s;
418        int pass;
419{
420        bool rebuildable;
421        register MAP *map;
422
423        /* has to be a map */
424        if (s->s_type != ST_MAP)
425                return;
426
427        map = &s->s_map;
428        if (!bitset(MF_VALID, map->map_mflags))
429                return;
430
431        if (tTd(38, 2))
432                printf("map_init(%s:%s, %s, %d)\n",
433                        map->map_class->map_cname == NULL ? "NULL" :
434                                map->map_class->map_cname,
435                        map->map_mname == NULL ? "NULL" : map->map_mname,
436                        map->map_file == NULL ? "NULL" : map->map_file,
437                        pass);
438
439        /*
440        **  Pass 0 opens all non-rebuildable maps.
441        **  Pass 1 opens all rebuildable maps for read.
442        **  Pass 2 rebuilds all rebuildable maps.
443        */
444
445        rebuildable = (bitset(MF_ALIAS, map->map_mflags) &&
446                       bitset(MCF_REBUILDABLE, map->map_class->map_cflags));
447
448        if ((pass == 0 && rebuildable) ||
449            ((pass == 1 || pass == 2) && !rebuildable))
450        {
451                if (tTd(38, 3))
452                        printf("\twrong pass (pass = %d, rebuildable = %d)\n",
453                               pass, rebuildable);
454                return;
455        }
456
457        /* if already open, close it (for nested open) */
458        if (bitset(MF_OPEN, map->map_mflags))
459        {
460                map->map_class->map_close(map);
461                map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
462        }
463
464        if (pass == 2)
465        {
466                (void) rebuildaliases(map, FALSE);
467                return;
468        }
469
470        if (map->map_class->map_open(map, O_RDONLY))
471        {
472                if (tTd(38, 4))
473                        printf("\t%s:%s %s: valid\n",
474                                map->map_class->map_cname == NULL ? "NULL" :
475                                        map->map_class->map_cname,
476                                map->map_mname == NULL ? "NULL" :
477                                        map->map_mname,
478                                map->map_file == NULL ? "NULL" :
479                                        map->map_file);
480                map->map_mflags |= MF_OPEN;
481                map->map_pid = getpid();
482        }
483        else
484        {
485                if (tTd(38, 4))
486                        printf("\t%s:%s %s: invalid: %s\n",
487                                map->map_class->map_cname == NULL ? "NULL" :
488                                        map->map_class->map_cname,
489                                map->map_mname == NULL ? "NULL" :
490                                        map->map_mname,
491                                map->map_file == NULL ? "NULL" :
492                                        map->map_file,
493                                errstring(errno));
494                if (!bitset(MF_OPTIONAL, map->map_mflags))
495                {
496                        extern MAPCLASS BogusMapClass;
497
498                        map->map_class = &BogusMapClass;
499                        map->map_mflags |= MF_OPEN;
500                        map->map_pid = getpid();
501                }
502        }
503}
504/*
505**  CLOSEMAPS -- close all open maps opened by the current pid.
506**
507**      Parameters:
508**              none
509**
510**      Returns:
511**              none.
512*/
513
514void
515closemaps()
516{
517        extern void map_close __P((STAB *, int));
518
519        stabapply(map_close, 0);
520}
521
522/* ARGSUSED1 */
523void
524map_close(s, unused)
525        register STAB *s;
526        int unused;
527{
528        MAP *map;
529
530        if (s->s_type != ST_MAP)
531                return;
532       
533        map = &s->s_map;
534
535        if (!bitset(MF_VALID, map->map_mflags) ||
536            !bitset(MF_OPEN, map->map_mflags) ||
537            map->map_pid != getpid())
538                return;
539       
540        if (tTd(38, 5))
541                printf("closemaps: closing %s (%s)\n",
542                       map->map_mname == NULL ? "NULL" : map->map_mname,
543                       map->map_file == NULL ? "NULL" : map->map_file);
544       
545        map->map_class->map_close(map);
546        map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
547}
548/*
549**  GETCANONNAME -- look up name using service switch
550**
551**      Parameters:
552**              host -- the host name to look up.
553**              hbsize -- the size of the host buffer.
554**              trymx -- if set, try MX records.
555**
556**      Returns:
557**              TRUE -- if the host was found.
558**              FALSE -- otherwise.
559*/
560
561bool
562getcanonname(host, hbsize, trymx)
563        char *host;
564        int hbsize;
565        bool trymx;
566{
567        int nmaps;
568        int mapno;
569        bool found = FALSE;
570        bool got_tempfail = FALSE;
571        auto int stat;
572        char *maptype[MAXMAPSTACK];
573        short mapreturn[MAXMAPACTIONS];
574
575        nmaps = switch_map_find("hosts", maptype, mapreturn);
576        for (mapno = 0; mapno < nmaps; mapno++)
577        {
578                int i;
579
580                if (tTd(38, 20))
581                        printf("getcanonname(%s), trying %s\n",
582                                host, maptype[mapno]);
583                if (strcmp("files", maptype[mapno]) == 0)
584                {
585                        extern bool text_getcanonname __P((char *, int, int *));
586
587                        found = text_getcanonname(host, hbsize, &stat);
588                }
589#ifdef NIS
590                else if (strcmp("nis", maptype[mapno]) == 0)
591                {
592                        extern bool nis_getcanonname __P((char *, int, int *));
593
594                        found = nis_getcanonname(host, hbsize, &stat);
595                }
596#endif
597#ifdef NISPLUS
598                else if (strcmp("nisplus", maptype[mapno]) == 0)
599                {
600                        extern bool nisplus_getcanonname __P((char *, int, int *));
601
602                        found = nisplus_getcanonname(host, hbsize, &stat);
603                }
604#endif
605#if NAMED_BIND
606                else if (strcmp("dns", maptype[mapno]) == 0)
607                {
608                        extern bool dns_getcanonname __P((char *, int, bool, int *));
609
610                        found = dns_getcanonname(host, hbsize, trymx, &stat);
611                }
612#endif
613#if NETINFO
614                else if (strcmp("netinfo", maptype[mapno]) == 0)
615                {
616                        extern bool ni_getcanonname __P((char *, int, int *));
617
618                        found = ni_getcanonname(host, hbsize, &stat);
619                }
620#endif
621                else
622                {
623                        found = FALSE;
624                        stat = EX_UNAVAILABLE;
625                }
626
627                /*
628                **  Heuristic: if $m is not set, we are running during system
629                **  startup.  In this case, when a name is apparently found
630                **  but has no dot, treat is as not found.  This avoids
631                **  problems if /etc/hosts has no FQDN but is listed first
632                **  in the service switch.
633                */
634
635                if (found &&
636                    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
637                        break;
638
639                /* see if we should continue */
640                if (stat == EX_TEMPFAIL)
641                {
642                        i = MA_TRYAGAIN;
643                        got_tempfail = TRUE;
644                }
645                else if (stat == EX_NOTFOUND)
646                        i = MA_NOTFOUND;
647                else
648                        i = MA_UNAVAIL;
649                if (bitset(1 << mapno, mapreturn[i]))
650                        break;
651        }
652
653        if (found)
654        {
655                char *d;
656
657                if (tTd(38, 20))
658                        printf("getcanonname(%s), found\n", host);
659
660                /*
661                **  If returned name is still single token, compensate
662                **  by tagging on $m.  This is because some sites set
663                **  up their DNS or NIS databases wrong.
664                */
665
666                if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
667                {
668                        d = macvalue('m', CurEnv);
669                        if (d != NULL &&
670                            hbsize > (int) (strlen(host) + strlen(d) + 1))
671                        {
672                                if (host[strlen(host) - 1] != '.')
673                                        strcat(host, ".");
674                                strcat(host, d);
675                        }
676                        else
677                        {
678                                return FALSE;
679                        }
680                }
681                return TRUE;
682        }
683
684        if (tTd(38, 20))
685                printf("getcanonname(%s), failed, stat=%d\n", host, stat);
686
687#if NAMED_BIND
688        if (got_tempfail)
689                h_errno = TRY_AGAIN;
690        else
691                h_errno = HOST_NOT_FOUND;
692#endif
693
694        return FALSE;
695}
696/*
697**  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
698**
699**      Parameters:
700**              name -- the name against which to match.
701**              line -- the /etc/hosts line.
702**              cbuf -- the location to store the result.
703**              cbuflen -- the size of cbuf.
704**
705**      Returns:
706**              TRUE -- if the line matched the desired name.
707**              FALSE -- otherwise.
708*/
709
710bool
711extract_canonname(name, line, cbuf, cbuflen)
712        char *name;
713        char *line;
714        char cbuf[];
715        int cbuflen;
716{
717        int i;
718        char *p;
719        bool found = FALSE;
720        extern char *get_column __P((char *, int, char, char *, int));
721
722        cbuf[0] = '\0';
723        if (line[0] == '#')
724                return FALSE;
725
726        for (i = 1; ; i++)
727        {
728                char nbuf[MAXNAME + 1];
729
730                p = get_column(line, i, '\0', nbuf, sizeof nbuf);
731                if (p == NULL)
732                        break;
733                if (*p == '\0')
734                        continue;
735                if (cbuf[0] == '\0' ||
736                    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
737                {
738                        snprintf(cbuf, cbuflen, "%s", p);
739                }
740                if (strcasecmp(name, p) == 0)
741                        found = TRUE;
742        }
743        if (found && strchr(cbuf, '.') == NULL)
744        {
745                /* try to add a domain on the end of the name */
746                char *domain = macvalue('m', CurEnv);
747
748                if (domain != NULL &&
749                    strlen(domain) + strlen(cbuf) + 1 < cbuflen)
750                {
751                        p = &cbuf[strlen(cbuf)];
752                        *p++ = '.';
753                        strcpy(p, domain);
754                }
755        }
756        return found;
757}
758/*
759**  NDBM modules
760*/
761
762#ifdef NDBM
763
764/*
765**  NDBM_MAP_OPEN -- DBM-style map open
766*/
767
768bool
769ndbm_map_open(map, mode)
770        MAP *map;
771        int mode;
772{
773        register DBM *dbm;
774        struct stat st;
775        int dfd;
776        int pfd;
777        int sff;
778        int ret;
779        int smode = S_IREAD;
780        char dirfile[MAXNAME + 1];
781        char pagfile[MAXNAME + 1];
782        struct stat std, stp;
783
784        if (tTd(38, 2))
785                printf("ndbm_map_open(%s, %s, %d)\n",
786                        map->map_mname, map->map_file, mode);
787        map->map_lockfd = -1;
788        mode &= O_ACCMODE;
789
790        /* do initial file and directory checks */
791        snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file);
792        snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file);
793        sff = SFF_ROOTOK|SFF_REGONLY;
794        if (mode == O_RDWR)
795        {
796                sff |= SFF_CREAT;
797                if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
798                        sff |= SFF_NOSLINK;
799                if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
800                        sff |= SFF_NOHLINK;
801                smode = S_IWRITE;
802        }
803        else
804        {
805                if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
806                        sff |= SFF_NOWLINK;
807        }
808        if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
809                sff |= SFF_SAFEDIRPATH;
810        ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
811                            sff, smode, &std);
812        if (ret == 0)
813                ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
814                               sff, smode, &stp);
815        if (ret == ENOENT && AutoRebuild &&
816            bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
817            (bitset(MF_IMPL_NDBM, map->map_mflags) ||
818             bitset(MF_ALIAS, map->map_mflags)) &&
819            mode == O_RDONLY)
820        {
821                bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
822                extern bool impl_map_open __P((MAP *, int));
823
824                /* may be able to rebuild */
825                map->map_mflags &= ~MF_IMPL_NDBM;
826                if (!rebuildaliases(map, TRUE))
827                        return FALSE;
828                if (impl)
829                        return impl_map_open(map, O_RDONLY);
830                else
831                        return ndbm_map_open(map, O_RDONLY);
832        }
833        if (ret != 0)
834        {
835                char *prob = "unsafe";
836
837                /* cannot open this map */
838                if (ret == ENOENT)
839                        prob = "missing";
840                if (tTd(38, 2))
841                        printf("\t%s map file: %d\n", prob, ret);
842                if (!bitset(MF_OPTIONAL, map->map_mflags))
843                        syserr("dbm map \"%s\": %s map file %s",
844                                map->map_mname, prob, map->map_file);
845                return FALSE;
846        }
847        if (std.st_mode == ST_MODE_NOFILE)
848                mode |= O_CREAT|O_EXCL;
849
850#if LOCK_ON_OPEN
851        if (mode == O_RDONLY)
852                mode |= O_SHLOCK;
853        else
854                mode |= O_TRUNC|O_EXLOCK;
855#else
856        if ((mode & O_ACCMODE) == O_RDWR)
857        {
858# if NOFTRUNCATE
859                /*
860                **  Warning: race condition.  Try to lock the file as
861                **  quickly as possible after opening it.
862                **      This may also have security problems on some systems,
863                **      but there isn't anything we can do about it.
864                */
865
866                mode |= O_TRUNC;
867# else
868                /*
869                **  This ugly code opens the map without truncating it,
870                **  locks the file, then truncates it.  Necessary to
871                **  avoid race conditions.
872                */
873
874                int dirfd;
875                int pagfd;
876                int sff = SFF_CREAT|SFF_OPENASROOT;
877
878                if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
879                        sff |= SFF_NOSLINK;
880                if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
881                        sff |= SFF_NOHLINK;
882
883                dirfd = safeopen(dirfile, mode, DBMMODE, sff);
884                pagfd = safeopen(pagfile, mode, DBMMODE, sff);
885
886                if (dirfd < 0 || pagfd < 0)
887                {
888                        int save_errno = errno;
889
890                        if (dirfd >= 0)
891                                (void) close(dirfd);
892                        if (pagfd >= 0)
893                                (void) close(pagfd);
894                        errno = save_errno;
895                        syserr("ndbm_map_open: cannot create database %s",
896                                map->map_file);
897                        return FALSE;
898                }
899                if (ftruncate(dirfd, (off_t) 0) < 0 ||
900                    ftruncate(pagfd, (off_t) 0) < 0)
901                {
902                        int save_errno = errno;
903
904                        (void) close(dirfd);
905                        (void) close(pagfd);
906                        errno = save_errno;
907                        syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
908                                map->map_file);
909                        return FALSE;
910                }
911
912                /* if new file, get "before" bits for later filechanged check */
913                if (std.st_mode == ST_MODE_NOFILE &&
914                    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
915                {
916                        int save_errno = errno;
917
918                        (void) close(dirfd);
919                        (void) close(pagfd);
920                        errno = save_errno;
921                        syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
922                                map->map_file);
923                        return FALSE;
924                }
925
926                /* have to save the lock for the duration (bletch) */
927                map->map_lockfd = dirfd;
928                close(pagfd);
929
930                /* twiddle bits for dbm_open */
931                mode &= ~(O_CREAT|O_EXCL);
932# endif
933        }
934#endif
935
936        /* open the database */
937        dbm = dbm_open(map->map_file, mode, DBMMODE);
938        if (dbm == NULL)
939        {
940                int save_errno = errno;
941
942                if (bitset(MF_ALIAS, map->map_mflags) &&
943                    aliaswait(map, ".pag", FALSE))
944                        return TRUE;
945#if !LOCK_ON_OPEN && !NOFTRUNCATE
946                if (map->map_lockfd >= 0)
947                        close(map->map_lockfd);
948#endif
949                errno = save_errno;
950                if (!bitset(MF_OPTIONAL, map->map_mflags))
951                        syserr("Cannot open DBM database %s", map->map_file);
952                return FALSE;
953        }
954        dfd = dbm_dirfno(dbm);
955        pfd = dbm_pagfno(dbm);
956        if (dfd == pfd)
957        {
958                /* heuristic: if files are linked, this is actually gdbm */
959                dbm_close(dbm);
960#if !LOCK_ON_OPEN && !NOFTRUNCATE
961                if (map->map_lockfd >= 0)
962                        close(map->map_lockfd);
963#endif
964                errno = 0;
965                syserr("dbm map \"%s\": cannot support GDBM",
966                        map->map_mname);
967                return FALSE;
968        }
969
970        if (filechanged(dirfile, dfd, &std) ||
971            filechanged(pagfile, pfd, &stp))
972        {
973                int save_errno = errno;
974
975                dbm_close(dbm);
976#if !LOCK_ON_OPEN && !NOFTRUNCATE
977                if (map->map_lockfd >= 0)
978                        close(map->map_lockfd);
979#endif
980                errno = save_errno;
981                syserr("ndbm_map_open(%s): file changed after open",
982                        map->map_file);
983                return FALSE;
984        }
985
986        map->map_db1 = (ARBPTR_T) dbm;
987        if (mode == O_RDONLY)
988        {
989#if LOCK_ON_OPEN
990                if (dfd >= 0)
991                        (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
992                if (pfd >= 0)
993                        (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
994#endif
995                if (bitset(MF_ALIAS, map->map_mflags) &&
996                    !aliaswait(map, ".pag", TRUE))
997                        return FALSE;
998        }
999        else
1000        {
1001                map->map_mflags |= MF_LOCKED;
1002#if _FFR_TRUSTED_USER
1003                if (geteuid() == 0 && TrustedUid != 0)
1004                {
1005                        if (fchown(dfd, TrustedUid, -1) < 0 ||
1006                            fchown(pfd, TrustedUid, -1) < 0)
1007                        {
1008                                int err = errno;
1009
1010                                sm_syslog(LOG_ALERT, NOQID,
1011                                          "ownership change on %s failed: %s",
1012                                          map->map_file, errstring(err));
1013                                message("050 ownership change on %s failed: %s",
1014                                        map->map_file, errstring(err));
1015                        }
1016                }
1017#endif
1018        }
1019        if (fstat(dfd, &st) >= 0)
1020                map->map_mtime = st.st_mtime;
1021        return TRUE;
1022}
1023
1024
1025/*
1026**  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1027*/
1028
1029char *
1030ndbm_map_lookup(map, name, av, statp)
1031        MAP *map;
1032        char *name;
1033        char **av;
1034        int *statp;
1035{
1036        datum key, val;
1037        int fd;
1038        char keybuf[MAXNAME + 1];
1039        struct stat stbuf;
1040
1041        if (tTd(38, 20))
1042                printf("ndbm_map_lookup(%s, %s)\n",
1043                        map->map_mname, name);
1044
1045        key.dptr = name;
1046        key.dsize = strlen(name);
1047        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1048        {
1049                if (key.dsize > sizeof keybuf - 1)
1050                        key.dsize = sizeof keybuf - 1;
1051                bcopy(key.dptr, keybuf, key.dsize);
1052                keybuf[key.dsize] = '\0';
1053                makelower(keybuf);
1054                key.dptr = keybuf;
1055        }
1056lockdbm:
1057        fd = dbm_dirfno((DBM *) map->map_db1);
1058        if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1059                (void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
1060        if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1061        {
1062                /* Reopen the database to sync the cache */
1063                int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1064                                                                 : O_RDONLY;
1065
1066                map->map_class->map_close(map);
1067                map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1068                if (map->map_class->map_open(map, omode))
1069                {
1070                        map->map_mflags |= MF_OPEN;
1071                        map->map_pid = getpid();
1072                        if ((omode && O_ACCMODE) == O_RDWR)
1073                                map->map_mflags |= MF_WRITABLE;
1074                        goto lockdbm;
1075                }
1076                else
1077                {
1078                        if (!bitset(MF_OPTIONAL, map->map_mflags))
1079                        {
1080                                extern MAPCLASS BogusMapClass;
1081
1082                                *statp = EX_TEMPFAIL;
1083                                map->map_class = &BogusMapClass;
1084                                map->map_mflags |= MF_OPEN;
1085                                map->map_pid = getpid();
1086                                syserr("Cannot reopen NDBM database %s",
1087                                        map->map_file);
1088                        }
1089                        return NULL;
1090                }
1091        }
1092        val.dptr = NULL;
1093        if (bitset(MF_TRY0NULL, map->map_mflags))
1094        {
1095                val = dbm_fetch((DBM *) map->map_db1, key);
1096                if (val.dptr != NULL)
1097                        map->map_mflags &= ~MF_TRY1NULL;
1098        }
1099        if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1100        {
1101                key.dsize++;
1102                val = dbm_fetch((DBM *) map->map_db1, key);
1103                if (val.dptr != NULL)
1104                        map->map_mflags &= ~MF_TRY0NULL;
1105        }
1106        if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1107                (void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
1108        if (val.dptr == NULL)
1109                return NULL;
1110        if (bitset(MF_MATCHONLY, map->map_mflags))
1111                return map_rewrite(map, name, strlen(name), NULL);
1112        else
1113                return map_rewrite(map, val.dptr, val.dsize, av);
1114}
1115
1116
1117/*
1118**  NDBM_MAP_STORE -- store a datum in the database
1119*/
1120
1121void
1122ndbm_map_store(map, lhs, rhs)
1123        register MAP *map;
1124        char *lhs;
1125        char *rhs;
1126{
1127        datum key;
1128        datum data;
1129        int stat;
1130        char keybuf[MAXNAME + 1];
1131
1132        if (tTd(38, 12))
1133                printf("ndbm_map_store(%s, %s, %s)\n",
1134                        map->map_mname, lhs, rhs);
1135
1136        key.dsize = strlen(lhs);
1137        key.dptr = lhs;
1138        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1139        {
1140                if (key.dsize > sizeof keybuf - 1)
1141                        key.dsize = sizeof keybuf - 1;
1142                bcopy(key.dptr, keybuf, key.dsize);
1143                keybuf[key.dsize] = '\0';
1144                makelower(keybuf);
1145                key.dptr = keybuf;
1146        }
1147
1148        data.dsize = strlen(rhs);
1149        data.dptr = rhs;
1150
1151        if (bitset(MF_INCLNULL, map->map_mflags))
1152        {
1153                key.dsize++;
1154                data.dsize++;
1155        }
1156
1157        stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1158        if (stat > 0)
1159        {
1160                if (!bitset(MF_APPEND, map->map_mflags))
1161                        message("050 Warning: duplicate alias name %s", lhs);
1162                else
1163                {
1164                        static char *buf = NULL;
1165                        static int bufsiz = 0;
1166                        auto int xstat;
1167                        datum old;
1168
1169                        old.dptr = ndbm_map_lookup(map, key.dptr,
1170                                                   (char **)NULL, &xstat);
1171                        if (old.dptr != NULL && *(char *) old.dptr != '\0')
1172                        {
1173                                old.dsize = strlen(old.dptr);
1174                                if (data.dsize + old.dsize + 2 > bufsiz)
1175                                {
1176                                        if (buf != NULL)
1177                                                (void) free(buf);
1178                                        bufsiz = data.dsize + old.dsize + 2;
1179                                        buf = xalloc(bufsiz);
1180                                }
1181                                snprintf(buf, bufsiz, "%s,%s",
1182                                        data.dptr, old.dptr);
1183                                data.dsize = data.dsize + old.dsize + 1;
1184                                data.dptr = buf;
1185                                if (tTd(38, 9))
1186                                        printf("ndbm_map_store append=%s\n", data.dptr);
1187                        }
1188                }
1189                stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
1190        }
1191        if (stat != 0)
1192                syserr("readaliases: dbm put (%s)", lhs);
1193}
1194
1195
1196/*
1197**  NDBM_MAP_CLOSE -- close the database
1198*/
1199
1200void
1201ndbm_map_close(map)
1202        register MAP  *map;
1203{
1204        if (tTd(38, 9))
1205                printf("ndbm_map_close(%s, %s, %lx)\n",
1206                        map->map_mname, map->map_file, map->map_mflags);
1207
1208        if (bitset(MF_WRITABLE, map->map_mflags))
1209        {
1210#ifdef NDBM_YP_COMPAT
1211                bool inclnull;
1212                char buf[MAXHOSTNAMELEN];
1213
1214                inclnull = bitset(MF_INCLNULL, map->map_mflags);
1215                map->map_mflags &= ~MF_INCLNULL;
1216
1217                if (strstr(map->map_file, "/yp/") != NULL)
1218                {
1219                        long save_mflags = map->map_mflags;
1220
1221                        map->map_mflags |= MF_NOFOLDCASE;
1222
1223                        (void) snprintf(buf, sizeof buf, "%010ld", curtime());
1224                        ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1225
1226                        (void) gethostname(buf, sizeof buf);
1227                        ndbm_map_store(map, "YP_MASTER_NAME", buf);
1228
1229                        map->map_mflags = save_mflags;
1230                }
1231
1232                if (inclnull)
1233                        map->map_mflags |= MF_INCLNULL;
1234#endif
1235
1236                /* write out the distinguished alias */
1237                ndbm_map_store(map, "@", "@");
1238        }
1239        dbm_close((DBM *) map->map_db1);
1240
1241        /* release lock (if needed) */
1242#if !LOCK_ON_OPEN
1243        if (map->map_lockfd >= 0)
1244                (void) close(map->map_lockfd);
1245#endif
1246}
1247
1248#endif
1249/*
1250**  NEWDB (Hash and BTree) Modules
1251*/
1252
1253#ifdef NEWDB
1254
1255/*
1256**  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1257**
1258**      These do rather bizarre locking.  If you can lock on open,
1259**      do that to avoid the condition of opening a database that
1260**      is being rebuilt.  If you don't, we'll try to fake it, but
1261**      there will be a race condition.  If opening for read-only,
1262**      we immediately release the lock to avoid freezing things up.
1263**      We really ought to hold the lock, but guarantee that we won't
1264**      be pokey about it.  That's hard to do.
1265*/
1266
1267#if DB_VERSION_MAJOR < 2
1268extern bool     db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
1269#else
1270extern bool     db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
1271#endif
1272
1273/* these should be K line arguments */
1274#if DB_VERSION_MAJOR < 2
1275# define db_cachesize   cachesize
1276# define h_nelem        nelem
1277# ifndef DB_CACHE_SIZE
1278#  define DB_CACHE_SIZE (1024 * 1024)   /* database memory cache size */
1279# endif
1280# ifndef DB_HASH_NELEM
1281#  define DB_HASH_NELEM 4096            /* (starting) size of hash table */
1282# endif
1283#endif
1284
1285bool
1286bt_map_open(map, mode)
1287        MAP *map;
1288        int mode;
1289{
1290#if DB_VERSION_MAJOR < 2
1291        BTREEINFO btinfo;
1292#else
1293        DB_INFO btinfo;
1294#endif
1295
1296        if (tTd(38, 2))
1297                printf("bt_map_open(%s, %s, %d)\n",
1298                        map->map_mname, map->map_file, mode);
1299
1300        bzero(&btinfo, sizeof btinfo);
1301#ifdef DB_CACHE_SIZE
1302        btinfo.db_cachesize = DB_CACHE_SIZE;
1303#endif
1304        return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1305}
1306
1307bool
1308hash_map_open(map, mode)
1309        MAP *map;
1310        int mode;
1311{
1312#if DB_VERSION_MAJOR < 2
1313        HASHINFO hinfo;
1314#else
1315        DB_INFO hinfo;
1316#endif
1317
1318        if (tTd(38, 2))
1319                printf("hash_map_open(%s, %s, %d)\n",
1320                        map->map_mname, map->map_file, mode);
1321
1322        bzero(&hinfo, sizeof hinfo);
1323#ifdef DB_HASH_NELEM
1324        hinfo.h_nelem = DB_HASH_NELEM;
1325#endif
1326#ifdef DB_CACHE_SIZE
1327        hinfo.db_cachesize = DB_CACHE_SIZE;
1328#endif
1329        return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1330}
1331
1332bool
1333db_map_open(map, mode, mapclassname, dbtype, openinfo)
1334        MAP *map;
1335        int mode;
1336        char *mapclassname;
1337        DBTYPE dbtype;
1338#if DB_VERSION_MAJOR < 2
1339        const void *openinfo;
1340#else
1341        DB_INFO *openinfo;
1342#endif
1343{
1344        DB *db = NULL;
1345        int i;
1346        int omode;
1347        int smode = S_IREAD;
1348        int fd;
1349        int sff;
1350        int saveerrno;
1351        struct stat st;
1352        char buf[MAXNAME + 1];
1353
1354        /* do initial file and directory checks */
1355        snprintf(buf, sizeof buf - 3, "%s", map->map_file);
1356        i = strlen(buf);
1357        if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1358                (void) strcat(buf, ".db");
1359
1360        mode &= O_ACCMODE;
1361        omode = mode;
1362
1363        sff = SFF_ROOTOK|SFF_REGONLY;
1364        if (mode == O_RDWR)
1365        {
1366                sff |= SFF_CREAT;
1367                if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1368                        sff |= SFF_NOSLINK;
1369                if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1370                        sff |= SFF_NOHLINK;
1371                smode = S_IWRITE;
1372        }
1373        else
1374        {
1375                if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1376                        sff |= SFF_NOWLINK;
1377        }
1378        if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1379                sff |= SFF_SAFEDIRPATH;
1380        i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
1381        if (i == ENOENT && AutoRebuild &&
1382            bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
1383            (bitset(MF_IMPL_HASH, map->map_mflags) ||
1384             bitset(MF_ALIAS, map->map_mflags)) &&
1385            mode == O_RDONLY)
1386        {
1387                bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
1388                extern bool impl_map_open __P((MAP *, int));
1389
1390                /* may be able to rebuild */
1391                map->map_mflags &= ~MF_IMPL_HASH;
1392                if (!rebuildaliases(map, TRUE))
1393                        return FALSE;
1394                if (impl)
1395                        return impl_map_open(map, O_RDONLY);
1396                else
1397                        return db_map_open(map, O_RDONLY, mapclassname,
1398                                           dbtype, openinfo);
1399        }
1400
1401        if (i != 0)
1402        {
1403                char *prob = "unsafe";
1404
1405                /* cannot open this map */
1406                if (i == ENOENT)
1407                        prob = "missing";
1408                if (tTd(38, 2))
1409                        printf("\t%s map file: %s\n", prob, errstring(i));
1410                errno = i;
1411                if (!bitset(MF_OPTIONAL, map->map_mflags))
1412                        syserr("%s map \"%s\": %s map file %s",
1413                                mapclassname, map->map_mname, prob, buf);
1414                return FALSE;
1415        }
1416        if (st.st_mode == ST_MODE_NOFILE)
1417                omode |= O_CREAT|O_EXCL;
1418
1419        map->map_lockfd = -1;
1420
1421#if LOCK_ON_OPEN
1422        if (mode == O_RDWR)
1423                omode |= O_TRUNC|O_EXLOCK;
1424        else
1425                omode |= O_SHLOCK;
1426#else
1427        /*
1428        **  Pre-lock the file to avoid race conditions.  In particular,
1429        **  since dbopen returns NULL if the file is zero length, we
1430        **  must have a locked instance around the dbopen.
1431        */
1432
1433        fd = open(buf, omode, DBMMODE);
1434        if (fd < 0)
1435        {
1436                if (!bitset(MF_OPTIONAL, map->map_mflags))
1437                        syserr("db_map_open: cannot pre-open database %s", buf);
1438                return FALSE;
1439        }
1440
1441        /* make sure no baddies slipped in just before the open... */
1442        if (filechanged(buf, fd, &st))
1443        {
1444                int save_errno = errno;
1445
1446                (void) close(fd);
1447                errno = save_errno;
1448                syserr("db_map_open(%s): file changed after pre-open", buf);
1449                return FALSE;
1450        }
1451
1452        /* if new file, get the "before" bits for later filechanged check */
1453        if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
1454        {
1455                int save_errno = errno;
1456
1457                (void) close(fd);
1458                errno = save_errno;
1459                syserr("db_map_open(%s): cannot fstat pre-opened file",
1460                        buf);
1461                return FALSE;
1462        }
1463
1464        /* actually lock the pre-opened file */
1465        if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
1466                syserr("db_map_open: cannot lock %s", buf);
1467
1468        /* set up mode bits for dbopen */
1469        if (mode == O_RDWR)
1470                omode |= O_TRUNC;
1471        omode &= ~(O_EXCL|O_CREAT);
1472#endif
1473
1474#if DB_VERSION_MAJOR < 2
1475        db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
1476#else
1477        {
1478                int flags = 0;
1479
1480                if (mode == O_RDONLY)
1481                        flags |= DB_RDONLY;
1482                if (bitset(O_CREAT, omode))
1483                        flags |= DB_CREATE;
1484                if (bitset(O_TRUNC, omode))
1485                        flags |= DB_TRUNCATE;
1486
1487                errno = db_open(buf, dbtype, flags, DBMMODE,
1488                                NULL, openinfo, &db);
1489        }
1490#endif
1491        saveerrno = errno;
1492
1493#if !LOCK_ON_OPEN
1494        if (mode == O_RDWR)
1495                map->map_lockfd = fd;
1496        else
1497                (void) close(fd);
1498#endif
1499
1500        if (db == NULL)
1501        {
1502                if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1503                    aliaswait(map, ".db", FALSE))
1504                        return TRUE;
1505#if !LOCK_ON_OPEN
1506                if (map->map_lockfd >= 0)
1507                        (void) close(map->map_lockfd);
1508#endif
1509                errno = saveerrno;
1510                if (!bitset(MF_OPTIONAL, map->map_mflags))
1511                        syserr("Cannot open %s database %s",
1512                                mapclassname, buf);
1513                return FALSE;
1514        }
1515
1516#if DB_VERSION_MAJOR < 2
1517        fd = db->fd(db);
1518#else
1519        fd = -1;
1520        errno = db->fd(db, &fd);
1521#endif
1522        if (filechanged(buf, fd, &st))
1523        {
1524                int save_errno = errno;
1525
1526#if DB_VERSION_MAJOR < 2
1527                db->close(db);
1528#else
1529                errno = db->close(db, 0);
1530#endif
1531#if !LOCK_ON_OPEN
1532                if (map->map_lockfd >= 0)
1533                        close(map->map_lockfd);
1534#endif
1535                errno = save_errno;
1536                syserr("db_map_open(%s): file changed after open", buf);
1537                return FALSE;
1538        }
1539
1540        if (mode == O_RDWR)
1541                map->map_mflags |= MF_LOCKED;
1542#if LOCK_ON_OPEN
1543        if (fd >= 0 && mode == O_RDONLY)
1544        {
1545                (void) lockfile(fd, buf, NULL, LOCK_UN);
1546        }
1547#endif
1548
1549        /* try to make sure that at least the database header is on disk */
1550        if (mode == O_RDWR)
1551        {
1552                (void) db->sync(db, 0);
1553#if _FFR_TRUSTED_USER
1554                if (geteuid() == 0 && TrustedUid != 0)
1555                {
1556                        if (fchown(fd, TrustedUid, -1) < 0)
1557                        {
1558                                int err = errno;
1559
1560                                sm_syslog(LOG_ALERT, NOQID,
1561                                          "ownership change on %s failed: %s",
1562                                          buf, errstring(err));
1563                                message("050 ownership change on %s failed: %s",
1564                                        buf, errstring(err));
1565                        }
1566                }
1567#endif
1568        }
1569
1570        if (fd >= 0 && fstat(fd, &st) >= 0)
1571                map->map_mtime = st.st_mtime;
1572
1573        map->map_db2 = (ARBPTR_T) db;
1574        if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1575            !aliaswait(map, ".db", TRUE))
1576                return FALSE;
1577        return TRUE;
1578}
1579
1580
1581/*
1582**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
1583*/
1584
1585char *
1586db_map_lookup(map, name, av, statp)
1587        MAP *map;
1588        char *name;
1589        char **av;
1590        int *statp;
1591{
1592        DBT key, val;
1593        register DB *db = (DB *) map->map_db2;
1594        int i;
1595        int st;
1596        int saveerrno;
1597        int fd;
1598        struct stat stbuf;
1599        char keybuf[MAXNAME + 1];
1600        char buf[MAXNAME + 1];
1601
1602        bzero(&key, sizeof key);
1603        bzero(&val, sizeof val);
1604
1605        if (tTd(38, 20))
1606                printf("db_map_lookup(%s, %s)\n",
1607                        map->map_mname, name);
1608
1609        i = strlen(map->map_file);
1610        if (i > MAXNAME)
1611                i = MAXNAME;
1612        strncpy(buf, map->map_file, i);
1613        buf[i] = '\0';
1614        if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
1615                buf[i - 3] = '\0';
1616
1617        key.size = strlen(name);
1618        if (key.size > sizeof keybuf - 1)
1619                key.size = sizeof keybuf - 1;
1620        key.data = keybuf;
1621        bcopy(name, keybuf, key.size);
1622        keybuf[key.size] = '\0';
1623        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1624                makelower(keybuf);
1625  lockdb:
1626#if DB_VERSION_MAJOR < 2
1627        fd = db->fd(db);
1628#else
1629        fd = -1;
1630        errno = db->fd(db, &fd);
1631#endif
1632        if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1633                (void) lockfile(fd, buf, ".db", LOCK_SH);
1634        if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1635        {
1636                /* Reopen the database to sync the cache */
1637                int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1638                                                                 : O_RDONLY;
1639
1640                map->map_class->map_close(map);
1641                map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1642                if (map->map_class->map_open(map, omode))
1643                {
1644                        map->map_mflags |= MF_OPEN;
1645                        map->map_pid = getpid();
1646                        if ((omode && O_ACCMODE) == O_RDWR)
1647                                map->map_mflags |= MF_WRITABLE;
1648                        db = (DB *) map->map_db2;
1649                        goto lockdb;
1650                }
1651                else
1652                {
1653                        if (!bitset(MF_OPTIONAL, map->map_mflags))
1654                        {
1655                                extern MAPCLASS BogusMapClass;
1656
1657                                *statp = EX_TEMPFAIL;
1658                                map->map_class = &BogusMapClass;
1659                                map->map_mflags |= MF_OPEN;
1660                                map->map_pid = getpid();
1661                                syserr("Cannot reopen DB database %s",
1662                                        map->map_file);
1663                        }
1664                        return NULL;
1665                }
1666        }
1667
1668        st = 1;
1669        if (bitset(MF_TRY0NULL, map->map_mflags))
1670        {
1671#if DB_VERSION_MAJOR < 2
1672                st = db->get(db, &key, &val, 0);
1673#else
1674                errno = db->get(db, NULL, &key, &val, 0);
1675                switch (errno)
1676                {
1677                  case DB_NOTFOUND:
1678                  case DB_KEYEMPTY:
1679                        st = 1;
1680                        break;
1681
1682                  case 0:
1683                        st = 0;
1684                        break;
1685
1686                  default:
1687                        st = -1;
1688                        break;
1689                }
1690#endif
1691                if (st == 0)
1692                        map->map_mflags &= ~MF_TRY1NULL;
1693        }
1694        if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
1695        {
1696                key.size++;
1697#if DB_VERSION_MAJOR < 2
1698                st = db->get(db, &key, &val, 0);
1699#else
1700                errno = db->get(db, NULL, &key, &val, 0);
1701                switch (errno)
1702                {
1703                  case DB_NOTFOUND:
1704                  case DB_KEYEMPTY:
1705                        st = 1;
1706                        break;
1707
1708                  case 0:
1709                        st = 0;
1710                        break;
1711
1712                  default:
1713                        st = -1;
1714                        break;
1715                }
1716#endif
1717                if (st == 0)
1718                        map->map_mflags &= ~MF_TRY0NULL;
1719        }
1720        saveerrno = errno;
1721        if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1722                (void) lockfile(fd, buf, ".db", LOCK_UN);
1723        if (st != 0)
1724        {
1725                errno = saveerrno;
1726                if (st < 0)
1727                        syserr("db_map_lookup: get (%s)", name);
1728                return NULL;
1729        }
1730        if (bitset(MF_MATCHONLY, map->map_mflags))
1731                return map_rewrite(map, name, strlen(name), NULL);
1732        else
1733                return map_rewrite(map, val.data, val.size, av);
1734}
1735
1736
1737/*
1738**  DB_MAP_STORE -- store a datum in the NEWDB database
1739*/
1740
1741void
1742db_map_store(map, lhs, rhs)
1743        register MAP *map;
1744        char *lhs;
1745        char *rhs;
1746{
1747        int stat;
1748        DBT key;
1749        DBT data;
1750        register DB *db = map->map_db2;
1751        char keybuf[MAXNAME + 1];
1752
1753        bzero(&key, sizeof key);
1754        bzero(&data, sizeof data);
1755
1756        if (tTd(38, 12))
1757                printf("db_map_store(%s, %s, %s)\n",
1758                        map->map_mname, lhs, rhs);
1759
1760        key.size = strlen(lhs);
1761        key.data = lhs;
1762        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1763        {
1764                if (key.size > sizeof keybuf - 1)
1765                        key.size = sizeof keybuf - 1;
1766                bcopy(key.data, keybuf, key.size);
1767                keybuf[key.size] = '\0';
1768                makelower(keybuf);
1769                key.data = keybuf;
1770        }
1771
1772        data.size = strlen(rhs);
1773        data.data = rhs;
1774
1775        if (bitset(MF_INCLNULL, map->map_mflags))
1776        {
1777                key.size++;
1778                data.size++;
1779        }
1780
1781#if DB_VERSION_MAJOR < 2
1782        stat = db->put(db, &key, &data, R_NOOVERWRITE);
1783#else
1784        errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
1785        switch (errno)
1786        {
1787          case DB_KEYEXIST:
1788                stat = 1;
1789                break;
1790
1791          case 0:
1792                stat = 0;
1793                break;
1794
1795          default:
1796                stat = -1;
1797                break;
1798        }
1799#endif
1800        if (stat > 0)
1801        {
1802                if (!bitset(MF_APPEND, map->map_mflags))
1803                        message("050 Warning: duplicate alias name %s", lhs);
1804                else
1805                {
1806                        static char *buf = NULL;
1807                        static int bufsiz = 0;
1808                        DBT old;
1809
1810                        bzero(&old, sizeof old);
1811
1812                        old.data = db_map_lookup(map, key.data,
1813                                                 (char **)NULL, &stat);
1814                        if (old.data != NULL)
1815                        {
1816                                old.size = strlen(old.data);
1817                                if (data.size + old.size + 2 > bufsiz)
1818                                {
1819                                        if (buf != NULL)
1820                                                (void) free(buf);
1821                                        bufsiz = data.size + old.size + 2;
1822                                        buf = xalloc(bufsiz);
1823                                }
1824                                snprintf(buf, bufsiz, "%s,%s",
1825                                        (char *) data.data, (char *) old.data);
1826                                data.size = data.size + old.size + 1;
1827                                data.data = buf;
1828                                if (tTd(38, 9))
1829                                        printf("db_map_store append=%s\n",
1830                                               (char *) data.data);
1831                        }
1832                }
1833#if DB_VERSION_MAJOR < 2
1834                stat = db->put(db, &key, &data, 0);
1835#else
1836                stat = errno = db->put(db, NULL, &key, &data, 0);
1837#endif
1838        }
1839        if (stat != 0)
1840                syserr("readaliases: db put (%s)", lhs);
1841}
1842
1843
1844/*
1845**  DB_MAP_CLOSE -- add distinguished entries and close the database
1846*/
1847
1848void
1849db_map_close(map)
1850        MAP *map;
1851{
1852        register DB *db = map->map_db2;
1853
1854        if (tTd(38, 9))
1855                printf("db_map_close(%s, %s, %lx)\n",
1856                        map->map_mname, map->map_file, map->map_mflags);
1857
1858        if (bitset(MF_WRITABLE, map->map_mflags))
1859        {
1860                /* write out the distinguished alias */
1861                db_map_store(map, "@", "@");
1862        }
1863
1864        (void) db->sync(db, 0);
1865
1866#if !LOCK_ON_OPEN
1867        if (map->map_lockfd >= 0)
1868                (void) close(map->map_lockfd);
1869#endif
1870
1871#if DB_VERSION_MAJOR < 2
1872        if (db->close(db) != 0)
1873#else
1874        /*
1875        **  Berkeley DB can use internal shared memory
1876        **  locking for its memory pool.  Closing a map
1877        **  opened by another process will interfere
1878        **  with the shared memory and locks of the parent
1879        **  process leaving things in a bad state.
1880        */
1881
1882        /*
1883        **  If this map was not opened by the current
1884        **  process, do not close the map but recover
1885        **  the file descriptor.
1886        */
1887        if (map->map_pid != getpid())
1888        {
1889                int fd = -1;
1890
1891                errno = db->fd(db, &fd);
1892                if (fd >= 0)
1893                        (void) close(fd);
1894                return;
1895        }
1896
1897        if ((errno = db->close(db, 0)) != 0)
1898#endif
1899                syserr("db_map_close(%s, %s, %lx): db close failure",
1900                        map->map_mname, map->map_file, map->map_mflags);
1901}
1902
1903#endif
1904/*
1905**  NIS Modules
1906*/
1907
1908# ifdef NIS
1909
1910# ifndef YPERR_BUSY
1911#  define YPERR_BUSY    16
1912# endif
1913
1914/*
1915**  NIS_MAP_OPEN -- open DBM map
1916*/
1917
1918bool
1919nis_map_open(map, mode)
1920        MAP *map;
1921        int mode;
1922{
1923        int yperr;
1924        register char *p;
1925        auto char *vp;
1926        auto int vsize;
1927
1928        if (tTd(38, 2))
1929                printf("nis_map_open(%s, %s, %d)\n",
1930                        map->map_mname, map->map_file, mode);
1931
1932        mode &= O_ACCMODE;
1933        if (mode != O_RDONLY)
1934        {
1935                /* issue a pseudo-error message */
1936#ifdef ENOSYS
1937                errno = ENOSYS;
1938#else
1939# ifdef EFTYPE
1940                errno = EFTYPE;
1941# else
1942                errno = ENXIO;
1943# endif
1944#endif
1945                return FALSE;
1946        }
1947
1948        p = strchr(map->map_file, '@');
1949        if (p != NULL)
1950        {
1951                *p++ = '\0';
1952                if (*p != '\0')
1953                        map->map_domain = p;
1954        }
1955
1956        if (*map->map_file == '\0')
1957                map->map_file = "mail.aliases";
1958
1959        if (map->map_domain == NULL)
1960        {
1961                yperr = yp_get_default_domain(&map->map_domain);
1962                if (yperr != 0)
1963                {
1964                        if (!bitset(MF_OPTIONAL, map->map_mflags))
1965                                syserr("421 NIS map %s specified, but NIS not running",
1966                                        map->map_file);
1967                        return FALSE;
1968                }
1969        }
1970
1971        /* check to see if this map actually exists */
1972        yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1973                        &vp, &vsize);
1974        if (tTd(38, 10))
1975                printf("nis_map_open: yp_match(@, %s, %s) => %s\n",
1976                        map->map_domain, map->map_file, yperr_string(yperr));
1977        if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1978        {
1979                /*
1980                **  We ought to be calling aliaswait() here if this is an
1981                **  alias file, but powerful HP-UX NIS servers  apparently
1982                **  don't insert the @:@ token into the alias map when it
1983                **  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
1984                */
1985
1986#if 0
1987                if (!bitset(MF_ALIAS, map->map_mflags) ||
1988                    aliaswait(map, NULL, TRUE))
1989#endif
1990                        return TRUE;
1991        }
1992
1993        if (!bitset(MF_OPTIONAL, map->map_mflags))
1994        {
1995                syserr("421 Cannot bind to map %s in domain %s: %s",
1996                        map->map_file, map->map_domain, yperr_string(yperr));
1997        }
1998
1999        return FALSE;
2000}
2001
2002
2003/*
2004**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2005*/
2006
2007/* ARGSUSED3 */
2008char *
2009nis_map_lookup(map, name, av, statp)
2010        MAP *map;
2011        char *name;
2012        char **av;
2013        int *statp;
2014{
2015        char *vp;
2016        auto int vsize;
2017        int buflen;
2018        int yperr;
2019        char keybuf[MAXNAME + 1];
2020
2021        if (tTd(38, 20))
2022                printf("nis_map_lookup(%s, %s)\n",
2023                        map->map_mname, name);
2024
2025        buflen = strlen(name);
2026        if (buflen > sizeof keybuf - 1)
2027                buflen = sizeof keybuf - 1;
2028        bcopy(name, keybuf, buflen);
2029        keybuf[buflen] = '\0';
2030        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2031                makelower(keybuf);
2032        yperr = YPERR_KEY;
2033        if (bitset(MF_TRY0NULL, map->map_mflags))
2034        {
2035                yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2036                             &vp, &vsize);
2037                if (yperr == 0)
2038                        map->map_mflags &= ~MF_TRY1NULL;
2039        }
2040        if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2041        {
2042                buflen++;
2043                yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2044                             &vp, &vsize);
2045                if (yperr == 0)
2046                        map->map_mflags &= ~MF_TRY0NULL;
2047        }
2048        if (yperr != 0)
2049        {
2050                if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2051                        map->map_mflags &= ~(MF_VALID|MF_OPEN);
2052                return NULL;
2053        }
2054        if (bitset(MF_MATCHONLY, map->map_mflags))
2055                return map_rewrite(map, name, strlen(name), NULL);
2056        else
2057                return map_rewrite(map, vp, vsize, av);
2058}
2059
2060
2061/*
2062**  NIS_GETCANONNAME -- look up canonical name in NIS
2063*/
2064
2065bool
2066nis_getcanonname(name, hbsize, statp)
2067        char *name;
2068        int hbsize;
2069        int *statp;
2070{
2071        char *vp;
2072        auto int vsize;
2073        int keylen;
2074        int yperr;
2075        static bool try0null = TRUE;
2076        static bool try1null = TRUE;
2077        static char *yp_domain = NULL;
2078        char host_record[MAXLINE];
2079        char cbuf[MAXNAME];
2080        char nbuf[MAXNAME + 1];
2081
2082        if (tTd(38, 20))
2083                printf("nis_getcanonname(%s)\n", name);
2084
2085        if (strlen(name) >= sizeof nbuf)
2086        {
2087                *statp = EX_UNAVAILABLE;
2088                return FALSE;
2089        }
2090        (void) strcpy(nbuf, name);
2091        shorten_hostname(nbuf);
2092        keylen = strlen(nbuf);
2093
2094        if (yp_domain == NULL)
2095                yp_get_default_domain(&yp_domain);
2096        makelower(nbuf);
2097        yperr = YPERR_KEY;
2098        if (try0null)
2099        {
2100                yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2101                             &vp, &vsize);
2102                if (yperr == 0)
2103                        try1null = FALSE;
2104        }
2105        if (yperr == YPERR_KEY && try1null)
2106        {
2107                keylen++;
2108                yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2109                             &vp, &vsize);
2110                if (yperr == 0)
2111                        try0null = FALSE;
2112        }
2113        if (yperr != 0)
2114        {
2115                if (yperr == YPERR_KEY)
2116                        *statp = EX_NOHOST;
2117                else if (yperr == YPERR_BUSY)
2118                        *statp = EX_TEMPFAIL;
2119                else
2120                        *statp = EX_UNAVAILABLE;
2121                return FALSE;
2122        }
2123        if (vsize >= sizeof host_record)
2124                vsize = sizeof host_record - 1;
2125        strncpy(host_record, vp, vsize);
2126        host_record[vsize] = '\0';
2127        if (tTd(38, 44))
2128                printf("got record `%s'\n", host_record);
2129        if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
2130        {
2131                /* this should not happen, but.... */
2132                *statp = EX_NOHOST;
2133                return FALSE;
2134        }
2135        if (hbsize < strlen(cbuf))
2136        {
2137                *statp = EX_UNAVAILABLE;
2138                return FALSE;
2139        }
2140        strcpy(name, cbuf);
2141        *statp = EX_OK;
2142        return TRUE;
2143}
2144
2145#endif
2146/*
2147**  NISPLUS Modules
2148**
2149**      This code donated by Sun Microsystems.
2150*/
2151
2152#ifdef NISPLUS
2153
2154#undef NIS              /* symbol conflict in nis.h */
2155#undef T_UNSPEC         /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2156#include <rpcsvc/nis.h>
2157#include <rpcsvc/nislib.h>
2158
2159#define EN_col(col)     zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2160#define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2161#define COL_MAX(res)    ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2162#define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2163
2164/*
2165**  NISPLUS_MAP_OPEN -- open nisplus table
2166*/
2167
2168bool
2169nisplus_map_open(map, mode)
2170        MAP *map;
2171        int mode;
2172{
2173        nis_result *res = NULL;
2174        int retry_cnt, max_col, i;
2175        char qbuf[MAXLINE + NIS_MAXNAMELEN];
2176
2177        if (tTd(38, 2))
2178                printf("nisplus_map_open(%s, %s, %d)\n",
2179                        map->map_mname, map->map_file, mode);
2180
2181        mode &= O_ACCMODE;
2182        if (mode != O_RDONLY)
2183        {
2184                errno = EPERM;
2185                return FALSE;
2186        }
2187
2188        if (*map->map_file == '\0')
2189                map->map_file = "mail_aliases.org_dir";
2190
2191        if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2192        {
2193                /* set default NISPLUS Domain to $m */
2194                extern char *nisplus_default_domain __P((void));
2195
2196                map->map_domain = newstr(nisplus_default_domain());
2197                if (tTd(38, 2))
2198                        printf("nisplus_map_open(%s): using domain %s\n",
2199                                 map->map_file, map->map_domain);
2200        }
2201        if (!PARTIAL_NAME(map->map_file))
2202        {
2203                map->map_domain = newstr("");
2204                snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
2205        }
2206        else
2207        {
2208                /* check to see if this map actually exists */
2209                snprintf(qbuf, sizeof qbuf, "%s.%s",
2210                        map->map_file, map->map_domain);
2211        }
2212
2213        retry_cnt = 0;
2214        while (res == NULL || res->status != NIS_SUCCESS)
2215        {
2216                res = nis_lookup(qbuf, FOLLOW_LINKS);
2217                switch (res->status)
2218                {
2219                  case NIS_SUCCESS:
2220                        break;
2221
2222                  case NIS_TRYAGAIN:
2223                  case NIS_RPCERROR:
2224                  case NIS_NAMEUNREACHABLE:
2225                        if (retry_cnt++ > 4)
2226                        {
2227                                errno = EAGAIN;
2228                                return FALSE;
2229                        }
2230                        /* try not to overwhelm hosed server */
2231                        sleep(2);
2232                        break;
2233
2234                  default:              /* all other nisplus errors */
2235#if 0
2236                        if (!bitset(MF_OPTIONAL, map->map_mflags))
2237                                syserr("421 Cannot find table %s.%s: %s",
2238                                        map->map_file, map->map_domain,
2239                                        nis_sperrno(res->status));
2240#endif
2241                        errno = EAGAIN;
2242                        return FALSE;
2243                }
2244        }
2245
2246        if (NIS_RES_NUMOBJ(res) != 1 ||
2247            (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2248        {
2249                if (tTd(38, 10))
2250                        printf("nisplus_map_open: %s is not a table\n", qbuf);
2251#if 0
2252                if (!bitset(MF_OPTIONAL, map->map_mflags))
2253                        syserr("421 %s.%s: %s is not a table",
2254                                map->map_file, map->map_domain,
2255                                nis_sperrno(res->status));
2256#endif
2257                errno = EBADF;
2258                return FALSE;
2259        }
2260        /* default key column is column 0 */
2261        if (map->map_keycolnm == NULL)
2262                map->map_keycolnm = newstr(COL_NAME(res,0));
2263
2264        max_col = COL_MAX(res);
2265
2266        /* verify the key column exist */
2267        for (i=0; i< max_col; i++)
2268        {
2269                if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
2270                        break;
2271        }
2272        if (i == max_col)
2273        {
2274                if (tTd(38, 2))
2275                        printf("nisplus_map_open(%s): can not find key column %s\n",
2276                                map->map_file, map->map_keycolnm);
2277                errno = ENOENT;
2278                return FALSE;
2279        }
2280
2281        /* default value column is the last column */
2282        if (map->map_valcolnm == NULL)
2283        {
2284                map->map_valcolno = max_col - 1;
2285                return TRUE;
2286        }
2287
2288        for (i=0; i< max_col; i++)
2289        {
2290                if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2291                {
2292                        map->map_valcolno = i;
2293                        return TRUE;
2294                }
2295        }
2296
2297        if (tTd(38, 2))
2298                printf("nisplus_map_open(%s): can not find column %s\n",
2299                         map->map_file, map->map_keycolnm);
2300        errno = ENOENT;
2301        return FALSE;
2302}
2303
2304
2305/*
2306**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2307*/
2308
2309char *
2310nisplus_map_lookup(map, name, av, statp)
2311        MAP *map;
2312        char *name;
2313        char **av;
2314        int *statp;
2315{
2316        char *p;
2317        auto int vsize;
2318        char *skp;
2319        int skleft;
2320        char search_key[MAXNAME + 4];
2321        char qbuf[MAXLINE + NIS_MAXNAMELEN];
2322        nis_result *result;
2323
2324        if (tTd(38, 20))
2325                printf("nisplus_map_lookup(%s, %s)\n",
2326                        map->map_mname, name);
2327
2328        if (!bitset(MF_OPEN, map->map_mflags))
2329        {
2330                if (nisplus_map_open(map, O_RDONLY))
2331                {
2332                        map->map_mflags |= MF_OPEN;
2333                        map->map_pid = getpid();
2334                }
2335                else
2336                {
2337                        *statp = EX_UNAVAILABLE;
2338                        return NULL;
2339                }
2340        }
2341
2342        /*
2343        **  Copy the name to the key buffer, escaping double quote characters
2344        **  by doubling them and quoting "]" and "," to avoid having the
2345        **  NIS+ parser choke on them.
2346        */
2347
2348        skleft = sizeof search_key - 4;
2349        skp = search_key;
2350        for (p = name; *p != '\0' && skleft > 0; p++)
2351        {
2352                switch (*p)
2353                {
2354                  case ']':
2355                  case ',':
2356                        /* quote the character */
2357                        *skp++ = '"';
2358                        *skp++ = *p;
2359                        *skp++ = '"';
2360                        skleft -= 3;
2361                        break;
2362
2363                  case '"':
2364                        /* double the quote */
2365                        *skp++ = '"';
2366                        skleft--;
2367                        /* fall through... */
2368
2369                  default:
2370                        *skp++ = *p;
2371                        skleft--;
2372                        break;
2373                }
2374        }
2375        *skp = '\0';
2376        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2377                makelower(search_key);
2378
2379        /* construct the query */
2380        if (PARTIAL_NAME(map->map_file))
2381                snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
2382                        map->map_keycolnm, search_key, map->map_file,
2383                        map->map_domain);
2384        else
2385                snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
2386                        map->map_keycolnm, search_key, map->map_file);
2387
2388        if (tTd(38, 20))
2389                printf("qbuf=%s\n", qbuf);
2390        result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
2391        if (result->status == NIS_SUCCESS)
2392        {
2393                int count;
2394                char *str;
2395
2396                if ((count = NIS_RES_NUMOBJ(result)) != 1)
2397                {
2398                        if (LogLevel > 10)
2399                                sm_syslog(LOG_WARNING, CurEnv->e_id,
2400                                  "%s: lookup error, expected 1 entry, got %d",
2401                                    map->map_file, count);
2402
2403                        /* ignore second entry */
2404                        if (tTd(38, 20))
2405                                printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
2406                                        name, count);
2407                }
2408
2409                p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
2410                /* set the length of the result */
2411                if (p == NULL)
2412                        p = "";
2413                vsize = strlen(p);
2414                if (tTd(38, 20))
2415                        printf("nisplus_map_lookup(%s), found %s\n",
2416                                name, p);
2417                if (bitset(MF_MATCHONLY, map->map_mflags))
2418                        str = map_rewrite(map, name, strlen(name), NULL);
2419                else
2420                        str = map_rewrite(map, p, vsize, av);
2421                nis_freeresult(result);
2422                *statp = EX_OK;
2423                return str;
2424        }
2425        else
2426        {
2427                if (result->status == NIS_NOTFOUND)
2428                        *statp = EX_NOTFOUND;
2429                else if (result->status == NIS_TRYAGAIN)
2430                        *statp = EX_TEMPFAIL;
2431                else
2432                {
2433                        *statp = EX_UNAVAILABLE;
2434                        map->map_mflags &= ~(MF_VALID|MF_OPEN);
2435                }
2436        }
2437        if (tTd(38, 20))
2438                printf("nisplus_map_lookup(%s), failed\n", name);
2439        nis_freeresult(result);
2440        return NULL;
2441}
2442
2443
2444
2445/*
2446**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
2447*/
2448
2449bool
2450nisplus_getcanonname(name, hbsize, statp)
2451        char *name;
2452        int hbsize;
2453        int *statp;
2454{
2455        char *vp;
2456        auto int vsize;
2457        nis_result *result;
2458        char *p;
2459        char nbuf[MAXNAME + 1];
2460        char qbuf[MAXLINE + NIS_MAXNAMELEN];
2461
2462        if (strlen(name) >= sizeof nbuf)
2463        {
2464                *statp = EX_UNAVAILABLE;
2465                return FALSE;
2466        }
2467        (void) strcpy(nbuf, name);
2468        shorten_hostname(nbuf);
2469
2470        p = strchr(nbuf, '.');
2471        if (p == NULL)
2472        {
2473                /* single token */
2474                snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
2475        }
2476        else if (p[1] != '\0')
2477        {
2478                /* multi token -- take only first token in nbuf */
2479                *p = '\0';
2480                snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
2481                        nbuf, &p[1]);
2482        }
2483        else
2484        {
2485                *statp = EX_NOHOST;
2486                return FALSE;
2487        }
2488
2489        if (tTd(38, 20))
2490                printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
2491                         name, qbuf);
2492
2493        result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
2494                NULL, NULL);
2495
2496        if (result->status == NIS_SUCCESS)
2497        {
2498                int count;
2499                char *domain;
2500
2501                if ((count = NIS_RES_NUMOBJ(result)) != 1)
2502                {
2503                        if (LogLevel > 10)
2504                                sm_syslog(LOG_WARNING, CurEnv->e_id,
2505                                       "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
2506                                       count);
2507
2508                        /* ignore second entry */
2509                        if (tTd(38, 20))
2510                                printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
2511                                        name, count);
2512                }
2513
2514                if (tTd(38, 20))
2515                        printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
2516                               name, (NIS_RES_OBJECT(result))->zo_domain);
2517
2518
2519                vp = ((NIS_RES_OBJECT(result))->EN_col(0));
2520                vsize = strlen(vp);
2521                if (tTd(38, 20))
2522                        printf("nisplus_getcanonname(%s), found %s\n",
2523                                name, vp);
2524                if (strchr(vp, '.') != NULL)
2525                {
2526                        domain = "";
2527                }
2528                else
2529                {
2530                        domain = macvalue('m', CurEnv);
2531                        if (domain == NULL)
2532                                domain = "";
2533                }
2534                if (hbsize > vsize + (int) strlen(domain) + 1)
2535                {
2536                        if (domain[0] == '\0')
2537                                strcpy(name, vp);
2538                        else
2539                                snprintf(name, hbsize, "%s.%s", vp, domain);
2540                        *statp = EX_OK;
2541                }
2542                else
2543                        *statp = EX_NOHOST;
2544                nis_freeresult(result);
2545                return TRUE;
2546        }
2547        else
2548        {
2549                if (result->status == NIS_NOTFOUND)
2550                        *statp = EX_NOHOST;
2551                else if (result->status == NIS_TRYAGAIN)
2552                        *statp = EX_TEMPFAIL;
2553                else
2554                        *statp = EX_UNAVAILABLE;
2555        }
2556        if (tTd(38, 20))
2557                printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
2558                        name, result->status, *statp);
2559        nis_freeresult(result);
2560        return FALSE;
2561}
2562
2563
2564char *
2565nisplus_default_domain()
2566{
2567        static char default_domain[MAXNAME + 1] = "";
2568        char *p;
2569
2570        if (default_domain[0] != '\0')
2571                return(default_domain);
2572
2573        p = nis_local_directory();
2574        snprintf(default_domain, sizeof default_domain, "%s", p);
2575        return default_domain;
2576}
2577
2578#endif /* NISPLUS */
2579/*
2580**  LDAP Modules
2581**
2582**      Contributed by Booker C. Bense <bbense@networking.stanford.edu>.
2583**      Get your support from him.
2584*/
2585
2586#ifdef LDAPMAP
2587
2588# undef NEEDGETOPT              /* used for something else in LDAP */
2589
2590# include <lber.h>
2591# include <ldap.h>
2592# include "ldap_map.h"
2593
2594/*
2595**  LDAP_MAP_OPEN -- open LDAP map
2596**
2597**      Since LDAP is TCP-based there is not much we can or should do
2598**      here.  It might be a good idea to attempt an open/close here.
2599*/
2600
2601bool
2602ldap_map_open(map, mode)
2603        MAP *map;
2604        int mode;
2605{
2606        if (tTd(38, 2))
2607                printf("ldap_map_open(%s, %d)\n", map->map_mname, mode);
2608
2609        mode &= O_ACCMODE;
2610        if (mode != O_RDONLY)
2611        {
2612                /* issue a pseudo-error message */
2613#ifdef ENOSYS
2614                errno = ENOSYS;
2615#else
2616# ifdef EFTYPE
2617                errno = EFTYPE;
2618# else
2619                errno = ENXIO;
2620# endif
2621#endif
2622                return FALSE;
2623        }
2624        return TRUE;
2625}
2626
2627
2628/*
2629**  LDAP_MAP_START -- actually open LDAP map
2630**
2631**      Caching should be investigated.
2632*/
2633
2634static jmp_buf  LDAPTimeout;
2635
2636static void
2637ldaptimeout(sig_no)
2638        int sig_no;
2639{
2640        longjmp(LDAPTimeout, 1);
2641}
2642
2643bool
2644ldap_map_start(map)
2645        MAP *map;
2646{
2647        LDAP_MAP_STRUCT *lmap;
2648        LDAP *ld;
2649        register EVENT *ev = NULL;
2650
2651        if (tTd(38, 2))
2652                printf("ldap_map_start(%s)\n", map->map_mname);
2653
2654        lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2655
2656        if (tTd(38,9))
2657                printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport);
2658
2659        /* Need to set an alarm here, ldap_open is hopelessly broken. */
2660
2661        /* set the timeout */
2662        if (lmap->timeout.tv_sec != 0)
2663        {
2664                if (setjmp(LDAPTimeout) != 0)
2665                {
2666                        if (LogLevel > 1)
2667                                sm_syslog(LOG_NOTICE, CurEnv->e_id,
2668                                       "timeout waiting for ldap_open to %.100s",
2669                                       lmap->ldaphost);
2670                        return (FALSE);
2671                }
2672                ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0);
2673        }
2674
2675#ifdef USE_LDAP_INIT
2676        ld = ldap_init(lmap->ldaphost,lmap->ldapport);
2677#else
2678        ld = ldap_open(lmap->ldaphost,lmap->ldapport);
2679#endif
2680
2681        /* clear the event if it has not sprung */
2682        if (lmap->timeout.tv_sec != 0)
2683                clrevent(ev);
2684
2685        if (ld == NULL)
2686        {
2687                if (!bitset(MF_OPTIONAL, map->map_mflags))
2688                {
2689                        syserr("%sldapopen failed to %s in map %s",
2690                                bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2691                                lmap->ldaphost, map->map_mname);
2692                }
2693                return FALSE;
2694        }
2695
2696#ifdef USE_LDAP_SET_OPTION
2697        ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref);
2698        ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit);
2699        ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit);
2700        ldap_set_option(ld, LDAP_OPT_REFERRALS,
2701                        bitset(LDAP_OPT_REFERRALS, lmap->ldap_options) ?
2702                        LDAP_OPT_ON : LDAP_OPT_OFF);
2703#else
2704        /* From here on in we can use ldap internal timelimits */
2705        ld->ld_deref = lmap->deref;
2706        ld->ld_timelimit = lmap->timelimit;
2707        ld->ld_sizelimit = lmap->sizelimit;
2708        ld->ld_options = lmap->ldap_options;
2709#endif
2710
2711#ifdef USE_LDAP_INIT
2712        /* ld needs to be cast into the map struct */
2713        lmap->ld = ld;
2714        return TRUE;
2715#else
2716        if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS)
2717        {
2718                if (!bitset(MF_OPTIONAL, map->map_mflags))
2719                {
2720                        syserr("421 Cannot bind to map %s in ldap server %s",
2721                                map->map_mname, lmap->ldaphost);
2722                }
2723        }
2724        else
2725        {
2726                /* We need to cast ld into the map structure */
2727                lmap->ld = ld;
2728                return TRUE;
2729        }
2730
2731        return FALSE;
2732#endif
2733}
2734
2735
2736/*
2737**  LDAP_MAP_STOP -- close the ldap connection
2738*/
2739
2740void
2741ldap_map_stop(map)
2742        MAP *map;
2743{
2744        LDAP_MAP_STRUCT *lmap;
2745
2746        lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2747        if (lmap->ld != NULL)
2748        {
2749                ldap_unbind(lmap->ld);
2750                lmap->ld = NULL;
2751        }
2752}
2753
2754/*
2755**  LDAP_MAP_CLOSE -- close ldap map
2756*/
2757
2758void
2759ldap_map_close(map)
2760        MAP *map;
2761{
2762        ldap_map_stop(map);
2763}
2764
2765#ifdef SUNET_ID
2766/*
2767**  SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
2768**  This only makes sense at Stanford University.
2769*/
2770
2771char *
2772sunet_id_hash(str)
2773        char *str;
2774{
2775        char *p, *p_last;
2776
2777        p = str;
2778        p_last = p;
2779        while (*p != '\0')
2780        {
2781                if (islower(*p) || isdigit(*p))
2782                {
2783                        *p_last = *p;
2784                        p_last++;
2785                }
2786                else if (isupper(*p))
2787                {
2788                        *p_last = tolower(*p);
2789                        p_last++;
2790                }
2791                ++p;
2792        }
2793        if (*p_last != '\0')
2794                *p_last = '\0';
2795        return (str);
2796}
2797
2798
2799
2800#endif /* SUNET_ID */
2801/*
2802**  LDAP_MAP_LOOKUP -- look up a datum in a LDAP map
2803*/
2804
2805char *
2806ldap_map_lookup(map, name, av, statp)
2807        MAP *map;
2808        char *name;
2809        char **av;
2810        int *statp;
2811{
2812        LDAP_MAP_STRUCT *lmap = NULL;
2813        LDAPMessage *entry;
2814        char *vp;
2815        auto int vsize;
2816        char keybuf[MAXNAME + 1];
2817        char filter[LDAP_MAP_MAX_FILTER + 1];
2818        char **attr_values = NULL;
2819        char *result;
2820        int name_len;
2821        char *fp, *p, *q;
2822
2823        if (tTd(38, 20))
2824                printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name);
2825
2826        /* actually open the map */
2827        if (!ldap_map_start(map))
2828        {
2829                result = NULL;
2830                *statp = EX_TEMPFAIL;
2831                goto quick_exit;
2832        }
2833
2834        /* Get ldap struct pointer from map */
2835        lmap = (LDAP_MAP_STRUCT *) map->map_db1;
2836
2837        name_len = strlen(name);
2838        if (name_len > MAXNAME)
2839                name_len = MAXNAME;
2840        strncpy(keybuf, name, name_len);
2841        keybuf[name_len] = '\0';
2842
2843        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2844#ifdef SUNET_ID
2845                sunet_id_hash(keybuf);
2846#else
2847                makelower(keybuf);
2848#endif /*SUNET_ID */
2849
2850        /* substitute keybuf into filter, perhaps multiple times */
2851        fp = filter;
2852        p = lmap->filter;
2853        while ((q = strchr(p, '%')) != NULL)
2854        {
2855                if (q[1] == 's')
2856                {
2857                        snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
2858                                 q - p, p, keybuf);
2859                        p = q + 2;
2860                }
2861                else
2862                {
2863                        snprintf(fp, SPACELEFT(filter, fp), "%.*s",
2864                                 q - p + 1, p);
2865                        p = q + (q[1] == '%' ? 2 : 1);
2866                }
2867                fp += strlen(fp);
2868        }
2869        snprintf(fp, SPACELEFT(filter, fp), "%s", p);
2870        if (tTd(38, 20))
2871                printf("ldap search filter=%s\n", filter);
2872
2873        if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter,
2874                           lmap->attr, lmap->attrsonly, &(lmap->timeout),
2875                           &(lmap->res)) != LDAP_SUCCESS)
2876        {
2877                /* try stopping/starting map */
2878                ldap_map_stop(map);
2879                if (!ldap_map_start(map))
2880                {
2881                        result = NULL;
2882                        *statp = EX_TEMPFAIL;
2883                        goto quick_exit;
2884                }
2885                if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter,
2886                                   lmap->attr, lmap->attrsonly,
2887                                   &(lmap->timeout), &(lmap->res))
2888                        != LDAP_SUCCESS)
2889                {
2890                        if (!bitset(MF_OPTIONAL, map->map_mflags))
2891                        {
2892                                syserr("%sError in ldap_search_st using %s in map %s",
2893                                        bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
2894                                        filter, map->map_mname);
2895                        }
2896                        result = NULL;
2897                        *statp = EX_TEMPFAIL;
2898                        goto quick_exit;
2899                }
2900        }
2901
2902        entry = ldap_first_entry(lmap->ld,lmap->res);
2903        if (entry == NULL)
2904        {
2905                result = NULL;
2906                *statp = EX_NOTFOUND;
2907                goto quick_exit;
2908        }
2909
2910        /* Need to build the args for map_rewrite here */
2911        attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]);
2912        if (attr_values == NULL)
2913        {
2914                /* bad things happened */
2915                result = NULL;
2916                *statp = EX_NOTFOUND;
2917                goto quick_exit;
2918        }
2919
2920        *statp = EX_OK;
2921
2922        /* If there is more that one use the first */
2923        vp = attr_values[0];
2924        vsize = strlen(vp);
2925
2926        if (LogLevel > 9)
2927                sm_syslog(LOG_INFO, CurEnv->e_id,
2928                        "ldap %.100s => %s",
2929                        name, vp);
2930        if (bitset(MF_MATCHONLY, map->map_mflags))
2931                result = map_rewrite(map, name, strlen(name), NULL);
2932        else
2933                result = map_rewrite(map, vp, vsize, av);
2934
2935  quick_exit:
2936        if (attr_values != NULL)
2937                ldap_value_free(attr_values);
2938        if (lmap != NULL)
2939                ldap_msgfree(lmap->res);
2940        ldap_map_stop(map);
2941        return result ;
2942}
2943
2944
2945/*
2946**  LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs
2947*/
2948
2949char *
2950ldap_map_dequote(str)
2951        char *str;
2952{
2953        char *p;
2954        char *start;
2955        p = str;
2956
2957        if (*p == '"')
2958        {
2959                start = ++p;
2960                /* Should probably swallow initial whitespace here */
2961        }
2962        else
2963        {
2964                return(str);
2965        }
2966        while (*p != '"' && *p != '\0')
2967        {
2968                p++;
2969        }
2970        if (*p != '\0')
2971                *p = '\0';
2972        return start;
2973}
2974
2975/*
2976**  LDAP_MAP_PARSEARGS -- parse ldap map definition args.
2977*/
2978
2979bool
2980ldap_map_parseargs(map,args)
2981        MAP *map;
2982        char *args;
2983{
2984        register char *p = args;
2985        register int done;
2986        LDAP_MAP_STRUCT *lmap;
2987
2988        /* We need to alloc an LDAP_MAP_STRUCT struct */
2989        lmap  = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT));
2990
2991        /* Set default int's here , default strings below */
2992        lmap->ldapport =  DEFAULT_LDAP_MAP_PORT;
2993        lmap->deref = DEFAULT_LDAP_MAP_DEREF;
2994        lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT;
2995        lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT;
2996        lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS;
2997        lmap->method = DEFAULT_LDAP_MAP_METHOD;
2998        lmap->scope = DEFAULT_LDAP_MAP_SCOPE;
2999        lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY;
3000        lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT;
3001        lmap->timeout.tv_usec = 0;
3002
3003        /* Default char ptrs to NULL */
3004        lmap->binddn = NULL;
3005        lmap->passwd = NULL;
3006        lmap->base   = NULL;
3007        lmap->ldaphost = NULL;
3008
3009        /* Default general ptrs to NULL */
3010        lmap->ld = NULL;
3011        lmap->res = NULL;
3012
3013        map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
3014        for (;;)
3015        {
3016                while (isascii(*p) && isspace(*p))
3017                        p++;
3018                if (*p != '-')
3019                        break;
3020                switch (*++p)
3021                {
3022                  case 'N':
3023                        map->map_mflags |= MF_INCLNULL;
3024                        map->map_mflags &= ~MF_TRY0NULL;
3025                        break;
3026
3027                  case 'O':
3028                        map->map_mflags &= ~MF_TRY1NULL;
3029                        break;
3030
3031                  case 'o':
3032                        map->map_mflags |= MF_OPTIONAL;
3033                        break;
3034
3035                  case 'f':
3036                        map->map_mflags |= MF_NOFOLDCASE;
3037                        break;
3038
3039                  case 'm':
3040                        map->map_mflags |= MF_MATCHONLY;
3041                        break;
3042
3043                  case 'A':
3044                        map->map_mflags |= MF_APPEND;
3045                        break;
3046
3047                  case 'q':
3048                        map->map_mflags |= MF_KEEPQUOTES;
3049                        break;
3050
3051                  case 't':
3052                        map->map_mflags |= MF_NODEFER;
3053                        break;
3054
3055                  case 'a':
3056                        map->map_app = ++p;
3057                        break;
3058
3059                  case 'T':
3060                        map->map_tapp = ++p;
3061                        break;
3062
3063                        /* Start of ldap_map specific args */
3064                  case 'k':             /* search field */
3065                        while (isascii(*++p) && isspace(*p))
3066                                continue;
3067                        lmap->filter = p;
3068                        break;
3069
3070                  case 'v':             /* attr to return */
3071                        while (isascii(*++p) && isspace(*p))
3072                                continue;
3073                        lmap->attr[0] = p;
3074                        lmap->attr[1] = NULL;
3075                        break;
3076
3077                        /* args stolen from ldapsearch.c */
3078                  case 'R':             /* don't auto chase referrals */
3079#ifdef LDAP_REFERRALS
3080                        lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3081#else  /* LDAP_REFERRALS */
3082                        syserr("compile with -DLDAP_REFERRALS for referral support\n");
3083#endif /* LDAP_REFERRALS */
3084                        break;
3085
3086                  case 'n':             /* retrieve attribute names only -- no values */
3087                        lmap->attrsonly += 1;
3088                        break;
3089
3090                  case 's':             /* search scope */
3091                        if (strncasecmp(++p, "base", 4) == 0)
3092                        {
3093                                lmap->scope = LDAP_SCOPE_BASE;
3094                        }
3095                        else if (strncasecmp(p, "one", 3) == 0)
3096                        {
3097                                lmap->scope = LDAP_SCOPE_ONELEVEL;
3098                        }
3099                        else if (strncasecmp(p, "sub", 3) == 0)
3100                        {
3101                                lmap->scope = LDAP_SCOPE_SUBTREE;
3102                        }
3103                        else
3104                        {               /* bad config line */
3105                                if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3106                                {
3107                                        char *ptr;
3108
3109                                        if ((ptr = strchr(p, ' ')) != NULL)
3110                                                *ptr = '\0';
3111                                        syserr("Scope must be [base|one|sub] not %s in map %s",
3112                                                p, map->map_mname);
3113                                        if (ptr != NULL)
3114                                                *ptr = ' ';
3115                                        return FALSE;
3116                                }
3117                        }
3118                        break;
3119
3120                  case 'h':             /* ldap host */
3121                        while (isascii(*++p) && isspace(*p))
3122                                continue;
3123                        map->map_domain = p;
3124                        lmap->ldaphost = p;
3125                        break;
3126
3127                  case 'b':             /* search base */
3128                        while (isascii(*++p) && isspace(*p))
3129                                continue;
3130                        lmap->base = p;
3131                        break;
3132
3133                  case 'p':             /* ldap port */
3134                        while (isascii(*++p) && isspace(*p))
3135                                continue;
3136                        lmap->ldapport = atoi(p);
3137                        break;
3138
3139                  case 'l':             /* time limit */
3140                        while (isascii(*++p) && isspace(*p))
3141                                continue;
3142                        lmap->timelimit = atoi(p);
3143                        lmap->timeout.tv_sec = lmap->timelimit;
3144                        break;
3145
3146                }
3147
3148                /* need to account for quoted strings here arggg... */
3149                done =  isascii(*p) && isspace(*p);
3150                while (*p != '\0' && !done)
3151                {
3152                        if (*p == '"')
3153                        {
3154                                while (*++p != '"' && *p != '\0')
3155                                {
3156                                        continue;
3157                                }
3158                                if (*p != '\0')
3159                                        p++;
3160                        }
3161                        else
3162                        {
3163                                p++;
3164                        }
3165                        done = isascii(*p) && isspace(*p);
3166                }
3167
3168                if (*p != '\0')
3169                        *p++ = '\0';
3170        }
3171
3172        if (map->map_app != NULL)
3173                map->map_app = newstr(ldap_map_dequote(map->map_app));
3174        if (map->map_tapp != NULL)
3175                map->map_tapp = newstr(ldap_map_dequote(map->map_tapp));
3176        if (map->map_domain != NULL)
3177                map->map_domain = newstr(ldap_map_dequote(map->map_domain));
3178
3179        /*
3180        **  We need to swallow up all the stuff into a struct
3181        **  and dump it into map->map_dbptr1
3182        */
3183
3184        if (lmap->ldaphost != NULL)
3185                lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost));
3186        else
3187        {
3188                syserr("LDAP map: -h flag is required");
3189                return FALSE;
3190        }
3191
3192        if (lmap->binddn != NULL)
3193                lmap->binddn = newstr(ldap_map_dequote(lmap->binddn));
3194        else
3195                lmap->binddn = DEFAULT_LDAP_MAP_BINDDN;
3196
3197
3198        if (lmap->passwd != NULL)
3199                lmap->passwd = newstr(ldap_map_dequote(lmap->passwd));
3200        else
3201                lmap->passwd = DEFAULT_LDAP_MAP_PASSWD;
3202
3203        if (lmap->base != NULL)
3204                lmap->base = newstr(ldap_map_dequote(lmap->base));
3205        else
3206        {
3207                syserr("LDAP map: -b flag is required");
3208                return FALSE;
3209        }
3210
3211
3212        if (lmap->filter != NULL)
3213                lmap->filter = newstr(ldap_map_dequote(lmap->filter));
3214        else
3215        {
3216                if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3217                {
3218                        syserr("No filter given in map %s", map->map_mname);
3219                        return FALSE;
3220                }
3221        }
3222        if (lmap->attr[0] != NULL)
3223                lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0]));
3224        else
3225        {
3226                if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
3227                {
3228                        syserr("No return attribute in %s", map->map_mname);
3229                        return FALSE;
3230                }
3231        }
3232
3233        map->map_db1 = (ARBPTR_T) lmap;
3234        return TRUE;
3235}
3236
3237#endif /* LDAP Modules */
3238/*
3239**  syslog map
3240*/
3241
3242#if _FFR_MAP_SYSLOG
3243
3244#define map_prio        map_lockfd      /* overload field */
3245
3246/*
3247**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
3248*/
3249
3250bool
3251syslog_map_parseargs(map, args)
3252        MAP *map;
3253        char *args;
3254{
3255        char *p = args;
3256        char *priority = NULL;
3257
3258        for (;;)
3259        {
3260                while (isascii(*p) && isspace(*p))
3261                        p++;
3262                if (*p != '-')
3263                        break;
3264                if (*++p == 'L')
3265                        priority = ++p;
3266                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3267                        p++;
3268                if (*p != '\0')
3269                        *p++ = '\0';
3270        }
3271
3272        if (priority == NULL)
3273                map->map_prio = LOG_INFO;
3274        else
3275        {
3276                if (strncasecmp("LOG_", priority, 4) == 0)
3277                        priority += 4;
3278
3279#ifdef LOG_EMERG
3280                if (strcasecmp("EMERG", priority) == 0)
3281                        map->map_prio = LOG_EMERG;
3282                else
3283#endif
3284#ifdef LOG_ALERT
3285                if (strcasecmp("ALERT", priority) == 0)
3286                        map->map_prio = LOG_ALERT;
3287                else
3288#endif
3289#ifdef LOG_CRIT
3290                if (strcasecmp("CRIT", priority) == 0)
3291                        map->map_prio = LOG_CRIT;
3292                else
3293#endif
3294#ifdef LOG_ERR
3295                if (strcasecmp("ERR", priority) == 0)
3296                        map->map_prio = LOG_ERR;
3297                else
3298#endif
3299#ifdef LOG_WARNING
3300                if (strcasecmp("WARNING", priority) == 0)
3301                        map->map_prio = LOG_WARNING;
3302                else
3303#endif
3304#ifdef LOG_NOTICE
3305                if (strcasecmp("NOTICE", priority) == 0)
3306                        map->map_prio = LOG_NOTICE;
3307                else
3308#endif
3309#ifdef LOG_INFO
3310                if (strcasecmp("INFO", priority) == 0)
3311                        map->map_prio = LOG_INFO;
3312                else
3313#endif
3314#ifdef LOG_DEBUG
3315                if (strcasecmp("DEBUG", priority) == 0)
3316                        map->map_prio = LOG_DEBUG;
3317                else
3318#endif
3319                {
3320                        syserr("syslog_map_parseargs: Unknown priority %s\n",
3321                               priority);
3322                        return FALSE;
3323                }
3324        }
3325        return TRUE;
3326}
3327
3328/*
3329**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
3330*/
3331
3332char *
3333syslog_map_lookup(map, string, args, statp)
3334        MAP *map;
3335        char *string;
3336        char **args;
3337        int *statp;
3338{
3339        char *ptr = map_rewrite(map, string, strlen(string), args);
3340
3341        if (ptr != NULL)
3342        {
3343                if (tTd(38, 20))
3344                        printf("syslog_map_lookup(%s (priority %d): %s\n",
3345                               map->map_mname, map->map_prio, ptr);
3346
3347                sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
3348        }
3349
3350        *statp = EX_OK;
3351        return "";
3352}
3353
3354#endif /* _FFR_MAP_SYSLOG */
3355/*
3356**  HESIOD Modules
3357*/
3358
3359#ifdef HESIOD
3360
3361bool
3362hes_map_open(map, mode)
3363        MAP *map;
3364        int mode;
3365{
3366        if (tTd(38, 2))
3367                printf("hes_map_open(%s, %s, %d)\n",
3368                        map->map_mname, map->map_file, mode);
3369
3370        if (mode != O_RDONLY)
3371        {
3372                /* issue a pseudo-error message */
3373#ifdef ENOSYS
3374                errno = ENOSYS;
3375#else
3376# ifdef EFTYPE
3377                errno = EFTYPE;
3378# else
3379                errno = ENXIO;
3380# endif
3381#endif
3382                return FALSE;
3383        }
3384
3385#ifdef HESIOD_INIT
3386        if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
3387                return TRUE;
3388
3389        if (!bitset(MF_OPTIONAL, map->map_mflags))
3390                syserr("421 cannot initialize Hesiod map (%s)",
3391                        errstring(errno));
3392        return FALSE;
3393#else
3394        if (hes_error() == HES_ER_UNINIT)
3395                hes_init();
3396        switch (hes_error())
3397        {
3398          case HES_ER_OK:
3399          case HES_ER_NOTFOUND:
3400                return TRUE;
3401        }
3402
3403        if (!bitset(MF_OPTIONAL, map->map_mflags))
3404                syserr("421 cannot initialize Hesiod map (%d)", hes_error());
3405
3406        return FALSE;
3407#endif /* HESIOD_INIT */
3408}
3409
3410char *
3411hes_map_lookup(map, name, av, statp)
3412        MAP *map;
3413        char *name;
3414        char **av;
3415        int *statp;
3416{
3417        char **hp;
3418
3419        if (tTd(38, 20))
3420                printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
3421
3422        if (name[0] == '\\')
3423        {
3424                char *np;
3425                int nl;
3426                char nbuf[MAXNAME];
3427
3428                nl = strlen(name);
3429                if (nl < sizeof nbuf - 1)
3430                        np = nbuf;
3431                else
3432                        np = xalloc(strlen(name) + 2);
3433                np[0] = '\\';
3434                strcpy(&np[1], name);
3435#ifdef HESIOD_INIT
3436                hp = hesiod_resolve(HesiodContext, np, map->map_file);
3437#else
3438                hp = hes_resolve(np, map->map_file);
3439#endif /* HESIOD_INIT */
3440                if (np != nbuf)
3441                        free(np);
3442        }
3443        else
3444        {
3445#ifdef HESIOD_INIT
3446                hp = hesiod_resolve(HesiodContext, name, map->map_file);
3447#else
3448                hp = hes_resolve(name, map->map_file);
3449#endif /* HESIOD_INIT */
3450        }
3451#ifdef HESIOD_INIT
3452        if (hp == NULL)
3453                return NULL;
3454        if (*hp == NULL)
3455        {
3456                hesiod_free_list(HesiodContext, hp);
3457                switch (errno)
3458                {
3459                  case ENOENT:
3460                          *statp = EX_NOTFOUND;
3461                          break;
3462                  case ECONNREFUSED:
3463                  case EMSGSIZE:
3464                          *statp = EX_TEMPFAIL;
3465                          break;
3466                  case ENOMEM:
3467                  default:
3468                          *statp = EX_UNAVAILABLE;
3469                          break;
3470                }
3471                return NULL;
3472        }
3473#else
3474        if (hp == NULL || hp[0] == NULL)
3475        {
3476                switch (hes_error())
3477                {
3478                  case HES_ER_OK:
3479                        *statp = EX_OK;
3480                        break;
3481
3482                  case HES_ER_NOTFOUND:
3483                        *statp = EX_NOTFOUND;
3484                        break;
3485
3486                  case HES_ER_CONFIG:
3487                        *statp = EX_UNAVAILABLE;
3488                        break;
3489
3490                  case HES_ER_NET:
3491                        *statp = EX_TEMPFAIL;
3492                        break;
3493                }
3494                return NULL;
3495        }
3496#endif /* HESIOD_INIT */
3497
3498        if (bitset(MF_MATCHONLY, map->map_mflags))
3499                return map_rewrite(map, name, strlen(name), NULL);
3500        else
3501                return map_rewrite(map, hp[0], strlen(hp[0]), av);
3502}
3503
3504#endif
3505/*
3506**  NeXT NETINFO Modules
3507*/
3508
3509#if NETINFO
3510
3511# define NETINFO_DEFAULT_DIR            "/aliases"
3512# define NETINFO_DEFAULT_PROPERTY       "members"
3513
3514extern char     *ni_propval __P((char *, char *, char *, char *, int));
3515
3516
3517/*
3518**  NI_MAP_OPEN -- open NetInfo Aliases
3519*/
3520
3521bool
3522ni_map_open(map, mode)
3523        MAP *map;
3524        int mode;
3525{
3526        if (tTd(38, 2))
3527                printf("ni_map_open(%s, %s, %d)\n",
3528                        map->map_mname, map->map_file, mode);
3529        mode &= O_ACCMODE;
3530
3531        if (*map->map_file == '\0')
3532                map->map_file = NETINFO_DEFAULT_DIR;
3533
3534        if (map->map_valcolnm == NULL)
3535                map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
3536
3537        if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
3538                map->map_coldelim = ',';
3539
3540        return TRUE;
3541}
3542
3543
3544/*
3545**  NI_MAP_LOOKUP -- look up a datum in NetInfo
3546*/
3547
3548char *
3549ni_map_lookup(map, name, av, statp)
3550        MAP *map;
3551        char *name;
3552        char **av;
3553        int *statp;
3554{
3555        char *res;
3556        char *propval;
3557
3558        if (tTd(38, 20))
3559                printf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
3560
3561        propval = ni_propval(map->map_file, map->map_keycolnm, name,
3562                             map->map_valcolnm, map->map_coldelim);
3563
3564        if (propval == NULL)
3565                return NULL;
3566
3567        if (bitset(MF_MATCHONLY, map->map_mflags))
3568                res = map_rewrite(map, name, strlen(name), NULL);
3569        else
3570                res = map_rewrite(map, propval, strlen(propval), av);
3571        free(propval);
3572        return res;
3573}
3574
3575
3576bool
3577ni_getcanonname(name, hbsize, statp)
3578        char *name;
3579        int hbsize;
3580        int *statp;
3581{
3582        char *vptr;
3583        char *ptr;
3584        char nbuf[MAXNAME + 1];
3585
3586        if (tTd(38, 20))
3587                printf("ni_getcanonname(%s)\n", name);
3588
3589        if (strlen(name) >= sizeof nbuf)
3590        {
3591                *statp = EX_UNAVAILABLE;
3592                return FALSE;
3593        }
3594        (void) strcpy(nbuf, name);
3595        shorten_hostname(nbuf);
3596
3597        /* we only accept single token search key */
3598        if (strchr(nbuf, '.'))
3599        {
3600                *statp = EX_NOHOST;
3601                return FALSE;
3602        }
3603
3604        /* Do the search */
3605        vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
3606
3607        if (vptr == NULL)
3608        {
3609                *statp = EX_NOHOST;
3610                return FALSE;
3611        }
3612
3613        /* Only want the first machine name */
3614        if ((ptr = strchr(vptr, '\n')) != NULL)
3615                *ptr = '\0';
3616
3617        if (hbsize >= strlen(vptr))
3618        {
3619                strcpy(name, vptr);
3620                *statp = EX_OK;
3621                return TRUE;
3622        }
3623        *statp = EX_UNAVAILABLE;
3624        free(vptr);
3625        return FALSE;
3626}
3627
3628
3629/*
3630**  NI_PROPVAL -- NetInfo property value lookup routine
3631**
3632**      Parameters:
3633**              keydir -- the NetInfo directory name in which to search
3634**                      for the key.
3635**              keyprop -- the name of the property in which to find the
3636**                      property we are interested.  Defaults to "name".
3637**              keyval -- the value for which we are really searching.
3638**              valprop -- the property name for the value in which we
3639**                      are interested.
3640**              sepchar -- if non-nil, this can be multiple-valued, and
3641**                      we should return a string separated by this
3642**                      character.
3643**
3644**      Returns:
3645**              NULL -- if:
3646**                      1. the directory is not found
3647**                      2. the property name is not found
3648**                      3. the property contains multiple values
3649**                      4. some error occured
3650**              else -- the value of the lookup.
3651**
3652**      Example:
3653**              To search for an alias value, use:
3654**                ni_propval("/aliases", "name", aliasname, "members", ',')
3655**
3656**      Notes:
3657**              Caller should free the return value of ni_proval
3658*/
3659
3660# include <netinfo/ni.h>
3661
3662# define LOCAL_NETINFO_DOMAIN    "."
3663# define PARENT_NETINFO_DOMAIN   ".."
3664# define MAX_NI_LEVELS           256
3665
3666char *
3667ni_propval(keydir, keyprop, keyval, valprop, sepchar)
3668        char *keydir;
3669        char *keyprop;
3670        char *keyval;
3671        char *valprop;
3672        int sepchar;
3673{
3674        char *propval = NULL;
3675        int i;
3676        int j, alen;
3677        void *ni = NULL;
3678        void *lastni = NULL;
3679        ni_status nis;
3680        ni_id nid;
3681        ni_namelist ninl;
3682        register char *p;
3683        char keybuf[1024];
3684
3685        /*
3686        **  Create the full key from the two parts.
3687        **
3688        **      Note that directory can end with, e.g., "name=" to specify
3689        **      an alternate search property.
3690        */
3691
3692        i = strlen(keydir) + strlen(keyval) + 2;
3693        if (keyprop != NULL)
3694                i += strlen(keyprop) + 1;
3695        if (i > sizeof keybuf)
3696                return NULL;
3697        strcpy(keybuf, keydir);
3698        strcat(keybuf, "/");
3699        if (keyprop != NULL)
3700        {
3701                strcat(keybuf, keyprop);
3702                strcat(keybuf, "=");
3703        }
3704        strcat(keybuf, keyval);
3705
3706        if (tTd(38, 21))
3707                printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
3708                        keydir, keyprop, keyval, valprop, sepchar, keybuf);
3709        /*
3710        **  If the passed directory and property name are found
3711        **  in one of netinfo domains we need to search (starting
3712        **  from the local domain moving all the way back to the
3713        **  root domain) set propval to the property's value
3714        **  and return it.
3715        */
3716
3717        for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
3718        {
3719                if (i == 0)
3720                {
3721                        nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
3722                        if (tTd(38, 20))
3723                                printf("ni_open(LOCAL) = %d\n", nis);
3724                }
3725                else
3726                {
3727                        if (lastni != NULL)
3728                                ni_free(lastni);
3729                        lastni = ni;
3730                        nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
3731                        if (tTd(38, 20))
3732                                printf("ni_open(PARENT) = %d\n", nis);
3733                }
3734
3735                /*
3736                **  Don't bother if we didn't get a handle on a
3737                **  proper domain.  This is not necessarily an error.
3738                **  We would get a positive ni_status if, for instance
3739                **  we never found the directory or property and tried
3740                **  to open the parent of the root domain!
3741                */
3742
3743                if (nis != 0)
3744                        break;
3745
3746                /*
3747                **  Find the path to the server information.
3748                */
3749
3750                if (ni_pathsearch(ni, &nid, keybuf) != 0)
3751                        continue;
3752
3753                /*
3754                **  Find associated value information.
3755                */
3756
3757                if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
3758                        continue;
3759
3760                if (tTd(38, 20))
3761                        printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len);
3762                /*
3763                **  See if we have an acceptable number of values.
3764                */
3765
3766                if (ninl.ni_namelist_len <= 0)
3767                        continue;
3768
3769                if (sepchar == '\0' && ninl.ni_namelist_len > 1)
3770                {
3771                        ni_namelist_free(&ninl);
3772                        continue;
3773                }
3774
3775                /*
3776                **  Calculate number of bytes needed and build result
3777                */
3778
3779                alen = 1;
3780                for (j = 0; j < ninl.ni_namelist_len; j++)
3781                        alen += strlen(ninl.ni_namelist_val[j]) + 1;
3782                propval = p = xalloc(alen);
3783                for (j = 0; j < ninl.ni_namelist_len; j++)
3784                {
3785                        strcpy(p, ninl.ni_namelist_val[j]);
3786                        p += strlen(p);
3787                        *p++ = sepchar;
3788                }
3789                *--p = '\0';
3790
3791                ni_namelist_free(&ninl);
3792        }
3793
3794        /*
3795        **  Clean up.
3796        */
3797
3798        if (ni != NULL)
3799                ni_free(ni);
3800        if (lastni != NULL && ni != lastni)
3801                ni_free(lastni);
3802        if (tTd(38, 20))
3803                printf("ni_propval returns: '%s'\n", propval);
3804
3805        return propval;
3806}
3807
3808#endif /* NETINFO */
3809/*
3810**  TEXT (unindexed text file) Modules
3811**
3812**      This code donated by Sun Microsystems.
3813*/
3814
3815#define map_sff         map_lockfd      /* overload field */
3816
3817
3818/*
3819**  TEXT_MAP_OPEN -- open text table
3820*/
3821
3822bool
3823text_map_open(map, mode)
3824        MAP *map;
3825        int mode;
3826{
3827        int sff;
3828        int i;
3829
3830        if (tTd(38, 2))
3831                printf("text_map_open(%s, %s, %d)\n",
3832                        map->map_mname, map->map_file, mode);
3833
3834        mode &= O_ACCMODE;
3835        if (mode != O_RDONLY)
3836        {
3837                errno = EPERM;
3838                return FALSE;
3839        }
3840
3841        if (*map->map_file == '\0')
3842        {
3843                syserr("text map \"%s\": file name required",
3844                        map->map_mname);
3845                return FALSE;
3846        }
3847
3848        if (map->map_file[0] != '/')
3849        {
3850                syserr("text map \"%s\": file name must be fully qualified",
3851                        map->map_mname);
3852                return FALSE;
3853        }
3854
3855        sff = SFF_ROOTOK|SFF_REGONLY;
3856        if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
3857                sff |= SFF_NOWLINK;
3858        if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
3859                sff |= SFF_SAFEDIRPATH;
3860        if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
3861                          sff, S_IRUSR, NULL)) != 0)
3862        {
3863                /* cannot open this map */
3864                if (tTd(38, 2))
3865                        printf("\tunsafe map file: %d\n", i);
3866                if (!bitset(MF_OPTIONAL, map->map_mflags))
3867                        syserr("text map \"%s\": unsafe map file %s",
3868                                map->map_mname, map->map_file);
3869                return FALSE;
3870        }
3871
3872        if (map->map_keycolnm == NULL)
3873                map->map_keycolno = 0;
3874        else
3875        {
3876                if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
3877                {
3878                        syserr("text map \"%s\", file %s: -k should specify a number, not %s",
3879                                map->map_mname, map->map_file,
3880                                map->map_keycolnm);
3881                        return FALSE;
3882                }
3883                map->map_keycolno = atoi(map->map_keycolnm);
3884        }
3885
3886        if (map->map_valcolnm == NULL)
3887                map->map_valcolno = 0;
3888        else
3889        {
3890                if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
3891                {
3892                        syserr("text map \"%s\", file %s: -v should specify a number, not %s",
3893                                        map->map_mname, map->map_file,
3894                                        map->map_valcolnm);
3895                        return FALSE;
3896                }
3897                map->map_valcolno = atoi(map->map_valcolnm);
3898        }
3899
3900        if (tTd(38, 2))
3901        {
3902                printf("text_map_open(%s, %s): delimiter = ",
3903                        map->map_mname, map->map_file);
3904                if (map->map_coldelim == '\0')
3905                        printf("(white space)\n");
3906                else
3907                        printf("%c\n", map->map_coldelim);
3908        }
3909
3910        map->map_sff = sff;
3911        return TRUE;
3912}
3913
3914
3915/*
3916**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
3917*/
3918
3919char *
3920text_map_lookup(map, name, av, statp)
3921        MAP *map;
3922        char *name;
3923        char **av;
3924        int *statp;
3925{
3926        char *vp;
3927        auto int vsize;
3928        int buflen;
3929        FILE *f;
3930        char delim;
3931        int key_idx;
3932        bool found_it;
3933        int sff = map->map_sff;
3934        char search_key[MAXNAME + 1];
3935        char linebuf[MAXLINE];
3936        char buf[MAXNAME + 1];
3937        extern char *get_column __P((char *, int, char, char *, int));
3938
3939        found_it = FALSE;
3940        if (tTd(38, 20))
3941                printf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
3942
3943        buflen = strlen(name);
3944        if (buflen > sizeof search_key - 1)
3945                buflen = sizeof search_key - 1;
3946        bcopy(name, search_key, buflen);
3947        search_key[buflen] = '\0';
3948        if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3949                makelower(search_key);
3950
3951        f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
3952        if (f == NULL)
3953        {
3954                map->map_mflags &= ~(MF_VALID|MF_OPEN);
3955                *statp = EX_UNAVAILABLE;
3956                return NULL;
3957        }
3958        key_idx = map->map_keycolno;
3959        delim = map->map_coldelim;
3960        while (fgets(linebuf, MAXLINE, f) != NULL)
3961        {
3962                char *p;
3963
3964                /* skip comment line */
3965                if (linebuf[0] == '#')
3966                        continue;
3967                p = strchr(linebuf, '\n');
3968                if (p != NULL)
3969                        *p = '\0';
3970                p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
3971                if (p != NULL && strcasecmp(search_key, p) == 0)
3972                {
3973                        found_it = TRUE;
3974                        break;
3975                }
3976        }
3977        fclose(f);
3978        if (!found_it)
3979        {
3980                *statp = EX_NOTFOUND;
3981                return NULL;
3982        }
3983        vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
3984        if (vp == NULL)
3985        {
3986                *statp = EX_NOTFOUND;
3987                return NULL;
3988        }
3989        vsize = strlen(vp);
3990        *statp = EX_OK;
3991        if (bitset(MF_MATCHONLY, map->map_mflags))
3992                return map_rewrite(map, name, strlen(name), NULL);
3993        else
3994                return map_rewrite(map, vp, vsize, av);
3995}
3996
3997
3998/*
3999**  TEXT_GETCANONNAME -- look up canonical name in hosts file
4000*/
4001
4002bool
4003text_getcanonname(name, hbsize, statp)
4004        char *name;
4005        int hbsize;
4006        int *statp;
4007{
4008        bool found;
4009        FILE *f;
4010        char linebuf[MAXLINE];
4011        char cbuf[MAXNAME + 1];
4012        char nbuf[MAXNAME + 1];
4013
4014        if (tTd(38, 20))
4015                printf("text_getcanonname(%s)\n", name);
4016
4017        if (strlen(name) >= (SIZE_T) sizeof nbuf)
4018        {
4019                *statp = EX_UNAVAILABLE;
4020                return FALSE;
4021        }
4022        (void) strcpy(nbuf, name);
4023        shorten_hostname(nbuf);
4024
4025        f = fopen(HostsFile, "r");
4026        if (f == NULL)
4027        {
4028                *statp = EX_UNAVAILABLE;
4029                return FALSE;
4030        }
4031        found = FALSE;
4032        while (!found && fgets(linebuf, MAXLINE, f) != NULL)
4033        {
4034                char *p = strpbrk(linebuf, "#\n");
4035
4036                if (p != NULL)
4037                        *p = '\0';
4038                if (linebuf[0] != '\0')
4039                        found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
4040        }
4041        fclose(f);
4042        if (!found)
4043        {
4044                *statp = EX_NOHOST;
4045                return FALSE;
4046        }
4047
4048        if ((SIZE_T) hbsize >= strlen(cbuf))
4049        {
4050                strcpy(name, cbuf);
4051                *statp = EX_OK;
4052                return TRUE;
4053        }
4054        *statp = EX_UNAVAILABLE;
4055        return FALSE;
4056}
4057/*
4058**  STAB (Symbol Table) Modules
4059*/
4060
4061
4062/*
4063**  STAB_MAP_LOOKUP -- look up alias in symbol table
4064*/
4065
4066/* ARGSUSED2 */
4067char *
4068stab_map_lookup(map, name, av, pstat)
4069        register MAP *map;
4070        char *name;
4071        char **av;
4072        int *pstat;
4073{
4074        register STAB *s;
4075
4076        if (tTd(38, 20))
4077                printf("stab_lookup(%s, %s)\n",
4078                        map->map_mname, name);
4079
4080        s = stab(name, ST_ALIAS, ST_FIND);
4081        if (s != NULL)
4082                return (s->s_alias);
4083        return (NULL);
4084}
4085
4086
4087/*
4088**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
4089*/
4090
4091void
4092stab_map_store(map, lhs, rhs)
4093        register MAP *map;
4094        char *lhs;
4095        char *rhs;
4096{
4097        register STAB *s;
4098
4099        s = stab(lhs, ST_ALIAS, ST_ENTER);
4100        s->s_alias = newstr(rhs);
4101}
4102
4103
4104/*
4105**  STAB_MAP_OPEN -- initialize (reads data file)
4106**
4107**      This is a wierd case -- it is only intended as a fallback for
4108**      aliases.  For this reason, opens for write (only during a
4109**      "newaliases") always fails, and opens for read open the
4110**      actual underlying text file instead of the database.
4111*/
4112
4113bool
4114stab_map_open(map, mode)
4115        register MAP *map;
4116        int mode;
4117{
4118        FILE *af;
4119        int sff;
4120        struct stat st;
4121
4122        if (tTd(38, 2))
4123                printf("stab_map_open(%s, %s, %d)\n",
4124                        map->map_mname, map->map_file, mode);
4125
4126        mode &= O_ACCMODE;
4127        if (mode != O_RDONLY)
4128        {
4129                errno = EPERM;
4130                return FALSE;
4131        }
4132
4133        sff = SFF_ROOTOK|SFF_REGONLY;
4134        if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
4135                sff |= SFF_NOWLINK;
4136        if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
4137                sff |= SFF_SAFEDIRPATH;
4138        af = safefopen(map->map_file, O_RDONLY, 0444, sff);
4139        if (af == NULL)
4140                return FALSE;
4141        readaliases(map, af, FALSE, FALSE);
4142
4143        if (fstat(fileno(af), &st) >= 0)
4144                map->map_mtime = st.st_mtime;
4145        fclose(af);
4146
4147        return TRUE;
4148}
4149/*
4150**  Implicit Modules
4151**
4152**      Tries several types.  For back compatibility of aliases.
4153*/
4154
4155
4156/*
4157**  IMPL_MAP_LOOKUP -- lookup in best open database
4158*/
4159
4160char *
4161impl_map_lookup(map, name, av, pstat)
4162        MAP *map;
4163        char *name;
4164        char **av;
4165        int *pstat;
4166{
4167        if (tTd(38, 20))
4168                printf("impl_map_lookup(%s, %s)\n",
4169                        map->map_mname, name);
4170
4171#ifdef NEWDB
4172        if (bitset(MF_IMPL_HASH, map->map_mflags))
4173                return db_map_lookup(map, name, av, pstat);
4174#endif
4175#ifdef NDBM
4176        if (bitset(MF_IMPL_NDBM, map->map_mflags))
4177                return ndbm_map_lookup(map, name, av, pstat);
4178#endif
4179        return stab_map_lookup(map, name, av, pstat);
4180}
4181
4182/*
4183**  IMPL_MAP_STORE -- store in open databases
4184*/
4185
4186void
4187impl_map_store(map, lhs, rhs)
4188        MAP *map;
4189        char *lhs;
4190        char *rhs;
4191{
4192        if (tTd(38, 12))
4193                printf("impl_map_store(%s, %s, %s)\n",
4194                        map->map_mname, lhs, rhs);
4195#ifdef NEWDB
4196        if (bitset(MF_IMPL_HASH, map->map_mflags))
4197                db_map_store(map, lhs, rhs);
4198#endif
4199#ifdef NDBM
4200        if (bitset(MF_IMPL_NDBM, map->map_mflags))
4201                ndbm_map_store(map, lhs, rhs);
4202#endif
4203        stab_map_store(map, lhs, rhs);
4204}
4205
4206/*
4207**  IMPL_MAP_OPEN -- implicit database open
4208*/
4209
4210bool
4211impl_map_open(map, mode)
4212        MAP *map;
4213        int mode;
4214{
4215        if (tTd(38, 2))
4216                printf("impl_map_open(%s, %s, %d)\n",
4217                        map->map_mname, map->map_file, mode);
4218
4219        mode &= O_ACCMODE;
4220#ifdef NEWDB
4221        map->map_mflags |= MF_IMPL_HASH;
4222        if (hash_map_open(map, mode))
4223        {
4224# ifdef NDBM_YP_COMPAT
4225                if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
4226# endif
4227                        return TRUE;
4228        }
4229        else
4230                map->map_mflags &= ~MF_IMPL_HASH;
4231#endif
4232#ifdef NDBM
4233        map->map_mflags |= MF_IMPL_NDBM;
4234        if (ndbm_map_open(map, mode))
4235        {
4236                return TRUE;
4237        }
4238        else
4239                map->map_mflags &= ~MF_IMPL_NDBM;
4240#endif
4241
4242#if defined(NEWDB) || defined(NDBM)
4243        if (Verbose)
4244                message("WARNING: cannot open alias database %s%s",
4245                        map->map_file,
4246                        mode == O_RDONLY ? "; reading text version" : "");
4247#else
4248        if (mode != O_RDONLY)
4249                usrerr("Cannot rebuild aliases: no database format defined");
4250#endif
4251
4252        if (mode == O_RDONLY)
4253                return stab_map_open(map, mode);
4254        else
4255                return FALSE;
4256}
4257
4258
4259/*
4260**  IMPL_MAP_CLOSE -- close any open database(s)
4261*/
4262
4263void
4264impl_map_close(map)
4265        MAP *map;
4266{
4267        if (tTd(38, 9))
4268                printf("impl_map_close(%s, %s, %lx)\n",
4269                        map->map_mname, map->map_file, map->map_mflags);
4270#ifdef NEWDB
4271        if (bitset(MF_IMPL_HASH, map->map_mflags))
4272        {
4273                db_map_close(map);
4274                map->map_mflags &= ~MF_IMPL_HASH;
4275        }
4276#endif
4277
4278#ifdef NDBM
4279        if (bitset(MF_IMPL_NDBM, map->map_mflags))
4280        {
4281                ndbm_map_close(map);
4282                map->map_mflags &= ~MF_IMPL_NDBM;
4283        }
4284#endif
4285}
4286/*
4287**  User map class.
4288**
4289**      Provides access to the system password file.
4290*/
4291
4292/*
4293**  USER_MAP_OPEN -- open user map
4294**
4295**      Really just binds field names to field numbers.
4296*/
4297
4298bool
4299user_map_open(map, mode)
4300        MAP *map;
4301        int mode;
4302{
4303        if (tTd(38, 2))
4304                printf("user_map_open(%s, %d)\n",
4305                        map->map_mname, mode);
4306
4307        mode &= O_ACCMODE;
4308        if (mode != O_RDONLY)
4309        {
4310                /* issue a pseudo-error message */
4311#ifdef ENOSYS
4312                errno = ENOSYS;
4313#else
4314# ifdef EFTYPE
4315                errno = EFTYPE;
4316# else
4317                errno = ENXIO;
4318# endif
4319#endif
4320                return FALSE;
4321        }
4322        if (map->map_valcolnm == NULL)
4323                /* nothing */ ;
4324        else if (strcasecmp(map->map_valcolnm, "name") == 0)
4325                map->map_valcolno = 1;
4326        else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
4327                map->map_valcolno = 2;
4328        else if (strcasecmp(map->map_valcolnm, "uid") == 0)
4329                map->map_valcolno = 3;
4330        else if (strcasecmp(map->map_valcolnm, "gid") == 0)
4331                map->map_valcolno = 4;
4332        else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
4333                map->map_valcolno = 5;
4334        else if (strcasecmp(map->map_valcolnm, "dir") == 0)
4335                map->map_valcolno = 6;
4336        else if (strcasecmp(map->map_valcolnm, "shell") == 0)
4337                map->map_valcolno = 7;
4338        else
4339        {
4340                syserr("User map %s: unknown column name %s",
4341                        map->map_mname, map->map_valcolnm);
4342                return FALSE;
4343        }
4344        return TRUE;
4345}
4346
4347
4348/*
4349**  USER_MAP_LOOKUP -- look up a user in the passwd file.
4350*/
4351
4352/* ARGSUSED3 */
4353char *
4354user_map_lookup(map, key, av, statp)
4355        MAP *map;
4356        char *key;
4357        char **av;
4358        int *statp;
4359{
4360        struct passwd *pw;
4361        auto bool fuzzy;
4362
4363        if (tTd(38, 20))
4364                printf("user_map_lookup(%s, %s)\n",
4365                        map->map_mname, key);
4366
4367        pw = finduser(key, &fuzzy);
4368        if (pw == NULL)
4369                return NULL;
4370        if (bitset(MF_MATCHONLY, map->map_mflags))
4371                return map_rewrite(map, key, strlen(key), NULL);
4372        else
4373        {
4374                char *rwval = NULL;
4375                char buf[30];
4376
4377                switch (map->map_valcolno)
4378                {
4379                  case 0:
4380                  case 1:
4381                        rwval = pw->pw_name;
4382                        break;
4383
4384                  case 2:
4385                        rwval = pw->pw_passwd;
4386                        break;
4387
4388                  case 3:
4389                        snprintf(buf, sizeof buf, "%d", pw->pw_uid);
4390                        rwval = buf;
4391                        break;
4392
4393                  case 4:
4394                        snprintf(buf, sizeof buf, "%d", pw->pw_gid);
4395                        rwval = buf;
4396                        break;
4397
4398                  case 5:
4399                        rwval = pw->pw_gecos;
4400                        break;
4401
4402                  case 6:
4403                        rwval = pw->pw_dir;
4404                        break;
4405
4406                  case 7:
4407                        rwval = pw->pw_shell;
4408                        break;
4409                }
4410                return map_rewrite(map, rwval, strlen(rwval), av);
4411        }
4412}
4413/*
4414**  Program map type.
4415**
4416**      This provides access to arbitrary programs.  It should be used
4417**      only very sparingly, since there is no way to bound the cost
4418**      of invoking an arbitrary program.
4419*/
4420
4421char *
4422prog_map_lookup(map, name, av, statp)
4423        MAP *map;
4424        char *name;
4425        char **av;
4426        int *statp;
4427{
4428        int i;
4429        register char *p;
4430        int fd;
4431        auto pid_t pid;
4432        char *rval;
4433        int stat;
4434        char *argv[MAXPV + 1];
4435        char buf[MAXLINE];
4436
4437        if (tTd(38, 20))
4438                printf("prog_map_lookup(%s, %s) %s\n",
4439                        map->map_mname, name, map->map_file);
4440
4441        i = 0;
4442        argv[i++] = map->map_file;
4443        if (map->map_rebuild != NULL)
4444        {
4445                snprintf(buf, sizeof buf, "%s", map->map_rebuild);
4446                for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
4447                {
4448                        if (i >= MAXPV - 1)
4449                                break;
4450                        argv[i++] = p;
4451                }
4452        }
4453        argv[i++] = name;
4454        argv[i] = NULL;
4455        if (tTd(38, 21))
4456        {
4457                printf("prog_open:");
4458                for (i = 0; argv[i] != NULL; i++)
4459                        printf(" %s", argv[i]);
4460                printf("\n");
4461        }
4462        (void) blocksignal(SIGCHLD);
4463        pid = prog_open(argv, &fd, CurEnv);
4464        if (pid < 0)
4465        {
4466                if (!bitset(MF_OPTIONAL, map->map_mflags))
4467                        syserr("prog_map_lookup(%s) failed (%s) -- closing",
4468                                map->map_mname, errstring(errno));
4469                else if (tTd(38, 9))
4470                        printf("prog_map_lookup(%s) failed (%s) -- closing",
4471                                map->map_mname, errstring(errno));
4472                map->map_mflags &= ~(MF_VALID|MF_OPEN);
4473                *statp = EX_OSFILE;
4474                return NULL;
4475        }
4476        i = read(fd, buf, sizeof buf - 1);
4477        if (i < 0)
4478        {
4479                syserr("prog_map_lookup(%s): read error %s\n",
4480                        map->map_mname, errstring(errno));
4481                rval = NULL;
4482        }
4483        else if (i == 0)
4484        {
4485                if (tTd(38, 20))
4486                        printf("prog_map_lookup(%s): empty answer\n",
4487                                map->map_mname);
4488                rval = NULL;
4489        }
4490        else
4491        {
4492                buf[i] = '\0';
4493                p = strchr(buf, '\n');
4494                if (p != NULL)
4495                        *p = '\0';
4496
4497                /* collect the return value */
4498                if (bitset(MF_MATCHONLY, map->map_mflags))
4499                        rval = map_rewrite(map, name, strlen(name), NULL);
4500                else
4501                        rval = map_rewrite(map, buf, strlen(buf), NULL);
4502
4503                /* now flush any additional output */
4504                while ((i = read(fd, buf, sizeof buf)) > 0)
4505                        continue;
4506        }
4507
4508        /* wait for the process to terminate */
4509        close(fd);
4510        stat = waitfor(pid);
4511        (void) releasesignal(SIGCHLD);
4512
4513        if (stat == -1)
4514        {
4515                syserr("prog_map_lookup(%s): wait error %s\n",
4516                        map->map_mname, errstring(errno));
4517                *statp = EX_SOFTWARE;
4518                rval = NULL;
4519        }
4520        else if (WIFEXITED(stat))
4521        {
4522                if ((*statp = WEXITSTATUS(stat)) != EX_OK)
4523                        rval = NULL;
4524        }
4525        else
4526        {
4527                syserr("prog_map_lookup(%s): child died on signal %d",
4528                        map->map_mname, stat);
4529                *statp = EX_UNAVAILABLE;
4530                rval = NULL;
4531        }
4532        return rval;
4533}
4534/*
4535**  Sequenced map type.
4536**
4537**      Tries each map in order until something matches, much like
4538**      implicit.  Stores go to the first map in the list that can
4539**      support storing.
4540**
4541**      This is slightly unusual in that there are two interfaces.
4542**      The "sequence" interface lets you stack maps arbitrarily.
4543**      The "switch" interface builds a sequence map by looking
4544**      at a system-dependent configuration file such as
4545**      /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
4546**
4547**      We don't need an explicit open, since all maps are
4548**      opened during startup, including underlying maps.
4549*/
4550
4551/*
4552**  SEQ_MAP_PARSE -- Sequenced map parsing
4553*/
4554
4555bool
4556seq_map_parse(map, ap)
4557        MAP *map;
4558        char *ap;
4559{
4560        int maxmap;
4561
4562        if (tTd(38, 2))
4563                printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
4564        maxmap = 0;
4565        while (*ap != '\0')
4566        {
4567                register char *p;
4568                STAB *s;
4569
4570                /* find beginning of map name */
4571                while (isascii(*ap) && isspace(*ap))
4572                        ap++;
4573                for (p = ap; isascii(*p) && isalnum(*p); p++)
4574                        continue;
4575                if (*p != '\0')
4576                        *p++ = '\0';
4577                while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
4578                        p++;
4579                if (*ap == '\0')
4580                {
4581                        ap = p;
4582                        continue;
4583                }
4584                s = stab(ap, ST_MAP, ST_FIND);
4585                if (s == NULL)
4586                {
4587                        syserr("Sequence map %s: unknown member map %s",
4588                                map->map_mname, ap);
4589                }
4590                else if (maxmap == MAXMAPSTACK)
4591                {
4592                        syserr("Sequence map %s: too many member maps (%d max)",
4593                                map->map_mname, MAXMAPSTACK);
4594                        maxmap++;
4595                }
4596                else if (maxmap < MAXMAPSTACK)
4597                {
4598                        map->map_stack[maxmap++] = &s->s_map;
4599                }
4600                ap = p;
4601        }
4602        return TRUE;
4603}
4604
4605
4606/*
4607**  SWITCH_MAP_OPEN -- open a switched map
4608**
4609**      This looks at the system-dependent configuration and builds
4610**      a sequence map that does the same thing.
4611**
4612**      Every system must define a switch_map_find routine in conf.c
4613**      that will return the list of service types associated with a
4614**      given service class.
4615*/
4616
4617bool
4618switch_map_open(map, mode)
4619        MAP *map;
4620        int mode;
4621{
4622        int mapno;
4623        int nmaps;
4624        char *maptype[MAXMAPSTACK];
4625
4626        if (tTd(38, 2))
4627                printf("switch_map_open(%s, %s, %d)\n",
4628                        map->map_mname, map->map_file, mode);
4629
4630        mode &= O_ACCMODE;
4631        nmaps = switch_map_find(map->map_file, maptype, map->map_return);
4632        if (tTd(38, 19))
4633        {
4634                printf("\tswitch_map_find => %d\n", nmaps);
4635                for (mapno = 0; mapno < nmaps; mapno++)
4636                        printf("\t\t%s\n", maptype[mapno]);
4637        }
4638        if (nmaps <= 0 || nmaps > MAXMAPSTACK)
4639                return FALSE;
4640
4641        for (mapno = 0; mapno < nmaps; mapno++)
4642        {
4643                register STAB *s;
4644                char nbuf[MAXNAME + 1];
4645
4646                if (maptype[mapno] == NULL)
4647                        continue;
4648                (void) snprintf(nbuf, sizeof nbuf, "%s.%s",
4649                        map->map_mname, maptype[mapno]);
4650                s = stab(nbuf, ST_MAP, ST_FIND);
4651                if (s == NULL)
4652                {
4653                        syserr("Switch map %s: unknown member map %s",
4654                                map->map_mname, nbuf);
4655                }
4656                else
4657                {
4658                        map->map_stack[mapno] = &s->s_map;
4659                        if (tTd(38, 4))
4660                                printf("\tmap_stack[%d] = %s:%s\n",
4661                                        mapno, s->s_map.map_class->map_cname,
4662                                        nbuf);
4663                }
4664        }
4665        return TRUE;
4666}
4667
4668
4669/*
4670**  SEQ_MAP_CLOSE -- close all underlying maps
4671*/
4672
4673void
4674seq_map_close(map)
4675        MAP *map;
4676{
4677        int mapno;
4678
4679        if (tTd(38, 9))
4680                printf("seq_map_close(%s)\n", map->map_mname);
4681
4682        for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4683        {
4684                MAP *mm = map->map_stack[mapno];
4685
4686                if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
4687                        continue;
4688                mm->map_class->map_close(mm);
4689                mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4690        }
4691}
4692
4693
4694/*
4695**  SEQ_MAP_LOOKUP -- sequenced map lookup
4696*/
4697
4698char *
4699seq_map_lookup(map, key, args, pstat)
4700        MAP *map;
4701        char *key;
4702        char **args;
4703        int *pstat;
4704{
4705        int mapno;
4706        int mapbit = 0x01;
4707        bool tempfail = FALSE;
4708
4709        if (tTd(38, 20))
4710                printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
4711
4712        for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
4713        {
4714                MAP *mm = map->map_stack[mapno];
4715                char *rv;
4716
4717                if (mm == NULL)
4718                        continue;
4719                if (!bitset(MF_OPEN, mm->map_mflags))
4720                {
4721                        if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
4722                        {
4723                                *pstat = EX_UNAVAILABLE;
4724                                return NULL;
4725                        }
4726                        continue;
4727                }
4728                *pstat = EX_OK;
4729                rv = mm->map_class->map_lookup(mm, key, args, pstat);
4730                if (rv != NULL)
4731                        return rv;
4732                if (*pstat == EX_TEMPFAIL)
4733                {
4734                        if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
4735                                return NULL;
4736                        tempfail = TRUE;
4737                }
4738                else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
4739                        break;
4740        }
4741        if (tempfail)
4742                *pstat = EX_TEMPFAIL;
4743        else if (*pstat == EX_OK)
4744                *pstat = EX_NOTFOUND;
4745        return NULL;
4746}
4747
4748
4749/*
4750**  SEQ_MAP_STORE -- sequenced map store
4751*/
4752
4753void
4754seq_map_store(map, key, val)
4755        MAP *map;
4756        char *key;
4757        char *val;
4758{
4759        int mapno;
4760
4761        if (tTd(38, 12))
4762                printf("seq_map_store(%s, %s, %s)\n",
4763                        map->map_mname, key, val);
4764
4765        for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
4766        {
4767                MAP *mm = map->map_stack[mapno];
4768
4769                if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
4770                        continue;
4771
4772                mm->map_class->map_store(mm, key, val);
4773                return;
4774        }
4775        syserr("seq_map_store(%s, %s, %s): no writable map",
4776                map->map_mname, key, val);
4777}
4778/*
4779**  NULL stubs
4780*/
4781
4782/* ARGSUSED */
4783bool
4784null_map_open(map, mode)
4785        MAP *map;
4786        int mode;
4787{
4788        return TRUE;
4789}
4790
4791/* ARGSUSED */
4792void
4793null_map_close(map)
4794        MAP *map;
4795{
4796        return;
4797}
4798
4799char *
4800null_map_lookup(map, key, args, pstat)
4801        MAP *map;
4802        char *key;
4803        char **args;
4804        int *pstat;
4805{
4806        *pstat = EX_NOTFOUND;
4807        return NULL;
4808}
4809
4810/* ARGSUSED */
4811void
4812null_map_store(map, key, val)
4813        MAP *map;
4814        char *key;
4815        char *val;
4816{
4817        return;
4818}
4819
4820
4821/*
4822**  BOGUS stubs
4823*/
4824
4825char *
4826bogus_map_lookup(map, key, args, pstat)
4827        MAP *map;
4828        char *key;
4829        char **args;
4830        int *pstat;
4831{
4832        *pstat = EX_TEMPFAIL;
4833        return NULL;
4834}
4835
4836MAPCLASS        BogusMapClass =
4837{
4838        "bogus-map",            NULL,           0,
4839        NULL,           bogus_map_lookup,       null_map_store,
4840        null_map_open,  null_map_close,
4841};
4842/*
4843**  REGEX modules
4844*/
4845
4846#ifdef MAP_REGEX
4847
4848# include <regex.h>
4849
4850# define DEFAULT_DELIM  CONDELSE
4851
4852# define END_OF_FIELDS  -1
4853
4854# define ERRBUF_SIZE    80
4855# define MAX_MATCH      32
4856
4857# define xnalloc(s)     memset(xalloc(s), 0, s);
4858
4859struct regex_map
4860{
4861        regex_t pattern_buf;            /* xalloc it */
4862        int     *regex_subfields;       /* move to type MAP */
4863        char    *delim;                 /* move to type MAP */
4864};
4865
4866static int
4867parse_fields(s, ibuf, blen, nr_substrings)
4868        char *s;
4869        int *ibuf;              /* array */
4870        int blen;               /* number of elements in ibuf */
4871        int nr_substrings;      /* number of substrings in the pattern */
4872{
4873        register char *cp;
4874        int i = 0;
4875        bool lastone = FALSE;
4876
4877        blen--;         /* for terminating END_OF_FIELDS */
4878        cp = s;
4879        do
4880        {
4881                for (;; cp++)
4882                {
4883                        if (*cp == ',')
4884                        {
4885                                *cp = '\0';
4886                                break;
4887                        }
4888                        if (*cp == '\0')
4889                        {
4890                                lastone = TRUE;
4891                                break;
4892                        }
4893                }
4894                if (i < blen)
4895                {
4896                        int val = atoi(s);
4897
4898                        if (val < 0 || val >= nr_substrings)
4899                        {
4900                                syserr("field (%d) out of range, only %d substrings in pattern",
4901                                       val, nr_substrings);
4902                                return -1;
4903                        }
4904                        ibuf[i++] = val;
4905                }
4906                else
4907                {
4908                        syserr("too many fields, %d max\n", blen);
4909                        return -1;
4910                }
4911                s = ++cp;
4912        } while (!lastone);
4913        ibuf[i] = END_OF_FIELDS;
4914        return i;
4915}
4916
4917bool
4918regex_map_init(map, ap)
4919        MAP *map;
4920        char *ap;
4921{
4922        int regerr;
4923        struct regex_map *map_p;
4924        register char *p;
4925        char *sub_param = NULL;
4926        int pflags;
4927        static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
4928
4929        if (tTd(38, 2))
4930                printf("regex_map_init: mapname '%s', args '%s'\n",
4931                                map->map_mname, ap);
4932
4933        pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
4934
4935        p = ap;
4936
4937        map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map));
4938
4939        for (;;)
4940        {
4941                while (isascii(*p) && isspace(*p))
4942                        p++;
4943                if (*p != '-')
4944                        break;
4945                switch (*++p)
4946                {
4947                  case 'n':     /* not */
4948                        map->map_mflags |= MF_REGEX_NOT;
4949                        break;
4950
4951                  case 'f':     /* case sensitive */
4952                        map->map_mflags |= MF_NOFOLDCASE;
4953                        pflags &= ~REG_ICASE;
4954                        break;
4955
4956                  case 'b':     /* basic regular expressions */
4957                        pflags &= ~REG_EXTENDED;
4958                        break;
4959
4960                  case 's':     /* substring match () syntax */
4961                        sub_param = ++p;
4962                        pflags &= ~REG_NOSUB;
4963                        break;
4964
4965                  case 'd':     /* delimiter */
4966                        map_p->delim = ++p;
4967                        break;
4968
4969                  case 'a':     /* map append */
4970                        map->map_app = ++p;
4971                        break;
4972
4973                  case 'm':     /* matchonly */
4974                        map->map_mflags |= MF_MATCHONLY;
4975                        break;
4976
4977                }
4978                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4979                        p++;
4980                if (*p != '\0')
4981                        *p++ = '\0';
4982        }
4983        if (tTd(38, 3))
4984                printf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
4985
4986        if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0)
4987        {
4988                /* Errorhandling */
4989                char errbuf[ERRBUF_SIZE];
4990
4991                regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE);
4992                syserr("pattern-compile-error: %s\n", errbuf);
4993                free(map_p);
4994                return FALSE;
4995        }
4996
4997        if (map->map_app != NULL)
4998                map->map_app = newstr(map->map_app);
4999        if (map_p->delim != NULL)
5000                map_p->delim = newstr(map_p->delim);
5001        else
5002                map_p->delim = defdstr;
5003
5004        if (!bitset(REG_NOSUB, pflags))
5005        {
5006                /* substring matching */
5007                int substrings;
5008                int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1));
5009
5010                substrings = map_p->pattern_buf.re_nsub + 1;
5011
5012                if (tTd(38, 3))
5013                        printf("regex_map_init: nr of substrings %d\n", substrings);
5014
5015                if (substrings >= MAX_MATCH)
5016                {
5017                        syserr("too many substrings, %d max\n", MAX_MATCH);
5018                        free(map_p);
5019                        return FALSE;
5020                }
5021                if (sub_param != NULL && sub_param[0] != '\0')
5022                {
5023                        /* optional parameter -sfields */
5024                        if (parse_fields(sub_param, fields,
5025                                         MAX_MATCH + 1, substrings) == -1)
5026                                return FALSE;
5027                }
5028                else
5029                {
5030                        /* set default fields  */
5031                        int i;
5032
5033                        for (i = 0; i < substrings; i++)
5034                                fields[i] = i;
5035                        fields[i] = END_OF_FIELDS;
5036                }
5037                map_p->regex_subfields = fields;
5038                if (tTd(38, 3))
5039                {
5040                        int *ip;
5041
5042                        printf("regex_map_init: subfields");
5043                        for (ip = fields; *ip != END_OF_FIELDS; ip++)
5044                                printf(" %d", *ip);
5045                        printf("\n");
5046                }
5047        }
5048        map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */
5049
5050        return TRUE;
5051}
5052
5053static char *
5054regex_map_rewrite(map, s, slen, av)
5055        MAP *map;
5056        const char *s;
5057        size_t slen;
5058        char **av;
5059{
5060        if (bitset(MF_MATCHONLY, map->map_mflags))
5061                return map_rewrite(map, av[0], strlen(av[0]), NULL);
5062        else
5063                return map_rewrite(map, s, slen, NULL);
5064}
5065
5066char *
5067regex_map_lookup(map, name, av, statp)
5068        MAP *map;
5069        char *name;
5070        char **av;
5071        int *statp;
5072{
5073        int reg_res;
5074        struct regex_map *map_p;
5075        regmatch_t pmatch[MAX_MATCH];
5076
5077        if (tTd(38, 20))
5078        {
5079                char **cpp;
5080
5081                printf("regex_map_lookup: key '%s'\n", name);
5082                for (cpp = av; cpp && *cpp; cpp++)
5083                        printf("regex_map_lookup: arg '%s'\n", *cpp);
5084        }
5085
5086        map_p = (struct regex_map *)(map->map_db1);
5087        reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0);
5088
5089        if (bitset(MF_REGEX_NOT, map->map_mflags))
5090        {
5091                /* option -n */
5092                if (reg_res == REG_NOMATCH)
5093                        return regex_map_rewrite(map, "", (size_t)0, av);
5094                else
5095                        return NULL;
5096        }
5097        if (reg_res == REG_NOMATCH)
5098                return NULL;
5099
5100        if (map_p->regex_subfields != NULL)
5101        {
5102                /* option -s */
5103                static char retbuf[MAXNAME];
5104                int fields[MAX_MATCH + 1];
5105                bool first = TRUE;
5106                int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
5107                bool quotemode = FALSE, bslashmode = FALSE;
5108                register char *dp, *sp;
5109                char *endp, *ldp;
5110                int *ip;
5111
5112                dp = retbuf;
5113                ldp = retbuf + sizeof(retbuf) - 1;
5114
5115                if (av[1] != NULL)
5116                {
5117                        if (parse_fields(av[1], fields, MAX_MATCH + 1,
5118                                 (int) map_p->pattern_buf.re_nsub + 1) == -1)
5119                        {
5120                                *statp = EX_CONFIG;
5121                                return NULL;
5122                        }
5123                        ip = fields;
5124                }
5125                else
5126                        ip = map_p->regex_subfields;
5127
5128                for ( ; *ip != END_OF_FIELDS; ip++)
5129                {
5130                        if (!first)
5131                        {
5132                                for (sp = map_p->delim; *sp; sp++)
5133                                {
5134                                        if (dp < ldp)
5135                                                *dp++ = *sp;
5136                                }
5137                        }
5138                        else
5139                                first = FALSE;
5140
5141
5142                        if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
5143                                continue;
5144
5145                        sp = name + pmatch[*ip].rm_so;
5146                        endp = name + pmatch[*ip].rm_eo;
5147                        for (; endp > sp; sp++)
5148                        {
5149                                if (dp < ldp)
5150                                {
5151                                        if(bslashmode)
5152                                        {
5153                                                *dp++ = *sp;
5154                                                bslashmode = FALSE;
5155                                        }
5156                                        else if(quotemode && *sp != '"' &&
5157                                                *sp != '\\')
5158                                        {
5159                                                *dp++ = *sp;
5160                                        }
5161                                        else switch(*dp++ = *sp)
5162                                        {
5163                                                case '\\':
5164                                                bslashmode = TRUE;
5165                                                break;
5166
5167                                                case '(':
5168                                                cmntcnt++;
5169                                                break;
5170
5171                                                case ')':
5172                                                cmntcnt--;
5173                                                break;
5174
5175                                                case '<':
5176                                                anglecnt++;
5177                                                break;
5178
5179                                                case '>':
5180                                                anglecnt--;
5181                                                break;
5182
5183                                                case ' ':
5184                                                spacecnt++;
5185                                                break;
5186
5187                                                case '"':
5188                                                quotemode = !quotemode;
5189                                                break;
5190                                        }
5191                                }
5192                        }
5193                }
5194                if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
5195                    bslashmode || spacecnt != 0)
5196                {
5197                        sm_syslog(LOG_WARNING, NOQID,
5198                                 "Warning: regex may cause prescan() failure map=%s lookup=%s",
5199                                 map->map_mname, name);
5200                        return NULL;
5201                }
5202
5203                *dp = '\0';
5204
5205                return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
5206        }
5207        return regex_map_rewrite(map, "", (size_t)0, av);
5208}
5209#endif /* MAP_REGEX */
Note: See TracBrowser for help on using the repository browser.