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