Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
6f14531a RG |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
15637ed4 RG |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. All advertising materials mentioning features or use of this software | |
15 | * must display the following acknowledgement: | |
16 | * This product includes software developed by the University of | |
17 | * California, Berkeley and its contributors. | |
18 | * 4. Neither the name of the University nor the names of its contributors | |
19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #ifndef lint | |
d747e748 | 36 | static char sccsid[] = "@(#)headers.c 8.13 (Berkeley) 10/24/93"; |
15637ed4 RG |
37 | #endif /* not lint */ |
38 | ||
15637ed4 RG |
39 | # include <errno.h> |
40 | # include "sendmail.h" | |
41 | ||
42 | /* | |
43 | ** CHOMPHEADER -- process and save a header line. | |
44 | ** | |
45 | ** Called by collect and by readcf to deal with header lines. | |
46 | ** | |
47 | ** Parameters: | |
48 | ** line -- header as a text line. | |
49 | ** def -- if set, this is a default value. | |
6f14531a | 50 | ** e -- the envelope including this header. |
15637ed4 RG |
51 | ** |
52 | ** Returns: | |
53 | ** flags for this header. | |
54 | ** | |
55 | ** Side Effects: | |
56 | ** The header is saved on the header list. | |
57 | ** Contents of 'line' are destroyed. | |
58 | */ | |
59 | ||
6f14531a | 60 | chompheader(line, def, e) |
15637ed4 RG |
61 | char *line; |
62 | bool def; | |
6f14531a | 63 | register ENVELOPE *e; |
15637ed4 RG |
64 | { |
65 | register char *p; | |
66 | register HDR *h; | |
67 | HDR **hp; | |
68 | char *fname; | |
69 | char *fvalue; | |
70 | struct hdrinfo *hi; | |
71 | bool cond = FALSE; | |
72 | BITMAP mopts; | |
d747e748 | 73 | char buf[MAXNAME]; |
15637ed4 RG |
74 | |
75 | if (tTd(31, 6)) | |
76 | printf("chompheader: %s\n", line); | |
77 | ||
78 | /* strip off options */ | |
79 | clrbitmap(mopts); | |
80 | p = line; | |
81 | if (*p == '?') | |
82 | { | |
83 | /* have some */ | |
6f14531a | 84 | register char *q = strchr(p + 1, *p); |
15637ed4 RG |
85 | |
86 | if (q != NULL) | |
87 | { | |
88 | *q++ = '\0'; | |
89 | while (*++p != '\0') | |
90 | setbitn(*p, mopts); | |
91 | p = q; | |
92 | } | |
93 | else | |
6f14531a | 94 | usrerr("553 header syntax error, line \"%s\"", line); |
15637ed4 RG |
95 | cond = TRUE; |
96 | } | |
97 | ||
98 | /* find canonical name */ | |
99 | fname = p; | |
d747e748 JH |
100 | while (isascii(*p) && isgraph(*p) && *p != ':') |
101 | p++; | |
102 | fvalue = p; | |
103 | while (isascii(*p) && isspace(*p)) | |
104 | p++; | |
105 | if (*p++ != ':' || fname == fvalue) | |
15637ed4 | 106 | { |
6f14531a | 107 | syserr("553 header syntax error, line \"%s\"", line); |
15637ed4 RG |
108 | return (0); |
109 | } | |
d747e748 JH |
110 | *fvalue = '\0'; |
111 | fvalue = p; | |
15637ed4 RG |
112 | |
113 | /* strip field value on front */ | |
114 | if (*fvalue == ' ') | |
115 | fvalue++; | |
116 | ||
117 | /* see if it is a known type */ | |
118 | for (hi = HdrInfo; hi->hi_field != NULL; hi++) | |
119 | { | |
6f14531a | 120 | if (strcasecmp(hi->hi_field, fname) == 0) |
15637ed4 RG |
121 | break; |
122 | } | |
123 | ||
d747e748 JH |
124 | if (tTd(31, 9)) |
125 | { | |
126 | if (hi->hi_field == NULL) | |
127 | printf("no header match\n"); | |
128 | else | |
129 | printf("header match, hi_flags=%o\n", hi->hi_flags); | |
130 | } | |
131 | ||
15637ed4 RG |
132 | /* see if this is a resent message */ |
133 | if (!def && bitset(H_RESENT, hi->hi_flags)) | |
6f14531a | 134 | e->e_flags |= EF_RESENT; |
15637ed4 RG |
135 | |
136 | /* if this means "end of header" quit now */ | |
137 | if (bitset(H_EOH, hi->hi_flags)) | |
138 | return (hi->hi_flags); | |
139 | ||
d747e748 JH |
140 | /* |
141 | ** Drop explicit From: if same as what we would generate. | |
142 | ** This is to make MH (which doesn't always give a full name) | |
143 | ** insert the full name information in all circumstances. | |
144 | */ | |
145 | ||
15637ed4 | 146 | p = "resent-from"; |
6f14531a | 147 | if (!bitset(EF_RESENT, e->e_flags)) |
15637ed4 | 148 | p += 7; |
6f14531a | 149 | if (!def && !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0) |
15637ed4 | 150 | { |
3a363396 NW |
151 | if (tTd(31, 2)) |
152 | { | |
153 | printf("comparing header from (%s) against default (%s or %s)\n", | |
154 | fvalue, e->e_from.q_paddr, e->e_from.q_user); | |
155 | } | |
6f14531a | 156 | if (e->e_from.q_paddr != NULL && |
3a363396 NW |
157 | (strcmp(fvalue, e->e_from.q_paddr) == 0 || |
158 | strcmp(fvalue, e->e_from.q_user) == 0)) | |
15637ed4 | 159 | return (hi->hi_flags); |
d747e748 JH |
160 | #ifdef MAYBENEXTRELEASE /* XXX UNTESTED XXX UNTESTED XXX UNTESTED XXX */ |
161 | #ifdef USERDB | |
162 | else | |
163 | { | |
164 | auto ADDRESS a; | |
165 | char *fancy; | |
166 | extern char *crackaddr(); | |
167 | extern char *udbsender(); | |
168 | ||
169 | /* | |
170 | ** Try doing USERDB rewriting even on fully commented | |
171 | ** names; this saves the "comment" information (such | |
172 | ** as full name) and rewrites the electronic part. | |
173 | */ | |
174 | ||
175 | fancy = crackaddr(fvalue); | |
176 | if (parseaddr(fvalue, &a, RF_COPYNONE, '\0', NULL, e) != NULL && | |
177 | a.q_mailer == LocalMailer && | |
178 | (p = udbsender(a.q_user)) != NULL) | |
179 | { | |
180 | char *oldg = macvalue('g', e); | |
181 | ||
182 | define('g', p, e); | |
183 | expand(fancy, buf, &buf[sizeof buf], e); | |
184 | define('g', oldg, e); | |
185 | fvalue = buf; | |
186 | } | |
187 | } | |
188 | #endif | |
189 | #endif | |
15637ed4 RG |
190 | } |
191 | ||
192 | /* delete default value for this header */ | |
6f14531a | 193 | for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) |
15637ed4 | 194 | { |
6f14531a | 195 | if (strcasecmp(fname, h->h_field) == 0 && |
15637ed4 RG |
196 | bitset(H_DEFAULT, h->h_flags) && |
197 | !bitset(H_FORCE, h->h_flags)) | |
198 | h->h_value = NULL; | |
199 | } | |
200 | ||
201 | /* create a new node */ | |
202 | h = (HDR *) xalloc(sizeof *h); | |
203 | h->h_field = newstr(fname); | |
d747e748 | 204 | h->h_value = newstr(fvalue); |
15637ed4 RG |
205 | h->h_link = NULL; |
206 | bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); | |
207 | *hp = h; | |
208 | h->h_flags = hi->hi_flags; | |
209 | if (def) | |
210 | h->h_flags |= H_DEFAULT; | |
211 | if (cond) | |
212 | h->h_flags |= H_CHECK; | |
15637ed4 RG |
213 | |
214 | /* hack to see if this is a new format message */ | |
215 | if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && | |
6f14531a RG |
216 | (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || |
217 | strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) | |
15637ed4 | 218 | { |
6f14531a | 219 | e->e_flags &= ~EF_OLDSTYLE; |
15637ed4 RG |
220 | } |
221 | ||
222 | return (h->h_flags); | |
223 | } | |
224 | \f/* | |
225 | ** ADDHEADER -- add a header entry to the end of the queue. | |
226 | ** | |
227 | ** This bypasses the special checking of chompheader. | |
228 | ** | |
229 | ** Parameters: | |
230 | ** field -- the name of the header field. | |
6f14531a | 231 | ** value -- the value of the field. |
15637ed4 RG |
232 | ** e -- the envelope to add them to. |
233 | ** | |
234 | ** Returns: | |
235 | ** none. | |
236 | ** | |
237 | ** Side Effects: | |
238 | ** adds the field on the list of headers for this envelope. | |
239 | */ | |
240 | ||
241 | addheader(field, value, e) | |
242 | char *field; | |
243 | char *value; | |
244 | ENVELOPE *e; | |
245 | { | |
246 | register HDR *h; | |
247 | register struct hdrinfo *hi; | |
248 | HDR **hp; | |
249 | ||
250 | /* find info struct */ | |
251 | for (hi = HdrInfo; hi->hi_field != NULL; hi++) | |
252 | { | |
6f14531a | 253 | if (strcasecmp(field, hi->hi_field) == 0) |
15637ed4 RG |
254 | break; |
255 | } | |
256 | ||
257 | /* find current place in list -- keep back pointer? */ | |
258 | for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) | |
259 | { | |
6f14531a | 260 | if (strcasecmp(field, h->h_field) == 0) |
15637ed4 RG |
261 | break; |
262 | } | |
263 | ||
264 | /* allocate space for new header */ | |
265 | h = (HDR *) xalloc(sizeof *h); | |
266 | h->h_field = field; | |
267 | h->h_value = newstr(value); | |
268 | h->h_link = *hp; | |
269 | h->h_flags = hi->hi_flags | H_DEFAULT; | |
270 | clrbitmap(h->h_mflags); | |
271 | *hp = h; | |
272 | } | |
273 | \f/* | |
274 | ** HVALUE -- return value of a header. | |
275 | ** | |
276 | ** Only "real" fields (i.e., ones that have not been supplied | |
277 | ** as a default) are used. | |
278 | ** | |
279 | ** Parameters: | |
280 | ** field -- the field name. | |
6f14531a | 281 | ** e -- the envelope containing the header. |
15637ed4 RG |
282 | ** |
283 | ** Returns: | |
284 | ** pointer to the value part. | |
285 | ** NULL if not found. | |
286 | ** | |
287 | ** Side Effects: | |
288 | ** none. | |
289 | */ | |
290 | ||
291 | char * | |
6f14531a | 292 | hvalue(field, e) |
15637ed4 | 293 | char *field; |
6f14531a | 294 | register ENVELOPE *e; |
15637ed4 RG |
295 | { |
296 | register HDR *h; | |
297 | ||
6f14531a | 298 | for (h = e->e_header; h != NULL; h = h->h_link) |
15637ed4 | 299 | { |
6f14531a RG |
300 | if (!bitset(H_DEFAULT, h->h_flags) && |
301 | strcasecmp(h->h_field, field) == 0) | |
15637ed4 RG |
302 | return (h->h_value); |
303 | } | |
304 | return (NULL); | |
305 | } | |
306 | \f/* | |
307 | ** ISHEADER -- predicate telling if argument is a header. | |
308 | ** | |
309 | ** A line is a header if it has a single word followed by | |
310 | ** optional white space followed by a colon. | |
311 | ** | |
312 | ** Parameters: | |
313 | ** s -- string to check for possible headerness. | |
314 | ** | |
315 | ** Returns: | |
316 | ** TRUE if s is a header. | |
317 | ** FALSE otherwise. | |
318 | ** | |
319 | ** Side Effects: | |
320 | ** none. | |
321 | */ | |
322 | ||
323 | bool | |
324 | isheader(s) | |
325 | register char *s; | |
326 | { | |
327 | while (*s > ' ' && *s != ':' && *s != '\0') | |
328 | s++; | |
329 | ||
330 | /* following technically violates RFC822 */ | |
6f14531a | 331 | while (isascii(*s) && isspace(*s)) |
15637ed4 RG |
332 | s++; |
333 | ||
334 | return (*s == ':'); | |
335 | } | |
336 | \f/* | |
337 | ** EATHEADER -- run through the stored header and extract info. | |
338 | ** | |
339 | ** Parameters: | |
340 | ** e -- the envelope to process. | |
6f14531a RG |
341 | ** full -- if set, do full processing (e.g., compute |
342 | ** message priority). | |
15637ed4 RG |
343 | ** |
344 | ** Returns: | |
345 | ** none. | |
346 | ** | |
347 | ** Side Effects: | |
348 | ** Sets a bunch of global variables from information | |
349 | ** in the collected header. | |
350 | ** Aborts the message if the hop count is exceeded. | |
351 | */ | |
352 | ||
6f14531a | 353 | eatheader(e, full) |
15637ed4 | 354 | register ENVELOPE *e; |
6f14531a | 355 | bool full; |
15637ed4 RG |
356 | { |
357 | register HDR *h; | |
358 | register char *p; | |
359 | int hopcnt = 0; | |
6f14531a RG |
360 | char *msgid; |
361 | char buf[MAXLINE]; | |
362 | ||
363 | /* | |
364 | ** Set up macros for possible expansion in headers. | |
365 | */ | |
366 | ||
367 | define('f', e->e_sender, e); | |
368 | define('g', e->e_sender, e); | |
d747e748 JH |
369 | if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') |
370 | define('u', e->e_origrcpt, e); | |
371 | else | |
372 | define('u', NULL, e); | |
15637ed4 RG |
373 | |
374 | if (tTd(32, 1)) | |
375 | printf("----- collected header -----\n"); | |
6f14531a | 376 | msgid = "<none>"; |
15637ed4 RG |
377 | for (h = e->e_header; h != NULL; h = h->h_link) |
378 | { | |
d747e748 JH |
379 | if (h->h_value == NULL) |
380 | { | |
381 | if (tTd(32, 1)) | |
382 | printf("%s: <NULL>\n", h->h_field); | |
383 | continue; | |
384 | } | |
385 | ||
6f14531a | 386 | /* do early binding */ |
d747e748 | 387 | if (bitset(H_DEFAULT, h->h_flags)) |
6f14531a RG |
388 | { |
389 | expand(h->h_value, buf, &buf[sizeof buf], e); | |
390 | if (buf[0] != '\0') | |
391 | { | |
392 | h->h_value = newstr(buf); | |
393 | h->h_flags &= ~H_DEFAULT; | |
394 | } | |
395 | } | |
15637ed4 RG |
396 | |
397 | if (tTd(32, 1)) | |
6f14531a RG |
398 | printf("%s: %s\n", h->h_field, h->h_value); |
399 | ||
15637ed4 RG |
400 | /* count the number of times it has been processed */ |
401 | if (bitset(H_TRACE, h->h_flags)) | |
402 | hopcnt++; | |
403 | ||
404 | /* send to this person if we so desire */ | |
405 | if (GrabTo && bitset(H_RCPT, h->h_flags) && | |
406 | !bitset(H_DEFAULT, h->h_flags) && | |
6f14531a | 407 | (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) |
15637ed4 | 408 | { |
d747e748 JH |
409 | int saveflags = e->e_flags; |
410 | ||
411 | (void) sendtolist(h->h_value, NULLADDR, | |
6f14531a | 412 | &e->e_sendqueue, e); |
d747e748 JH |
413 | |
414 | /* delete fatal errors generated by this address */ | |
415 | if (!GrabTo && !bitset(EF_FATALERRS, saveflags)) | |
416 | e->e_flags &= ~EF_FATALERRS; | |
15637ed4 RG |
417 | } |
418 | ||
6f14531a | 419 | /* save the message-id for logging */ |
d747e748 | 420 | if (full && strcasecmp(h->h_field, "message-id") == 0) |
15637ed4 | 421 | { |
6f14531a RG |
422 | msgid = h->h_value; |
423 | while (isascii(*msgid) && isspace(*msgid)) | |
424 | msgid++; | |
15637ed4 | 425 | } |
6f14531a RG |
426 | |
427 | /* see if this is a return-receipt header */ | |
428 | if (bitset(H_RECEIPTTO, h->h_flags)) | |
429 | e->e_receiptto = h->h_value; | |
430 | ||
431 | /* see if this is an errors-to header */ | |
432 | if (UseErrorsTo && bitset(H_ERRORSTO, h->h_flags)) | |
d747e748 | 433 | (void) sendtolist(h->h_value, NULLADDR, |
6f14531a | 434 | &e->e_errorqueue, e); |
15637ed4 RG |
435 | } |
436 | if (tTd(32, 1)) | |
437 | printf("----------------------------\n"); | |
438 | ||
6f14531a RG |
439 | /* if we are just verifying (that is, sendmail -t -bv), drop out now */ |
440 | if (OpMode == MD_VERIFY) | |
441 | return; | |
442 | ||
15637ed4 RG |
443 | /* store hop count */ |
444 | if (hopcnt > e->e_hopcount) | |
445 | e->e_hopcount = hopcnt; | |
446 | ||
447 | /* message priority */ | |
6f14531a | 448 | p = hvalue("precedence", e); |
15637ed4 RG |
449 | if (p != NULL) |
450 | e->e_class = priencode(p); | |
6f14531a | 451 | if (full) |
15637ed4 RG |
452 | e->e_msgpriority = e->e_msgsize |
453 | - e->e_class * WkClassFact | |
454 | + e->e_nrcpts * WkRecipFact; | |
455 | ||
15637ed4 | 456 | /* full name of from person */ |
6f14531a | 457 | p = hvalue("full-name", e); |
15637ed4 RG |
458 | if (p != NULL) |
459 | define('x', p, e); | |
460 | ||
461 | /* date message originated */ | |
6f14531a | 462 | p = hvalue("posted-date", e); |
15637ed4 | 463 | if (p == NULL) |
6f14531a | 464 | p = hvalue("date", e); |
15637ed4 | 465 | if (p != NULL) |
15637ed4 | 466 | define('a', p, e); |
15637ed4 RG |
467 | |
468 | /* | |
469 | ** Log collection information. | |
470 | */ | |
471 | ||
472 | # ifdef LOG | |
6f14531a | 473 | if (full && LogLevel > 4) |
15637ed4 | 474 | { |
6f14531a RG |
475 | char *name; |
476 | register char *sbp; | |
477 | char hbuf[MAXNAME]; | |
478 | char sbuf[MAXLINE]; | |
479 | ||
480 | if (bitset(EF_RESPONSE, e->e_flags)) | |
481 | name = "[RESPONSE]"; | |
482 | else if ((name = macvalue('_', e)) != NULL) | |
483 | ; | |
15637ed4 RG |
484 | else if (RealHostName[0] == '[') |
485 | name = RealHostName; | |
486 | else | |
6f14531a RG |
487 | { |
488 | name = hbuf; | |
489 | (void) sprintf(hbuf, "%.80s", RealHostName); | |
490 | if (RealHostAddr.sa.sa_family != 0) | |
491 | { | |
492 | p = &hbuf[strlen(hbuf)]; | |
493 | (void) sprintf(p, " (%s)", | |
494 | anynet_ntoa(&RealHostAddr)); | |
495 | } | |
496 | } | |
497 | ||
498 | /* some versions of syslog only take 5 printf args */ | |
499 | sbp = sbuf; | |
500 | sprintf(sbp, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d, msgid=%.100s", | |
501 | e->e_from.q_paddr, e->e_msgsize, e->e_class, | |
502 | e->e_msgpriority, e->e_nrcpts, msgid); | |
503 | sbp += strlen(sbp); | |
504 | if (e->e_bodytype != NULL) | |
505 | { | |
506 | (void) sprintf(sbp, ", bodytype=%.20s", e->e_bodytype); | |
507 | sbp += strlen(sbp); | |
508 | } | |
509 | p = macvalue('r', e); | |
510 | if (p != NULL) | |
511 | (void) sprintf(sbp, ", proto=%.20s", p); | |
512 | syslog(LOG_INFO, "%s: %s, relay=%s", | |
513 | e->e_id, sbuf, name); | |
15637ed4 | 514 | } |
6f14531a | 515 | # endif /* LOG */ |
15637ed4 RG |
516 | } |
517 | \f/* | |
518 | ** PRIENCODE -- encode external priority names into internal values. | |
519 | ** | |
520 | ** Parameters: | |
521 | ** p -- priority in ascii. | |
522 | ** | |
523 | ** Returns: | |
524 | ** priority as a numeric level. | |
525 | ** | |
526 | ** Side Effects: | |
527 | ** none. | |
528 | */ | |
529 | ||
530 | priencode(p) | |
531 | char *p; | |
532 | { | |
533 | register int i; | |
534 | ||
535 | for (i = 0; i < NumPriorities; i++) | |
536 | { | |
537 | if (!strcasecmp(p, Priorities[i].pri_name)) | |
538 | return (Priorities[i].pri_val); | |
539 | } | |
540 | ||
541 | /* unknown priority */ | |
542 | return (0); | |
543 | } | |
544 | \f/* | |
545 | ** CRACKADDR -- parse an address and turn it into a macro | |
546 | ** | |
547 | ** This doesn't actually parse the address -- it just extracts | |
548 | ** it and replaces it with "$g". The parse is totally ad hoc | |
549 | ** and isn't even guaranteed to leave something syntactically | |
550 | ** identical to what it started with. However, it does leave | |
551 | ** something semantically identical. | |
552 | ** | |
6f14531a RG |
553 | ** This algorithm has been cleaned up to handle a wider range |
554 | ** of cases -- notably quoted and backslash escaped strings. | |
555 | ** This modification makes it substantially better at preserving | |
556 | ** the original syntax. | |
15637ed4 RG |
557 | ** |
558 | ** Parameters: | |
559 | ** addr -- the address to be cracked. | |
560 | ** | |
561 | ** Returns: | |
562 | ** a pointer to the new version. | |
563 | ** | |
564 | ** Side Effects: | |
565 | ** none. | |
566 | ** | |
567 | ** Warning: | |
568 | ** The return value is saved in local storage and should | |
569 | ** be copied if it is to be reused. | |
570 | */ | |
571 | ||
572 | char * | |
573 | crackaddr(addr) | |
574 | register char *addr; | |
575 | { | |
576 | register char *p; | |
6f14531a RG |
577 | register char c; |
578 | int cmtlev; | |
579 | int realcmtlev; | |
580 | int anglelev, realanglelev; | |
581 | int copylev; | |
582 | bool qmode; | |
583 | bool realqmode; | |
584 | bool skipping; | |
585 | bool putgmac = FALSE; | |
586 | bool quoteit = FALSE; | |
d747e748 | 587 | bool gotangle = FALSE; |
15637ed4 | 588 | register char *bp; |
6f14531a RG |
589 | char *buflim; |
590 | static char buf[MAXNAME]; | |
15637ed4 RG |
591 | |
592 | if (tTd(33, 1)) | |
593 | printf("crackaddr(%s)\n", addr); | |
594 | ||
15637ed4 | 595 | /* strip leading spaces */ |
6f14531a | 596 | while (*addr != '\0' && isascii(*addr) && isspace(*addr)) |
15637ed4 RG |
597 | addr++; |
598 | ||
599 | /* | |
6f14531a RG |
600 | ** Start by assuming we have no angle brackets. This will be |
601 | ** adjusted later if we find them. | |
15637ed4 RG |
602 | */ |
603 | ||
6f14531a RG |
604 | bp = buf; |
605 | buflim = &buf[sizeof buf - 5]; | |
606 | p = addr; | |
607 | copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; | |
608 | qmode = realqmode = FALSE; | |
609 | ||
610 | while ((c = *p++) != '\0') | |
15637ed4 | 611 | { |
6f14531a RG |
612 | /* |
613 | ** If the buffer is overful, go into a special "skipping" | |
614 | ** mode that tries to keep legal syntax but doesn't actually | |
615 | ** output things. | |
616 | */ | |
15637ed4 | 617 | |
6f14531a RG |
618 | skipping = bp >= buflim; |
619 | ||
620 | if (copylev > 0 && !skipping) | |
621 | *bp++ = c; | |
15637ed4 | 622 | |
6f14531a RG |
623 | /* check for backslash escapes */ |
624 | if (c == '\\') | |
15637ed4 | 625 | { |
6f14531a RG |
626 | /* arrange to quote the address */ |
627 | if (cmtlev <= 0 && !qmode) | |
628 | quoteit = TRUE; | |
15637ed4 | 629 | |
6f14531a RG |
630 | if ((c = *p++) == '\0') |
631 | { | |
632 | /* too far */ | |
633 | p--; | |
634 | goto putg; | |
15637ed4 | 635 | } |
6f14531a RG |
636 | if (copylev > 0 && !skipping) |
637 | *bp++ = c; | |
638 | goto putg; | |
15637ed4 RG |
639 | } |
640 | ||
6f14531a RG |
641 | /* check for quoted strings */ |
642 | if (c == '"') | |
15637ed4 | 643 | { |
6f14531a RG |
644 | qmode = !qmode; |
645 | if (copylev > 0 && !skipping) | |
646 | realqmode = !realqmode; | |
647 | continue; | |
15637ed4 | 648 | } |
6f14531a RG |
649 | if (qmode) |
650 | goto putg; | |
15637ed4 | 651 | |
6f14531a RG |
652 | /* check for comments */ |
653 | if (c == '(') | |
654 | { | |
655 | cmtlev++; | |
15637ed4 | 656 | |
6f14531a RG |
657 | /* allow space for closing paren */ |
658 | if (!skipping) | |
659 | { | |
660 | buflim--; | |
661 | realcmtlev++; | |
662 | if (copylev++ <= 0) | |
663 | { | |
664 | *bp++ = ' '; | |
665 | *bp++ = c; | |
666 | } | |
667 | } | |
668 | } | |
669 | if (cmtlev > 0) | |
15637ed4 | 670 | { |
6f14531a | 671 | if (c == ')') |
15637ed4 | 672 | { |
6f14531a RG |
673 | cmtlev--; |
674 | copylev--; | |
675 | if (!skipping) | |
15637ed4 | 676 | { |
6f14531a RG |
677 | realcmtlev--; |
678 | buflim++; | |
15637ed4 | 679 | } |
15637ed4 RG |
680 | } |
681 | continue; | |
682 | } | |
6f14531a RG |
683 | else if (c == ')') |
684 | { | |
685 | /* syntax error: unmatched ) */ | |
686 | if (!skipping) | |
687 | bp--; | |
688 | } | |
15637ed4 | 689 | |
15637ed4 | 690 | |
6f14531a | 691 | /* check for characters that may have to be quoted */ |
d747e748 | 692 | if (strchr(".'@,;:\\()[]", c) != NULL) |
15637ed4 | 693 | { |
6f14531a RG |
694 | /* |
695 | ** If these occur as the phrase part of a <> | |
696 | ** construct, but are not inside of () or already | |
697 | ** quoted, they will have to be quoted. Note that | |
698 | ** now (but don't actually do the quoting). | |
699 | */ | |
700 | ||
701 | if (cmtlev <= 0 && !qmode) | |
702 | quoteit = TRUE; | |
15637ed4 | 703 | } |
15637ed4 | 704 | |
6f14531a RG |
705 | /* check for angle brackets */ |
706 | if (c == '<') | |
707 | { | |
708 | register char *q; | |
15637ed4 | 709 | |
d747e748 JH |
710 | /* assume first of two angles is bogus */ |
711 | if (gotangle) | |
712 | quoteit = TRUE; | |
713 | gotangle = TRUE; | |
714 | ||
6f14531a | 715 | /* oops -- have to change our mind */ |
d747e748 | 716 | anglelev = 1; |
6f14531a | 717 | if (!skipping) |
d747e748 | 718 | realanglelev = 1; |
6f14531a RG |
719 | |
720 | bp = buf; | |
721 | if (quoteit) | |
722 | { | |
723 | *bp++ = '"'; | |
724 | ||
725 | /* back up over the '<' and any spaces */ | |
726 | --p; | |
727 | while (isascii(*--p) && isspace(*p)) | |
728 | continue; | |
729 | p++; | |
730 | } | |
731 | for (q = addr; q < p; ) | |
732 | { | |
733 | c = *q++; | |
734 | if (bp < buflim) | |
735 | { | |
736 | if (quoteit && c == '"') | |
737 | *bp++ = '\\'; | |
738 | *bp++ = c; | |
739 | } | |
740 | } | |
741 | if (quoteit) | |
742 | { | |
d747e748 JH |
743 | if (bp == &buf[1]) |
744 | bp--; | |
745 | else | |
746 | *bp++ = '"'; | |
6f14531a RG |
747 | while ((c = *p++) != '<') |
748 | { | |
749 | if (bp < buflim) | |
750 | *bp++ = c; | |
751 | } | |
752 | *bp++ = c; | |
753 | } | |
754 | copylev = 0; | |
755 | putgmac = quoteit = FALSE; | |
756 | continue; | |
757 | } | |
758 | ||
759 | if (c == '>') | |
760 | { | |
761 | if (anglelev > 0) | |
762 | { | |
763 | anglelev--; | |
764 | if (!skipping) | |
765 | { | |
766 | realanglelev--; | |
767 | buflim++; | |
768 | } | |
769 | } | |
770 | else if (!skipping) | |
771 | { | |
772 | /* syntax error: unmatched > */ | |
773 | if (copylev > 0) | |
774 | bp--; | |
d747e748 | 775 | quoteit = TRUE; |
6f14531a RG |
776 | continue; |
777 | } | |
778 | if (copylev++ <= 0) | |
779 | *bp++ = c; | |
780 | continue; | |
781 | } | |
782 | ||
783 | /* must be a real address character */ | |
784 | putg: | |
785 | if (copylev <= 0 && !putgmac) | |
786 | { | |
787 | *bp++ = MACROEXPAND; | |
788 | *bp++ = 'g'; | |
789 | putgmac = TRUE; | |
790 | } | |
15637ed4 RG |
791 | } |
792 | ||
6f14531a RG |
793 | /* repair any syntactic damage */ |
794 | if (realqmode) | |
795 | *bp++ = '"'; | |
796 | while (realcmtlev-- > 0) | |
797 | *bp++ = ')'; | |
798 | while (realanglelev-- > 0) | |
799 | *bp++ = '>'; | |
800 | *bp++ = '\0'; | |
801 | ||
15637ed4 RG |
802 | if (tTd(33, 1)) |
803 | printf("crackaddr=>`%s'\n", buf); | |
804 | ||
805 | return (buf); | |
806 | } | |
807 | \f/* | |
808 | ** PUTHEADER -- put the header part of a message from the in-core copy | |
809 | ** | |
810 | ** Parameters: | |
811 | ** fp -- file to put it on. | |
812 | ** m -- mailer to use. | |
813 | ** e -- envelope to use. | |
814 | ** | |
815 | ** Returns: | |
816 | ** none. | |
817 | ** | |
818 | ** Side Effects: | |
819 | ** none. | |
820 | */ | |
821 | ||
6f14531a RG |
822 | /* |
823 | * Macro for fast max (not available in e.g. DG/UX, 386/ix). | |
824 | */ | |
825 | #ifndef MAX | |
826 | # define MAX(a,b) (((a)>(b))?(a):(b)) | |
827 | #endif | |
828 | ||
15637ed4 RG |
829 | putheader(fp, m, e) |
830 | register FILE *fp; | |
831 | register MAILER *m; | |
832 | register ENVELOPE *e; | |
833 | { | |
6f14531a | 834 | char buf[MAX(MAXLINE,BUFSIZ)]; |
15637ed4 | 835 | register HDR *h; |
6f14531a RG |
836 | char obuf[MAXLINE]; |
837 | ||
838 | if (tTd(34, 1)) | |
839 | printf("--- putheader, mailer = %s ---\n", m->m_name); | |
15637ed4 RG |
840 | |
841 | for (h = e->e_header; h != NULL; h = h->h_link) | |
842 | { | |
843 | register char *p; | |
844 | extern bool bitintersect(); | |
845 | ||
6f14531a RG |
846 | if (tTd(34, 11)) |
847 | { | |
848 | printf(" %s: ", h->h_field); | |
849 | xputs(h->h_value); | |
850 | } | |
851 | ||
15637ed4 RG |
852 | if (bitset(H_CHECK|H_ACHECK, h->h_flags) && |
853 | !bitintersect(h->h_mflags, m->m_flags)) | |
6f14531a RG |
854 | { |
855 | if (tTd(34, 11)) | |
856 | printf(" (skipped)\n"); | |
15637ed4 | 857 | continue; |
6f14531a | 858 | } |
15637ed4 RG |
859 | |
860 | /* handle Resent-... headers specially */ | |
861 | if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) | |
6f14531a RG |
862 | { |
863 | if (tTd(34, 11)) | |
864 | printf(" (skipped (resent))\n"); | |
15637ed4 | 865 | continue; |
6f14531a RG |
866 | } |
867 | if (tTd(34, 11)) | |
868 | printf("\n"); | |
15637ed4 RG |
869 | |
870 | p = h->h_value; | |
871 | if (bitset(H_DEFAULT, h->h_flags)) | |
872 | { | |
873 | /* macro expand value if generated internally */ | |
874 | expand(p, buf, &buf[sizeof buf], e); | |
875 | p = buf; | |
876 | if (p == NULL || *p == '\0') | |
877 | continue; | |
878 | } | |
879 | ||
880 | if (bitset(H_FROM|H_RCPT, h->h_flags)) | |
881 | { | |
882 | /* address field */ | |
883 | bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); | |
884 | ||
885 | if (bitset(H_FROM, h->h_flags)) | |
886 | oldstyle = FALSE; | |
6f14531a | 887 | commaize(h, p, fp, oldstyle, m, e); |
15637ed4 RG |
888 | } |
889 | else | |
890 | { | |
891 | /* vanilla header line */ | |
892 | register char *nlp; | |
893 | ||
6f14531a RG |
894 | (void) sprintf(obuf, "%s: ", h->h_field); |
895 | while ((nlp = strchr(p, '\n')) != NULL) | |
15637ed4 RG |
896 | { |
897 | *nlp = '\0'; | |
898 | (void) strcat(obuf, p); | |
899 | *nlp = '\n'; | |
900 | putline(obuf, fp, m); | |
901 | p = ++nlp; | |
902 | obuf[0] = '\0'; | |
903 | } | |
904 | (void) strcat(obuf, p); | |
905 | putline(obuf, fp, m); | |
906 | } | |
907 | } | |
908 | } | |
909 | \f/* | |
910 | ** COMMAIZE -- output a header field, making a comma-translated list. | |
911 | ** | |
912 | ** Parameters: | |
913 | ** h -- the header field to output. | |
914 | ** p -- the value to put in it. | |
915 | ** fp -- file to put it to. | |
916 | ** oldstyle -- TRUE if this is an old style header. | |
917 | ** m -- a pointer to the mailer descriptor. If NULL, | |
918 | ** don't transform the name at all. | |
6f14531a | 919 | ** e -- the envelope containing the message. |
15637ed4 RG |
920 | ** |
921 | ** Returns: | |
922 | ** none. | |
923 | ** | |
924 | ** Side Effects: | |
925 | ** outputs "p" to file "fp". | |
926 | */ | |
927 | ||
6f14531a | 928 | commaize(h, p, fp, oldstyle, m, e) |
15637ed4 RG |
929 | register HDR *h; |
930 | register char *p; | |
931 | FILE *fp; | |
932 | bool oldstyle; | |
933 | register MAILER *m; | |
6f14531a | 934 | register ENVELOPE *e; |
15637ed4 RG |
935 | { |
936 | register char *obp; | |
937 | int opos; | |
938 | bool firstone = TRUE; | |
939 | char obuf[MAXLINE + 3]; | |
940 | ||
941 | /* | |
942 | ** Output the address list translated by the | |
943 | ** mailer and with commas. | |
944 | */ | |
945 | ||
946 | if (tTd(14, 2)) | |
947 | printf("commaize(%s: %s)\n", h->h_field, p); | |
948 | ||
949 | obp = obuf; | |
6f14531a | 950 | (void) sprintf(obp, "%s: ", h->h_field); |
15637ed4 RG |
951 | opos = strlen(h->h_field) + 2; |
952 | obp += opos; | |
953 | ||
954 | /* | |
955 | ** Run through the list of values. | |
956 | */ | |
957 | ||
958 | while (*p != '\0') | |
959 | { | |
960 | register char *name; | |
6f14531a | 961 | register int c; |
15637ed4 | 962 | char savechar; |
6f14531a RG |
963 | int flags; |
964 | auto int stat; | |
15637ed4 RG |
965 | |
966 | /* | |
967 | ** Find the end of the name. New style names | |
968 | ** end with a comma, old style names end with | |
969 | ** a space character. However, spaces do not | |
970 | ** necessarily delimit an old-style name -- at | |
971 | ** signs mean keep going. | |
972 | */ | |
973 | ||
974 | /* find end of name */ | |
6f14531a | 975 | while ((isascii(*p) && isspace(*p)) || *p == ',') |
15637ed4 RG |
976 | p++; |
977 | name = p; | |
978 | for (;;) | |
979 | { | |
6f14531a | 980 | auto char *oldp; |
15637ed4 | 981 | char pvpbuf[PSBUFSIZE]; |
15637ed4 | 982 | |
6f14531a RG |
983 | (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, &oldp); |
984 | p = oldp; | |
15637ed4 RG |
985 | |
986 | /* look to see if we have an at sign */ | |
6f14531a | 987 | while (*p != '\0' && isascii(*p) && isspace(*p)) |
15637ed4 RG |
988 | p++; |
989 | ||
6f14531a | 990 | if (*p != '@') |
15637ed4 RG |
991 | { |
992 | p = oldp; | |
993 | break; | |
994 | } | |
995 | p += *p == '@' ? 1 : 2; | |
6f14531a | 996 | while (*p != '\0' && isascii(*p) && isspace(*p)) |
15637ed4 RG |
997 | p++; |
998 | } | |
999 | /* at the end of one complete name */ | |
1000 | ||
1001 | /* strip off trailing white space */ | |
6f14531a RG |
1002 | while (p >= name && |
1003 | ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) | |
15637ed4 RG |
1004 | p--; |
1005 | if (++p == name) | |
1006 | continue; | |
1007 | savechar = *p; | |
1008 | *p = '\0'; | |
1009 | ||
1010 | /* translate the name to be relative */ | |
6f14531a RG |
1011 | flags = RF_HEADERADDR|RF_ADDDOMAIN; |
1012 | if (bitset(H_FROM, h->h_flags)) | |
1013 | flags |= RF_SENDERADDR; | |
1014 | stat = EX_OK; | |
1015 | name = remotename(name, m, flags, &stat, e); | |
15637ed4 RG |
1016 | if (*name == '\0') |
1017 | { | |
1018 | *p = savechar; | |
1019 | continue; | |
1020 | } | |
1021 | ||
1022 | /* output the name with nice formatting */ | |
6f14531a | 1023 | opos += strlen(name); |
15637ed4 RG |
1024 | if (!firstone) |
1025 | opos += 2; | |
1026 | if (opos > 78 && !firstone) | |
1027 | { | |
1028 | (void) strcpy(obp, ",\n"); | |
1029 | putline(obuf, fp, m); | |
1030 | obp = obuf; | |
1031 | (void) sprintf(obp, " "); | |
1032 | opos = strlen(obp); | |
1033 | obp += opos; | |
6f14531a | 1034 | opos += strlen(name); |
15637ed4 RG |
1035 | } |
1036 | else if (!firstone) | |
1037 | { | |
1038 | (void) sprintf(obp, ", "); | |
1039 | obp += 2; | |
1040 | } | |
1041 | ||
6f14531a RG |
1042 | while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) |
1043 | *obp++ = c; | |
15637ed4 RG |
1044 | firstone = FALSE; |
1045 | *p = savechar; | |
1046 | } | |
1047 | (void) strcpy(obp, "\n"); | |
1048 | putline(obuf, fp, m); | |
1049 | } | |
1050 | \f/* | |
6f14531a RG |
1051 | ** COPYHEADER -- copy header list |
1052 | ** | |
1053 | ** This routine is the equivalent of newstr for header lists | |
15637ed4 RG |
1054 | ** |
1055 | ** Parameters: | |
6f14531a | 1056 | ** header -- list of header structures to copy. |
15637ed4 RG |
1057 | ** |
1058 | ** Returns: | |
6f14531a | 1059 | ** a copy of 'header'. |
15637ed4 RG |
1060 | ** |
1061 | ** Side Effects: | |
1062 | ** none. | |
1063 | */ | |
1064 | ||
6f14531a RG |
1065 | HDR * |
1066 | copyheader(header) | |
1067 | register HDR *header; | |
15637ed4 | 1068 | { |
6f14531a RG |
1069 | register HDR *newhdr; |
1070 | HDR *ret; | |
1071 | register HDR **tail = &ret; | |
15637ed4 | 1072 | |
6f14531a RG |
1073 | while (header != NULL) |
1074 | { | |
1075 | newhdr = (HDR *) xalloc(sizeof(HDR)); | |
1076 | STRUCTCOPY(*header, *newhdr); | |
1077 | *tail = newhdr; | |
1078 | tail = &newhdr->h_link; | |
1079 | header = header->h_link; | |
1080 | } | |
1081 | *tail = NULL; | |
1082 | ||
1083 | return ret; | |
15637ed4 | 1084 | } |