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