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