1) Add the sysctl(9) manual page from FreeBSD with the following
[dragonfly.git] / contrib / sendmail-8.13.7 / sendmail / util.c
1 /*
2  * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: util.c,v 8.394 2006/05/03 23:55:29 ca Exp $")
17
18 #include <sysexits.h>
19 #include <sm/xtrap.h>
20
21 /*
22 **  NEWSTR -- Create a copy of a C string
23 **
24 **      Parameters:
25 **              s -- the string to copy.
26 **
27 **      Returns:
28 **              pointer to newly allocated string.
29 */
30
31 char *
32 newstr(s)
33         const char *s;
34 {
35         size_t l;
36         char *n;
37
38         l = strlen(s);
39         SM_ASSERT(l + 1 > l);
40         n = xalloc(l + 1);
41         sm_strlcpy(n, s, l + 1);
42         return n;
43 }
44
45 /*
46 **  ADDQUOTES -- Adds quotes & quote bits to a string.
47 **
48 **      Runs through a string and adds backslashes and quote bits.
49 **
50 **      Parameters:
51 **              s -- the string to modify.
52 **              rpool -- resource pool from which to allocate result
53 **
54 **      Returns:
55 **              pointer to quoted string.
56 */
57
58 char *
59 addquotes(s, rpool)
60         char *s;
61         SM_RPOOL_T *rpool;
62 {
63         int len = 0;
64         char c;
65         char *p = s, *q, *r;
66
67         if (s == NULL)
68                 return NULL;
69
70         /* Find length of quoted string */
71         while ((c = *p++) != '\0')
72         {
73                 len++;
74                 if (c == '\\' || c == '"')
75                         len++;
76         }
77
78         q = r = sm_rpool_malloc_x(rpool, len + 3);
79         p = s;
80
81         /* add leading quote */
82         *q++ = '"';
83         while ((c = *p++) != '\0')
84         {
85                 /* quote \ or " */
86                 if (c == '\\' || c == '"')
87                         *q++ = '\\';
88                 *q++ = c;
89         }
90         *q++ = '"';
91         *q = '\0';
92         return r;
93 }
94
95 /*
96 **  STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
97 **      the following character is alpha-numerical.
98 **
99 **      This is done in place.
100 **
101 **      Parameters:
102 **              s -- the string to strip.
103 **
104 **      Returns:
105 **              none.
106 */
107
108 void
109 stripbackslash(s)
110         char *s;
111 {
112         char *p, *q, c;
113
114         if (s == NULL || *s == '\0')
115                 return;
116         p = q = s;
117         while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
118                 p++;
119         do
120         {
121                 c = *q++ = *p++;
122         } while (c != '\0');
123 }
124
125 /*
126 **  RFC822_STRING -- Checks string for proper RFC822 string quoting.
127 **
128 **      Runs through a string and verifies RFC822 special characters
129 **      are only found inside comments, quoted strings, or backslash
130 **      escaped.  Also verified balanced quotes and parenthesis.
131 **
132 **      Parameters:
133 **              s -- the string to modify.
134 **
135 **      Returns:
136 **              true iff the string is RFC822 compliant, false otherwise.
137 */
138
139 bool
140 rfc822_string(s)
141         char *s;
142 {
143         bool quoted = false;
144         int commentlev = 0;
145         char *c = s;
146
147         if (s == NULL)
148                 return false;
149
150         while (*c != '\0')
151         {
152                 /* escaped character */
153                 if (*c == '\\')
154                 {
155                         c++;
156                         if (*c == '\0')
157                                 return false;
158                 }
159                 else if (commentlev == 0 && *c == '"')
160                         quoted = !quoted;
161                 else if (!quoted)
162                 {
163                         if (*c == ')')
164                         {
165                                 /* unbalanced ')' */
166                                 if (commentlev == 0)
167                                         return false;
168                                 else
169                                         commentlev--;
170                         }
171                         else if (*c == '(')
172                                 commentlev++;
173                         else if (commentlev == 0 &&
174                                  strchr(MustQuoteChars, *c) != NULL)
175                                 return false;
176                 }
177                 c++;
178         }
179
180         /* unbalanced '"' or '(' */
181         return !quoted && commentlev == 0;
182 }
183 /*
184 **  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
185 **
186 **      Arbitrarily shorten (in place) an RFC822 string and rebalance
187 **      comments and quotes.
188 **
189 **      Parameters:
190 **              string -- the string to shorten
191 **              length -- the maximum size, 0 if no maximum
192 **
193 **      Returns:
194 **              true if string is changed, false otherwise
195 **
196 **      Side Effects:
197 **              Changes string in place, possibly resulting
198 **              in a shorter string.
199 */
200
201 bool
202 shorten_rfc822_string(string, length)
203         char *string;
204         size_t length;
205 {
206         bool backslash = false;
207         bool modified = false;
208         bool quoted = false;
209         size_t slen;
210         int parencount = 0;
211         char *ptr = string;
212
213         /*
214         **  If have to rebalance an already short enough string,
215         **  need to do it within allocated space.
216         */
217
218         slen = strlen(string);
219         if (length == 0 || slen < length)
220                 length = slen;
221
222         while (*ptr != '\0')
223         {
224                 if (backslash)
225                 {
226                         backslash = false;
227                         goto increment;
228                 }
229
230                 if (*ptr == '\\')
231                         backslash = true;
232                 else if (*ptr == '(')
233                 {
234                         if (!quoted)
235                                 parencount++;
236                 }
237                 else if (*ptr == ')')
238                 {
239                         if (--parencount < 0)
240                                 parencount = 0;
241                 }
242
243                 /* Inside a comment, quotes don't matter */
244                 if (parencount <= 0 && *ptr == '"')
245                         quoted = !quoted;
246
247 increment:
248                 /* Check for sufficient space for next character */
249                 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
250                                                 parencount +
251                                                 (quoted ? 1 : 0)))
252                 {
253                         /* Not enough, backtrack */
254                         if (*ptr == '\\')
255                                 backslash = false;
256                         else if (*ptr == '(' && !quoted)
257                                 parencount--;
258                         else if (*ptr == '"' && parencount == 0)
259                                 quoted = false;
260                         break;
261                 }
262                 ptr++;
263         }
264
265         /* Rebalance */
266         while (parencount-- > 0)
267         {
268                 if (*ptr != ')')
269                 {
270                         modified = true;
271                         *ptr = ')';
272                 }
273                 ptr++;
274         }
275         if (quoted)
276         {
277                 if (*ptr != '"')
278                 {
279                         modified = true;
280                         *ptr = '"';
281                 }
282                 ptr++;
283         }
284         if (*ptr != '\0')
285         {
286                 modified = true;
287                 *ptr = '\0';
288         }
289         return modified;
290 }
291 /*
292 **  FIND_CHARACTER -- find an unquoted character in an RFC822 string
293 **
294 **      Find an unquoted, non-commented character in an RFC822
295 **      string and return a pointer to its location in the
296 **      string.
297 **
298 **      Parameters:
299 **              string -- the string to search
300 **              character -- the character to find
301 **
302 **      Returns:
303 **              pointer to the character, or
304 **              a pointer to the end of the line if character is not found
305 */
306
307 char *
308 find_character(string, character)
309         char *string;
310         int character;
311 {
312         bool backslash = false;
313         bool quoted = false;
314         int parencount = 0;
315
316         while (string != NULL && *string != '\0')
317         {
318                 if (backslash)
319                 {
320                         backslash = false;
321                         if (!quoted && character == '\\' && *string == '\\')
322                                 break;
323                         string++;
324                         continue;
325                 }
326                 switch (*string)
327                 {
328                   case '\\':
329                         backslash = true;
330                         break;
331
332                   case '(':
333                         if (!quoted)
334                                 parencount++;
335                         break;
336
337                   case ')':
338                         if (--parencount < 0)
339                                 parencount = 0;
340                         break;
341                 }
342
343                 /* Inside a comment, nothing matters */
344                 if (parencount > 0)
345                 {
346                         string++;
347                         continue;
348                 }
349
350                 if (*string == '"')
351                         quoted = !quoted;
352                 else if (*string == character && !quoted)
353                         break;
354                 string++;
355         }
356
357         /* Return pointer to the character */
358         return string;
359 }
360
361 /*
362 **  CHECK_BODYTYPE -- check bodytype parameter
363 **
364 **      Parameters:
365 **              bodytype -- bodytype parameter
366 **
367 **      Returns:
368 **              BODYTYPE_* according to parameter
369 **
370 */
371
372 int
373 check_bodytype(bodytype)
374         char *bodytype;
375 {
376         /* check body type for legality */
377         if (bodytype == NULL)
378                 return BODYTYPE_NONE;
379         if (sm_strcasecmp(bodytype, "7BIT") == 0)
380                 return BODYTYPE_7BIT;
381         if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
382                 return BODYTYPE_8BITMIME;
383         return BODYTYPE_ILLEGAL;
384 }
385
386 #if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
387 /*
388 **  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
389 **
390 **      Parameters:
391 **              str -- string to truncate
392 **              len -- maximum length (including '\0') (0 for unlimited)
393 **              delim -- delimiter character
394 **
395 **      Returns:
396 **              None.
397 */
398
399 void
400 truncate_at_delim(str, len, delim)
401         char *str;
402         size_t len;
403         int delim;
404 {
405         char *p;
406
407         if (str == NULL || len == 0 || strlen(str) < len)
408                 return;
409
410         *(str + len - 1) = '\0';
411         while ((p = strrchr(str, delim)) != NULL)
412         {
413                 *p = '\0';
414                 if (p - str + 4 < len)
415                 {
416                         *p++ = (char) delim;
417                         *p = '\0';
418                         (void) sm_strlcat(str, "...", len);
419                         return;
420                 }
421         }
422
423         /* Couldn't find a place to append "..." */
424         if (len > 3)
425                 (void) sm_strlcpy(str, "...", len);
426         else
427                 str[0] = '\0';
428 }
429 #endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
430 /*
431 **  XALLOC -- Allocate memory, raise an exception on error
432 **
433 **      Parameters:
434 **              sz -- size of area to allocate.
435 **
436 **      Returns:
437 **              pointer to data region.
438 **
439 **      Exceptions:
440 **              SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
441 **
442 **      Side Effects:
443 **              Memory is allocated.
444 */
445
446 char *
447 #if SM_HEAP_CHECK
448 xalloc_tagged(sz, file, line)
449         register int sz;
450         char *file;
451         int line;
452 #else /* SM_HEAP_CHECK */
453 xalloc(sz)
454         register int sz;
455 #endif /* SM_HEAP_CHECK */
456 {
457         register char *p;
458
459         SM_REQUIRE(sz >= 0);
460
461         /* some systems can't handle size zero mallocs */
462         if (sz <= 0)
463                 sz = 1;
464
465         /* scaffolding for testing error handling code */
466         sm_xtrap_raise_x(&SmHeapOutOfMemory);
467
468         p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
469         if (p == NULL)
470         {
471                 sm_exc_raise_x(&SmHeapOutOfMemory);
472         }
473         return p;
474 }
475 /*
476 **  COPYPLIST -- copy list of pointers.
477 **
478 **      This routine is the equivalent of strdup for lists of
479 **      pointers.
480 **
481 **      Parameters:
482 **              list -- list of pointers to copy.
483 **                      Must be NULL terminated.
484 **              copycont -- if true, copy the contents of the vector
485 **                      (which must be a string) also.
486 **              rpool -- resource pool from which to allocate storage,
487 **                      or NULL
488 **
489 **      Returns:
490 **              a copy of 'list'.
491 */
492
493 char **
494 copyplist(list, copycont, rpool)
495         char **list;
496         bool copycont;
497         SM_RPOOL_T *rpool;
498 {
499         register char **vp;
500         register char **newvp;
501
502         for (vp = list; *vp != NULL; vp++)
503                 continue;
504
505         vp++;
506
507         newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp);
508         memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
509
510         if (copycont)
511         {
512                 for (vp = newvp; *vp != NULL; vp++)
513                         *vp = sm_rpool_strdup_x(rpool, *vp);
514         }
515
516         return newvp;
517 }
518 /*
519 **  COPYQUEUE -- copy address queue.
520 **
521 **      This routine is the equivalent of strdup for address queues;
522 **      addresses marked as QS_IS_DEAD() aren't copied
523 **
524 **      Parameters:
525 **              addr -- list of address structures to copy.
526 **              rpool -- resource pool from which to allocate storage
527 **
528 **      Returns:
529 **              a copy of 'addr'.
530 */
531
532 ADDRESS *
533 copyqueue(addr, rpool)
534         ADDRESS *addr;
535         SM_RPOOL_T *rpool;
536 {
537         register ADDRESS *newaddr;
538         ADDRESS *ret;
539         register ADDRESS **tail = &ret;
540
541         while (addr != NULL)
542         {
543                 if (!QS_IS_DEAD(addr->q_state))
544                 {
545                         newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
546                                                         sizeof *newaddr);
547                         STRUCTCOPY(*addr, *newaddr);
548                         *tail = newaddr;
549                         tail = &newaddr->q_next;
550                 }
551                 addr = addr->q_next;
552         }
553         *tail = NULL;
554
555         return ret;
556 }
557 /*
558 **  LOG_SENDMAIL_PID -- record sendmail pid and command line.
559 **
560 **      Parameters:
561 **              e -- the current envelope.
562 **
563 **      Returns:
564 **              none.
565 **
566 **      Side Effects:
567 **              writes pidfile, logs command line.
568 **              keeps file open and locked to prevent overwrite of active file
569 */
570
571 static SM_FILE_T        *Pidf = NULL;
572
573 void
574 log_sendmail_pid(e)
575         ENVELOPE *e;
576 {
577         long sff;
578         char pidpath[MAXPATHLEN];
579         extern char *CommandLineArgs;
580
581         /* write the pid to the log file for posterity */
582         sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
583         if (TrustedUid != 0 && RealUid == TrustedUid)
584                 sff |= SFF_OPENASROOT;
585         expand(PidFile, pidpath, sizeof pidpath, e);
586         Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
587         if (Pidf == NULL)
588         {
589                 if (errno == EWOULDBLOCK)
590                         sm_syslog(LOG_ERR, NOQID,
591                                   "unable to write pid to %s: file in use by another process",
592                                   pidpath);
593                 else
594                         sm_syslog(LOG_ERR, NOQID,
595                                   "unable to write pid to %s: %s",
596                                   pidpath, sm_errstring(errno));
597         }
598         else
599         {
600                 PidFilePid = getpid();
601
602                 /* write the process id on line 1 */
603                 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
604                                      (long) PidFilePid);
605
606                 /* line 2 contains all command line flags */
607                 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
608                                      CommandLineArgs);
609
610                 /* flush */
611                 (void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
612
613                 /*
614                 **  Leave pid file open until process ends
615                 **  so it's not overwritten by another
616                 **  process.
617                 */
618         }
619         if (LogLevel > 9)
620                 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
621 }
622
623 /*
624 **  CLOSE_SENDMAIL_PID -- close sendmail pid file
625 **
626 **      Parameters:
627 **              none.
628 **
629 **      Returns:
630 **              none.
631 */
632
633 void
634 close_sendmail_pid()
635 {
636         if (Pidf == NULL)
637                 return;
638
639         (void) sm_io_close(Pidf, SM_TIME_DEFAULT);
640         Pidf = NULL;
641 }
642
643 /*
644 **  SET_DELIVERY_MODE -- set and record the delivery mode
645 **
646 **      Parameters:
647 **              mode -- delivery mode
648 **              e -- the current envelope.
649 **
650 **      Returns:
651 **              none.
652 **
653 **      Side Effects:
654 **              sets {deliveryMode} macro
655 */
656
657 void
658 set_delivery_mode(mode, e)
659         int mode;
660         ENVELOPE *e;
661 {
662         char buf[2];
663
664         e->e_sendmode = (char) mode;
665         buf[0] = (char) mode;
666         buf[1] = '\0';
667         macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
668 }
669 /*
670 **  SET_OP_MODE -- set and record the op mode
671 **
672 **      Parameters:
673 **              mode -- op mode
674 **              e -- the current envelope.
675 **
676 **      Returns:
677 **              none.
678 **
679 **      Side Effects:
680 **              sets {opMode} macro
681 */
682
683 void
684 set_op_mode(mode)
685         int mode;
686 {
687         char buf[2];
688         extern ENVELOPE BlankEnvelope;
689
690         OpMode = (char) mode;
691         buf[0] = (char) mode;
692         buf[1] = '\0';
693         macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
694 }
695 /*
696 **  PRINTAV -- print argument vector.
697 **
698 **      Parameters:
699 **              fp -- output file pointer.
700 **              av -- argument vector.
701 **
702 **      Returns:
703 **              none.
704 **
705 **      Side Effects:
706 **              prints av.
707 */
708
709 void
710 printav(fp, av)
711         SM_FILE_T *fp;
712         register char **av;
713 {
714         while (*av != NULL)
715         {
716                 if (tTd(0, 44))
717                         sm_dprintf("\n\t%08lx=", (unsigned long) *av);
718                 else
719                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
720                 xputs(fp, *av++);
721         }
722         (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
723 }
724 /*
725 **  XPUTS -- put string doing control escapes.
726 **
727 **      Parameters:
728 **              fp -- output file pointer.
729 **              s -- string to put.
730 **
731 **      Returns:
732 **              none.
733 **
734 **      Side Effects:
735 **              output to stdout
736 */
737
738 void
739 xputs(fp, s)
740         SM_FILE_T *fp;
741         register const char *s;
742 {
743         register int c;
744         register struct metamac *mp;
745         bool shiftout = false;
746         extern struct metamac MetaMacros[];
747         static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
748                 "@(#)$Debug: ANSI - enable reverse video in debug output $");
749
750         /*
751         **  TermEscape is set here, rather than in main(),
752         **  because ANSI mode can be turned on or off at any time
753         **  if we are in -bt rule testing mode.
754         */
755
756         if (sm_debug_unknown(&DebugANSI))
757         {
758                 if (sm_debug_active(&DebugANSI, 1))
759                 {
760                         TermEscape.te_rv_on = "\033[7m";
761                         TermEscape.te_rv_off = "\033[0m";
762                 }
763                 else
764                 {
765                         TermEscape.te_rv_on = "";
766                         TermEscape.te_rv_off = "";
767                 }
768         }
769
770         if (s == NULL)
771         {
772                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
773                                      TermEscape.te_rv_on, TermEscape.te_rv_off);
774                 return;
775         }
776         while ((c = (*s++ & 0377)) != '\0')
777         {
778                 if (shiftout)
779                 {
780                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
781                                              TermEscape.te_rv_off);
782                         shiftout = false;
783                 }
784                 if (!isascii(c))
785                 {
786                         if (c == MATCHREPL)
787                         {
788                                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
789                                                      "%s$",
790                                                      TermEscape.te_rv_on);
791                                 shiftout = true;
792                                 if (*s == '\0')
793                                         continue;
794                                 c = *s++ & 0377;
795                                 goto printchar;
796                         }
797                         if (c == MACROEXPAND || c == MACRODEXPAND)
798                         {
799                                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
800                                                      "%s$",
801                                                      TermEscape.te_rv_on);
802                                 if (c == MACRODEXPAND)
803                                         (void) sm_io_putc(fp,
804                                                           SM_TIME_DEFAULT, '&');
805                                 shiftout = true;
806                                 if (*s == '\0')
807                                         continue;
808                                 if (strchr("=~&?", *s) != NULL)
809                                         (void) sm_io_putc(fp,
810                                                           SM_TIME_DEFAULT,
811                                                           *s++);
812                                 if (bitset(0200, *s))
813                                         (void) sm_io_fprintf(fp,
814                                                              SM_TIME_DEFAULT,
815                                                              "{%s}",
816                                                              macname(bitidx(*s++)));
817                                 else
818                                         (void) sm_io_fprintf(fp,
819                                                              SM_TIME_DEFAULT,
820                                                              "%c",
821                                                              *s++);
822                                 continue;
823                         }
824                         for (mp = MetaMacros; mp->metaname != '\0'; mp++)
825                         {
826                                 if (bitidx(mp->metaval) == c)
827                                 {
828                                         (void) sm_io_fprintf(fp,
829                                                              SM_TIME_DEFAULT,
830                                                              "%s$%c",
831                                                              TermEscape.te_rv_on,
832                                                              mp->metaname);
833                                         shiftout = true;
834                                         break;
835                                 }
836                         }
837                         if (c == MATCHCLASS || c == MATCHNCLASS)
838                         {
839                                 if (bitset(0200, *s))
840                                         (void) sm_io_fprintf(fp,
841                                                              SM_TIME_DEFAULT,
842                                                              "{%s}",
843                                                              macname(bitidx(*s++)));
844                                 else if (*s != '\0')
845                                         (void) sm_io_fprintf(fp,
846                                                              SM_TIME_DEFAULT,
847                                                              "%c",
848                                                              *s++);
849                         }
850                         if (mp->metaname != '\0')
851                                 continue;
852
853                         /* unrecognized meta character */
854                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
855                                              TermEscape.te_rv_on);
856                         shiftout = true;
857                         c &= 0177;
858                 }
859   printchar:
860                 if (isprint(c))
861                 {
862                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
863                         continue;
864                 }
865
866                 /* wasn't a meta-macro -- find another way to print it */
867                 switch (c)
868                 {
869                   case '\n':
870                         c = 'n';
871                         break;
872
873                   case '\r':
874                         c = 'r';
875                         break;
876
877                   case '\t':
878                         c = 't';
879                         break;
880                 }
881                 if (!shiftout)
882                 {
883                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
884                                              TermEscape.te_rv_on);
885                         shiftout = true;
886                 }
887                 if (isprint(c))
888                 {
889                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
890                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
891                 }
892                 else
893                 {
894                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
895                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
896                 }
897         }
898         if (shiftout)
899                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
900                                      TermEscape.te_rv_off);
901         (void) sm_io_flush(fp, SM_TIME_DEFAULT);
902 }
903 /*
904 **  MAKELOWER -- Translate a line into lower case
905 **
906 **      Parameters:
907 **              p -- the string to translate.  If NULL, return is
908 **                      immediate.
909 **
910 **      Returns:
911 **              none.
912 **
913 **      Side Effects:
914 **              String pointed to by p is translated to lower case.
915 */
916
917 void
918 makelower(p)
919         register char *p;
920 {
921         register char c;
922
923         if (p == NULL)
924                 return;
925         for (; (c = *p) != '\0'; p++)
926                 if (isascii(c) && isupper(c))
927                         *p = tolower(c);
928 }
929 /*
930 **  FIXCRLF -- fix <CR><LF> in line.
931 **
932 **      Looks for the <CR><LF> combination and turns it into the
933 **      UNIX canonical <NL> character.  It only takes one line,
934 **      i.e., it is assumed that the first <NL> found is the end
935 **      of the line.
936 **
937 **      Parameters:
938 **              line -- the line to fix.
939 **              stripnl -- if true, strip the newline also.
940 **
941 **      Returns:
942 **              none.
943 **
944 **      Side Effects:
945 **              line is changed in place.
946 */
947
948 void
949 fixcrlf(line, stripnl)
950         char *line;
951         bool stripnl;
952 {
953         register char *p;
954
955         p = strchr(line, '\n');
956         if (p == NULL)
957                 return;
958         if (p > line && p[-1] == '\r')
959                 p--;
960         if (!stripnl)
961                 *p++ = '\n';
962         *p = '\0';
963 }
964 /*
965 **  PUTLINE -- put a line like fputs obeying SMTP conventions
966 **
967 **      This routine always guarantees outputing a newline (or CRLF,
968 **      as appropriate) at the end of the string.
969 **
970 **      Parameters:
971 **              l -- line to put.
972 **              mci -- the mailer connection information.
973 **
974 **      Returns:
975 **              true iff line was written successfully
976 **
977 **      Side Effects:
978 **              output of l to mci->mci_out.
979 */
980
981 bool
982 putline(l, mci)
983         register char *l;
984         register MCI *mci;
985 {
986         return putxline(l, strlen(l), mci, PXLF_MAPFROM);
987 }
988 /*
989 **  PUTXLINE -- putline with flags bits.
990 **
991 **      This routine always guarantees outputing a newline (or CRLF,
992 **      as appropriate) at the end of the string.
993 **
994 **      Parameters:
995 **              l -- line to put.
996 **              len -- the length of the line.
997 **              mci -- the mailer connection information.
998 **              pxflags -- flag bits:
999 **                  PXLF_MAPFROM -- map From_ to >From_.
1000 **                  PXLF_STRIP8BIT -- strip 8th bit.
1001 **                  PXLF_HEADER -- map bare newline in header to newline space.
1002 **                  PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1003 **
1004 **      Returns:
1005 **              true iff line was written successfully
1006 **
1007 **      Side Effects:
1008 **              output of l to mci->mci_out.
1009 */
1010
1011 bool
1012 putxline(l, len, mci, pxflags)
1013         register char *l;
1014         size_t len;
1015         register MCI *mci;
1016         int pxflags;
1017 {
1018         bool dead = false;
1019         register char *p, *end;
1020         int slop = 0;
1021
1022         /* strip out 0200 bits -- these can look like TELNET protocol */
1023         if (bitset(MCIF_7BIT, mci->mci_flags) ||
1024             bitset(PXLF_STRIP8BIT, pxflags))
1025         {
1026                 register char svchar;
1027
1028                 for (p = l; (svchar = *p) != '\0'; ++p)
1029                         if (bitset(0200, svchar))
1030                                 *p = svchar &~ 0200;
1031         }
1032
1033         end = l + len;
1034         do
1035         {
1036                 bool noeol = false;
1037
1038                 /* find the end of the line */
1039                 p = memchr(l, '\n', end - l);
1040                 if (p == NULL)
1041                 {
1042                         p = end;
1043                         noeol = true;
1044                 }
1045
1046                 if (TrafficLogFile != NULL)
1047                         (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1048                                              "%05d >>> ", (int) CurrentPid);
1049
1050                 /* check for line overflow */
1051                 while (mci->mci_mailer->m_linelimit > 0 &&
1052                        (p - l + slop) > mci->mci_mailer->m_linelimit)
1053                 {
1054                         char *l_base = l;
1055                         register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1056
1057                         if (l[0] == '.' && slop == 0 &&
1058                             bitnset(M_XDOT, mci->mci_mailer->m_flags))
1059                         {
1060                                 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1061                                                '.') == SM_IO_EOF)
1062                                         dead = true;
1063                                 if (TrafficLogFile != NULL)
1064                                         (void) sm_io_putc(TrafficLogFile,
1065                                                           SM_TIME_DEFAULT, '.');
1066                         }
1067                         else if (l[0] == 'F' && slop == 0 &&
1068                                  bitset(PXLF_MAPFROM, pxflags) &&
1069                                  strncmp(l, "From ", 5) == 0 &&
1070                                  bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1071                         {
1072                                 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1073                                                '>') == SM_IO_EOF)
1074                                         dead = true;
1075                                 if (TrafficLogFile != NULL)
1076                                         (void) sm_io_putc(TrafficLogFile,
1077                                                           SM_TIME_DEFAULT,
1078                                                           '>');
1079                         }
1080                         if (dead)
1081                                 break;
1082
1083                         while (l < q)
1084                         {
1085                                 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1086                                        (unsigned char) *l++) == SM_IO_EOF)
1087                                 {
1088                                         dead = true;
1089                                         break;
1090                                 }
1091                         }
1092                         if (dead)
1093                                 break;
1094
1095                         if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') ==
1096                             SM_IO_EOF ||
1097                             sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1098                                         mci->mci_mailer->m_eol) ==
1099                             SM_IO_EOF ||
1100                             sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') ==
1101                             SM_IO_EOF)
1102                         {
1103                                 dead = true;
1104                                 break;
1105                         }
1106                         if (TrafficLogFile != NULL)
1107                         {
1108                                 for (l = l_base; l < q; l++)
1109                                         (void) sm_io_putc(TrafficLogFile,
1110                                                           SM_TIME_DEFAULT,
1111                                                           (unsigned char)*l);
1112                                 (void) sm_io_fprintf(TrafficLogFile,
1113                                                      SM_TIME_DEFAULT,
1114                                                      "!\n%05d >>>  ",
1115                                                      (int) CurrentPid);
1116                         }
1117                         slop = 1;
1118                 }
1119
1120                 if (dead)
1121                         break;
1122
1123                 /* output last part */
1124                 if (l[0] == '.' && slop == 0 &&
1125                     bitnset(M_XDOT, mci->mci_mailer->m_flags))
1126                 {
1127                         if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1128                             SM_IO_EOF)
1129                         {
1130                                 dead = true;
1131                                 break;
1132                         }
1133                         if (TrafficLogFile != NULL)
1134                                 (void) sm_io_putc(TrafficLogFile,
1135                                                   SM_TIME_DEFAULT, '.');
1136                 }
1137                 else if (l[0] == 'F' && slop == 0 &&
1138                          bitset(PXLF_MAPFROM, pxflags) &&
1139                          strncmp(l, "From ", 5) == 0 &&
1140                          bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1141                 {
1142                         if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1143                             SM_IO_EOF)
1144                         {
1145                                 dead = true;
1146                                 break;
1147                         }
1148                         if (TrafficLogFile != NULL)
1149                                 (void) sm_io_putc(TrafficLogFile,
1150                                                   SM_TIME_DEFAULT, '>');
1151                 }
1152                 for ( ; l < p; ++l)
1153                 {
1154                         if (TrafficLogFile != NULL)
1155                                 (void) sm_io_putc(TrafficLogFile,
1156                                                   SM_TIME_DEFAULT,
1157                                                   (unsigned char)*l);
1158                         if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1159                                        (unsigned char) *l) == SM_IO_EOF)
1160                         {
1161                                 dead = true;
1162                                 break;
1163                         }
1164                 }
1165                 if (dead)
1166                         break;
1167
1168                 if (TrafficLogFile != NULL)
1169                         (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1170                                           '\n');
1171                 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) &&
1172                     sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1173                                 mci->mci_mailer->m_eol) == SM_IO_EOF)
1174                 {
1175                         dead = true;
1176                         break;
1177                 }
1178                 if (l < end && *l == '\n')
1179                 {
1180                         if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1181                             bitset(PXLF_HEADER, pxflags))
1182                         {
1183                                 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1184                                                ' ') == SM_IO_EOF)
1185                                 {
1186                                         dead = true;
1187                                         break;
1188                                 }
1189
1190                                 if (TrafficLogFile != NULL)
1191                                         (void) sm_io_putc(TrafficLogFile,
1192                                                           SM_TIME_DEFAULT, ' ');
1193                         }
1194                 }
1195
1196         } while (l < end);
1197         return !dead;
1198 }
1199
1200 /*
1201 **  XUNLINK -- unlink a file, doing logging as appropriate.
1202 **
1203 **      Parameters:
1204 **              f -- name of file to unlink.
1205 **
1206 **      Returns:
1207 **              return value of unlink()
1208 **
1209 **      Side Effects:
1210 **              f is unlinked.
1211 */
1212
1213 int
1214 xunlink(f)
1215         char *f;
1216 {
1217         register int i;
1218         int save_errno;
1219
1220         if (LogLevel > 98)
1221                 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1222
1223         i = unlink(f);
1224         save_errno = errno;
1225         if (i < 0 && LogLevel > 97)
1226                 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1227                           f, errno);
1228         if (i >= 0)
1229                 SYNC_DIR(f, false);
1230         errno = save_errno;
1231         return i;
1232 }
1233 /*
1234 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1235 **
1236 **      Parameters:
1237 **              buf -- place to put the input line.
1238 **              siz -- size of buf.
1239 **              fp -- file to read from.
1240 **              timeout -- the timeout before error occurs.
1241 **              during -- what we are trying to read (for error messages).
1242 **
1243 **      Returns:
1244 **              NULL on error (including timeout).  This may also leave
1245 **                      buf containing a null string.
1246 **              buf otherwise.
1247 */
1248
1249
1250 char *
1251 sfgets(buf, siz, fp, timeout, during)
1252         char *buf;
1253         int siz;
1254         SM_FILE_T *fp;
1255         time_t timeout;
1256         char *during;
1257 {
1258         register char *p;
1259         int save_errno;
1260         int io_timeout;
1261
1262         SM_REQUIRE(siz > 0);
1263         SM_REQUIRE(buf != NULL);
1264
1265         if (fp == NULL)
1266         {
1267                 buf[0] = '\0';
1268                 errno = EBADF;
1269                 return NULL;
1270         }
1271
1272         /* try to read */
1273         p = NULL;
1274         errno = 0;
1275
1276         /* convert the timeout to sm_io notation */
1277         io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1278         while (!sm_io_eof(fp) && !sm_io_error(fp))
1279         {
1280                 errno = 0;
1281                 p = sm_io_fgets(fp, io_timeout, buf, siz);
1282                 if (p == NULL && errno == EAGAIN)
1283                 {
1284                         /* The sm_io_fgets() call timedout */
1285                         if (LogLevel > 1)
1286                                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
1287                                           "timeout waiting for input from %.100s during %s",
1288                                           CURHOSTNAME,
1289                                           during);
1290                         buf[0] = '\0';
1291 #if XDEBUG
1292                         checkfd012(during);
1293 #endif /* XDEBUG */
1294                         if (TrafficLogFile != NULL)
1295                                 (void) sm_io_fprintf(TrafficLogFile,
1296                                                      SM_TIME_DEFAULT,
1297                                                      "%05d <<< [TIMEOUT]\n",
1298                                                      (int) CurrentPid);
1299                         errno = ETIMEDOUT;
1300                         return NULL;
1301                 }
1302                 if (p != NULL || errno != EINTR)
1303                         break;
1304                 (void) sm_io_clearerr(fp);
1305         }
1306         save_errno = errno;
1307
1308         /* clean up the books and exit */
1309         LineNumber++;
1310         if (p == NULL)
1311         {
1312                 buf[0] = '\0';
1313                 if (TrafficLogFile != NULL)
1314                         (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1315                                              "%05d <<< [EOF]\n",
1316                                              (int) CurrentPid);
1317                 errno = save_errno;
1318                 return NULL;
1319         }
1320         if (TrafficLogFile != NULL)
1321                 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1322                                      "%05d <<< %s", (int) CurrentPid, buf);
1323         if (SevenBitInput)
1324         {
1325                 for (p = buf; *p != '\0'; p++)
1326                         *p &= ~0200;
1327         }
1328         else if (!HasEightBits)
1329         {
1330                 for (p = buf; *p != '\0'; p++)
1331                 {
1332                         if (bitset(0200, *p))
1333                         {
1334                                 HasEightBits = true;
1335                                 break;
1336                         }
1337                 }
1338         }
1339         return buf;
1340 }
1341 /*
1342 **  FGETFOLDED -- like fgets, but knows about folded lines.
1343 **
1344 **      Parameters:
1345 **              buf -- place to put result.
1346 **              n -- bytes available.
1347 **              f -- file to read from.
1348 **
1349 **      Returns:
1350 **              input line(s) on success, NULL on error or SM_IO_EOF.
1351 **              This will normally be buf -- unless the line is too
1352 **                      long, when it will be sm_malloc_x()ed.
1353 **
1354 **      Side Effects:
1355 **              buf gets lines from f, with continuation lines (lines
1356 **              with leading white space) appended.  CRLF's are mapped
1357 **              into single newlines.  Any trailing NL is stripped.
1358 */
1359
1360 char *
1361 fgetfolded(buf, n, f)
1362         char *buf;
1363         register int n;
1364         SM_FILE_T *f;
1365 {
1366         register char *p = buf;
1367         char *bp = buf;
1368         register int i;
1369
1370         SM_REQUIRE(n > 0);
1371         SM_REQUIRE(buf != NULL);
1372         if (f == NULL)
1373         {
1374                 buf[0] = '\0';
1375                 errno = EBADF;
1376                 return NULL;
1377         }
1378
1379         n--;
1380         while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1381         {
1382                 if (i == '\r')
1383                 {
1384                         i = sm_io_getc(f, SM_TIME_DEFAULT);
1385                         if (i != '\n')
1386                         {
1387                                 if (i != SM_IO_EOF)
1388                                         (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1389                                                             i);
1390                                 i = '\r';
1391                         }
1392                 }
1393                 if (--n <= 0)
1394                 {
1395                         /* allocate new space */
1396                         char *nbp;
1397                         int nn;
1398
1399                         nn = (p - bp);
1400                         if (nn < MEMCHUNKSIZE)
1401                                 nn *= 2;
1402                         else
1403                                 nn += MEMCHUNKSIZE;
1404                         nbp = sm_malloc_x(nn);
1405                         memmove(nbp, bp, p - bp);
1406                         p = &nbp[p - bp];
1407                         if (bp != buf)
1408                                 sm_free(bp);
1409                         bp = nbp;
1410                         n = nn - (p - bp);
1411                 }
1412                 *p++ = i;
1413                 if (i == '\n')
1414                 {
1415                         LineNumber++;
1416                         i = sm_io_getc(f, SM_TIME_DEFAULT);
1417                         if (i != SM_IO_EOF)
1418                                 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1419                         if (i != ' ' && i != '\t')
1420                                 break;
1421                 }
1422         }
1423         if (p == bp)
1424                 return NULL;
1425         if (p[-1] == '\n')
1426                 p--;
1427         *p = '\0';
1428         return bp;
1429 }
1430 /*
1431 **  CURTIME -- return current time.
1432 **
1433 **      Parameters:
1434 **              none.
1435 **
1436 **      Returns:
1437 **              the current time.
1438 */
1439
1440 time_t
1441 curtime()
1442 {
1443         auto time_t t;
1444
1445         (void) time(&t);
1446         return t;
1447 }
1448 /*
1449 **  ATOBOOL -- convert a string representation to boolean.
1450 **
1451 **      Defaults to false
1452 **
1453 **      Parameters:
1454 **              s -- string to convert.  Takes "tTyY", empty, and NULL as true,
1455 **                      others as false.
1456 **
1457 **      Returns:
1458 **              A boolean representation of the string.
1459 */
1460
1461 bool
1462 atobool(s)
1463         register char *s;
1464 {
1465         if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1466                 return true;
1467         return false;
1468 }
1469 /*
1470 **  ATOOCT -- convert a string representation to octal.
1471 **
1472 **      Parameters:
1473 **              s -- string to convert.
1474 **
1475 **      Returns:
1476 **              An integer representing the string interpreted as an
1477 **              octal number.
1478 */
1479
1480 int
1481 atooct(s)
1482         register char *s;
1483 {
1484         register int i = 0;
1485
1486         while (*s >= '0' && *s <= '7')
1487                 i = (i << 3) | (*s++ - '0');
1488         return i;
1489 }
1490 /*
1491 **  BITINTERSECT -- tell if two bitmaps intersect
1492 **
1493 **      Parameters:
1494 **              a, b -- the bitmaps in question
1495 **
1496 **      Returns:
1497 **              true if they have a non-null intersection
1498 **              false otherwise
1499 */
1500
1501 bool
1502 bitintersect(a, b)
1503         BITMAP256 a;
1504         BITMAP256 b;
1505 {
1506         int i;
1507
1508         for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1509         {
1510                 if ((a[i] & b[i]) != 0)
1511                         return true;
1512         }
1513         return false;
1514 }
1515 /*
1516 **  BITZEROP -- tell if a bitmap is all zero
1517 **
1518 **      Parameters:
1519 **              map -- the bit map to check
1520 **
1521 **      Returns:
1522 **              true if map is all zero.
1523 **              false if there are any bits set in map.
1524 */
1525
1526 bool
1527 bitzerop(map)
1528         BITMAP256 map;
1529 {
1530         int i;
1531
1532         for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1533         {
1534                 if (map[i] != 0)
1535                         return false;
1536         }
1537         return true;
1538 }
1539 /*
1540 **  STRCONTAINEDIN -- tell if one string is contained in another
1541 **
1542 **      Parameters:
1543 **              icase -- ignore case?
1544 **              a -- possible substring.
1545 **              b -- possible superstring.
1546 **
1547 **      Returns:
1548 **              true if a is contained in b (case insensitive).
1549 **              false otherwise.
1550 */
1551
1552 bool
1553 strcontainedin(icase, a, b)
1554         bool icase;
1555         register char *a;
1556         register char *b;
1557 {
1558         int la;
1559         int lb;
1560         int c;
1561
1562         la = strlen(a);
1563         lb = strlen(b);
1564         c = *a;
1565         if (icase && isascii(c) && isupper(c))
1566                 c = tolower(c);
1567         for (; lb-- >= la; b++)
1568         {
1569                 if (icase)
1570                 {
1571                         if (*b != c &&
1572                             isascii(*b) && isupper(*b) && tolower(*b) != c)
1573                                 continue;
1574                         if (sm_strncasecmp(a, b, la) == 0)
1575                                 return true;
1576                 }
1577                 else
1578                 {
1579                         if (*b != c)
1580                                 continue;
1581                         if (strncmp(a, b, la) == 0)
1582                                 return true;
1583                 }
1584         }
1585         return false;
1586 }
1587 /*
1588 **  CHECKFD012 -- check low numbered file descriptors
1589 **
1590 **      File descriptors 0, 1, and 2 should be open at all times.
1591 **      This routine verifies that, and fixes it if not true.
1592 **
1593 **      Parameters:
1594 **              where -- a tag printed if the assertion failed
1595 **
1596 **      Returns:
1597 **              none
1598 */
1599
1600 void
1601 checkfd012(where)
1602         char *where;
1603 {
1604 #if XDEBUG
1605         register int i;
1606
1607         for (i = 0; i < 3; i++)
1608                 fill_fd(i, where);
1609 #endif /* XDEBUG */
1610 }
1611 /*
1612 **  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1613 **
1614 **      Parameters:
1615 **              fd -- file descriptor to check.
1616 **              where -- tag to print on failure.
1617 **
1618 **      Returns:
1619 **              none.
1620 */
1621
1622 void
1623 checkfdopen(fd, where)
1624         int fd;
1625         char *where;
1626 {
1627 #if XDEBUG
1628         struct stat st;
1629
1630         if (fstat(fd, &st) < 0 && errno == EBADF)
1631         {
1632                 syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1633                 printopenfds(true);
1634         }
1635 #endif /* XDEBUG */
1636 }
1637 /*
1638 **  CHECKFDS -- check for new or missing file descriptors
1639 **
1640 **      Parameters:
1641 **              where -- tag for printing.  If null, take a base line.
1642 **
1643 **      Returns:
1644 **              none
1645 **
1646 **      Side Effects:
1647 **              If where is set, shows changes since the last call.
1648 */
1649
1650 void
1651 checkfds(where)
1652         char *where;
1653 {
1654         int maxfd;
1655         register int fd;
1656         bool printhdr = true;
1657         int save_errno = errno;
1658         static BITMAP256 baseline;
1659         extern int DtableSize;
1660
1661         if (DtableSize > BITMAPBITS)
1662                 maxfd = BITMAPBITS;
1663         else
1664                 maxfd = DtableSize;
1665         if (where == NULL)
1666                 clrbitmap(baseline);
1667
1668         for (fd = 0; fd < maxfd; fd++)
1669         {
1670                 struct stat stbuf;
1671
1672                 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1673                 {
1674                         if (!bitnset(fd, baseline))
1675                                 continue;
1676                         clrbitn(fd, baseline);
1677                 }
1678                 else if (!bitnset(fd, baseline))
1679                         setbitn(fd, baseline);
1680                 else
1681                         continue;
1682
1683                 /* file state has changed */
1684                 if (where == NULL)
1685                         continue;
1686                 if (printhdr)
1687                 {
1688                         sm_syslog(LOG_DEBUG, CurEnv->e_id,
1689                                   "%s: changed fds:",
1690                                   where);
1691                         printhdr = false;
1692                 }
1693                 dumpfd(fd, true, true);
1694         }
1695         errno = save_errno;
1696 }
1697 /*
1698 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1699 **
1700 **      Parameters:
1701 **              logit -- if set, send output to syslog; otherwise
1702 **                      print for debugging.
1703 **
1704 **      Returns:
1705 **              none.
1706 */
1707
1708 #if NETINET || NETINET6
1709 # include <arpa/inet.h>
1710 #endif /* NETINET || NETINET6 */
1711
1712 void
1713 printopenfds(logit)
1714         bool logit;
1715 {
1716         register int fd;
1717         extern int DtableSize;
1718
1719         for (fd = 0; fd < DtableSize; fd++)
1720                 dumpfd(fd, false, logit);
1721 }
1722 /*
1723 **  DUMPFD -- dump a file descriptor
1724 **
1725 **      Parameters:
1726 **              fd -- the file descriptor to dump.
1727 **              printclosed -- if set, print a notification even if
1728 **                      it is closed; otherwise print nothing.
1729 **              logit -- if set, use sm_syslog instead of sm_dprintf()
1730 **
1731 **      Returns:
1732 **              none.
1733 */
1734
1735 void
1736 dumpfd(fd, printclosed, logit)
1737         int fd;
1738         bool printclosed;
1739         bool logit;
1740 {
1741         register char *p;
1742         char *hp;
1743 #ifdef S_IFSOCK
1744         SOCKADDR sa;
1745 #endif /* S_IFSOCK */
1746         auto SOCKADDR_LEN_T slen;
1747         int i;
1748 #if STAT64 > 0
1749         struct stat64 st;
1750 #else /* STAT64 > 0 */
1751         struct stat st;
1752 #endif /* STAT64 > 0 */
1753         char buf[200];
1754
1755         p = buf;
1756         (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1757         p += strlen(p);
1758
1759         if (
1760 #if STAT64 > 0
1761             fstat64(fd, &st)
1762 #else /* STAT64 > 0 */
1763             fstat(fd, &st)
1764 #endif /* STAT64 > 0 */
1765             < 0)
1766         {
1767                 if (errno != EBADF)
1768                 {
1769                         (void) sm_snprintf(p, SPACELEFT(buf, p),
1770                                 "CANNOT STAT (%s)",
1771                                 sm_errstring(errno));
1772                         goto printit;
1773                 }
1774                 else if (printclosed)
1775                 {
1776                         (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1777                         goto printit;
1778                 }
1779                 return;
1780         }
1781
1782         i = fcntl(fd, F_GETFL, 0);
1783         if (i != -1)
1784         {
1785                 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1786                 p += strlen(p);
1787         }
1788
1789         (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1790                         (int) st.st_mode);
1791         p += strlen(p);
1792         switch (st.st_mode & S_IFMT)
1793         {
1794 #ifdef S_IFSOCK
1795           case S_IFSOCK:
1796                 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1797                 p += strlen(p);
1798                 memset(&sa, '\0', sizeof sa);
1799                 slen = sizeof sa;
1800                 if (getsockname(fd, &sa.sa, &slen) < 0)
1801                         (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1802                                  sm_errstring(errno));
1803                 else
1804                 {
1805                         hp = hostnamebyanyaddr(&sa);
1806                         if (hp == NULL)
1807                         {
1808                                 /* EMPTY */
1809                                 /* do nothing */
1810                         }
1811 # if NETINET
1812                         else if (sa.sa.sa_family == AF_INET)
1813                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1814                                         "%s/%d", hp, ntohs(sa.sin.sin_port));
1815 # endif /* NETINET */
1816 # if NETINET6
1817                         else if (sa.sa.sa_family == AF_INET6)
1818                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1819                                         "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1820 # endif /* NETINET6 */
1821                         else
1822                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1823                                         "%s", hp);
1824                 }
1825                 p += strlen(p);
1826                 (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
1827                 p += strlen(p);
1828                 slen = sizeof sa;
1829                 if (getpeername(fd, &sa.sa, &slen) < 0)
1830                         (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1831                                         sm_errstring(errno));
1832                 else
1833                 {
1834                         hp = hostnamebyanyaddr(&sa);
1835                         if (hp == NULL)
1836                         {
1837                                 /* EMPTY */
1838                                 /* do nothing */
1839                         }
1840 # if NETINET
1841                         else if (sa.sa.sa_family == AF_INET)
1842                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1843                                         "%s/%d", hp, ntohs(sa.sin.sin_port));
1844 # endif /* NETINET */
1845 # if NETINET6
1846                         else if (sa.sa.sa_family == AF_INET6)
1847                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1848                                         "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1849 # endif /* NETINET6 */
1850                         else
1851                                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1852                                         "%s", hp);
1853                 }
1854                 break;
1855 #endif /* S_IFSOCK */
1856
1857           case S_IFCHR:
1858                 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
1859                 p += strlen(p);
1860                 goto defprint;
1861
1862 #ifdef S_IFBLK
1863           case S_IFBLK:
1864                 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
1865                 p += strlen(p);
1866                 goto defprint;
1867 #endif /* S_IFBLK */
1868
1869 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1870           case S_IFIFO:
1871                 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1872                 p += strlen(p);
1873                 goto defprint;
1874 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1875
1876 #ifdef S_IFDIR
1877           case S_IFDIR:
1878                 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
1879                 p += strlen(p);
1880                 goto defprint;
1881 #endif /* S_IFDIR */
1882
1883 #ifdef S_IFLNK
1884           case S_IFLNK:
1885                 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
1886                 p += strlen(p);
1887                 goto defprint;
1888 #endif /* S_IFLNK */
1889
1890           default:
1891 defprint:
1892                 (void) sm_snprintf(p, SPACELEFT(buf, p),
1893                          "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
1894                          major(st.st_dev), minor(st.st_dev),
1895                          (ULONGLONG_T) st.st_ino,
1896                          (int) st.st_nlink, (int) st.st_uid,
1897                          (int) st.st_gid);
1898                 p += strlen(p);
1899                 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
1900                          (ULONGLONG_T) st.st_size);
1901                 break;
1902         }
1903
1904 printit:
1905         if (logit)
1906                 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1907                           "%.800s", buf);
1908         else
1909                 sm_dprintf("%s\n", buf);
1910 }
1911 /*
1912 **  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1913 **
1914 **      Parameters:
1915 **              host -- the host to shorten (stripped in place).
1916 **
1917 **      Returns:
1918 **              place where string was truncated, NULL if not truncated.
1919 */
1920
1921 char *
1922 shorten_hostname(host)
1923         char host[];
1924 {
1925         register char *p;
1926         char *mydom;
1927         int i;
1928         bool canon = false;
1929
1930         /* strip off final dot */
1931         i = strlen(host);
1932         p = &host[(i == 0) ? 0 : i - 1];
1933         if (*p == '.')
1934         {
1935                 *p = '\0';
1936                 canon = true;
1937         }
1938
1939         /* see if there is any domain at all -- if not, we are done */
1940         p = strchr(host, '.');
1941         if (p == NULL)
1942                 return NULL;
1943
1944         /* yes, we have a domain -- see if it looks like us */
1945         mydom = macvalue('m', CurEnv);
1946         if (mydom == NULL)
1947                 mydom = "";
1948         i = strlen(++p);
1949         if ((canon ? sm_strcasecmp(p, mydom)
1950                    : sm_strncasecmp(p, mydom, i)) == 0 &&
1951                         (mydom[i] == '.' || mydom[i] == '\0'))
1952         {
1953                 *--p = '\0';
1954                 return p;
1955         }
1956         return NULL;
1957 }
1958 /*
1959 **  PROG_OPEN -- open a program for reading
1960 **
1961 **      Parameters:
1962 **              argv -- the argument list.
1963 **              pfd -- pointer to a place to store the file descriptor.
1964 **              e -- the current envelope.
1965 **
1966 **      Returns:
1967 **              pid of the process -- -1 if it failed.
1968 */
1969
1970 pid_t
1971 prog_open(argv, pfd, e)
1972         char **argv;
1973         int *pfd;
1974         ENVELOPE *e;
1975 {
1976         pid_t pid;
1977         int save_errno;
1978         int sff;
1979         int ret;
1980         int fdv[2];
1981         char *p, *q;
1982         char buf[MAXPATHLEN];
1983         extern int DtableSize;
1984
1985         if (pipe(fdv) < 0)
1986         {
1987                 syserr("%s: cannot create pipe for stdout", argv[0]);
1988                 return -1;
1989         }
1990         pid = fork();
1991         if (pid < 0)
1992         {
1993                 syserr("%s: cannot fork", argv[0]);
1994                 (void) close(fdv[0]);
1995                 (void) close(fdv[1]);
1996                 return -1;
1997         }
1998         if (pid > 0)
1999         {
2000                 /* parent */
2001                 (void) close(fdv[1]);
2002                 *pfd = fdv[0];
2003                 return pid;
2004         }
2005
2006         /* Reset global flags */
2007         RestartRequest = NULL;
2008         RestartWorkGroup = false;
2009         ShutdownRequest = NULL;
2010         PendingSignal = 0;
2011         CurrentPid = getpid();
2012
2013         /*
2014         **  Initialize exception stack and default exception
2015         **  handler for child process.
2016         */
2017
2018         sm_exc_newthread(fatal_error);
2019
2020         /* child -- close stdin */
2021         (void) close(0);
2022
2023         /* stdout goes back to parent */
2024         (void) close(fdv[0]);
2025         if (dup2(fdv[1], 1) < 0)
2026         {
2027                 syserr("%s: cannot dup2 for stdout", argv[0]);
2028                 _exit(EX_OSERR);
2029         }
2030         (void) close(fdv[1]);
2031
2032         /* stderr goes to transcript if available */
2033         if (e->e_xfp != NULL)
2034         {
2035                 int xfd;
2036
2037                 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
2038                 if (xfd >= 0 && dup2(xfd, 2) < 0)
2039                 {
2040                         syserr("%s: cannot dup2 for stderr", argv[0]);
2041                         _exit(EX_OSERR);
2042                 }
2043         }
2044
2045         /* this process has no right to the queue file */
2046         if (e->e_lockfp != NULL)
2047         {
2048                 int fd;
2049
2050                 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
2051                 if (fd >= 0)
2052                         (void) close(fd);
2053                 else
2054                         syserr("%s: lockfp does not have a fd", argv[0]);
2055         }
2056
2057         /* chroot to the program mailer directory, if defined */
2058         if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2059         {
2060                 expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
2061                 if (chroot(buf) < 0)
2062                 {
2063                         syserr("prog_open: cannot chroot(%s)", buf);
2064                         exit(EX_TEMPFAIL);
2065                 }
2066                 if (chdir("/") < 0)
2067                 {
2068                         syserr("prog_open: cannot chdir(/)");
2069                         exit(EX_TEMPFAIL);
2070                 }
2071         }
2072
2073         /* run as default user */
2074         endpwent();
2075         sm_mbdb_terminate();
2076 #if _FFR_MEMSTAT
2077         (void) sm_memstat_close();
2078 #endif /* _FFR_MEMSTAT */
2079         if (setgid(DefGid) < 0 && geteuid() == 0)
2080         {
2081                 syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2082                 exit(EX_TEMPFAIL);
2083         }
2084         if (setuid(DefUid) < 0 && geteuid() == 0)
2085         {
2086                 syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2087                 exit(EX_TEMPFAIL);
2088         }
2089
2090         /* run in some directory */
2091         if (ProgMailer != NULL)
2092                 p = ProgMailer->m_execdir;
2093         else
2094                 p = NULL;
2095         for (; p != NULL; p = q)
2096         {
2097                 q = strchr(p, ':');
2098                 if (q != NULL)
2099                         *q = '\0';
2100                 expand(p, buf, sizeof buf, e);
2101                 if (q != NULL)
2102                         *q++ = ':';
2103                 if (buf[0] != '\0' && chdir(buf) >= 0)
2104                         break;
2105         }
2106         if (p == NULL)
2107         {
2108                 /* backup directories */
2109                 if (chdir("/tmp") < 0)
2110                         (void) chdir("/");
2111         }
2112
2113         /* Check safety of program to be run */
2114         sff = SFF_ROOTOK|SFF_EXECOK;
2115         if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2116                 sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2117         if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2118                 sff |= SFF_NOPATHCHECK;
2119         else
2120                 sff |= SFF_SAFEDIRPATH;
2121         ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2122         if (ret != 0)
2123                 sm_syslog(LOG_INFO, e->e_id,
2124                           "Warning: prog_open: program %s unsafe: %s",
2125                           argv[0], sm_errstring(ret));
2126
2127         /* arrange for all the files to be closed */
2128         sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2129
2130         /* now exec the process */
2131         (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2132
2133         /* woops!  failed */
2134         save_errno = errno;
2135         syserr("%s: cannot exec", argv[0]);
2136         if (transienterror(save_errno))
2137                 _exit(EX_OSERR);
2138         _exit(EX_CONFIG);
2139         return -1;      /* avoid compiler warning on IRIX */
2140 }
2141 /*
2142 **  GET_COLUMN -- look up a Column in a line buffer
2143 **
2144 **      Parameters:
2145 **              line -- the raw text line to search.
2146 **              col -- the column number to fetch.
2147 **              delim -- the delimiter between columns.  If null,
2148 **                      use white space.
2149 **              buf -- the output buffer.
2150 **              buflen -- the length of buf.
2151 **
2152 **      Returns:
2153 **              buf if successful.
2154 **              NULL otherwise.
2155 */
2156
2157 char *
2158 get_column(line, col, delim, buf, buflen)
2159         char line[];
2160         int col;
2161         int delim;
2162         char buf[];
2163         int buflen;
2164 {
2165         char *p;
2166         char *begin, *end;
2167         int i;
2168         char delimbuf[4];
2169
2170         if ((char) delim == '\0')
2171                 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2172         else
2173         {
2174                 delimbuf[0] = (char) delim;
2175                 delimbuf[1] = '\0';
2176         }
2177
2178         p = line;
2179         if (*p == '\0')
2180                 return NULL;                    /* line empty */
2181         if (*p == (char) delim && col == 0)
2182                 return NULL;                    /* first column empty */
2183
2184         begin = line;
2185
2186         if (col == 0 && (char) delim == '\0')
2187         {
2188                 while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2189                         begin++;
2190         }
2191
2192         for (i = 0; i < col; i++)
2193         {
2194                 if ((begin = strpbrk(begin, delimbuf)) == NULL)
2195                         return NULL;            /* no such column */
2196                 begin++;
2197                 if ((char) delim == '\0')
2198                 {
2199                         while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2200                                 begin++;
2201                 }
2202         }
2203
2204         end = strpbrk(begin, delimbuf);
2205         if (end == NULL)
2206                 i = strlen(begin);
2207         else
2208                 i = end - begin;
2209         if (i >= buflen)
2210                 i = buflen - 1;
2211         (void) sm_strlcpy(buf, begin, i + 1);
2212         return buf;
2213 }
2214 /*
2215 **  CLEANSTRCPY -- copy string keeping out bogus characters
2216 **
2217 **      Parameters:
2218 **              t -- "to" string.
2219 **              f -- "from" string.
2220 **              l -- length of space available in "to" string.
2221 **
2222 **      Returns:
2223 **              none.
2224 */
2225
2226 void
2227 cleanstrcpy(t, f, l)
2228         register char *t;
2229         register char *f;
2230         int l;
2231 {
2232         /* check for newlines and log if necessary */
2233         (void) denlstring(f, true, true);
2234
2235         if (l <= 0)
2236                 syserr("!cleanstrcpy: length == 0");
2237
2238         l--;
2239         while (l > 0 && *f != '\0')
2240         {
2241                 if (isascii(*f) &&
2242                     (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2243                 {
2244                         l--;
2245                         *t++ = *f;
2246                 }
2247                 f++;
2248         }
2249         *t = '\0';
2250 }
2251 /*
2252 **  DENLSTRING -- convert newlines in a string to spaces
2253 **
2254 **      Parameters:
2255 **              s -- the input string
2256 **              strict -- if set, don't permit continuation lines.
2257 **              logattacks -- if set, log attempted attacks.
2258 **
2259 **      Returns:
2260 **              A pointer to a version of the string with newlines
2261 **              mapped to spaces.  This should be copied.
2262 */
2263
2264 char *
2265 denlstring(s, strict, logattacks)
2266         char *s;
2267         bool strict;
2268         bool logattacks;
2269 {
2270         register char *p;
2271         int l;
2272         static char *bp = NULL;
2273         static int bl = 0;
2274
2275         p = s;
2276         while ((p = strchr(p, '\n')) != NULL)
2277                 if (strict || (*++p != ' ' && *p != '\t'))
2278                         break;
2279         if (p == NULL)
2280                 return s;
2281
2282         l = strlen(s) + 1;
2283         if (bl < l)
2284         {
2285                 /* allocate more space */
2286                 char *nbp = sm_pmalloc_x(l);
2287
2288                 if (bp != NULL)
2289                         sm_free(bp);
2290                 bp = nbp;
2291                 bl = l;
2292         }
2293         (void) sm_strlcpy(bp, s, l);
2294         for (p = bp; (p = strchr(p, '\n')) != NULL; )
2295                 *p++ = ' ';
2296
2297         if (logattacks)
2298         {
2299                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
2300                           "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2301                           RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2302                           shortenstring(bp, MAXSHORTSTR));
2303         }
2304
2305         return bp;
2306 }
2307
2308 /*
2309 **  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2310 **
2311 **      Parameters:
2312 **              s -- string to manipulate (in place)
2313 **              subst -- character to use as replacement
2314 **
2315 **      Returns:
2316 **              true iff string did not contain "unprintable" characters
2317 */
2318
2319 bool
2320 strreplnonprt(s, c)
2321         char *s;
2322         int c;
2323 {
2324         bool ok;
2325
2326         ok = true;
2327         if (s == NULL)
2328                 return ok;
2329         while (*s != '\0')
2330         {
2331                 if (!(isascii(*s) && isprint(*s)))
2332                 {
2333                         *s = c;
2334                         ok = false;
2335                 }
2336                 ++s;
2337         }
2338         return ok;
2339 }
2340
2341 /*
2342 **  STR2PRT -- convert "unprintable" characters in a string to \oct
2343 **
2344 **      Parameters:
2345 **              s -- string to convert
2346 **
2347 **      Returns:
2348 **              converted string.
2349 **              This is a static local buffer, string must be copied
2350 **              before this function is called again!
2351 */
2352
2353 char *
2354 str2prt(s)
2355         char *s;
2356 {
2357         int l;
2358         char c, *h;
2359         bool ok;
2360         static int len = 0;
2361         static char *buf = NULL;
2362
2363         if (s == NULL)
2364                 return NULL;
2365         ok = true;
2366         for (h = s, l = 1; *h != '\0'; h++, l++)
2367         {
2368                 if (*h == '\\')
2369                 {
2370                         ++l;
2371                         ok = false;
2372                 }
2373                 else if (!(isascii(*h) && isprint(*h)))
2374                 {
2375                         l += 3;
2376                         ok = false;
2377                 }
2378         }
2379         if (ok)
2380                 return s;
2381         if (l > len)
2382         {
2383                 char *nbuf = sm_pmalloc_x(l);
2384
2385                 if (buf != NULL)
2386                         sm_free(buf);
2387                 len = l;
2388                 buf = nbuf;
2389         }
2390         for (h = buf; *s != '\0' && l > 0; s++, l--)
2391         {
2392                 c = *s;
2393                 if (isascii(c) && isprint(c) && c != '\\')
2394                 {
2395                         *h++ = c;
2396                 }
2397                 else
2398                 {
2399                         *h++ = '\\';
2400                         --l;
2401                         switch (c)
2402                         {
2403                           case '\\':
2404                                 *h++ = '\\';
2405                                 break;
2406                           case '\t':
2407                                 *h++ = 't';
2408                                 break;
2409                           case '\n':
2410                                 *h++ = 'n';
2411                                 break;
2412                           case '\r':
2413                                 *h++ = 'r';
2414                                 break;
2415                           default:
2416                                 SM_ASSERT(l >= 2);
2417                                 (void) sm_snprintf(h, l, "%03o",
2418                                         (unsigned int)((unsigned char) c));
2419
2420                                 /*
2421                                 **  XXX since l is unsigned this may
2422                                 **  wrap around if the calculation is screwed
2423                                 **  up...
2424                                 */
2425
2426                                 l -= 2;
2427                                 h += 3;
2428                                 break;
2429                         }
2430                 }
2431         }
2432         *h = '\0';
2433         buf[len - 1] = '\0';
2434         return buf;
2435 }
2436 /*
2437 **  PATH_IS_DIR -- check to see if file exists and is a directory.
2438 **
2439 **      There are some additional checks for security violations in
2440 **      here.  This routine is intended to be used for the host status
2441 **      support.
2442 **
2443 **      Parameters:
2444 **              pathname -- pathname to check for directory-ness.
2445 **              createflag -- if set, create directory if needed.
2446 **
2447 **      Returns:
2448 **              true -- if the indicated pathname is a directory
2449 **              false -- otherwise
2450 */
2451
2452 bool
2453 path_is_dir(pathname, createflag)
2454         char *pathname;
2455         bool createflag;
2456 {
2457         struct stat statbuf;
2458
2459 #if HASLSTAT
2460         if (lstat(pathname, &statbuf) < 0)
2461 #else /* HASLSTAT */
2462         if (stat(pathname, &statbuf) < 0)
2463 #endif /* HASLSTAT */
2464         {
2465                 if (errno != ENOENT || !createflag)
2466                         return false;
2467                 if (mkdir(pathname, 0755) < 0)
2468                         return false;
2469                 return true;
2470         }
2471         if (!S_ISDIR(statbuf.st_mode))
2472         {
2473                 errno = ENOTDIR;
2474                 return false;
2475         }
2476
2477         /* security: don't allow writable directories */
2478         if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2479         {
2480                 errno = EACCES;
2481                 return false;
2482         }
2483         return true;
2484 }
2485 /*
2486 **  PROC_LIST_ADD -- add process id to list of our children
2487 **
2488 **      Parameters:
2489 **              pid -- pid to add to list.
2490 **              task -- task of pid.
2491 **              type -- type of process.
2492 **              count -- number of processes.
2493 **              other -- other information for this type.
2494 **
2495 **      Returns:
2496 **              none
2497 **
2498 **      Side Effects:
2499 **              May increase CurChildren. May grow ProcList.
2500 */
2501
2502 typedef struct procs    PROCS_T;
2503
2504 struct procs
2505 {
2506         pid_t           proc_pid;
2507         char            *proc_task;
2508         int             proc_type;
2509         int             proc_count;
2510         int             proc_other;
2511         SOCKADDR        proc_hostaddr;
2512 };
2513
2514 static PROCS_T  *volatile ProcListVec = NULL;
2515 static int      ProcListSize = 0;
2516
2517 void
2518 proc_list_add(pid, task, type, count, other, hostaddr)
2519         pid_t pid;
2520         char *task;
2521         int type;
2522         int count;
2523         int other;
2524         SOCKADDR *hostaddr;
2525 {
2526         int i;
2527
2528         for (i = 0; i < ProcListSize; i++)
2529         {
2530                 if (ProcListVec[i].proc_pid == NO_PID)
2531                         break;
2532         }
2533         if (i >= ProcListSize)
2534         {
2535                 /* probe the existing vector to avoid growing infinitely */
2536                 proc_list_probe();
2537
2538                 /* now scan again */
2539                 for (i = 0; i < ProcListSize; i++)
2540                 {
2541                         if (ProcListVec[i].proc_pid == NO_PID)
2542                                 break;
2543                 }
2544         }
2545         if (i >= ProcListSize)
2546         {
2547                 /* grow process list */
2548                 PROCS_T *npv;
2549
2550                 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2551                 npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) *
2552                                                (ProcListSize + PROC_LIST_SEG));
2553                 if (ProcListSize > 0)
2554                 {
2555                         memmove(npv, ProcListVec,
2556                                 ProcListSize * sizeof (PROCS_T));
2557                         sm_free(ProcListVec);
2558                 }
2559
2560                 /* XXX just use memset() to initialize this part? */
2561                 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2562                 {
2563                         npv[i].proc_pid = NO_PID;
2564                         npv[i].proc_task = NULL;
2565                         npv[i].proc_type = PROC_NONE;
2566                 }
2567                 i = ProcListSize;
2568                 ProcListSize += PROC_LIST_SEG;
2569                 ProcListVec = npv;
2570         }
2571         ProcListVec[i].proc_pid = pid;
2572         PSTRSET(ProcListVec[i].proc_task, task);
2573         ProcListVec[i].proc_type = type;
2574         ProcListVec[i].proc_count = count;
2575         ProcListVec[i].proc_other = other;
2576         if (hostaddr != NULL)
2577                 ProcListVec[i].proc_hostaddr = *hostaddr;
2578         else
2579                 memset(&ProcListVec[i].proc_hostaddr, 0,
2580                         sizeof(ProcListVec[i].proc_hostaddr));
2581
2582         /* if process adding itself, it's not a child */
2583         if (pid != CurrentPid)
2584         {
2585                 SM_ASSERT(CurChildren < INT_MAX);
2586                 CurChildren++;
2587         }
2588 }
2589 /*
2590 **  PROC_LIST_SET -- set pid task in process list
2591 **
2592 **      Parameters:
2593 **              pid -- pid to set
2594 **              task -- task of pid
2595 **
2596 **      Returns:
2597 **              none.
2598 */
2599
2600 void
2601 proc_list_set(pid, task)
2602         pid_t pid;
2603         char *task;
2604 {
2605         int i;
2606
2607         for (i = 0; i < ProcListSize; i++)
2608         {
2609                 if (ProcListVec[i].proc_pid == pid)
2610                 {
2611                         PSTRSET(ProcListVec[i].proc_task, task);
2612                         break;
2613                 }
2614         }
2615 }
2616 /*
2617 **  PROC_LIST_DROP -- drop pid from process list
2618 **
2619 **      Parameters:
2620 **              pid -- pid to drop
2621 **              st -- process status
2622 **              other -- storage for proc_other (return).
2623 **
2624 **      Returns:
2625 **              none.
2626 **
2627 **      Side Effects:
2628 **              May decrease CurChildren, CurRunners, or
2629 **              set RestartRequest or ShutdownRequest.
2630 **
2631 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2632 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2633 **              DOING.
2634 */
2635
2636 void
2637 proc_list_drop(pid, st, other)
2638         pid_t pid;
2639         int st;
2640         int *other;
2641 {
2642         int i;
2643         int type = PROC_NONE;
2644
2645         for (i = 0; i < ProcListSize; i++)
2646         {
2647                 if (ProcListVec[i].proc_pid == pid)
2648                 {
2649                         ProcListVec[i].proc_pid = NO_PID;
2650                         type = ProcListVec[i].proc_type;
2651                         if (other != NULL)
2652                                 *other = ProcListVec[i].proc_other;
2653                         if (CurChildren > 0)
2654                                 CurChildren--;
2655                         break;
2656                 }
2657         }
2658
2659
2660         if (type == PROC_CONTROL && WIFEXITED(st))
2661         {
2662                 /* if so, see if we need to restart or shutdown */
2663                 if (WEXITSTATUS(st) == EX_RESTART)
2664                         RestartRequest = "control socket";
2665                 else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2666                         ShutdownRequest = "control socket";
2667         }
2668         else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2669                  ProcListVec[i].proc_other > -1)
2670         {
2671                 /* restart this persistent runner */
2672                 mark_work_group_restart(ProcListVec[i].proc_other, st);
2673         }
2674         else if (type == PROC_QUEUE)
2675                 CurRunners -= ProcListVec[i].proc_count;
2676 }
2677 /*
2678 **  PROC_LIST_CLEAR -- clear the process list
2679 **
2680 **      Parameters:
2681 **              none.
2682 **
2683 **      Returns:
2684 **              none.
2685 **
2686 **      Side Effects:
2687 **              Sets CurChildren to zero.
2688 */
2689
2690 void
2691 proc_list_clear()
2692 {
2693         int i;
2694
2695         /* start from 1 since 0 is the daemon itself */
2696         for (i = 1; i < ProcListSize; i++)
2697                 ProcListVec[i].proc_pid = NO_PID;
2698         CurChildren = 0;
2699 }
2700 /*
2701 **  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2702 **
2703 **      Parameters:
2704 **              none
2705 **
2706 **      Returns:
2707 **              none
2708 **
2709 **      Side Effects:
2710 **              May decrease CurChildren.
2711 */
2712
2713 void
2714 proc_list_probe()
2715 {
2716         int i, children;
2717         int chldwasblocked;
2718         pid_t pid;
2719
2720         children = 0;
2721         chldwasblocked = sm_blocksignal(SIGCHLD);
2722
2723         /* start from 1 since 0 is the daemon itself */
2724         for (i = 1; i < ProcListSize; i++)
2725         {
2726                 pid = ProcListVec[i].proc_pid;
2727                 if (pid == NO_PID || pid == CurrentPid)
2728                         continue;
2729                 if (kill(pid, 0) < 0)
2730                 {
2731                         if (LogLevel > 3)
2732                                 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2733                                           "proc_list_probe: lost pid %d",
2734                                           (int) ProcListVec[i].proc_pid);
2735                         ProcListVec[i].proc_pid = NO_PID;
2736                         SM_FREE_CLR(ProcListVec[i].proc_task);
2737                         CurChildren--;
2738                 }
2739                 else
2740                 {
2741                         ++children;
2742                 }
2743         }
2744         if (CurChildren < 0)
2745                 CurChildren = 0;
2746         if (chldwasblocked == 0)
2747                 (void) sm_releasesignal(SIGCHLD);
2748         if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
2749         {
2750                 sm_syslog(LOG_ERR, NOQID,
2751                           "proc_list_probe: found %d children, expected %d",
2752                           children, CurChildren);
2753         }
2754 }
2755
2756 /*
2757 **  PROC_LIST_DISPLAY -- display the process list
2758 **
2759 **      Parameters:
2760 **              out -- output file pointer
2761 **              prefix -- string to output in front of each line.
2762 **
2763 **      Returns:
2764 **              none.
2765 */
2766
2767 void
2768 proc_list_display(out, prefix)
2769         SM_FILE_T *out;
2770         char *prefix;
2771 {
2772         int i;
2773
2774         for (i = 0; i < ProcListSize; i++)
2775         {
2776                 if (ProcListVec[i].proc_pid == NO_PID)
2777                         continue;
2778
2779                 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2780                                      prefix,
2781                                      (int) ProcListVec[i].proc_pid,
2782                                      ProcListVec[i].proc_task != NULL ?
2783                                      ProcListVec[i].proc_task : "(unknown)",
2784                                      (OpMode == MD_SMTP ||
2785                                       OpMode == MD_DAEMON ||
2786                                       OpMode == MD_ARPAFTP) ? "\r" : "");
2787         }
2788 }
2789
2790 /*
2791 **  PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2792 **
2793 **      Parameters:
2794 **              type -- type of process to signal
2795 **              signal -- the type of signal to send
2796 **
2797 **      Results:
2798 **              none.
2799 **
2800 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2801 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2802 **              DOING.
2803 */
2804
2805 void
2806 proc_list_signal(type, signal)
2807         int type;
2808         int signal;
2809 {
2810         int chldwasblocked;
2811         int alrmwasblocked;
2812         int i;
2813         pid_t mypid = getpid();
2814
2815         /* block these signals so that we may signal cleanly */
2816         chldwasblocked = sm_blocksignal(SIGCHLD);
2817         alrmwasblocked = sm_blocksignal(SIGALRM);
2818
2819         /* Find all processes of type and send signal */
2820         for (i = 0; i < ProcListSize; i++)
2821         {
2822                 if (ProcListVec[i].proc_pid == NO_PID ||
2823                     ProcListVec[i].proc_pid == mypid)
2824                         continue;
2825                 if (ProcListVec[i].proc_type != type)
2826                         continue;
2827                 (void) kill(ProcListVec[i].proc_pid, signal);
2828         }
2829
2830         /* restore the signals */
2831         if (alrmwasblocked == 0)
2832                 (void) sm_releasesignal(SIGALRM);
2833         if (chldwasblocked == 0)
2834                 (void) sm_releasesignal(SIGCHLD);
2835 }
2836
2837 /*
2838 **  COUNT_OPEN_CONNECTIONS
2839 **
2840 **      Parameters:
2841 **              hostaddr - ClientAddress
2842 **
2843 **      Returns:
2844 **              the number of open connections for this client
2845 **
2846 */
2847
2848 int
2849 count_open_connections(hostaddr)
2850         SOCKADDR *hostaddr;
2851 {
2852         int i, n;
2853
2854         if (hostaddr == NULL)
2855                 return 0;
2856         n = 0;
2857         for (i = 0; i < ProcListSize; i++)
2858         {
2859                 if (ProcListVec[i].proc_pid == NO_PID)
2860                         continue;
2861                 if (hostaddr->sa.sa_family !=
2862                     ProcListVec[i].proc_hostaddr.sa.sa_family)
2863                         continue;
2864 #if NETINET
2865                 if (hostaddr->sa.sa_family == AF_INET &&
2866                     (hostaddr->sin.sin_addr.s_addr ==
2867                      ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
2868                         n++;
2869 #endif /* NETINET */
2870 #if NETINET6
2871                 if (hostaddr->sa.sa_family == AF_INET6 &&
2872                     IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
2873                                        &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
2874                         n++;
2875 #endif /* NETINET6 */
2876         }
2877         return n;
2878 }