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

Revision 12554, 44.6 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.
RevLine 
[12553]1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 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[] = "@(#)util.c      8.168 (Berkeley) 1/21/1999";
15#endif /* not lint */
16
17# include "sendmail.h"
18# include <sysexits.h>
19/*
20**  STRIPQUOTES -- Strip quotes & quote bits from a string.
21**
22**      Runs through a string and strips off unquoted quote
23**      characters and quote bits.  This is done in place.
24**
25**      Parameters:
26**              s -- the string to strip.
27**
28**      Returns:
29**              none.
30**
31**      Side Effects:
32**              none.
33**
34**      Called By:
35**              deliver
36*/
37
38void
39stripquotes(s)
40        char *s;
41{
42        register char *p;
43        register char *q;
44        register char c;
45
46        if (s == NULL)
47                return;
48
49        p = q = s;
50        do
51        {
52                c = *p++;
53                if (c == '\\')
54                        c = *p++;
55                else if (c == '"')
56                        continue;
57                *q++ = c;
58        } while (c != '\0');
59}
60/*
61**  ADDQUOTES -- Adds quotes & quote bits to a string.
62**
63**      Runs through a string and adds characters and quote bits.
64**
65**      Parameters:
66**              s -- the string to modify.
67**
68**      Returns:
69**              pointer to quoted string.
70**
71**      Side Effects:
72**              none.
73**
74*/
75
76char *
77addquotes(s)
78        char *s;
79{
80        int len = 0;
81        char c;
82        char *p = s, *q, *r;
83
84        if (s == NULL)
85                return NULL;
86
87        /* Find length of quoted string */
88        while ((c = *p++) != '\0')
89        {
90                len++;
91                if (c == '\\' || c == '"')
92                        len++;
93        }
94       
95        q = r = xalloc(len + 3);
96        p = s;
97
98        /* add leading quote */
99        *q++ = '"';
100        while ((c = *p++) != '\0')
101        {
102                /* quote \ or " */
103                if (c == '\\' || c == '"')
104                        *q++ = '\\';
105                *q++ = c;
106        }
107        *q++ = '"';
108        *q = '\0';
109        return r;
110}
111/*
112**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
113**
114**      Runs through a string and verifies RFC822 special characters
115**      are only found inside comments, quoted strings, or backslash
116**      escaped.  Also verified balanced quotes and parenthesis.
117**
118**      Parameters:
119**              s -- the string to modify.
120**
121**      Returns:
122**              TRUE -- if the string is RFC822 compliant.
123**              FALSE -- if the string is not RFC822 compliant.
124**
125**      Side Effects:
126**              none.
127**
128*/
129
130bool
131rfc822_string(s)
132        char *s;
133{
134        bool quoted = FALSE;
135        int commentlev = 0;
136        char *c = s;
137
138        if (s == NULL)
139                return FALSE;
140
141        while (*c != '\0')
142        {
143                /* escaped character */
144                if (*c == '\\')
145                {
146                        c++;
147                        if (*c == '\0')
148                                return FALSE;
149                }
150                else if (commentlev == 0 && *c == '"')
151                        quoted = !quoted;
152                else if (!quoted)
153                {
154                        if (*c == ')')
155                        {
156                                /* unbalanced ')' */
157                                if (commentlev == 0)
158                                        return FALSE;
159                                else
160                                        commentlev--;
161                        }
162                        else if (*c == '(')
163                                commentlev++;
164                        else if (commentlev == 0 &&
165                                 strchr(MustQuoteChars, *c) != NULL)
166                                return FALSE;
167                }
168                c++;
169        }
170        /* unbalanced '"' or '(' */
171        if (quoted || commentlev != 0)
172                return FALSE;
173        else
174                return TRUE;
175}
176/*
177**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
178**
179**      Arbitratily shorten (in place) an RFC822 string and rebalance
180**      comments and quotes.
181**
182**      Parameters:
183**              string -- the string to shorten
184**              length -- the maximum size, 0 if no maximum
185**
186**      Returns:
187**              TRUE if string is changed, FALSE otherwise
188**
189**      Side Effects:
190**              Changes string in place, possibly resulting
191**              in a shorter string.
192*/
193
194bool
195shorten_rfc822_string(string, length)
196        char *string;
197        size_t length;
198{
199        bool backslash = FALSE;
200        bool modified = FALSE;
201        bool quoted = FALSE;
202        size_t slen;
203        int parencount = 0;
204        char *ptr = string;
205       
206        /*
207        **  If have to rebalance an already short enough string,
208        **  need to do it within allocated space.
209        */
210        slen = strlen(string);
211        if (length == 0 || slen < length)
212                length = slen;
213
214        while (*ptr != '\0')
215        {
216                if (backslash)
217                {
218                        backslash = FALSE;
219                        goto increment;
220                }
221
222                if (*ptr == '\\')
223                        backslash = TRUE;
224                else if (*ptr == '(')
225                {
226                        if (!quoted)
227                                parencount++;
228                }
229                else if (*ptr == ')')
230                {
231                        if (--parencount < 0)
232                                parencount = 0;
233                }
234               
235                /* Inside a comment, quotes don't matter */
236                if (parencount <= 0 && *ptr == '"')
237                        quoted = !quoted;
238
239increment:
240                /* Check for sufficient space for next character */
241                if (length - (ptr - string) <= ((backslash ? 1 : 0) +
242                                                parencount +
243                                                (quoted ? 1 : 0)))
244                {
245                        /* Not enough, backtrack */
246                        if (*ptr == '\\')
247                                backslash = FALSE;
248                        else if (*ptr == '(' && !quoted)
249                                parencount--;
250                        else if (*ptr == '"' && parencount == 0)
251                                quoted = FALSE;
252                        break;
253                }
254                ptr++;
255        }
256
257        /* Rebalance */
258        while (parencount-- > 0)
259        {
260                if (*ptr != ')')
261                {
262                        modified = TRUE;
263                        *ptr = ')';
264                }
265                ptr++;
266        }
267        if (quoted)
268        {
269                if (*ptr != '"')
270                {
271                        modified = TRUE;
272                        *ptr = '"';
273                }
274                ptr++;
275        }
276        if (*ptr != '\0')
277        {
278                modified = TRUE;
279                *ptr = '\0';
280        }
281        return modified;
282}
283/*
284**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
285**
286**      Find an unquoted, non-commented character in an RFC822
287**      string and return a pointer to its location in the
288**      string.
289**
290**      Parameters:
291**              string -- the string to search
292**              character -- the character to find
293**
294**      Returns:
295**              pointer to the character, or
296**              a pointer to the end of the line if character is not found
297*/
298
299char *
300find_character(string, character)
301        char *string;
302        char character;
303{
304        bool backslash = FALSE;
305        bool quoted = FALSE;
306        int parencount = 0;
307               
308        while (string != NULL && *string != '\0')
309        {
310                if (backslash)
311                {
312                        backslash = FALSE;
313                        if (!quoted && character == '\\' && *string == '\\')
314                                break;
315                        string++;
316                        continue;
317                }
318                switch (*string)
319                {
320                  case '\\':
321                        backslash = TRUE;
322                        break;
323                       
324                  case '(':
325                        if (!quoted)
326                                parencount++;
327                        break;
328                       
329                  case ')':
330                        if (--parencount < 0)
331                                parencount = 0;
332                        break;
333                }
334               
335                /* Inside a comment, nothing matters */
336                if (parencount > 0)
337                {
338                        string++;
339                        continue;
340                }
341               
342                if (*string == '"')
343                        quoted = !quoted;
344                else if (*string == character && !quoted)
345                        break;
346                string++;
347        }
348
349        /* Return pointer to the character */
350        return string;
351}
352/*
353**  XALLOC -- Allocate memory and bitch wildly on failure.
354**
355**      THIS IS A CLUDGE.  This should be made to give a proper
356**      error -- but after all, what can we do?
357**
358**      Parameters:
359**              sz -- size of area to allocate.
360**
361**      Returns:
362**              pointer to data region.
363**
364**      Side Effects:
365**              Memory is allocated.
366*/
367
368char *
369xalloc(sz)
370        register int sz;
371{
372        register char *p;
373
374        /* some systems can't handle size zero mallocs */
375        if (sz <= 0)
376                sz = 1;
377
378        p = malloc((unsigned) sz);
379        if (p == NULL)
380        {
381                syserr("!Out of memory!!");
382                /* exit(EX_UNAVAILABLE); */
383        }
384        return (p);
385}
386/*
387**  COPYPLIST -- copy list of pointers.
388**
389**      This routine is the equivalent of newstr for lists of
390**      pointers.
391**
392**      Parameters:
393**              list -- list of pointers to copy.
394**                      Must be NULL terminated.
395**              copycont -- if TRUE, copy the contents of the vector
396**                      (which must be a string) also.
397**
398**      Returns:
399**              a copy of 'list'.
400**
401**      Side Effects:
402**              none.
403*/
404
405char **
406copyplist(list, copycont)
407        char **list;
408        bool copycont;
409{
410        register char **vp;
411        register char **newvp;
412
413        for (vp = list; *vp != NULL; vp++)
414                continue;
415
416        vp++;
417
418        newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
419        bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
420
421        if (copycont)
422        {
423                for (vp = newvp; *vp != NULL; vp++)
424                        *vp = newstr(*vp);
425        }
426
427        return (newvp);
428}
429/*
430**  COPYQUEUE -- copy address queue.
431**
432**      This routine is the equivalent of newstr for address queues
433**      addresses marked with QDONTSEND aren't copied
434**
435**      Parameters:
436**              addr -- list of address structures to copy.
437**
438**      Returns:
439**              a copy of 'addr'.
440**
441**      Side Effects:
442**              none.
443*/
444
445ADDRESS *
446copyqueue(addr)
447        ADDRESS *addr;
448{
449        register ADDRESS *newaddr;
450        ADDRESS *ret;
451        register ADDRESS **tail = &ret;
452
453        while (addr != NULL)
454        {
455                if (!bitset(QDONTSEND, addr->q_flags))
456                {
457                        newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
458                        STRUCTCOPY(*addr, *newaddr);
459                        *tail = newaddr;
460                        tail = &newaddr->q_next;
461                }
462                addr = addr->q_next;
463        }
464        *tail = NULL;
465       
466        return ret;
467}
468/*
469**  PRINTAV -- print argument vector.
470**
471**      Parameters:
472**              av -- argument vector.
473**
474**      Returns:
475**              none.
476**
477**      Side Effects:
478**              prints av.
479*/
480
481void
482printav(av)
483        register char **av;
484{
485        while (*av != NULL)
486        {
487                if (tTd(0, 44))
488                        printf("\n\t%08lx=", (u_long) *av);
489                else
490                        (void) putchar(' ');
491                xputs(*av++);
492        }
493        (void) putchar('\n');
494}
495/*
496**  LOWER -- turn letter into lower case.
497**
498**      Parameters:
499**              c -- character to turn into lower case.
500**
501**      Returns:
502**              c, in lower case.
503**
504**      Side Effects:
505**              none.
506*/
507
508char
509lower(c)
510        register char c;
511{
512        return((isascii(c) && isupper(c)) ? tolower(c) : c);
513}
514/*
515**  XPUTS -- put string doing control escapes.
516**
517**      Parameters:
518**              s -- string to put.
519**
520**      Returns:
521**              none.
522**
523**      Side Effects:
524**              output to stdout
525*/
526
527void
528xputs(s)
529        register const char *s;
530{
531        register int c;
532        register struct metamac *mp;
533        bool shiftout = FALSE;
534        extern struct metamac MetaMacros[];
535
536        if (s == NULL)
537        {
538                printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
539                return;
540        }
541        while ((c = (*s++ & 0377)) != '\0')
542        {
543                if (shiftout)
544                {
545                        printf("%s", TermEscape.te_rv_off);
546                        shiftout = FALSE;
547                }
548                if (!isascii(c))
549                {
550                        if (c == MATCHREPL)
551                        {
552                                printf("%s$", TermEscape.te_rv_on);
553                                shiftout = TRUE;
554                                if (*s == '\0')
555                                        continue;
556                                c = *s++ & 0377;
557                                goto printchar;
558                        }
559                        if (c == MACROEXPAND || c == MACRODEXPAND)
560                        {
561                                printf("%s$", TermEscape.te_rv_on);
562                                if (c == MACRODEXPAND)
563                                        putchar('&');
564                                shiftout = TRUE;
565                                if (*s == '\0')
566                                        continue;
567                                if (strchr("=~&?", *s) != NULL)
568                                        putchar(*s++);
569                                if (bitset(0200, *s))
570                                        printf("{%s}", macname(*s++ & 0377));
571                                else
572                                        printf("%c", *s++);
573                                continue;
574                        }
575                        for (mp = MetaMacros; mp->metaname != '\0'; mp++)
576                        {
577                                if ((mp->metaval & 0377) == c)
578                                {
579                                        printf("%s$%c",
580                                                TermEscape.te_rv_on,
581                                                mp->metaname);
582                                        shiftout = TRUE;
583                                        break;
584                                }
585                        }
586                        if (c == MATCHCLASS || c == MATCHNCLASS)
587                        {
588                                if (bitset(0200, *s))
589                                        printf("{%s}", macname(*s++ & 0377));
590                                else if (*s != '\0')
591                                        printf("%c", *s++);
592                        }
593                        if (mp->metaname != '\0')
594                                continue;
595
596                        /* unrecognized meta character */
597                        printf("%sM-", TermEscape.te_rv_on);
598                        shiftout = TRUE;
599                        c &= 0177;
600                }
601  printchar:
602                if (isprint(c))
603                {
604                        putchar(c);
605                        continue;
606                }
607
608                /* wasn't a meta-macro -- find another way to print it */
609                switch (c)
610                {
611                  case '\n':
612                        c = 'n';
613                        break;
614
615                  case '\r':
616                        c = 'r';
617                        break;
618
619                  case '\t':
620                        c = 't';
621                        break;
622                }
623                if (!shiftout)
624                {
625                        printf("%s", TermEscape.te_rv_on);
626                        shiftout = TRUE;
627                }
628                if (isprint(c))
629                {
630                        (void) putchar('\\');
631                        (void) putchar(c);
632                }
633                else
634                {
635                        (void) putchar('^');
636                        (void) putchar(c ^ 0100);
637                }
638        }
639        if (shiftout)
640                printf("%s", TermEscape.te_rv_off);
641        (void) fflush(stdout);
642}
643/*
644**  MAKELOWER -- Translate a line into lower case
645**
646**      Parameters:
647**              p -- the string to translate.  If NULL, return is
648**                      immediate.
649**
650**      Returns:
651**              none.
652**
653**      Side Effects:
654**              String pointed to by p is translated to lower case.
655**
656**      Called By:
657**              parse
658*/
659
660void
661makelower(p)
662        register char *p;
663{
664        register char c;
665
666        if (p == NULL)
667                return;
668        for (; (c = *p) != '\0'; p++)
669                if (isascii(c) && isupper(c))
670                        *p = tolower(c);
671}
672/*
673**  BUILDFNAME -- build full name from gecos style entry.
674**
675**      This routine interprets the strange entry that would appear
676**      in the GECOS field of the password file.
677**
678**      Parameters:
679**              p -- name to build.
680**              login -- the login name of this user (for &).
681**              buf -- place to put the result.
682**              buflen -- length of buf.
683**
684**      Returns:
685**              none.
686**
687**      Side Effects:
688**              none.
689*/
690
691void
692buildfname(gecos, login, buf, buflen)
693        register char *gecos;
694        char *login;
695        char *buf;
696        int buflen;
697{
698        register char *p;
699        register char *bp = buf;
700
701        if (*gecos == '*')
702                gecos++;
703
704        /* copy gecos, interpolating & to be full name */
705        for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
706        {
707                if (bp >= &buf[buflen - 1])
708                {
709                        /* buffer overflow -- just use login name */
710                        snprintf(buf, buflen, "%s", login);
711                        return;
712                }
713                if (*p == '&')
714                {
715                        /* interpolate full name */
716                        snprintf(bp, buflen - (bp - buf), "%s", login);
717                        *bp = toupper(*bp);
718                        bp += strlen(bp);
719                }
720                else
721                        *bp++ = *p;
722        }
723        *bp = '\0';
724}
725/*
726**  FIXCRLF -- fix <CR><LF> in line.
727**
728**      Looks for the <CR><LF> combination and turns it into the
729**      UNIX canonical <NL> character.  It only takes one line,
730**      i.e., it is assumed that the first <NL> found is the end
731**      of the line.
732**
733**      Parameters:
734**              line -- the line to fix.
735**              stripnl -- if true, strip the newline also.
736**
737**      Returns:
738**              none.
739**
740**      Side Effects:
741**              line is changed in place.
742*/
743
744void
745fixcrlf(line, stripnl)
746        char *line;
747        bool stripnl;
748{
749        register char *p;
750
751        p = strchr(line, '\n');
752        if (p == NULL)
753                return;
754        if (p > line && p[-1] == '\r')
755                p--;
756        if (!stripnl)
757                *p++ = '\n';
758        *p = '\0';
759}
760/*
761**  PUTLINE -- put a line like fputs obeying SMTP conventions
762**
763**      This routine always guarantees outputing a newline (or CRLF,
764**      as appropriate) at the end of the string.
765**
766**      Parameters:
767**              l -- line to put.
768**              mci -- the mailer connection information.
769**
770**      Returns:
771**              none
772**
773**      Side Effects:
774**              output of l to fp.
775*/
776
777void
778putline(l, mci)
779        register char *l;
780        register MCI *mci;
781{
782        putxline(l, strlen(l), mci, PXLF_MAPFROM);
783}
784/*
785**  PUTXLINE -- putline with flags bits.
786**
787**      This routine always guarantees outputing a newline (or CRLF,
788**      as appropriate) at the end of the string.
789**
790**      Parameters:
791**              l -- line to put.
792**              len -- the length of the line.
793**              mci -- the mailer connection information.
794**              pxflags -- flag bits:
795**                  PXLF_MAPFROM -- map From_ to >From_.
796**                  PXLF_STRIP8BIT -- strip 8th bit.
797**                  PXLF_HEADER -- map bare newline in header to newline space.
798**
799**      Returns:
800**              none
801**
802**      Side Effects:
803**              output of l to fp.
804*/
805
806void
807putxline(l, len, mci, pxflags)
808        register char *l;
809        size_t len;
810        register MCI *mci;
811        int pxflags;
812{
813        register char *p, *end;
814        int slop = 0;
815        size_t eol_len = strlen(mci->mci_mailer->m_eol);
816
817        /* strip out 0200 bits -- these can look like TELNET protocol */
818        if (bitset(MCIF_7BIT, mci->mci_flags) ||
819            bitset(PXLF_STRIP8BIT, pxflags))
820        {
821                register char svchar;
822
823                for (p = l; (svchar = *p) != '\0'; ++p)
824                        if (bitset(0200, svchar))
825                                *p = svchar &~ 0200;
826        }
827
828        end = l + len;
829        do
830        {
831                /* find the end of the line */
832                p = memchr(l, '\n', end - l);
833                if (p == NULL)
834                        p = end;
835
836                if (TrafficLogFile != NULL)
837                        fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
838
839                /* check for line overflow */
840                while (mci->mci_mailer->m_linelimit > 0 &&
841                       (p - l + slop) > mci->mci_mailer->m_linelimit)
842                {
843                        char *l_base = l;
844                        register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
845
846                        if (l[0] == '.' && slop == 0 &&
847                            bitnset(M_XDOT, mci->mci_mailer->m_flags))
848                        {
849                                (void) putc('.', mci->mci_out);
850                                if (!bitset(MCIF_INHEADER, mci->mci_flags))
851                                        mci->mci_contentlen++;
852                                if (TrafficLogFile != NULL)
853                                        (void) putc('.', TrafficLogFile);
854                        }
855                        else if (l[0] == 'F' && slop == 0 &&
856                                 bitset(PXLF_MAPFROM, pxflags) &&
857                                 strncmp(l, "From ", 5) == 0 &&
858                                 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
859                        {
860                                (void) putc('>', mci->mci_out);
861                                if (!bitset(MCIF_INHEADER, mci->mci_flags))
862                                        mci->mci_contentlen++;
863                                if (TrafficLogFile != NULL)
864                                        (void) putc('>', TrafficLogFile);
865                        }
866                        while (l < q)
867                        {
868                                (void) putc(*l++, mci->mci_out);
869                                if (!bitset(MCIF_INHEADER, mci->mci_flags))
870                                        mci->mci_contentlen++;
871                        }
872                        (void) putc('!', mci->mci_out);
873                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
874                                mci->mci_contentlen++;
875                        fputs(mci->mci_mailer->m_eol, mci->mci_out);
876                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
877                                mci->mci_contentlen += eol_len;
878                        (void) putc(' ', mci->mci_out);
879                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
880                                mci->mci_contentlen++;
881                        if (TrafficLogFile != NULL)
882                        {
883                                for (l = l_base; l < q; l++)
884                                        (void) putc(*l, TrafficLogFile);
885                                fprintf(TrafficLogFile, "!\n%05d >>>  ",
886                                        (int) getpid());
887                        }
888                        slop = 1;
889                }
890
891                /* output last part */
892                if (l[0] == '.' && slop == 0 &&
893                    bitnset(M_XDOT, mci->mci_mailer->m_flags))
894                {
895                        (void) putc('.', mci->mci_out);
896                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
897                                mci->mci_contentlen++;
898                        if (TrafficLogFile != NULL)
899                                (void) putc('.', TrafficLogFile);
900                }
901                else if (l[0] == 'F' && slop == 0 &&
902                         bitset(PXLF_MAPFROM, pxflags) &&
903                         strncmp(l, "From ", 5) == 0 &&
904                         bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
905                {
906                        (void) putc('>', mci->mci_out);
907                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
908                                mci->mci_contentlen++;
909                        if (TrafficLogFile != NULL)
910                                (void) putc('>', TrafficLogFile);
911                }
912                for ( ; l < p; ++l)
913                {
914                        if (TrafficLogFile != NULL)
915                                (void) putc(*l, TrafficLogFile);
916                        (void) putc(*l, mci->mci_out);
917                        if (!bitset(MCIF_INHEADER, mci->mci_flags))
918                                mci->mci_contentlen++;
919                }
920                if (TrafficLogFile != NULL)
921                        (void) putc('\n', TrafficLogFile);
922                fputs(mci->mci_mailer->m_eol, mci->mci_out);
923                if (!bitset(MCIF_INHEADER, mci->mci_flags))
924                        mci->mci_contentlen += eol_len;
925                if (l < end && *l == '\n')
926                {
927                        if (*++l != ' ' && *l != '\t' && *l != '\0' &&
928                            bitset(PXLF_HEADER, pxflags))
929                        {
930                                (void) putc(' ', mci->mci_out);
931                                if (!bitset(MCIF_INHEADER, mci->mci_flags))
932                                        mci->mci_contentlen++;
933                                if (TrafficLogFile != NULL)
934                                        (void) putc(' ', TrafficLogFile);
935                        }
936                }
937        } while (l < end);
938}
939/*
940**  XUNLINK -- unlink a file, doing logging as appropriate.
941**
942**      Parameters:
943**              f -- name of file to unlink.
944**
945**      Returns:
946**              none.
947**
948**      Side Effects:
949**              f is unlinked.
950*/
951
952void
953xunlink(f)
954        char *f;
955{
956        register int i;
957
958        if (LogLevel > 98)
959                sm_syslog(LOG_DEBUG, CurEnv->e_id,
960                        "unlink %s",
961                        f);
962
963        i = unlink(f);
964        if (i < 0 && LogLevel > 97)
965                sm_syslog(LOG_DEBUG, CurEnv->e_id,
966                        "%s: unlink-fail %d",
967                        f, errno);
968}
969/*
970**  XFCLOSE -- close a file, doing logging as appropriate.
971**
972**      Parameters:
973**              fp -- file pointer for the file to close
974**              a, b -- miscellaneous crud to print for debugging
975**
976**      Returns:
977**              none.
978**
979**      Side Effects:
980**              fp is closed.
981*/
982
983void
984xfclose(fp, a, b)
985        FILE *fp;
986        char *a, *b;
987{
988        if (tTd(53, 99))
989                printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b);
990#if XDEBUG
991        if (fileno(fp) == 1)
992                syserr("xfclose(%s %s): fd = 1", a, b);
993#endif
994        if (fclose(fp) < 0 && tTd(53, 99))
995                printf("xfclose FAILURE: %s\n", errstring(errno));
996}
997/*
998**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
999**
1000**      Parameters:
1001**              buf -- place to put the input line.
1002**              siz -- size of buf.
1003**              fp -- file to read from.
1004**              timeout -- the timeout before error occurs.
1005**              during -- what we are trying to read (for error messages).
1006**
1007**      Returns:
1008**              NULL on error (including timeout).  This will also leave
1009**                      buf containing a null string.
1010**              buf otherwise.
1011**
1012**      Side Effects:
1013**              none.
1014*/
1015
1016static jmp_buf  CtxReadTimeout;
1017static void     readtimeout __P((time_t));
1018
1019char *
1020sfgets(buf, siz, fp, timeout, during)
1021        char *buf;
1022        int siz;
1023        FILE *fp;
1024        time_t timeout;
1025        char *during;
1026{
1027        register EVENT *ev = NULL;
1028        register char *p;
1029        int save_errno;
1030
1031        if (fp == NULL)
1032        {
1033                buf[0] = '\0';
1034                return NULL;
1035        }
1036
1037        /* set the timeout */
1038        if (timeout != 0)
1039        {
1040                if (setjmp(CtxReadTimeout) != 0)
1041                {
1042                        if (LogLevel > 1)
1043                                sm_syslog(LOG_NOTICE, CurEnv->e_id,
1044                                       "timeout waiting for input from %.100s during %s",
1045                                       CurHostName ? CurHostName : "local",
1046                                       during);
1047                        buf[0] = '\0';
1048#if XDEBUG
1049                        checkfd012(during);
1050#endif
1051                        if (TrafficLogFile != NULL)
1052                                fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
1053                                        (int) getpid());
1054                        errno = 0;
1055                        return (NULL);
1056                }
1057                ev = setevent(timeout, readtimeout, 0);
1058        }
1059
1060        /* try to read */
1061        p = NULL;
1062        errno = 0;
1063        while (!feof(fp) && !ferror(fp))
1064        {
1065                errno = 0;
1066                p = fgets(buf, siz, fp);
1067                if (p != NULL || errno != EINTR)
1068                        break;
1069                clearerr(fp);
1070        }
1071        save_errno = errno;
1072
1073        /* clear the event if it has not sprung */
1074        clrevent(ev);
1075
1076        /* clean up the books and exit */
1077        LineNumber++;
1078        if (p == NULL)
1079        {
1080                buf[0] = '\0';
1081                if (TrafficLogFile != NULL)
1082                        fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1083                errno = save_errno;
1084                return (NULL);
1085        }
1086        if (TrafficLogFile != NULL)
1087                fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1088        if (SevenBitInput)
1089        {
1090                for (p = buf; *p != '\0'; p++)
1091                        *p &= ~0200;
1092        }
1093        else if (!HasEightBits)
1094        {
1095                for (p = buf; *p != '\0'; p++)
1096                {
1097                        if (bitset(0200, *p))
1098                        {
1099                                HasEightBits = TRUE;
1100                                break;
1101                        }
1102                }
1103        }
1104        return (buf);
1105}
1106
1107/* ARGSUSED */
1108static void
1109readtimeout(timeout)
1110        time_t timeout;
1111{
1112        longjmp(CtxReadTimeout, 1);
1113}
1114/*
1115**  FGETFOLDED -- like fgets, but know about folded lines.
1116**
1117**      Parameters:
1118**              buf -- place to put result.
1119**              n -- bytes available.
1120**              f -- file to read from.
1121**
1122**      Returns:
1123**              input line(s) on success, NULL on error or EOF.
1124**              This will normally be buf -- unless the line is too
1125**                      long, when it will be xalloc()ed.
1126**
1127**      Side Effects:
1128**              buf gets lines from f, with continuation lines (lines
1129**              with leading white space) appended.  CRLF's are mapped
1130**              into single newlines.  Any trailing NL is stripped.
1131*/
1132
1133char *
1134fgetfolded(buf, n, f)
1135        char *buf;
1136        register int n;
1137        FILE *f;
1138{
1139        register char *p = buf;
1140        char *bp = buf;
1141        register int i;
1142
1143        n--;
1144        while ((i = getc(f)) != EOF)
1145        {
1146                if (i == '\r')
1147                {
1148                        i = getc(f);
1149                        if (i != '\n')
1150                        {
1151                                if (i != EOF)
1152                                        (void) ungetc(i, f);
1153                                i = '\r';
1154                        }
1155                }
1156                if (--n <= 0)
1157                {
1158                        /* allocate new space */
1159                        char *nbp;
1160                        int nn;
1161
1162                        nn = (p - bp);
1163                        if (nn < MEMCHUNKSIZE)
1164                                nn *= 2;
1165                        else
1166                                nn += MEMCHUNKSIZE;
1167                        nbp = xalloc(nn);
1168                        bcopy(bp, nbp, p - bp);
1169                        p = &nbp[p - bp];
1170                        if (bp != buf)
1171                                free(bp);
1172                        bp = nbp;
1173                        n = nn - (p - bp);
1174                }
1175                *p++ = i;
1176                if (i == '\n')
1177                {
1178                        LineNumber++;
1179                        i = getc(f);
1180                        if (i != EOF)
1181                                (void) ungetc(i, f);
1182                        if (i != ' ' && i != '\t')
1183                                break;
1184                }
1185        }
1186        if (p == bp)
1187                return (NULL);
1188        if (p[-1] == '\n')
1189                p--;
1190        *p = '\0';
1191        return (bp);
1192}
1193/*
1194**  CURTIME -- return current time.
1195**
1196**      Parameters:
1197**              none.
1198**
1199**      Returns:
1200**              the current time.
1201**
1202**      Side Effects:
1203**              none.
1204*/
1205
1206time_t
1207curtime()
1208{
1209        auto time_t t;
1210
1211        (void) time(&t);
1212        return (t);
1213}
1214/*
1215**  ATOBOOL -- convert a string representation to boolean.
1216**
1217**      Defaults to "TRUE"
1218**
1219**      Parameters:
1220**              s -- string to convert.  Takes "tTyY" as true,
1221**                      others as false.
1222**
1223**      Returns:
1224**              A boolean representation of the string.
1225**
1226**      Side Effects:
1227**              none.
1228*/
1229
1230bool
1231atobool(s)
1232        register char *s;
1233{
1234        if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1235                return (TRUE);
1236        return (FALSE);
1237}
1238/*
1239**  ATOOCT -- convert a string representation to octal.
1240**
1241**      Parameters:
1242**              s -- string to convert.
1243**
1244**      Returns:
1245**              An integer representing the string interpreted as an
1246**              octal number.
1247**
1248**      Side Effects:
1249**              none.
1250*/
1251
1252int
1253atooct(s)
1254        register char *s;
1255{
1256        register int i = 0;
1257
1258        while (*s >= '0' && *s <= '7')
1259                i = (i << 3) | (*s++ - '0');
1260        return (i);
1261}
1262/*
1263**  BITINTERSECT -- tell if two bitmaps intersect
1264**
1265**      Parameters:
1266**              a, b -- the bitmaps in question
1267**
1268**      Returns:
1269**              TRUE if they have a non-null intersection
1270**              FALSE otherwise
1271**
1272**      Side Effects:
1273**              none.
1274*/
1275
1276bool
1277bitintersect(a, b)
1278        BITMAP a;
1279        BITMAP b;
1280{
1281        int i;
1282
1283        for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1284                if ((a[i] & b[i]) != 0)
1285                        return (TRUE);
1286        return (FALSE);
1287}
1288/*
1289**  BITZEROP -- tell if a bitmap is all zero
1290**
1291**      Parameters:
1292**              map -- the bit map to check
1293**
1294**      Returns:
1295**              TRUE if map is all zero.
1296**              FALSE if there are any bits set in map.
1297**
1298**      Side Effects:
1299**              none.
1300*/
1301
1302bool
1303bitzerop(map)
1304        BITMAP map;
1305{
1306        int i;
1307
1308        for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1309                if (map[i] != 0)
1310                        return (FALSE);
1311        return (TRUE);
1312}
1313/*
1314**  STRCONTAINEDIN -- tell if one string is contained in another
1315**
1316**      Parameters:
1317**              a -- possible substring.
1318**              b -- possible superstring.
1319**
1320**      Returns:
1321**              TRUE if a is contained in b.
1322**              FALSE otherwise.
1323*/
1324
1325bool
1326strcontainedin(a, b)
1327        register char *a;
1328        register char *b;
1329{
1330        int la;
1331        int lb;
1332        int c;
1333
1334        la = strlen(a);
1335        lb = strlen(b);
1336        c = *a;
1337        if (isascii(c) && isupper(c))
1338                c = tolower(c);
1339        for (; lb-- >= la; b++)
1340        {
1341                if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1342                        continue;
1343                if (strncasecmp(a, b, la) == 0)
1344                        return TRUE;
1345        }
1346        return FALSE;
1347}
1348/*
1349**  CHECKFD012 -- check low numbered file descriptors
1350**
1351**      File descriptors 0, 1, and 2 should be open at all times.
1352**      This routine verifies that, and fixes it if not true.
1353**
1354**      Parameters:
1355**              where -- a tag printed if the assertion failed
1356**
1357**      Returns:
1358**              none
1359*/
1360
1361void
1362checkfd012(where)
1363        char *where;
1364{
1365#if XDEBUG
1366        register int i;
1367
1368        for (i = 0; i < 3; i++)
1369                fill_fd(i, where);
1370#endif /* XDEBUG */
1371}
1372/*
1373**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1374**
1375**      Parameters:
1376**              fd -- file descriptor to check.
1377**              where -- tag to print on failure.
1378**
1379**      Returns:
1380**              none.
1381*/
1382
1383void
1384checkfdopen(fd, where)
1385        int fd;
1386        char *where;
1387{
1388#if XDEBUG
1389        struct stat st;
1390
1391        if (fstat(fd, &st) < 0 && errno == EBADF)
1392        {
1393                syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1394                printopenfds(TRUE);
1395        }
1396#endif
1397}
1398/*
1399**  CHECKFDS -- check for new or missing file descriptors
1400**
1401**      Parameters:
1402**              where -- tag for printing.  If null, take a base line.
1403**
1404**      Returns:
1405**              none
1406**
1407**      Side Effects:
1408**              If where is set, shows changes since the last call.
1409*/
1410
1411void
1412checkfds(where)
1413        char *where;
1414{
1415        int maxfd;
1416        register int fd;
1417        bool printhdr = TRUE;
1418        int save_errno = errno;
1419        static BITMAP baseline;
1420        extern int DtableSize;
1421
1422        if (DtableSize > 256)
1423                maxfd = 256;
1424        else
1425                maxfd = DtableSize;
1426        if (where == NULL)
1427                clrbitmap(baseline);
1428
1429        for (fd = 0; fd < maxfd; fd++)
1430        {
1431                struct stat stbuf;
1432
1433                if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1434                {
1435                        if (!bitnset(fd, baseline))
1436                                continue;
1437                        clrbitn(fd, baseline);
1438                }
1439                else if (!bitnset(fd, baseline))
1440                        setbitn(fd, baseline);
1441                else
1442                        continue;
1443
1444                /* file state has changed */
1445                if (where == NULL)
1446                        continue;
1447                if (printhdr)
1448                {
1449                        sm_syslog(LOG_DEBUG, CurEnv->e_id,
1450                                "%s: changed fds:",
1451                                where);
1452                        printhdr = FALSE;
1453                }
1454                dumpfd(fd, TRUE, TRUE);
1455        }
1456        errno = save_errno;
1457}
1458/*
1459**  PRINTOPENFDS -- print the open file descriptors (for debugging)
1460**
1461**      Parameters:
1462**              logit -- if set, send output to syslog; otherwise
1463**                      print for debugging.
1464**
1465**      Returns:
1466**              none.
1467*/
1468
1469#include <arpa/inet.h>
1470
1471void
1472printopenfds(logit)
1473        bool logit;
1474{
1475        register int fd;
1476        extern int DtableSize;
1477
1478        for (fd = 0; fd < DtableSize; fd++)
1479                dumpfd(fd, FALSE, logit);
1480}
1481/*
1482**  DUMPFD -- dump a file descriptor
1483**
1484**      Parameters:
1485**              fd -- the file descriptor to dump.
1486**              printclosed -- if set, print a notification even if
1487**                      it is closed; otherwise print nothing.
1488**              logit -- if set, send output to syslog instead of stdout.
1489*/
1490
1491void
1492dumpfd(fd, printclosed, logit)
1493        int fd;
1494        bool printclosed;
1495        bool logit;
1496{
1497        register char *p;
1498        char *hp;
1499#ifdef S_IFSOCK
1500        SOCKADDR sa;
1501#endif
1502        auto SOCKADDR_LEN_T slen;
1503        int i;
1504#if STAT64 > 0
1505        struct stat64 st;
1506#else
1507        struct stat st;
1508#endif
1509        char buf[200];
1510
1511        p = buf;
1512        snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1513        p += strlen(p);
1514
1515        if (
1516#if STAT64 > 0
1517            fstat64(fd, &st)
1518#else
1519            fstat(fd, &st)
1520#endif
1521            < 0)
1522        {
1523                if (errno != EBADF)
1524                {
1525                        snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1526                                errstring(errno));
1527                        goto printit;
1528                }
1529                else if (printclosed)
1530                {
1531                        snprintf(p, SPACELEFT(buf, p), "CLOSED");
1532                        goto printit;
1533                }
1534                return;
1535        }
1536
1537        i = fcntl(fd, F_GETFL, NULL);
1538        if (i != -1)
1539        {
1540                snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1541                p += strlen(p);
1542        }
1543
1544        snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode);
1545        p += strlen(p);
1546        switch (st.st_mode & S_IFMT)
1547        {
1548#ifdef S_IFSOCK
1549          case S_IFSOCK:
1550                snprintf(p, SPACELEFT(buf, p), "SOCK ");
1551                p += strlen(p);
1552                slen = sizeof sa;
1553                if (getsockname(fd, &sa.sa, &slen) < 0)
1554                        snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1555                else
1556                {
1557                        hp = hostnamebyanyaddr(&sa);
1558                        if (sa.sa.sa_family == AF_INET)
1559                                snprintf(p, SPACELEFT(buf, p), "%s/%d",
1560                                        hp, ntohs(sa.sin.sin_port));
1561                        else
1562                                snprintf(p, SPACELEFT(buf, p), "%s", hp);
1563                }
1564                p += strlen(p);
1565                snprintf(p, SPACELEFT(buf, p), "->");
1566                p += strlen(p);
1567                slen = sizeof sa;
1568                if (getpeername(fd, &sa.sa, &slen) < 0)
1569                        snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1570                else
1571                {
1572                        hp = hostnamebyanyaddr(&sa);
1573                        if (sa.sa.sa_family == AF_INET)
1574                                snprintf(p, SPACELEFT(buf, p), "%s/%d",
1575                                        hp, ntohs(sa.sin.sin_port));
1576                        else
1577                                snprintf(p, SPACELEFT(buf, p), "%s", hp);
1578                }
1579                break;
1580#endif
1581
1582          case S_IFCHR:
1583                snprintf(p, SPACELEFT(buf, p), "CHR: ");
1584                p += strlen(p);
1585                goto defprint;
1586
1587          case S_IFBLK:
1588                snprintf(p, SPACELEFT(buf, p), "BLK: ");
1589                p += strlen(p);
1590                goto defprint;
1591
1592#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1593          case S_IFIFO:
1594                snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1595                p += strlen(p);
1596                goto defprint;
1597#endif
1598
1599#ifdef S_IFDIR
1600          case S_IFDIR:
1601                snprintf(p, SPACELEFT(buf, p), "DIR: ");
1602                p += strlen(p);
1603                goto defprint;
1604#endif
1605
1606#ifdef S_IFLNK
1607          case S_IFLNK:
1608                snprintf(p, SPACELEFT(buf, p), "LNK: ");
1609                p += strlen(p);
1610                goto defprint;
1611#endif
1612
1613          default:
1614defprint:
1615                if (sizeof st.st_ino > sizeof (long))
1616                        snprintf(p, SPACELEFT(buf, p),
1617                                 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1618                                 major(st.st_dev), minor(st.st_dev),
1619                                 quad_to_string(st.st_ino),
1620                                 st.st_nlink, st.st_uid, st.st_gid);
1621                else
1622                        snprintf(p, SPACELEFT(buf, p),
1623                                "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1624                                major(st.st_dev), minor(st.st_dev),
1625                                (unsigned long) st.st_ino,
1626                                st.st_nlink, st.st_uid, st.st_gid);
1627                if (sizeof st.st_size > sizeof (long))
1628                        snprintf(p, SPACELEFT(buf, p), "size=%s",
1629                                 quad_to_string(st.st_size));
1630                else
1631                        snprintf(p, SPACELEFT(buf, p), "size=%lu",
1632                                 (unsigned long) st.st_size);
1633                break;
1634        }
1635
1636printit:
1637        if (logit)
1638                sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1639                        "%.800s", buf);
1640        else
1641                printf("%s\n", buf);
1642}
1643/*
1644**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1645**
1646**      Parameters:
1647**              host -- the host to shorten (stripped in place).
1648**
1649**      Returns:
1650**              none.
1651*/
1652
1653void
1654shorten_hostname(host)
1655        char host[];
1656{
1657        register char *p;
1658        char *mydom;
1659        int i;
1660        bool canon = FALSE;
1661
1662        /* strip off final dot */
1663        p = &host[strlen(host) - 1];
1664        if (*p == '.')
1665        {
1666                *p = '\0';
1667                canon = TRUE;
1668        }
1669
1670        /* see if there is any domain at all -- if not, we are done */
1671        p = strchr(host, '.');
1672        if (p == NULL)
1673                return;
1674
1675        /* yes, we have a domain -- see if it looks like us */
1676        mydom = macvalue('m', CurEnv);
1677        if (mydom == NULL)
1678                mydom = "";
1679        i = strlen(++p);
1680        if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1681            (mydom[i] == '.' || mydom[i] == '\0'))
1682                *--p = '\0';
1683}
1684/*
1685**  PROG_OPEN -- open a program for reading
1686**
1687**      Parameters:
1688**              argv -- the argument list.
1689**              pfd -- pointer to a place to store the file descriptor.
1690**              e -- the current envelope.
1691**
1692**      Returns:
1693**              pid of the process -- -1 if it failed.
1694*/
1695
1696int
1697prog_open(argv, pfd, e)
1698        char **argv;
1699        int *pfd;
1700        ENVELOPE *e;
1701{
1702        int pid;
1703        int i;
1704        int saveerrno;
1705        int fdv[2];
1706        char *p, *q;
1707        char buf[MAXLINE + 1];
1708        extern int DtableSize;
1709
1710        if (pipe(fdv) < 0)
1711        {
1712                syserr("%s: cannot create pipe for stdout", argv[0]);
1713                return -1;
1714        }
1715        pid = fork();
1716        if (pid < 0)
1717        {
1718                syserr("%s: cannot fork", argv[0]);
1719                close(fdv[0]);
1720                close(fdv[1]);
1721                return -1;
1722        }
1723        if (pid > 0)
1724        {
1725                /* parent */
1726                close(fdv[1]);
1727                *pfd = fdv[0];
1728                return pid;
1729        }
1730
1731        /* child -- close stdin */
1732        close(0);
1733
1734        /* stdout goes back to parent */
1735        close(fdv[0]);
1736        if (dup2(fdv[1], 1) < 0)
1737        {
1738                syserr("%s: cannot dup2 for stdout", argv[0]);
1739                _exit(EX_OSERR);
1740        }
1741        close(fdv[1]);
1742
1743        /* stderr goes to transcript if available */
1744        if (e->e_xfp != NULL)
1745        {
1746                if (dup2(fileno(e->e_xfp), 2) < 0)
1747                {
1748                        syserr("%s: cannot dup2 for stderr", argv[0]);
1749                        _exit(EX_OSERR);
1750                }
1751        }
1752
1753        /* this process has no right to the queue file */
1754        if (e->e_lockfp != NULL)
1755                close(fileno(e->e_lockfp));
1756
1757        /* run as default user */
1758        endpwent();
1759        if (setgid(DefGid) < 0 && geteuid() == 0)
1760                syserr("prog_open: setgid(%ld) failed", (long) DefGid);
1761        if (setuid(DefUid) < 0 && geteuid() == 0)
1762                syserr("prog_open: setuid(%ld) failed", (long) DefUid);
1763
1764        /* run in some directory */
1765        if (ProgMailer != NULL)
1766                p = ProgMailer->m_execdir;
1767        else
1768                p = NULL;
1769        for (; p != NULL; p = q)
1770        {
1771                q = strchr(p, ':');
1772                if (q != NULL)
1773                        *q = '\0';
1774                expand(p, buf, sizeof buf, e);
1775                if (q != NULL)
1776                        *q++ = ':';
1777                if (buf[0] != '\0' && chdir(buf) >= 0)
1778                        break;
1779        }
1780        if (p == NULL)
1781        {
1782                /* backup directories */
1783                if (chdir("/tmp") < 0)
1784                        (void) chdir("/");
1785        }
1786
1787        /* arrange for all the files to be closed */
1788        for (i = 3; i < DtableSize; i++)
1789        {
1790                register int j;
1791
1792                if ((j = fcntl(i, F_GETFD, 0)) != -1)
1793                        (void) fcntl(i, F_SETFD, j | 1);
1794        }
1795
1796        /* now exec the process */
1797        execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1798
1799        /* woops!  failed */
1800        saveerrno = errno;
1801        syserr("%s: cannot exec", argv[0]);
1802        if (transienterror(saveerrno))
1803                _exit(EX_OSERR);
1804        _exit(EX_CONFIG);
1805        return -1;      /* avoid compiler warning on IRIX */
1806}
1807/*
1808**  GET_COLUMN  -- look up a Column in a line buffer
1809**
1810**      Parameters:
1811**              line -- the raw text line to search.
1812**              col -- the column number to fetch.
1813**              delim -- the delimiter between columns.  If null,
1814**                      use white space.
1815**              buf -- the output buffer.
1816**              buflen -- the length of buf.
1817**
1818**      Returns:
1819**              buf if successful.
1820**              NULL otherwise.
1821*/
1822
1823char *
1824get_column(line, col, delim, buf, buflen)
1825        char line[];
1826        int col;
1827        char delim;
1828        char buf[];
1829        int buflen;
1830{
1831        char *p;
1832        char *begin, *end;
1833        int i;
1834        char delimbuf[4];
1835       
1836        if (delim == '\0')
1837                strcpy(delimbuf, "\n\t ");
1838        else
1839        {
1840                delimbuf[0] = delim;
1841                delimbuf[1] = '\0';
1842        }
1843
1844        p = line;
1845        if (*p == '\0')
1846                return NULL;                    /* line empty */
1847        if (*p == delim && col == 0)
1848                return NULL;                    /* first column empty */
1849
1850        begin = line;
1851
1852        if (col == 0 && delim == '\0')
1853        {
1854                while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1855                        begin++;
1856        }
1857
1858        for (i = 0; i < col; i++)
1859        {
1860                if ((begin = strpbrk(begin, delimbuf)) == NULL)
1861                        return NULL;            /* no such column */
1862                begin++;
1863                if (delim == '\0')
1864                {
1865                        while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1866                                begin++;
1867                }
1868        }
1869       
1870        end = strpbrk(begin, delimbuf);
1871        if (end == NULL)
1872                i = strlen(begin);
1873        else
1874                i = end - begin;
1875        if (i >= buflen)
1876                i = buflen - 1;
1877        strncpy(buf, begin, i);
1878        buf[i] = '\0';
1879        return buf;
1880}
1881/*
1882**  CLEANSTRCPY -- copy string keeping out bogus characters
1883**
1884**      Parameters:
1885**              t -- "to" string.
1886**              f -- "from" string.
1887**              l -- length of space available in "to" string.
1888**
1889**      Returns:
1890**              none.
1891*/
1892
1893void
1894cleanstrcpy(t, f, l)
1895        register char *t;
1896        register char *f;
1897        int l;
1898{
1899        /* check for newlines and log if necessary */
1900        (void) denlstring(f, TRUE, TRUE);
1901
1902        l--;
1903        while (l > 0 && *f != '\0')
1904        {
1905                if (isascii(*f) &&
1906                    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1907                {
1908                        l--;
1909                        *t++ = *f;
1910                }
1911                f++;
1912        }
1913        *t = '\0';
1914}
1915/*
1916**  DENLSTRING -- convert newlines in a string to spaces
1917**
1918**      Parameters:
1919**              s -- the input string
1920**              strict -- if set, don't permit continuation lines.
1921**              logattacks -- if set, log attempted attacks.
1922**
1923**      Returns:
1924**              A pointer to a version of the string with newlines
1925**              mapped to spaces.  This should be copied.
1926*/
1927
1928char *
1929denlstring(s, strict, logattacks)
1930        char *s;
1931        bool strict;
1932        bool logattacks;
1933{
1934        register char *p;
1935        int l;
1936        static char *bp = NULL;
1937        static int bl = 0;
1938
1939        p = s;
1940        while ((p = strchr(p, '\n')) != NULL)
1941                if (strict || (*++p != ' ' && *p != '\t'))
1942                        break;
1943        if (p == NULL)
1944                return s;
1945
1946        l = strlen(s) + 1;
1947        if (bl < l)
1948        {
1949                /* allocate more space */
1950                if (bp != NULL)
1951                        free(bp);
1952                bp = xalloc(l);
1953                bl = l;
1954        }
1955        strcpy(bp, s);
1956        for (p = bp; (p = strchr(p, '\n')) != NULL; )
1957                *p++ = ' ';
1958
1959        if (logattacks)
1960        {
1961                sm_syslog(LOG_NOTICE, CurEnv->e_id,
1962                        "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
1963                        RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
1964                        shortenstring(bp, MAXSHORTSTR));
1965        }
1966
1967        return bp;
1968}
1969/*
1970**  PATH_IS_DIR -- check to see if file exists and is a directory.
1971**
1972**      There are some additional checks for security violations in
1973**      here.  This routine is intended to be used for the host status
1974**      support.
1975**
1976**      Parameters:
1977**              pathname -- pathname to check for directory-ness.
1978**              createflag -- if set, create directory if needed.
1979**
1980**      Returns:
1981**              TRUE -- if the indicated pathname is a directory
1982**              FALSE -- otherwise
1983*/
1984
1985int
1986path_is_dir(pathname, createflag)
1987        char *pathname;
1988        bool createflag;
1989{
1990        struct stat statbuf;
1991
1992#if HASLSTAT
1993        if (lstat(pathname, &statbuf) < 0)
1994#else
1995        if (stat(pathname, &statbuf) < 0)
1996#endif
1997        {
1998                if (errno != ENOENT || !createflag)
1999                        return FALSE;
2000                if (mkdir(pathname, 0755) < 0)
2001                        return FALSE;
2002                return TRUE;
2003        }
2004        if (!S_ISDIR(statbuf.st_mode))
2005        {
2006                errno = ENOTDIR;
2007                return FALSE;
2008        }
2009
2010        /* security: don't allow writable directories */
2011        if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2012        {
2013                errno = EACCES;
2014                return FALSE;
2015        }
2016
2017        return TRUE;
2018}
2019/*
2020**  PROC_LIST_ADD -- add process id to list of our children
2021**
2022**      Parameters:
2023**              pid -- pid to add to list.
2024**
2025**      Returns:
2026**              none
2027*/
2028
2029struct procs
2030{
2031        pid_t   proc_pid;
2032        char    *proc_task;
2033};
2034
2035static struct procs     *ProcListVec    = NULL;
2036static int      ProcListSize    = 0;
2037
2038#define NO_PID          ((pid_t) 0)
2039#ifndef PROC_LIST_SEG
2040# define PROC_LIST_SEG  32              /* number of pids to alloc at a time */
2041#endif
2042
2043void
2044proc_list_add(pid, task)
2045        pid_t pid;
2046        char *task;
2047{
2048        int i;
2049
2050        for (i = 0; i < ProcListSize; i++)
2051        {
2052                if (ProcListVec[i].proc_pid == NO_PID)
2053                        break;
2054        }
2055        if (i >= ProcListSize)
2056        {
2057                /* probe the existing vector to avoid growing infinitely */
2058                proc_list_probe();
2059
2060                /* now scan again */
2061                for (i = 0; i < ProcListSize; i++)
2062                {
2063                        if (ProcListVec[i].proc_pid == NO_PID)
2064                                break;
2065                }
2066        }
2067        if (i >= ProcListSize)
2068        {
2069                /* grow process list */
2070                struct procs *npv;
2071
2072                npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG));
2073                if (ProcListSize > 0)
2074                {
2075                        bcopy(ProcListVec, npv, ProcListSize *
2076                                                sizeof (struct procs));
2077                        free(ProcListVec);
2078                }
2079                for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2080                {
2081                        npv[i].proc_pid = NO_PID;
2082                        npv[i].proc_task = NULL;
2083                }
2084                i = ProcListSize;
2085                ProcListSize += PROC_LIST_SEG;
2086                ProcListVec = npv;
2087        }
2088        ProcListVec[i].proc_pid = pid;
2089        ProcListVec[i].proc_task = newstr(task);
2090
2091        /* if process adding itself, it's not a child */
2092        if (pid != getpid())
2093                CurChildren++;
2094}
2095/*
2096**  PROC_LIST_SET -- set pid task in process list
2097**
2098**      Parameters:
2099**              pid -- pid to set
2100**              task -- task of pid
2101**
2102**      Returns:
2103**              none.
2104*/
2105
2106void
2107proc_list_set(pid, task)
2108        pid_t pid;
2109        char *task;
2110{
2111        int i;
2112
2113        for (i = 0; i < ProcListSize; i++)
2114        {
2115                if (ProcListVec[i].proc_pid == pid)
2116                {
2117                        if (ProcListVec[i].proc_task != NULL)
2118                                free(ProcListVec[i].proc_task);
2119                        ProcListVec[i].proc_task = newstr(task);
2120                        break;
2121                }
2122        }
2123}
2124/*
2125**  PROC_LIST_DROP -- drop pid from process list
2126**
2127**      Parameters:
2128**              pid -- pid to drop
2129**
2130**      Returns:
2131**              none.
2132*/
2133
2134void
2135proc_list_drop(pid)
2136        pid_t pid;
2137{
2138        int i;
2139
2140        for (i = 0; i < ProcListSize; i++)
2141        {
2142                if (ProcListVec[i].proc_pid == pid)
2143                {
2144                        ProcListVec[i].proc_pid = NO_PID;
2145                        if (ProcListVec[i].proc_task != NULL)
2146                        {
2147                                free(ProcListVec[i].proc_task);
2148                                ProcListVec[i].proc_task = NULL;
2149                        }
2150                        break;
2151                }
2152        }
2153        if (CurChildren > 0)
2154                CurChildren--;
2155}
2156/*
2157**  PROC_LIST_CLEAR -- clear the process list
2158**
2159**      Parameters:
2160**              none.
2161**
2162**      Returns:
2163**              none.
2164*/
2165
2166void
2167proc_list_clear()
2168{
2169        int i;
2170
2171        /* start from 1 since 0 is the daemon itself */
2172        for (i = 1; i < ProcListSize; i++)
2173        {
2174                ProcListVec[i].proc_pid = NO_PID;
2175                if (ProcListVec[i].proc_task != NULL)
2176                {
2177                        free(ProcListVec[i].proc_task);
2178                        ProcListVec[i].proc_task = NULL;
2179                }
2180        }
2181        CurChildren = 0;
2182}
2183/*
2184**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2185**
2186**      Parameters:
2187**              none
2188**
2189**      Returns:
2190**              none
2191*/
2192
2193void
2194proc_list_probe()
2195{
2196        int i;
2197
2198        /* start from 1 since 0 is the daemon itself */
2199        for (i = 1; i < ProcListSize; i++)
2200        {
2201                if (ProcListVec[i].proc_pid == NO_PID)
2202                        continue;
2203                if (kill(ProcListVec[i].proc_pid, 0) < 0)
2204                {
2205                        if (LogLevel > 3)
2206                                sm_syslog(LOG_DEBUG, CurEnv->e_id,
2207                                        "proc_list_probe: lost pid %d",
2208                                        (int) ProcListVec[i].proc_pid);
2209                        ProcListVec[i].proc_pid = NO_PID;
2210                        if (ProcListVec[i].proc_task != NULL)
2211                        {
2212                                free(ProcListVec[i].proc_task);
2213                                ProcListVec[i].proc_task = NULL;
2214                        }
2215                        CurChildren--;
2216                }
2217        }
2218        if (CurChildren < 0)
2219                CurChildren = 0;
2220}
2221/*
2222**  PROC_LIST_DISPLAY -- display the process list
2223**
2224**      Parameters:
2225**              out -- output file pointer
2226**
2227**      Returns:
2228**              none.
2229*/
2230
2231void
2232proc_list_display(out)
2233        FILE *out;
2234{
2235        int i;
2236
2237        for (i = 0; i < ProcListSize; i++)
2238        {
2239                if (ProcListVec[i].proc_pid == NO_PID)
2240                        continue;
2241
2242                fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2243                        ProcListVec[i].proc_task != NULL ?
2244                        ProcListVec[i].proc_task : "(unknown)",
2245                        (OpMode == MD_SMTP ||
2246                         OpMode == MD_DAEMON ||
2247                         OpMode == MD_ARPAFTP) ? "\r" : "");
2248        }
2249}
2250/*
2251**  SM_STRCASECMP -- 8-bit clean version of strcasecmp
2252**
2253**      Thank you, vendors, for making this all necessary.
2254*/
2255
2256/*
2257 * Copyright (c) 1987, 1993
2258 *      The Regents of the University of California.  All rights reserved.
2259 *
2260 * Redistribution and use in source and binary forms, with or without
2261 * modification, are permitted provided that the following conditions
2262 * are met:
2263 * 1. Redistributions of source code must retain the above copyright
2264 *    notice, this list of conditions and the following disclaimer.
2265 * 2. Redistributions in binary form must reproduce the above copyright
2266 *    notice, this list of conditions and the following disclaimer in the
2267 *    documentation and/or other materials provided with the distribution.
2268 * 3. All advertising materials mentioning features or use of this software
2269 *    must display the following acknowledgement:
2270 *      This product includes software developed by the University of
2271 *      California, Berkeley and its contributors.
2272 * 4. Neither the name of the University nor the names of its contributors
2273 *    may be used to endorse or promote products derived from this software
2274 *    without specific prior written permission.
2275 *
2276 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2277 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2278 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2279 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2280 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2281 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2282 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2283 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2284 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2285 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2286 * SUCH DAMAGE.
2287 */
2288
2289#if defined(LIBC_SCCS) && !defined(lint)
2290static char sccsid[] = "@(#)strcasecmp.c        8.1 (Berkeley) 6/4/93";
2291#endif /* LIBC_SCCS and not lint */
2292
2293/*
2294 * This array is designed for mapping upper and lower case letter
2295 * together for a case independent comparison.  The mappings are
2296 * based upon ascii character sequences.
2297 */
2298static const u_char charmap[] = {
2299        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2300        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2301        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2302        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2303        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2304        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2305        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2306        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2307        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2308        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2309        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2310        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
2311        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2312        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2313        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2314        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2315        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2316        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2317        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2318        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2319        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2320        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2321        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2322        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2323        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2324        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2325        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2326        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2327        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2328        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2329        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2330        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2331};
2332
2333int
2334sm_strcasecmp(s1, s2)
2335        const char *s1, *s2;
2336{
2337        register const u_char *cm = charmap,
2338                        *us1 = (const u_char *)s1,
2339                        *us2 = (const u_char *)s2;
2340
2341        while (cm[*us1] == cm[*us2++])
2342                if (*us1++ == '\0')
2343                        return (0);
2344        return (cm[*us1] - cm[*--us2]);
2345}
2346
2347int
2348sm_strncasecmp(s1, s2, n)
2349        const char *s1, *s2;
2350        register size_t n;
2351{
2352        if (n != 0) {
2353                register const u_char *cm = charmap,
2354                                *us1 = (const u_char *)s1,
2355                                *us2 = (const u_char *)s2;
2356
2357                do {
2358                        if (cm[*us1] != cm[*us2++])
2359                                return (cm[*us1] - cm[*--us2]);
2360                        if (*us1++ == '\0')
2361                                break;
2362                } while (--n != 0);
2363        }
2364        return (0);
2365}
Note: See TracBrowser for help on using the repository browser.