1 /*
2 * Copyright (c) 1998-2004, 2006, 2007 Proofpoint, 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 #include <sm/sendmail.h>
16 #include <sm/ixlen.h>
17
18 SM_RCSID("@(#)$Id: headers.c,v 8.320 2013-11-22 20:51:55 ca Exp $")
19
20 static HDR *allocheader __P((char *, char *, int, SM_RPOOL_T *, bool));
21 static size_t fix_mime_header __P((HDR *, ENVELOPE *));
22 static int priencode __P((char *));
23 static bool put_vanilla_header __P((HDR *, char *, MCI *));
24
25 /*
26 ** SETUPHEADERS -- initialize headers in symbol table
27 **
28 ** Parameters:
29 ** none
30 **
31 ** Returns:
32 ** none
33 */
34
35 void
setupheaders()36 setupheaders()
37 {
38 struct hdrinfo *hi;
39 STAB *s;
40
41 for (hi = HdrInfo; hi->hi_field != NULL; hi++)
42 {
43 s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
44 s->s_header.hi_flags = hi->hi_flags;
45 s->s_header.hi_ruleset = NULL;
46 }
47 }
48
49 /*
50 ** DOCHOMPHEADER -- process and save a header line.
51 **
52 ** Called by chompheader.
53 **
54 ** Parameters:
55 ** line -- header as a text line.
56 ** pflag -- flags for chompheader() (from sendmail.h)
57 ** hdrp -- a pointer to the place to save the header.
58 ** e -- the envelope including this header.
59 **
60 ** Returns:
61 ** flags for this header.
62 **
63 ** Side Effects:
64 ** The header is saved on the header list.
65 ** Contents of 'line' are destroyed.
66 */
67
68 static struct hdrinfo NormalHeader = { NULL, 0, NULL };
69 static unsigned long dochompheader __P((char *, int, HDR **, ENVELOPE *));
70
71 static unsigned long
dochompheader(line,pflag,hdrp,e)72 dochompheader(line, pflag, hdrp, e)
73 char *line;
74 int pflag;
75 HDR **hdrp;
76 ENVELOPE *e;
77 {
78 unsigned char mid = '\0';
79 register char *p;
80 register HDR *h;
81 HDR **hp;
82 char *fname;
83 char *fvalue;
84 bool cond = false;
85 bool dropfrom;
86 bool headeronly;
87 STAB *s;
88 struct hdrinfo *hi;
89 bool nullheader = false;
90 BITMAP256 mopts;
91
92 headeronly = hdrp != NULL;
93 if (!headeronly)
94 hdrp = &e->e_header;
95
96 /* strip off options */
97 clrbitmap(mopts);
98 p = line;
99 if (!bitset(pflag, CHHDR_USER) && *p == '?')
100 {
101 int c;
102 register char *q;
103
104 q = strchr(++p, '?');
105 if (q == NULL)
106 goto hse;
107
108 *q = '\0';
109 c = *p & 0377;
110
111 /* possibly macro conditional */
112 if (c == MACROEXPAND)
113 {
114 /* catch ?$? */
115 if (*++p == '\0')
116 {
117 *q = '?';
118 goto hse;
119 }
120
121 mid = (unsigned char) *p++;
122
123 /* catch ?$abc? */
124 if (*p != '\0')
125 {
126 *q = '?';
127 goto hse;
128 }
129 }
130 else if (*p == '$')
131 {
132 /* catch ?$? */
133 if (*++p == '\0')
134 {
135 *q = '?';
136 goto hse;
137 }
138
139 mid = (unsigned char) macid(p);
140 if (bitset(0200, mid))
141 {
142 p += strlen(macname(mid)) + 2;
143 SM_ASSERT(p <= q);
144 }
145 else
146 p++;
147
148 /* catch ?$abc? */
149 if (*p != '\0')
150 {
151 *q = '?';
152 goto hse;
153 }
154 }
155 else
156 {
157 while (*p != '\0')
158 {
159 if (!isascii(*p))
160 {
161 *q = '?';
162 goto hse;
163 }
164
165 setbitn(bitidx(*p), mopts);
166 cond = true;
167 p++;
168 }
169 }
170 p = q + 1;
171 }
172
173 /* find canonical name */
174 fname = p;
175 while (isascii(*p) && isgraph(*p) && *p != ':')
176 p++;
177 fvalue = p;
178 while (SM_ISSPACE(*p))
179 p++;
180 if (*p++ != ':' || fname == fvalue)
181 {
182 hse:
183 syserr("553 5.3.0 header syntax error, line \"%s\"", line);
184 return 0;
185 }
186 *fvalue = '\0';
187 fvalue = p;
188
189 /* if the field is null, go ahead and use the default */
190 while (SM_ISSPACE(*p))
191 p++;
192 if (*p == '\0')
193 nullheader = true;
194
195 /* security scan: long field names are end-of-header */
196 if (strlen(fname) > 100)
197 return H_EOH;
198
199 /* check to see if it represents a ruleset call */
200 if (bitset(pflag, CHHDR_DEF))
201 {
202 char hbuf[50];
203
204 (void) expand(fvalue, hbuf, sizeof(hbuf), e);
205 for (p = hbuf; SM_ISSPACE(*p); )
206 p++;
207 if ((*p++ & 0377) == CALLSUBR)
208 {
209 auto char *endp;
210 bool strc;
211
212 strc = *p == '+'; /* strip comments? */
213 if (strc)
214 ++p;
215 if (strtorwset(p, &endp, ST_ENTER) > 0)
216 {
217 *endp = '\0';
218 s = stab(fname, ST_HEADER, ST_ENTER);
219 if (LogLevel > 9 &&
220 s->s_header.hi_ruleset != NULL)
221 sm_syslog(LOG_WARNING, NOQID,
222 "Warning: redefined ruleset for header=%s, old=%s, new=%s",
223 fname,
224 s->s_header.hi_ruleset, p);
225 s->s_header.hi_ruleset = newstr(p);
226 if (!strc)
227 s->s_header.hi_flags |= H_STRIPCOMM;
228 }
229 return 0;
230 }
231 }
232
233 /* see if it is a known type */
234 s = stab(fname, ST_HEADER, ST_FIND);
235 if (s != NULL)
236 hi = &s->s_header;
237 else
238 hi = &NormalHeader;
239
240 if (tTd(31, 9))
241 {
242 if (s == NULL)
243 sm_dprintf("no header flags match\n");
244 else
245 sm_dprintf("header match, flags=%lx, ruleset=%s\n",
246 hi->hi_flags,
247 hi->hi_ruleset == NULL ? "<NULL>"
248 : hi->hi_ruleset);
249 }
250
251 /* see if this is a resent message */
252 if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
253 bitset(H_RESENT, hi->hi_flags))
254 e->e_flags |= EF_RESENT;
255
256 /* if this is an Errors-To: header keep track of it now */
257 if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly &&
258 bitset(H_ERRORSTO, hi->hi_flags))
259 (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
260
261 /* if this means "end of header" quit now */
262 if (!headeronly && bitset(H_EOH, hi->hi_flags))
263 return hi->hi_flags;
264
265 /*
266 ** Horrible hack to work around problem with Lotus Notes SMTP
267 ** mail gateway, which generates From: headers with newlines in
268 ** them and the <address> on the second line. Although this is
269 ** legal RFC 822, many MUAs don't handle this properly and thus
270 ** never find the actual address.
271 */
272
273 if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
274 {
275 while ((p = strchr(fvalue, '\n')) != NULL)
276 *p = ' ';
277 }
278
279 /*
280 ** If there is a check ruleset, verify it against the header.
281 */
282
283 if (bitset(pflag, CHHDR_CHECK))
284 {
285 int rscheckflags;
286 char *rs;
287
288 rscheckflags = RSF_COUNT;
289 if (!bitset(hi->hi_flags, H_FROM|H_RCPT))
290 rscheckflags |= RSF_UNSTRUCTURED;
291
292 /* no ruleset? look for default */
293 rs = hi->hi_ruleset;
294 if (rs == NULL)
295 {
296 s = stab("*", ST_HEADER, ST_FIND);
297 if (s != NULL)
298 {
299 rs = (&s->s_header)->hi_ruleset;
300 if (bitset((&s->s_header)->hi_flags,
301 H_STRIPCOMM))
302 rscheckflags |= RSF_RMCOMM;
303 }
304 }
305 else if (bitset(hi->hi_flags, H_STRIPCOMM))
306 rscheckflags |= RSF_RMCOMM;
307 if (rs != NULL)
308 {
309 int l, k;
310 char qval[MAXNAME_I];
311 XLENDECL
312
313 l = 0;
314 XLEN('"');
315 qval[l++] = '"';
316
317 /* - 3 to avoid problems with " at the end */
318 for (k = 0;
319 fvalue[k] != '\0' && l < sizeof(qval) - 3
320 && xlen < MAXNAME - 3;
321 k++)
322 {
323 XLEN(fvalue[k]);
324 switch (fvalue[k])
325 {
326 /* XXX other control chars? */
327 case '\011': /* ht */
328 case '\012': /* nl */
329 case '\013': /* vt */
330 case '\014': /* np */
331 case '\015': /* cr */
332 qval[l++] = ' ';
333 break;
334 case '\\':
335 qval[l++] = fvalue[k];
336 ++k;
337 XLEN(fvalue[k]);
338 qval[l++] = fvalue[k];
339 break;
340 case '"':
341 XLEN('\\');
342 qval[l++] = '\\';
343 /* FALLTHROUGH */
344 default:
345 qval[l++] = fvalue[k];
346 break;
347 }
348 }
349 /* just for "completeness": xlen not used afterwards */
350 XLEN('"');
351 qval[l++] = '"';
352 qval[l] = '\0';
353 l = strlen(fvalue + k);
354
355 /*
356 ** If there is something left in fvalue
357 ** then it has been truncated.
358 ** Note: the log entry might not be correct
359 ** in the EAI case: to get the "real" length
360 ** ilenx() would have to be applied to fvalue.
361 */
362
363 if (l > 0)
364 {
365 if (LogLevel > 9)
366 sm_syslog(LOG_WARNING, e->e_id,
367 "Warning: truncated header '%s' before check with '%s' len=%d max=%d",
368 fname, rs, xlen + l, MAXNAME);
369 }
370 macdefine(&e->e_macro, A_TEMP,
371 macid("{currHeader}"), qval);
372 macdefine(&e->e_macro, A_TEMP,
373 macid("{hdr_name}"), fname);
374
375 (void) sm_snprintf(qval, sizeof(qval), "%d", k);
376 macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval);
377 if (bitset(H_FROM, hi->hi_flags))
378 macdefine(&e->e_macro, A_PERM,
379 macid("{addr_type}"), "h s");
380 else if (bitset(H_RCPT, hi->hi_flags))
381 macdefine(&e->e_macro, A_PERM,
382 macid("{addr_type}"), "h r");
383 else
384 macdefine(&e->e_macro, A_PERM,
385 macid("{addr_type}"), "h");
386 (void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3,
387 NULL, e->e_id, NULL, NULL);
388 }
389 }
390
391 /*
392 ** Drop explicit From: if same as what we would generate.
393 ** This is to make MH (which doesn't always give a full name)
394 ** insert the full name information in all circumstances.
395 */
396
397 dropfrom = false;
398 p = "resent-from";
399 if (!bitset(EF_RESENT, e->e_flags))
400 p += 7;
401 if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
402 !bitset(EF_QUEUERUN, e->e_flags) && SM_STRCASEEQ(fname, p))
403 {
404 if (e->e_from.q_paddr != NULL &&
405 e->e_from.q_mailer != NULL &&
406 bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
407 (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
408 strcmp(fvalue, e->e_from.q_user) == 0))
409 dropfrom = true;
410 if (tTd(31, 2))
411 {
412 sm_dprintf("comparing header from (%s) against default (%s or %s), drop=%d\n",
413 fvalue, e->e_from.q_paddr, e->e_from.q_user,
414 dropfrom);
415 }
416 }
417
418 /* delete default value for this header */
419 for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
420 {
421 if (SM_STRCASEEQ(fname, h->h_field) &&
422 !bitset(H_USER, h->h_flags) &&
423 !bitset(H_FORCE, h->h_flags))
424 {
425 if (nullheader)
426 {
427 /* user-supplied value was null */
428 return 0;
429 }
430 if (dropfrom)
431 {
432 /* make this look like the user entered it */
433 h->h_flags |= H_USER;
434
435 /*
436 ** If the MH hack is selected, allow to turn
437 ** it off via a mailer flag to avoid problems
438 ** with setups that remove the F flag from
439 ** the RCPT mailer.
440 */
441
442 if (bitnset(M_NOMHHACK,
443 e->e_from.q_mailer->m_flags))
444 {
445 h->h_flags &= ~H_CHECK;
446 }
447 return hi->hi_flags;
448 }
449 h->h_value = NULL;
450 if (!cond)
451 {
452 /* copy conditions from default case */
453 memmove((char *) mopts, (char *) h->h_mflags,
454 sizeof(mopts));
455 }
456 h->h_macro = mid;
457 }
458 }
459
460 /* create a new node */
461 h = (HDR *) sm_rpool_malloc_tagged_x(e->e_rpool, sizeof(*h), "header",
462 pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
463 h->h_field = sm_rpool_strdup_tagged_x(e->e_rpool, fname, "h_field",
464 pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
465 h->h_value = sm_rpool_strdup_tagged_x(e->e_rpool, fvalue, "h_value",
466 pflag, bitset(pflag, CHHDR_DEF) ? 0 : 1);
467 h->h_link = NULL;
468 memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts));
469 h->h_macro = mid;
470 *hp = h;
471 h->h_flags = hi->hi_flags;
472 if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE))
473 h->h_flags |= H_USER;
474
475 /* strip EOH flag if parsing MIME headers */
476 if (headeronly)
477 h->h_flags &= ~H_EOH;
478 if (bitset(pflag, CHHDR_DEF))
479 h->h_flags |= H_DEFAULT;
480 if (cond || mid != '\0')
481 h->h_flags |= H_CHECK;
482
483 /* hack to see if this is a new format message */
484 if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
485 bitset(H_RCPT|H_FROM, h->h_flags) &&
486 (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
487 strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
488 {
489 e->e_flags &= ~EF_OLDSTYLE;
490 }
491
492 return h->h_flags;
493 }
494
495 /*
496 ** CHOMPHEADER -- process and save a header line.
497 **
498 ** Called by collect, readcf, and readqf to deal with header lines.
499 ** This is just a wrapper for dochompheader().
500 **
501 ** Parameters:
502 ** line -- header as a text line.
503 ** pflag -- flags for chompheader() (from sendmail.h)
504 ** hdrp -- a pointer to the place to save the header.
505 ** e -- the envelope including this header.
506 **
507 ** Returns:
508 ** flags for this header.
509 **
510 ** Side Effects:
511 ** The header is saved on the header list.
512 ** Contents of 'line' are destroyed.
513 */
514
515 unsigned long
chompheader(line,pflag,hdrp,e)516 chompheader(line, pflag, hdrp, e)
517 char *line;
518 int pflag;
519 HDR **hdrp;
520 register ENVELOPE *e;
521 {
522 unsigned long rval;
523
524 if (tTd(31, 6))
525 {
526 sm_dprintf("chompheader: ");
527 xputs(sm_debug_file(), line);
528 sm_dprintf("\n");
529 }
530
531 /* quote this if user (not config file) input */
532 if (bitset(pflag, CHHDR_USER))
533 {
534 char xbuf[MAXLINE]; /* EAI:ok; actual buffer might be greater */
535 char *xbp = NULL;
536 int xbufs;
537
538 xbufs = sizeof(xbuf);
539 xbp = quote_internal_chars(line, xbuf, &xbufs, NULL);
540 if (tTd(31, 7))
541 {
542 sm_dprintf("chompheader: quoted: ");
543 xputs(sm_debug_file(), xbp);
544 sm_dprintf("\n");
545 }
546 rval = dochompheader(xbp, pflag, hdrp, e);
547 if (xbp != xbuf)
548 sm_free(xbp);
549 }
550 else
551 rval = dochompheader(line, pflag, hdrp, e);
552
553 return rval;
554 }
555
556 /*
557 ** ALLOCHEADER -- allocate a header entry
558 **
559 ** Parameters:
560 ** field -- the name of the header field (will not be copied).
561 ** value -- the value of the field (will be copied).
562 ** flags -- flags to add to h_flags.
563 ** rp -- resource pool for allocations
564 ** space -- add leading space?
565 **
566 ** Returns:
567 ** Pointer to a newly allocated and populated HDR.
568 **
569 ** Notes:
570 ** o field and value must be in internal format, i.e.,
571 ** metacharacters must be "quoted", see quote_internal_chars().
572 ** o maybe add more flags to decide:
573 ** - what to copy (field/value)
574 ** - whether to convert value to an internal format
575 */
576
577 static HDR *
allocheader(field,value,flags,rp,space)578 allocheader(field, value, flags, rp, space)
579 char *field;
580 char *value;
581 int flags;
582 SM_RPOOL_T *rp;
583 bool space;
584 {
585 HDR *h;
586 STAB *s;
587
588 /* find info struct */
589 s = stab(field, ST_HEADER, ST_FIND);
590
591 /* allocate space for new header */
592 h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h));
593 h->h_field = field;
594 if (space)
595 {
596 size_t l;
597 char *n;
598
599 l = strlen(value);
600 SM_ASSERT(l + 2 > l);
601 n = sm_rpool_malloc_x(rp, l + 2);
602 n[0] = ' ';
603 n[1] = '\0';
604 sm_strlcpy(n + 1, value, l + 1);
605 h->h_value = n;
606 }
607 else
608 h->h_value = sm_rpool_strdup_x(rp, value);
609 h->h_flags = flags;
610 if (s != NULL)
611 h->h_flags |= s->s_header.hi_flags;
612 clrbitmap(h->h_mflags);
613 h->h_macro = '\0';
614
615 return h;
616 }
617
618 /*
619 ** ADDHEADER -- add a header entry to the end of the queue.
620 **
621 ** This bypasses the special checking of chompheader.
622 **
623 ** Parameters:
624 ** field -- the name of the header field (will not be copied).
625 ** value -- the value of the field (will be copied).
626 ** flags -- flags to add to h_flags.
627 ** e -- envelope.
628 ** space -- add leading space?
629 **
630 ** Returns:
631 ** none.
632 **
633 ** Side Effects:
634 ** adds the field on the list of headers for this envelope.
635 **
636 ** Notes: field and value must be in internal format, i.e.,
637 ** metacharacters must be "quoted", see quote_internal_chars().
638 */
639
640 void
addheader(field,value,flags,e,space)641 addheader(field, value, flags, e, space)
642 char *field;
643 char *value;
644 int flags;
645 ENVELOPE *e;
646 bool space;
647 {
648 register HDR *h;
649 HDR **hp;
650 HDR **hdrlist = &e->e_header;
651
652 /* find current place in list -- keep back pointer? */
653 for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
654 {
655 if (SM_STRCASEEQ(field, h->h_field))
656 break;
657 }
658
659 /* allocate space for new header */
660 h = allocheader(field, value, flags, e->e_rpool, space);
661 h->h_link = *hp;
662 *hp = h;
663 }
664
665 /*
666 ** INSHEADER -- insert a header entry at the specified index
667 ** This bypasses the special checking of chompheader.
668 **
669 ** Parameters:
670 ** idx -- index into the header list at which to insert
671 ** field -- the name of the header field (will be copied).
672 ** value -- the value of the field (will be copied).
673 ** flags -- flags to add to h_flags.
674 ** e -- envelope.
675 ** space -- add leading space?
676 **
677 ** Returns:
678 ** none.
679 **
680 ** Side Effects:
681 ** inserts the field on the list of headers for this envelope.
682 **
683 ** Notes:
684 ** - field and value must be in internal format, i.e.,
685 ** metacharacters must be "quoted", see quote_internal_chars().
686 ** - the header list contains headers that might not be
687 ** sent "out" (see putheader(): "skip"), hence there is no
688 ** reliable way to insert a header at an exact position
689 ** (except at the front or end).
690 */
691
692 void
insheader(idx,field,value,flags,e,space)693 insheader(idx, field, value, flags, e, space)
694 int idx;
695 char *field;
696 char *value;
697 int flags;
698 ENVELOPE *e;
699 bool space;
700 {
701 HDR *h, *srch, *last = NULL;
702
703 /* allocate space for new header */
704 h = allocheader(field, value, flags, e->e_rpool, space);
705
706 /* find insertion position */
707 for (srch = e->e_header; srch != NULL && idx > 0;
708 srch = srch->h_link, idx--)
709 last = srch;
710
711 if (e->e_header == NULL)
712 {
713 e->e_header = h;
714 h->h_link = NULL;
715 }
716 else if (srch == NULL)
717 {
718 SM_ASSERT(last != NULL);
719 last->h_link = h;
720 h->h_link = NULL;
721 }
722 else
723 {
724 h->h_link = srch->h_link;
725 srch->h_link = h;
726 }
727 }
728
729 /*
730 ** HVALUE -- return value of a header.
731 **
732 ** Only "real" fields (i.e., ones that have not been supplied
733 ** as a default) are used.
734 **
735 ** Parameters:
736 ** field -- the field name.
737 ** header -- the header list.
738 **
739 ** Returns:
740 ** pointer to the value part (internal format).
741 ** NULL if not found.
742 **
743 ** Side Effects:
744 ** none.
745 */
746
747 char *
hvalue(field,header)748 hvalue(field, header)
749 char *field;
750 HDR *header;
751 {
752 register HDR *h;
753
754 for (h = header; h != NULL; h = h->h_link)
755 {
756 if (!bitset(H_DEFAULT, h->h_flags) &&
757 SM_STRCASEEQ(h->h_field, field))
758 {
759 char *s;
760
761 s = h->h_value;
762 if (s == NULL)
763 return NULL;
764 while (SM_ISSPACE(*s))
765 s++;
766 return s;
767 }
768 }
769 return NULL;
770 }
771
772 /*
773 ** ISHEADER -- predicate telling if argument is a header.
774 **
775 ** A line is a header if it has a single word followed by
776 ** optional white space followed by a colon.
777 **
778 ** Header fields beginning with two dashes, although technically
779 ** permitted by RFC822, are automatically rejected in order
780 ** to make MIME work out. Without this we could have a technically
781 ** legal header such as ``--"foo:bar"'' that would also be a legal
782 ** MIME separator.
783 **
784 ** Parameters:
785 ** h -- string to check for possible headerness.
786 **
787 ** Returns:
788 ** true if h is a header.
789 ** false otherwise.
790 **
791 ** Side Effects:
792 ** none.
793 */
794
795 bool
isheader(h)796 isheader(h)
797 char *h;
798 {
799 char *s;
800
801 s = h;
802 if (s[0] == '-' && s[1] == '-')
803 return false;
804
805 while (*s > ' ' && *s != ':' && *s != '\0')
806 s++;
807
808 if (h == s)
809 return false;
810
811 /* following technically violates RFC822 */
812 while (SM_ISSPACE(*s))
813 s++;
814
815 return (*s == ':');
816 }
817
818 /*
819 ** EATHEADER -- run through the stored header and extract info.
820 **
821 ** Parameters:
822 ** e -- the envelope to process.
823 ** full -- if set, do full processing (e.g., compute
824 ** message priority). This should not be set
825 ** when reading a queue file because some info
826 ** needed to compute the priority is wrong.
827 ** log -- call logsender()?
828 **
829 ** Returns:
830 ** none.
831 **
832 ** Side Effects:
833 ** Sets a bunch of global variables from information
834 ** in the collected header.
835 */
836
837 void
eatheader(e,full,log)838 eatheader(e, full, log)
839 register ENVELOPE *e;
840 bool full;
841 bool log;
842 {
843 register HDR *h;
844 register char *p;
845 int hopcnt = 0;
846 char buf[MAXLINE];
847
848 /*
849 ** Set up macros for possible expansion in headers.
850 */
851
852 macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
853 macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
854 if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
855 macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt);
856 else
857 macdefine(&e->e_macro, A_PERM, 'u', NULL);
858
859 /* full name of from person */
860 p = hvalue("full-name", e->e_header);
861 if (p != NULL)
862 {
863 if (!rfc822_string(p))
864 {
865 /*
866 ** Quote a full name with special characters
867 ** as a comment so crackaddr() doesn't destroy
868 ** the name portion of the address.
869 */
870
871 p = addquotes(p, e->e_rpool);
872 }
873 macdefine(&e->e_macro, A_PERM, 'x', p);
874 }
875
876 if (tTd(32, 1))
877 sm_dprintf("----- collected header -----\n");
878 e->e_msgid = NULL;
879 for (h = e->e_header; h != NULL; h = h->h_link)
880 {
881 if (tTd(32, 1))
882 sm_dprintf("%s:", h->h_field);
883 if (h->h_value == NULL)
884 {
885 if (tTd(32, 1))
886 sm_dprintf("<NULL>\n");
887 continue;
888 }
889
890 /* do early binding */
891 if (bitset(H_DEFAULT, h->h_flags) &&
892 !bitset(H_BINDLATE, h->h_flags))
893 {
894 if (tTd(32, 1))
895 {
896 sm_dprintf("(");
897 xputs(sm_debug_file(), h->h_value);
898 sm_dprintf(") ");
899 }
900 expand(h->h_value, buf, sizeof(buf), e);
901 if (buf[0] != '\0' &&
902 (buf[0] != ' ' || buf[1] != '\0'))
903 {
904 if (bitset(H_FROM, h->h_flags))
905 expand(crackaddr(buf, e),
906 buf, sizeof(buf), e);
907 h->h_value = sm_rpool_strdup_x(e->e_rpool, buf);
908 h->h_flags &= ~H_DEFAULT;
909 }
910 }
911 if (tTd(32, 1))
912 {
913 xputs(sm_debug_file(), h->h_value);
914 sm_dprintf("\n");
915 }
916
917 /* count the number of times it has been processed */
918 if (bitset(H_TRACE, h->h_flags))
919 hopcnt++;
920
921 /* send to this person if we so desire */
922 if (GrabTo && bitset(H_RCPT, h->h_flags) &&
923 !bitset(H_DEFAULT, h->h_flags) &&
924 (!bitset(EF_RESENT, e->e_flags) ||
925 bitset(H_RESENT, h->h_flags)))
926 {
927 #if 0
928 int saveflags = e->e_flags;
929 #endif
930
931 (void) sendtolist(denlstring(h->h_value, true, false),
932 NULLADDR, &e->e_sendqueue, 0, e);
933
934 #if 0
935 /*
936 ** Change functionality so a fatal error on an
937 ** address doesn't affect the entire envelope.
938 */
939
940 /* delete fatal errors generated by this address */
941 if (!bitset(EF_FATALERRS, saveflags))
942 e->e_flags &= ~EF_FATALERRS;
943 #endif /* 0 */
944 }
945
946 /* save the message-id for logging */
947 p = "resent-message-id";
948 if (!bitset(EF_RESENT, e->e_flags))
949 p += 7;
950 if (SM_STRCASEEQ(h->h_field, p))
951 {
952 e->e_msgid = h->h_value;
953 while (SM_ISSPACE(*e->e_msgid))
954 e->e_msgid++;
955 macdefine(&e->e_macro, A_PERM, macid("{msg_id}"),
956 e->e_msgid);
957 }
958 }
959 if (tTd(32, 1))
960 sm_dprintf("----------------------------\n");
961
962 /* if we are just verifying (that is, sendmail -t -bv), drop out now */
963 if (OpMode == MD_VERIFY)
964 return;
965
966 /* store hop count */
967 if (hopcnt > e->e_hopcount)
968 {
969 e->e_hopcount = hopcnt;
970 (void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount);
971 macdefine(&e->e_macro, A_TEMP, 'c', buf);
972 }
973
974 /* message priority */
975 p = hvalue("precedence", e->e_header);
976 if (p != NULL)
977 e->e_class = priencode(p);
978 if (e->e_class < 0)
979 e->e_timeoutclass = TOC_NONURGENT;
980 else if (e->e_class > 0)
981 e->e_timeoutclass = TOC_URGENT;
982 if (full)
983 {
984 e->e_msgpriority = e->e_msgsize
985 - e->e_class * WkClassFact
986 + e->e_nrcpts * WkRecipFact;
987 }
988
989 /* check for DSN to properly set e_timeoutclass */
990 p = hvalue("content-type", e->e_header);
991 if (p != NULL)
992 {
993 bool oldsupr;
994 char **pvp;
995 char pvpbuf[MAXLINE];
996 extern unsigned char MimeTokenTab[256];
997
998 /* tokenize header */
999 oldsupr = SuprErrs;
1000 SuprErrs = true;
1001 pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
1002 MimeTokenTab, false);
1003 SuprErrs = oldsupr;
1004
1005 /* Check if multipart/report */
1006 if (pvp != NULL && pvp[0] != NULL &&
1007 pvp[1] != NULL && pvp[2] != NULL &&
1008 SM_STRCASEEQ(*pvp++, "multipart") &&
1009 strcmp(*pvp++, "/") == 0 &&
1010 SM_STRCASEEQ(*pvp++, "report"))
1011 {
1012 /* Look for report-type=delivery-status */
1013 while (*pvp != NULL)
1014 {
1015 /* skip to semicolon separator */
1016 while (*pvp != NULL && strcmp(*pvp, ";") != 0)
1017 pvp++;
1018
1019 /* skip semicolon */
1020 if (*pvp++ == NULL || *pvp == NULL)
1021 break;
1022
1023 /* look for report-type */
1024 if (!SM_STRCASEEQ(*pvp++, "report-type"))
1025 continue;
1026
1027 /* skip equal */
1028 if (*pvp == NULL || strcmp(*pvp, "=") != 0)
1029 continue;
1030
1031 /* check value */
1032 if (*++pvp != NULL &&
1033 SM_STRCASEEQ(*pvp, "delivery-status"))
1034 e->e_timeoutclass = TOC_DSN;
1035
1036 /* found report-type, no need to continue */
1037 break;
1038 }
1039 }
1040 }
1041
1042 /* message timeout priority */
1043 p = hvalue("priority", e->e_header);
1044 if (p != NULL)
1045 {
1046 /* (this should be in the configuration file) */
1047 if (SM_STRCASEEQ(p, "urgent"))
1048 e->e_timeoutclass = TOC_URGENT;
1049 else if (SM_STRCASEEQ(p, "normal"))
1050 e->e_timeoutclass = TOC_NORMAL;
1051 else if (SM_STRCASEEQ(p, "non-urgent"))
1052 e->e_timeoutclass = TOC_NONURGENT;
1053 else if (bitset(EF_RESPONSE, e->e_flags))
1054 e->e_timeoutclass = TOC_DSN;
1055 }
1056 else if (bitset(EF_RESPONSE, e->e_flags))
1057 e->e_timeoutclass = TOC_DSN;
1058
1059 /* date message originated */
1060 p = hvalue("posted-date", e->e_header);
1061 if (p == NULL)
1062 p = hvalue("date", e->e_header);
1063 if (p != NULL)
1064 macdefine(&e->e_macro, A_PERM, 'a', p);
1065
1066 /* check to see if this is a MIME message */
1067 if ((e->e_bodytype != NULL &&
1068 SM_STRCASEEQ(e->e_bodytype, "8bitmime")) ||
1069 hvalue("MIME-Version", e->e_header) != NULL)
1070 {
1071 e->e_flags |= EF_IS_MIME;
1072 if (HasEightBits)
1073 e->e_bodytype = "8BITMIME";
1074 }
1075 else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
1076 {
1077 /* this may be an RFC 1049 message */
1078 p = strpbrk(p, ";/");
1079 if (p == NULL || *p == ';')
1080 {
1081 /* yep, it is */
1082 e->e_flags |= EF_DONT_MIME;
1083 }
1084 }
1085
1086 /*
1087 ** From person in antiquated ARPANET mode
1088 ** required by UK Grey Book e-mail gateways (sigh)
1089 */
1090
1091 if (OpMode == MD_ARPAFTP)
1092 {
1093 register struct hdrinfo *hi;
1094
1095 for (hi = HdrInfo; hi->hi_field != NULL; hi++)
1096 {
1097 if (bitset(H_FROM, hi->hi_flags) &&
1098 (!bitset(H_RESENT, hi->hi_flags) ||
1099 bitset(EF_RESENT, e->e_flags)) &&
1100 (p = hvalue(hi->hi_field, e->e_header)) != NULL)
1101 break;
1102 }
1103 if (hi->hi_field != NULL)
1104 {
1105 if (tTd(32, 2))
1106 sm_dprintf("eatheader: setsender(*%s == %s)\n",
1107 hi->hi_field, p);
1108 setsender(p, e, NULL, '\0', true);
1109 }
1110 }
1111
1112 /*
1113 ** Log collection information.
1114 */
1115
1116 if (tTd(92, 2))
1117 sm_dprintf("eatheader: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d, log=%d\n",
1118 e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel,
1119 log);
1120 if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
1121 {
1122 logsender(e, e->e_msgid);
1123 e->e_flags &= ~EF_LOGSENDER;
1124 }
1125 }
1126
1127 /*
1128 ** LOGSENDER -- log sender information
1129 **
1130 ** Parameters:
1131 ** e -- the envelope to log
1132 ** msgid -- the message id
1133 **
1134 ** Returns:
1135 ** none
1136 */
1137
1138 #define XBUFLEN MAXNAME
1139 #if (SYSLOG_BUFSIZE) >= 256
1140 # ifndef MSGIDLOGLEN
1141 # define MSGIDLOGLEN 100
1142 # define FIRSTLOGLEN 850
1143 # else
1144 # if MSGIDLOGLEN < 100
1145 # error "MSGIDLOGLEN too short"
1146 # endif
1147 /* XREF: this is "sizeof(sbuf)", see above */
1148 # if MSGIDLOGLEN >= MAXLINE / 2
1149 # error "MSGIDLOGLEN too long"
1150 # endif
1151
1152 /* 850 - 100 for original MSGIDLOGLEN */
1153 # define FIRSTLOGLEN (750 + MSGIDLOGLEN)
1154
1155 /* check that total length is ok */
1156 # if FIRSTLOGLEN + 200 >= MAXLINE
1157 # error "MSGIDLOGLEN too long"
1158 # endif
1159 # if MSGIDLOGLEN > MAXNAME
1160 # undef XBUFLEN
1161 # define XBUFLEN MSGIDLOGLEN
1162 # endif
1163 # endif
1164 #endif /* (SYSLOG_BUFSIZE) >= 256 */
1165
1166 void
logsender(e,msgid)1167 logsender(e, msgid)
1168 register ENVELOPE *e;
1169 char *msgid;
1170 {
1171 char *name;
1172 register char *sbp;
1173 register char *p;
1174 char hbuf[MAXNAME + 1]; /* EAI:ok; restricted to short size */
1175 char sbuf[MAXLINE + 1]; /* EAI:ok; XREF: see also MSGIDLOGLEN */
1176 #if _FFR_8BITENVADDR
1177 char xbuf[XBUFLEN + 1]; /* EAI:ok */
1178 #endif
1179 char *xstr;
1180
1181 if (bitset(EF_RESPONSE, e->e_flags))
1182 name = "[RESPONSE]";
1183 else if ((name = macvalue('_', e)) != NULL)
1184 /* EMPTY */
1185 ;
1186 else if (RealHostName == NULL)
1187 name = "localhost";
1188 else if (RealHostName[0] == '[')
1189 name = RealHostName;
1190 else
1191 {
1192 name = hbuf;
1193 (void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName);
1194 if (RealHostAddr.sa.sa_family != 0)
1195 {
1196 p = &hbuf[strlen(hbuf)];
1197 (void) sm_snprintf(p, SPACELEFT(hbuf, p),
1198 " (%.100s)",
1199 anynet_ntoa(&RealHostAddr));
1200 }
1201 }
1202
1203 #if (SYSLOG_BUFSIZE) >= 256
1204 sbp = sbuf;
1205 if (NULL != e->e_from.q_paddr)
1206 {
1207 xstr = e->e_from.q_paddr;
1208 # if _FFR_8BITENVADDR
1209 (void) dequote_internal_chars(e->e_from.q_paddr, xbuf, sizeof(xbuf));
1210 xstr = xbuf;
1211 # endif
1212 }
1213 else
1214 xstr = "<NONE>";
1215 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1216 "from=%.200s, size=%ld, class=%d, nrcpts=%d", xstr,
1217 PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1218 sbp += strlen(sbp);
1219 if (msgid != NULL)
1220 {
1221 # if _FFR_8BITENVADDR
1222 (void) dequote_internal_chars(msgid, xbuf, sizeof(xbuf));
1223 msgid = xbuf;
1224 # endif
1225 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1226 ", msgid=%.*s", MSGIDLOGLEN, msgid);
1227 sbp += strlen(sbp);
1228 }
1229 if (e->e_bodytype != NULL)
1230 {
1231 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1232 ", bodytype=%.20s", e->e_bodytype);
1233 sbp += strlen(sbp);
1234 }
1235 p = macvalue('r', e);
1236 if (p != NULL)
1237 {
1238 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1239 ", proto=%.20s", p);
1240 sbp += strlen(sbp);
1241 }
1242 p = macvalue(macid("{daemon_name}"), e);
1243 if (p != NULL)
1244 {
1245 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1246 ", daemon=%.20s", p);
1247 sbp += strlen(sbp);
1248 }
1249 # if _FFR_LOG_MORE1
1250 LOG_MORE(sbuf, sbp);
1251 # if SASL
1252 p = macvalue(macid("{auth_type}"), e);
1253 if (SM_IS_EMPTY(p))
1254 p = "NONE";
1255 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.20s", p);
1256 sbp += strlen(sbp);
1257 # endif /* SASL */
1258 # endif /* _FFR_LOG_MORE1 */
1259 sm_syslog(LOG_INFO, e->e_id, "%.*s, relay=%s", FIRSTLOGLEN, sbuf, name);
1260
1261 #else /* (SYSLOG_BUFSIZE) >= 256 */
1262
1263 sm_syslog(LOG_INFO, e->e_id,
1264 "from=%s",
1265 e->e_from.q_paddr == NULL ? "<NONE>"
1266 : shortenstring(e->e_from.q_paddr,
1267 83));
1268 sm_syslog(LOG_INFO, e->e_id,
1269 "size=%ld, class=%ld, nrcpts=%d",
1270 PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1271 if (msgid != NULL)
1272 {
1273 # if _FFR_8BITENVADDR
1274 (void) dequote_internal_chars(msgid, xbuf, sizeof(xbuf));
1275 msgid = xbuf;
1276 # endif
1277 sm_syslog(LOG_INFO, e->e_id,
1278 "msgid=%s",
1279 shortenstring(msgid, 83));
1280 }
1281 sbp = sbuf;
1282 *sbp = '\0';
1283 if (e->e_bodytype != NULL)
1284 {
1285 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1286 "bodytype=%.20s, ", e->e_bodytype);
1287 sbp += strlen(sbp);
1288 }
1289 p = macvalue('r', e);
1290 if (p != NULL)
1291 {
1292 (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1293 "proto=%.20s, ", p);
1294 sbp += strlen(sbp);
1295 }
1296 sm_syslog(LOG_INFO, e->e_id,
1297 "%.400srelay=%s", sbuf, name);
1298 #endif /* (SYSLOG_BUFSIZE) >= 256 */
1299 }
1300
1301 /*
1302 ** PRIENCODE -- encode external priority names into internal values.
1303 **
1304 ** Parameters:
1305 ** p -- priority in ascii.
1306 **
1307 ** Returns:
1308 ** priority as a numeric level.
1309 **
1310 ** Side Effects:
1311 ** none.
1312 */
1313
1314 static int
priencode(p)1315 priencode(p)
1316 char *p;
1317 {
1318 register int i;
1319
1320 for (i = 0; i < NumPriorities; i++)
1321 {
1322 if (SM_STRCASEEQ(p, Priorities[i].pri_name))
1323 return Priorities[i].pri_val;
1324 }
1325
1326 /* unknown priority */
1327 return 0;
1328 }
1329
1330 /*
1331 ** CRACKADDR -- parse an address and turn it into a macro
1332 **
1333 ** This doesn't actually parse the address -- it just extracts
1334 ** it and replaces it with "$g". The parse is totally ad hoc
1335 ** and isn't even guaranteed to leave something syntactically
1336 ** identical to what it started with. However, it does leave
1337 ** something semantically identical if possible, else at least
1338 ** syntactically correct.
1339 **
1340 ** For example, it changes "Real Name <real@example.com> (Comment)"
1341 ** to "Real Name <$g> (Comment)".
1342 **
1343 ** This algorithm has been cleaned up to handle a wider range
1344 ** of cases -- notably quoted and backslash escaped strings.
1345 ** This modification makes it substantially better at preserving
1346 ** the original syntax.
1347 **
1348 ** Parameters:
1349 ** addr -- the address to be cracked. [A]
1350 ** e -- the current envelope.
1351 **
1352 ** Returns:
1353 ** a pointer to the new version.
1354 **
1355 ** Side Effects:
1356 ** none.
1357 **
1358 ** Warning:
1359 ** The return value is saved in static storage and should
1360 ** be copied if it is to be reused.
1361 */
1362
1363 #define SM_HAVE_ROOMB ((bp < buflim) && (buflim <= bufend))
1364 #if USE_EAI
1365 # define SM_HAVE_ROOM ((xlen < MAXNAME) && SM_HAVE_ROOMB)
1366 #else
1367 # define SM_HAVE_ROOM SM_HAVE_ROOMB
1368 #endif
1369
1370 /*
1371 ** Append a character to bp if we have room.
1372 ** If not, punt and return $g.
1373 */
1374
1375 #define SM_APPEND_CHAR(c) \
1376 do \
1377 { \
1378 XLEN(c); \
1379 if (SM_HAVE_ROOM) \
1380 *bp++ = (c); \
1381 else \
1382 goto returng; \
1383 } while (0)
1384
1385 #if MAXNAME < 10
1386 # error "MAXNAME must be at least 10"
1387 #endif
1388
1389 char *
crackaddr(addr,e)1390 crackaddr(addr, e)
1391 register char *addr;
1392 ENVELOPE *e;
1393 {
1394 register char *p;
1395 register char c;
1396 int cmtlev; /* comment level in input string */
1397 int realcmtlev; /* comment level in output string */
1398 int anglelev; /* angle level in input string */
1399 int copylev; /* 0 == in address, >0 copying */
1400 int bracklev; /* bracket level for IPv6 addr check */
1401 bool addangle; /* put closing angle in output */
1402 bool qmode; /* quoting in original string? */
1403 bool realqmode; /* quoting in output string? */
1404 bool putgmac = false; /* already wrote $g */
1405 bool quoteit = false; /* need to quote next character */
1406 bool gotangle = false; /* found first '<' */
1407 bool gotcolon = false; /* found a ':' */
1408 register char *bp;
1409 char *buflim;
1410 char *bufhead;
1411 char *addrhead;
1412 char *bufend;
1413 static char buf[MAXNAME_I + 1]; /* XXX: EAI? */
1414 XLENDECL
1415
1416 if (tTd(33, 1))
1417 sm_dprintf("crackaddr(%s)\n", addr);
1418
1419 buflim = bufend = &buf[sizeof(buf) - 1];
1420 bp = bufhead = buf;
1421
1422 /* skip over leading spaces but preserve them */
1423 while (*addr != '\0' && SM_ISSPACE(*addr))
1424 {
1425 SM_APPEND_CHAR(*addr);
1426 addr++;
1427 }
1428 bufhead = bp;
1429
1430 /*
1431 ** Start by assuming we have no angle brackets. This will be
1432 ** adjusted later if we find them.
1433 */
1434
1435 p = addrhead = addr;
1436 copylev = anglelev = cmtlev = realcmtlev = 0;
1437 bracklev = 0;
1438 qmode = realqmode = addangle = false;
1439
1440 while ((c = *p++) != '\0')
1441 {
1442 /*
1443 ** Try to keep legal syntax using spare buffer space
1444 ** (maintained by buflim).
1445 */
1446
1447 if (copylev > 0)
1448 SM_APPEND_CHAR(c);
1449
1450 /* check for backslash escapes */
1451 if (c == '\\')
1452 {
1453 /* arrange to quote the address */
1454 if (cmtlev <= 0 && !qmode)
1455 quoteit = true;
1456
1457 if ((c = *p++) == '\0')
1458 {
1459 /* too far */
1460 p--;
1461 goto putg;
1462 }
1463 if (copylev > 0)
1464 SM_APPEND_CHAR(c);
1465 goto putg;
1466 }
1467
1468 /* check for quoted strings */
1469 if (c == '"' && cmtlev <= 0)
1470 {
1471 qmode = !qmode;
1472 if (copylev > 0 && SM_HAVE_ROOM)
1473 {
1474 if (realqmode)
1475 buflim--;
1476 else
1477 buflim++;
1478 realqmode = !realqmode;
1479 }
1480 continue;
1481 }
1482 if (qmode)
1483 goto putg;
1484
1485 /* check for comments */
1486 if (c == '(')
1487 {
1488 cmtlev++;
1489
1490 /* allow space for closing paren */
1491 if (SM_HAVE_ROOM)
1492 {
1493 buflim--;
1494 realcmtlev++;
1495 if (copylev++ <= 0)
1496 {
1497 if (bp != bufhead)
1498 SM_APPEND_CHAR(' ');
1499 SM_APPEND_CHAR(c);
1500 }
1501 }
1502 }
1503 if (cmtlev > 0)
1504 {
1505 if (c == ')')
1506 {
1507 cmtlev--;
1508 copylev--;
1509 if (SM_HAVE_ROOM)
1510 {
1511 realcmtlev--;
1512 buflim++;
1513 }
1514 }
1515 continue;
1516 }
1517 else if (c == ')')
1518 {
1519 /* syntax error: unmatched ) */
1520 if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead)
1521 bp--;
1522 }
1523
1524 /* count nesting on [ ... ] (for IPv6 domain literals) */
1525 if (c == '[')
1526 bracklev++;
1527 else if (c == ']')
1528 bracklev--;
1529
1530 /* check for group: list; syntax */
1531 if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
1532 !gotcolon && !ColonOkInAddr)
1533 {
1534 register char *q;
1535
1536 /*
1537 ** Check for DECnet phase IV ``::'' (host::user)
1538 ** or DECnet phase V ``:.'' syntaxes. The latter
1539 ** covers ``user@DEC:.tay.myhost'' and
1540 ** ``DEC:.tay.myhost::user'' syntaxes (bletch).
1541 */
1542
1543 if (*p == ':' || *p == '.')
1544 {
1545 if (cmtlev <= 0 && !qmode)
1546 quoteit = true;
1547 if (copylev > 0)
1548 {
1549 SM_APPEND_CHAR(c);
1550 SM_APPEND_CHAR(*p);
1551 }
1552 p++;
1553 goto putg;
1554 }
1555
1556 gotcolon = true;
1557
1558 bp = bufhead;
1559 if (quoteit)
1560 {
1561 SM_APPEND_CHAR('"');
1562
1563 /* back up over the ':' and any spaces */
1564 --p;
1565 while (p > addr &&
1566 isascii(*--p) && isspace(*p))
1567 continue;
1568 p++;
1569 }
1570 for (q = addrhead; q < p; )
1571 {
1572 c = *q++;
1573 if (quoteit && c == '"')
1574 SM_APPEND_CHAR('\\');
1575 SM_APPEND_CHAR(c);
1576 }
1577 if (quoteit)
1578 {
1579 if (bp == &bufhead[1])
1580 bp--;
1581 else
1582 SM_APPEND_CHAR('"');
1583 while ((c = *p++) != ':')
1584 SM_APPEND_CHAR(c);
1585 SM_APPEND_CHAR(c);
1586 }
1587
1588 /* any trailing white space is part of group: */
1589 while (SM_ISSPACE(*p))
1590 {
1591 SM_APPEND_CHAR(*p);
1592 p++;
1593 }
1594 copylev = 0;
1595 putgmac = quoteit = false;
1596 bufhead = bp;
1597 addrhead = p;
1598 continue;
1599 }
1600
1601 if (c == ';' && copylev <= 0 && !ColonOkInAddr)
1602 SM_APPEND_CHAR(c);
1603
1604 /* check for characters that may have to be quoted */
1605 if (strchr(MustQuoteChars, c) != NULL)
1606 {
1607 /*
1608 ** If these occur as the phrase part of a <>
1609 ** construct, but are not inside of () or already
1610 ** quoted, they will have to be quoted. Note that
1611 ** now (but don't actually do the quoting).
1612 */
1613
1614 if (cmtlev <= 0 && !qmode)
1615 quoteit = true;
1616 }
1617
1618 /* check for angle brackets */
1619 if (c == '<')
1620 {
1621 register char *q;
1622
1623 /* assume first of two angles is bogus */
1624 if (gotangle)
1625 quoteit = true;
1626 gotangle = true;
1627
1628 /* oops -- have to change our mind */
1629 anglelev = 1;
1630 if (SM_HAVE_ROOM)
1631 {
1632 if (!addangle)
1633 buflim--;
1634 addangle = true;
1635 }
1636
1637 bp = bufhead;
1638 if (quoteit)
1639 {
1640 SM_APPEND_CHAR('"');
1641
1642 /* back up over the '<' and any spaces */
1643 --p;
1644 while (p > addr &&
1645 isascii(*--p) && isspace(*p))
1646 continue;
1647 p++;
1648 }
1649 for (q = addrhead; q < p; )
1650 {
1651 c = *q++;
1652 if (quoteit && c == '"')
1653 {
1654 SM_APPEND_CHAR('\\');
1655 SM_APPEND_CHAR(c);
1656 }
1657 else
1658 SM_APPEND_CHAR(c);
1659 }
1660 if (quoteit)
1661 {
1662 if (bp == &buf[1])
1663 bp--;
1664 else
1665 SM_APPEND_CHAR('"');
1666 while ((c = *p++) != '<')
1667 SM_APPEND_CHAR(c);
1668 SM_APPEND_CHAR(c);
1669 }
1670 copylev = 0;
1671 putgmac = quoteit = false;
1672 continue;
1673 }
1674
1675 if (c == '>')
1676 {
1677 if (anglelev > 0)
1678 {
1679 anglelev--;
1680 if (SM_HAVE_ROOM)
1681 {
1682 if (addangle)
1683 buflim++;
1684 addangle = false;
1685 }
1686 }
1687 else if (SM_HAVE_ROOM)
1688 {
1689 /* syntax error: unmatched > */
1690 if (copylev > 0 && bp > bufhead)
1691 bp--;
1692 quoteit = true;
1693 continue;
1694 }
1695 if (copylev++ <= 0)
1696 SM_APPEND_CHAR(c);
1697 continue;
1698 }
1699
1700 /* must be a real address character */
1701 putg:
1702 if (copylev <= 0 && !putgmac)
1703 {
1704 if (bp > buf && bp[-1] == ')')
1705 SM_APPEND_CHAR(' ');
1706 SM_APPEND_CHAR(MACROEXPAND);
1707 SM_APPEND_CHAR('g');
1708 putgmac = true;
1709 }
1710 }
1711
1712 /* repair any syntactic damage */
1713 if (realqmode && bp < bufend)
1714 *bp++ = '"';
1715 while (realcmtlev-- > 0 && bp < bufend)
1716 *bp++ = ')';
1717 if (addangle && bp < bufend)
1718 *bp++ = '>';
1719 *bp = '\0';
1720 if (bp < bufend)
1721 goto success;
1722
1723 returng:
1724 /* String too long, punt */
1725 buf[0] = '<';
1726 buf[1] = MACROEXPAND;
1727 buf[2]= 'g';
1728 buf[3] = '>';
1729 buf[4]= '\0';
1730 sm_syslog(LOG_ALERT, e->e_id,
1731 "Dropped invalid comments from header address");
1732
1733 success:
1734 if (tTd(33, 1))
1735 {
1736 sm_dprintf("crackaddr=>`");
1737 xputs(sm_debug_file(), buf);
1738 sm_dprintf("'\n");
1739 }
1740 return buf;
1741 }
1742
1743 /*
1744 ** PUTHEADER -- put the header part of a message from the in-core copy
1745 **
1746 ** Parameters:
1747 ** mci -- the connection information.
1748 ** hdr -- the header to put.
1749 ** e -- envelope to use.
1750 ** flags -- MIME conversion flags.
1751 **
1752 ** Returns:
1753 ** true iff header part was written successfully
1754 **
1755 ** Side Effects:
1756 ** none.
1757 */
1758
1759 bool
putheader(mci,hdr,e,flags)1760 putheader(mci, hdr, e, flags)
1761 register MCI *mci;
1762 HDR *hdr;
1763 register ENVELOPE *e;
1764 int flags;
1765 {
1766 register HDR *h;
1767 char buf[SM_MAX(MAXLINE,BUFSIZ)];
1768 char obuf[MAXLINE];
1769
1770 if (tTd(34, 1))
1771 sm_dprintf("--- putheader, mailer = %s ---\n",
1772 mci->mci_mailer->m_name);
1773
1774 /*
1775 ** If we're in MIME mode, we're not really in the header of the
1776 ** message, just the header of one of the parts of the body of
1777 ** the message. Therefore MCIF_INHEADER should not be turned on.
1778 */
1779
1780 if (!bitset(MCIF_INMIME, mci->mci_flags))
1781 mci->mci_flags |= MCIF_INHEADER;
1782
1783 for (h = hdr; h != NULL; h = h->h_link)
1784 {
1785 register char *p = h->h_value;
1786 char *q;
1787
1788 if (tTd(34, 11))
1789 {
1790 sm_dprintf(" %s:", h->h_field);
1791 xputs(sm_debug_file(), p);
1792 }
1793
1794 /* Skip empty headers */
1795 if (p == NULL)
1796 continue;
1797 #if _FFR_8BITENVADDR
1798 (void) dequote_internal_chars(p, buf, sizeof(buf));
1799 #endif
1800
1801 /* heuristic shortening of MIME fields to avoid MUA overflows */
1802 if (MaxMimeFieldLength > 0 &&
1803 wordinclass(h->h_field,
1804 macid("{checkMIMEFieldHeaders}")))
1805 {
1806 size_t len;
1807
1808 len = fix_mime_header(h, e);
1809 if (len > 0)
1810 {
1811 sm_syslog(LOG_ALERT, e->e_id,
1812 "Truncated MIME %s header due to field size (length = %ld) (possible attack)",
1813 h->h_field, (unsigned long) len);
1814 if (tTd(34, 11))
1815 sm_dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n",
1816 h->h_field,
1817 (unsigned long) len);
1818 }
1819 }
1820
1821 if (MaxMimeHeaderLength > 0 &&
1822 wordinclass(h->h_field,
1823 macid("{checkMIMETextHeaders}")))
1824 {
1825 size_t len;
1826
1827 len = strlen(p);
1828 if (len > (size_t) MaxMimeHeaderLength)
1829 {
1830 h->h_value[MaxMimeHeaderLength - 1] = '\0';
1831 sm_syslog(LOG_ALERT, e->e_id,
1832 "Truncated long MIME %s header (length = %ld) (possible attack)",
1833 h->h_field, (unsigned long) len);
1834 if (tTd(34, 11))
1835 sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n",
1836 h->h_field,
1837 (unsigned long) len);
1838 }
1839 }
1840
1841 if (MaxMimeHeaderLength > 0 &&
1842 wordinclass(h->h_field,
1843 macid("{checkMIMEHeaders}")))
1844 {
1845 size_t len;
1846
1847 len = strlen(h->h_value);
1848 if (shorten_rfc822_string(h->h_value,
1849 MaxMimeHeaderLength))
1850 {
1851 if (len < MaxMimeHeaderLength)
1852 {
1853 /* we only rebalanced a bogus header */
1854 sm_syslog(LOG_ALERT, e->e_id,
1855 "Fixed MIME %s header (possible attack)",
1856 h->h_field);
1857 if (tTd(34, 11))
1858 sm_dprintf(" fixed MIME %s header (possible attack)\n",
1859 h->h_field);
1860 }
1861 else
1862 {
1863 /* we actually shortened header */
1864 sm_syslog(LOG_ALERT, e->e_id,
1865 "Truncated long MIME %s header (length = %ld) (possible attack)",
1866 h->h_field,
1867 (unsigned long) len);
1868 if (tTd(34, 11))
1869 sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n",
1870 h->h_field,
1871 (unsigned long) len);
1872 }
1873 }
1874 }
1875
1876 /*
1877 ** Suppress Content-Transfer-Encoding: if we are MIMEing
1878 ** and we are potentially converting from 8 bit to 7 bit
1879 ** MIME. If converting, add a new CTE header in
1880 ** mime8to7().
1881 */
1882
1883 if (bitset(H_CTE, h->h_flags) &&
1884 bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
1885 mci->mci_flags) &&
1886 !bitset(M87F_NO8TO7, flags))
1887 {
1888 if (tTd(34, 11))
1889 sm_dprintf(" (skipped (content-transfer-encoding))\n");
1890 continue;
1891 }
1892
1893 if (bitset(MCIF_INMIME, mci->mci_flags))
1894 {
1895 if (tTd(34, 11))
1896 sm_dprintf("\n");
1897 if (!put_vanilla_header(h, p, mci))
1898 goto writeerr;
1899 continue;
1900 }
1901
1902 if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
1903 !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
1904 (h->h_macro == '\0' ||
1905 (q = macvalue(bitidx(h->h_macro), e)) == NULL ||
1906 *q == '\0'))
1907 {
1908 if (tTd(34, 11))
1909 sm_dprintf(" (skipped)\n");
1910 continue;
1911 }
1912
1913 /* handle Resent-... headers specially */
1914 if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
1915 {
1916 if (tTd(34, 11))
1917 sm_dprintf(" (skipped (resent))\n");
1918 continue;
1919 }
1920
1921 /* suppress return receipts if requested */
1922 if (bitset(H_RECEIPTTO, h->h_flags) &&
1923 (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
1924 {
1925 if (tTd(34, 11))
1926 sm_dprintf(" (skipped (receipt))\n");
1927 continue;
1928 }
1929
1930 /* macro expand value if generated internally */
1931 if (bitset(H_DEFAULT, h->h_flags) ||
1932 bitset(H_BINDLATE, h->h_flags))
1933 {
1934 expand(p, buf, sizeof(buf), e);
1935 p = buf;
1936 if (*p == '\0')
1937 {
1938 if (tTd(34, 11))
1939 sm_dprintf(" (skipped -- null value)\n");
1940 continue;
1941 }
1942 }
1943
1944 if (bitset(H_BCC, h->h_flags) && !KeepBcc)
1945 {
1946 /* Bcc: field -- either truncate or delete */
1947 if (bitset(EF_DELETE_BCC, e->e_flags))
1948 {
1949 if (tTd(34, 11))
1950 sm_dprintf(" (skipped -- bcc)\n");
1951 }
1952 else
1953 {
1954 /* no other recipient headers: truncate value */
1955 (void) sm_strlcpyn(obuf, sizeof(obuf), 2,
1956 h->h_field, ":");
1957 if (!putline(obuf, mci))
1958 goto writeerr;
1959 }
1960 continue;
1961 }
1962
1963 if (tTd(34, 11))
1964 sm_dprintf("\n");
1965
1966 if (bitset(H_FROM|H_RCPT, h->h_flags))
1967 {
1968 /* address field */
1969 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
1970
1971 if (bitset(H_FROM, h->h_flags))
1972 oldstyle = false;
1973 if (!commaize(h, p, oldstyle, mci, e,
1974 PXLF_HEADER | PXLF_STRIPMQUOTE)
1975 && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
1976 goto writeerr;
1977 }
1978 else
1979 {
1980 if (!put_vanilla_header(h, p, mci))
1981 goto writeerr;
1982 }
1983 }
1984
1985 /*
1986 ** If we are converting this to a MIME message, add the
1987 ** MIME headers (but not in MIME mode!).
1988 */
1989
1990 #if MIME8TO7
1991 if (bitset(MM_MIME8BIT, MimeMode) &&
1992 bitset(EF_HAS8BIT, e->e_flags) &&
1993 !bitset(EF_DONT_MIME, e->e_flags) &&
1994 !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
1995 !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) &&
1996 hvalue("MIME-Version", e->e_header) == NULL)
1997 {
1998 if (!putline("MIME-Version: 1.0", mci))
1999 goto writeerr;
2000 if (hvalue("Content-Type", e->e_header) == NULL)
2001 {
2002 (void) sm_snprintf(obuf, sizeof(obuf),
2003 "Content-Type: text/plain; charset=%s",
2004 defcharset(e));
2005 if (!putline(obuf, mci))
2006 goto writeerr;
2007 }
2008 if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL
2009 && !putline("Content-Transfer-Encoding: 8bit", mci))
2010 goto writeerr;
2011 }
2012 #endif /* MIME8TO7 */
2013 return true;
2014
2015 writeerr:
2016 return false;
2017 }
2018
2019 /*
2020 ** PUT_VANILLA_HEADER -- output a fairly ordinary header
2021 **
2022 ** Parameters:
2023 ** h -- the structure describing this header
2024 ** v -- the value of this header
2025 ** mci -- the connection info for output
2026 **
2027 ** Returns:
2028 ** true iff header was written successfully
2029 */
2030
2031 static bool
put_vanilla_header(h,v,mci)2032 put_vanilla_header(h, v, mci)
2033 HDR *h;
2034 char *v;
2035 MCI *mci;
2036 {
2037 register char *nlp;
2038 register char *obp;
2039 int putflags;
2040 char obuf[MAXLINE + 256]; /* additional length for h_field */
2041
2042 putflags = PXLF_HEADER | PXLF_STRIPMQUOTE;
2043 if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
2044 putflags |= PXLF_STRIP8BIT;
2045 (void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field);
2046 obp = obuf + strlen(obuf);
2047 while ((nlp = strchr(v, '\n')) != NULL)
2048 {
2049 int l;
2050
2051 l = nlp - v;
2052
2053 /*
2054 ** XXX This is broken for SPACELEFT()==0
2055 ** However, SPACELEFT() is always > 0 unless MAXLINE==1.
2056 */
2057
2058 if (SPACELEFT(obuf, obp) - 1 < (size_t) l)
2059 l = SPACELEFT(obuf, obp) - 1;
2060
2061 (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
2062 if (!putxline(obuf, strlen(obuf), mci, putflags))
2063 goto writeerr;
2064 v += l + 1;
2065 obp = obuf;
2066 if (*v != ' ' && *v != '\t')
2067 *obp++ = ' ';
2068 }
2069
2070 /* XXX This is broken for SPACELEFT()==0 */
2071 (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
2072 (int) (SPACELEFT(obuf, obp) - 1), v);
2073 return putxline(obuf, strlen(obuf), mci, putflags);
2074
2075 writeerr:
2076 return false;
2077 }
2078
2079 /*
2080 ** COMMAIZE -- output a header field, making a comma-translated list.
2081 **
2082 ** Parameters:
2083 ** h -- the header field to output.
2084 ** p -- the value to put in it.
2085 ** oldstyle -- true if this is an old style header.
2086 ** mci -- the connection information.
2087 ** e -- the envelope containing the message.
2088 ** putflags -- flags for putxline()
2089 **
2090 ** Returns:
2091 ** true iff header field was written successfully
2092 **
2093 ** Side Effects:
2094 ** outputs "p" to "mci".
2095 */
2096
2097 bool
commaize(h,p,oldstyle,mci,e,putflags)2098 commaize(h, p, oldstyle, mci, e, putflags)
2099 register HDR *h;
2100 register char *p;
2101 bool oldstyle;
2102 register MCI *mci;
2103 register ENVELOPE *e;
2104 int putflags;
2105 {
2106 register char *obp;
2107 int opos, omax, spaces;
2108 bool firstone = true;
2109 char **res;
2110 char obuf[MAXLINE + 3];
2111
2112 /*
2113 ** Output the address list translated by the
2114 ** mailer and with commas.
2115 */
2116
2117 if (tTd(14, 2))
2118 sm_dprintf("commaize(%s:%s)\n", h->h_field, p);
2119
2120 if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
2121 putflags |= PXLF_STRIP8BIT;
2122
2123 #if _FFR_MTA_MODE
2124 /* activate this per mailer? */
2125 if (bitset(H_FROM, h->h_flags) && bitset(H_ASIS, h->h_flags))
2126 {
2127 (void) sm_snprintf(obuf, sizeof(obuf), "%.200s:%s", h->h_field,
2128 h->h_value);
2129 return putxline(obuf, strlen(obuf), mci, putflags);
2130 }
2131 #endif
2132
2133 obp = obuf;
2134 (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
2135 /* opos = strlen(obp); instead of the next 3 lines? */
2136 opos = strlen(h->h_field) + 1;
2137 if (opos > 201)
2138 opos = 201;
2139 obp += opos;
2140
2141 spaces = 0;
2142 while (*p != '\0' && SM_ISSPACE(*p))
2143 {
2144 ++spaces;
2145 ++p;
2146 }
2147 if (spaces > 0)
2148 {
2149 SM_ASSERT(sizeof(obuf) > opos * 2);
2150
2151 /*
2152 ** Restrict number of spaces to half the length of buffer
2153 ** so the header field body can be put in here too.
2154 ** Note: this is a hack...
2155 */
2156
2157 if (spaces > sizeof(obuf) / 2)
2158 spaces = sizeof(obuf) / 2;
2159 (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces,
2160 "");
2161 opos += spaces;
2162 obp += spaces;
2163 SM_ASSERT(obp < &obuf[MAXLINE]);
2164 }
2165
2166 omax = mci->mci_mailer->m_linelimit - 2;
2167 if (omax < 0 || omax > 78)
2168 omax = 78;
2169
2170 /*
2171 ** Run through the list of values.
2172 */
2173
2174 while (*p != '\0')
2175 {
2176 register char *name;
2177 register int c;
2178 char savechar;
2179 int flags;
2180 auto int status;
2181
2182 /*
2183 ** Find the end of the name. New style names
2184 ** end with a comma, old style names end with
2185 ** a space character. However, spaces do not
2186 ** necessarily delimit an old-style name -- at
2187 ** signs mean keep going.
2188 */
2189
2190 /* find end of name */
2191 while ((SM_ISSPACE(*p)) || *p == ',')
2192 p++;
2193 name = p;
2194 res = NULL;
2195 for (;;)
2196 {
2197 auto char *oldp;
2198 char pvpbuf[PSBUFSIZE];
2199
2200 res = prescan(p, oldstyle ? ' ' : ',', pvpbuf,
2201 sizeof(pvpbuf), &oldp, ExtTokenTab, false);
2202 p = oldp;
2203 #if _FFR_IGNORE_BOGUS_ADDR
2204 /* ignore addresses that can't be parsed */
2205 if (res == NULL)
2206 {
2207 name = p;
2208 continue;
2209 }
2210 #endif /* _FFR_IGNORE_BOGUS_ADDR */
2211
2212 /* look to see if we have an at sign */
2213 while (*p != '\0' && SM_ISSPACE(*p))
2214 p++;
2215
2216 if (*p != '@')
2217 {
2218 p = oldp;
2219 break;
2220 }
2221 ++p;
2222 while (*p != '\0' && SM_ISSPACE(*p))
2223 p++;
2224 }
2225 /* at the end of one complete name */
2226
2227 /* strip off trailing white space */
2228 while (p >= name &&
2229 ((SM_ISSPACE(*p)) || *p == ',' || *p == '\0'))
2230 p--;
2231 if (++p == name)
2232 continue;
2233
2234 /*
2235 ** if prescan() failed go a bit backwards; this is a hack,
2236 ** there should be some better error recovery.
2237 */
2238
2239 if (res == NULL && p > name &&
2240 !((SM_ISSPACE(*p)) || *p == ',' || *p == '\0'))
2241 --p;
2242 savechar = *p;
2243 *p = '\0';
2244
2245 /* translate the name to be relative */
2246 flags = RF_HEADERADDR|RF_ADDDOMAIN;
2247 if (bitset(H_FROM, h->h_flags))
2248 flags |= RF_SENDERADDR;
2249 #if USERDB
2250 else if (e->e_from.q_mailer != NULL &&
2251 bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
2252 {
2253 char *q;
2254
2255 q = udbsender(name, e->e_rpool);
2256 if (q != NULL)
2257 name = q;
2258 }
2259 #endif /* USERDB */
2260 status = EX_OK;
2261 name = remotename(name, mci->mci_mailer, flags, &status, e);
2262 if (status != EX_OK && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
2263 {
2264 if (status == EX_TEMPFAIL)
2265 mci->mci_flags |= MCIF_NOTSTICKY;
2266 goto writeerr;
2267 }
2268 if (*name == '\0')
2269 {
2270 *p = savechar;
2271 continue;
2272 }
2273 name = denlstring(name, false, true);
2274
2275 /* output the name with nice formatting */
2276 opos += strlen(name);
2277 if (!firstone)
2278 opos += 2;
2279 if (opos > omax && !firstone)
2280 {
2281 (void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp));
2282 if (!putxline(obuf, strlen(obuf), mci, putflags))
2283 goto writeerr;
2284 obp = obuf;
2285 (void) sm_strlcpy(obp, " ", sizeof(obuf));
2286 opos = strlen(obp);
2287 obp += opos;
2288 opos += strlen(name);
2289 }
2290 else if (!firstone)
2291 {
2292 (void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp));
2293 obp += 2;
2294 }
2295
2296 while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
2297 *obp++ = c;
2298 firstone = false;
2299 *p = savechar;
2300 }
2301 if (obp < &obuf[sizeof(obuf)])
2302 *obp = '\0';
2303 else
2304 obuf[sizeof(obuf) - 1] = '\0';
2305 return putxline(obuf, strlen(obuf), mci, putflags);
2306
2307 writeerr:
2308 return false;
2309 }
2310
2311 /*
2312 ** COPYHEADER -- copy header list
2313 **
2314 ** This routine is the equivalent of newstr for header lists
2315 **
2316 ** Parameters:
2317 ** header -- list of header structures to copy.
2318 ** rpool -- resource pool, or NULL
2319 **
2320 ** Returns:
2321 ** a copy of 'header'.
2322 **
2323 ** Side Effects:
2324 ** none.
2325 */
2326
2327 HDR *
copyheader(header,rpool)2328 copyheader(header, rpool)
2329 register HDR *header;
2330 SM_RPOOL_T *rpool;
2331 {
2332 register HDR *newhdr;
2333 HDR *ret;
2334 register HDR **tail = &ret;
2335
2336 while (header != NULL)
2337 {
2338 newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr));
2339 STRUCTCOPY(*header, *newhdr);
2340 *tail = newhdr;
2341 tail = &newhdr->h_link;
2342 header = header->h_link;
2343 }
2344 *tail = NULL;
2345
2346 return ret;
2347 }
2348
2349 /*
2350 ** FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
2351 **
2352 ** Run through all of the parameters of a MIME header and
2353 ** possibly truncate and rebalance the parameter according
2354 ** to MaxMimeFieldLength.
2355 **
2356 ** Parameters:
2357 ** h -- the header to truncate/rebalance
2358 ** e -- the current envelope
2359 **
2360 ** Returns:
2361 ** length of last offending field, 0 if all ok.
2362 **
2363 ** Side Effects:
2364 ** string modified in place
2365 */
2366
2367 static size_t
fix_mime_header(h,e)2368 fix_mime_header(h, e)
2369 HDR *h;
2370 ENVELOPE *e;
2371 {
2372 char *begin = h->h_value;
2373 char *end;
2374 size_t len = 0;
2375 size_t retlen = 0;
2376
2377 if (SM_IS_EMPTY(begin))
2378 return 0;
2379
2380 /* Split on each ';' */
2381 /* find_character() never returns NULL */
2382 while ((end = find_character(begin, ';')) != NULL)
2383 {
2384 char save = *end;
2385 char *bp;
2386
2387 *end = '\0';
2388
2389 len = strlen(begin);
2390
2391 /* Shorten individual parameter */
2392 if (shorten_rfc822_string(begin, MaxMimeFieldLength))
2393 {
2394 if (len < MaxMimeFieldLength)
2395 {
2396 /* we only rebalanced a bogus field */
2397 sm_syslog(LOG_ALERT, e->e_id,
2398 "Fixed MIME %s header field (possible attack)",
2399 h->h_field);
2400 if (tTd(34, 11))
2401 sm_dprintf(" fixed MIME %s header field (possible attack)\n",
2402 h->h_field);
2403 }
2404 else
2405 {
2406 /* we actually shortened the header */
2407 retlen = len;
2408 }
2409 }
2410
2411 /* Collapse the possibly shortened string with rest */
2412 bp = begin + strlen(begin);
2413 if (bp != end)
2414 {
2415 char *ep = end;
2416
2417 *end = save;
2418 end = bp;
2419
2420 /* copy character by character due to overlap */
2421 while (*ep != '\0')
2422 *bp++ = *ep++;
2423 *bp = '\0';
2424 }
2425 else
2426 *end = save;
2427 if (*end == '\0')
2428 break;
2429
2430 /* Move past ';' */
2431 begin = end + 1;
2432 }
2433 return retlen;
2434 }
2435