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