1 | /* |
---|
2 | * Copyright (c) 1998-2002 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: savemail.c,v 1.2 2006-03-23 21:02:46 zacheiss Exp $") |
---|
17 | |
---|
18 | static bool errbody __P((MCI *, ENVELOPE *, char *)); |
---|
19 | static bool pruneroute __P((char *)); |
---|
20 | |
---|
21 | /* |
---|
22 | ** SAVEMAIL -- Save mail on error |
---|
23 | ** |
---|
24 | ** If mailing back errors, mail it back to the originator |
---|
25 | ** together with an error message; otherwise, just put it in |
---|
26 | ** dead.letter in the user's home directory (if he exists on |
---|
27 | ** this machine). |
---|
28 | ** |
---|
29 | ** Parameters: |
---|
30 | ** e -- the envelope containing the message in error. |
---|
31 | ** sendbody -- if true, also send back the body of the |
---|
32 | ** message; otherwise just send the header. |
---|
33 | ** |
---|
34 | ** Returns: |
---|
35 | ** true if savemail panic'ed, (i.e., the data file should |
---|
36 | ** be preserved by dropenvelope()) |
---|
37 | ** |
---|
38 | ** Side Effects: |
---|
39 | ** Saves the letter, by writing or mailing it back to the |
---|
40 | ** sender, or by putting it in dead.letter in her home |
---|
41 | ** directory. |
---|
42 | */ |
---|
43 | |
---|
44 | /* defines for state machine */ |
---|
45 | #define ESM_REPORT 0 /* report to sender's terminal */ |
---|
46 | #define ESM_MAIL 1 /* mail back to sender */ |
---|
47 | #define ESM_QUIET 2 /* mail has already been returned */ |
---|
48 | #define ESM_DEADLETTER 3 /* save in ~/dead.letter */ |
---|
49 | #define ESM_POSTMASTER 4 /* return to postmaster */ |
---|
50 | #define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */ |
---|
51 | #define ESM_PANIC 6 /* call loseqfile() */ |
---|
52 | #define ESM_DONE 7 /* message is successfully delivered */ |
---|
53 | |
---|
54 | bool |
---|
55 | savemail(e, sendbody) |
---|
56 | register ENVELOPE *e; |
---|
57 | bool sendbody; |
---|
58 | { |
---|
59 | register SM_FILE_T *fp; |
---|
60 | bool panic = false; |
---|
61 | int state; |
---|
62 | auto ADDRESS *q = NULL; |
---|
63 | register char *p; |
---|
64 | MCI mcibuf; |
---|
65 | int flags; |
---|
66 | long sff; |
---|
67 | char buf[MAXLINE + 1]; |
---|
68 | char dlbuf[MAXPATHLEN]; |
---|
69 | SM_MBDB_T user; |
---|
70 | |
---|
71 | |
---|
72 | if (tTd(6, 1)) |
---|
73 | { |
---|
74 | sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", |
---|
75 | e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, |
---|
76 | ExitStat); |
---|
77 | printaddr(&e->e_from, false); |
---|
78 | } |
---|
79 | |
---|
80 | if (e->e_id == NULL) |
---|
81 | { |
---|
82 | /* can't return a message with no id */ |
---|
83 | return panic; |
---|
84 | } |
---|
85 | |
---|
86 | /* |
---|
87 | ** In the unhappy event we don't know who to return the mail |
---|
88 | ** to, make someone up. |
---|
89 | */ |
---|
90 | |
---|
91 | if (e->e_from.q_paddr == NULL) |
---|
92 | { |
---|
93 | e->e_sender = "Postmaster"; |
---|
94 | if (parseaddr(e->e_sender, &e->e_from, |
---|
95 | RF_COPYPARSE|RF_SENDERADDR, |
---|
96 | '\0', NULL, e, false) == NULL) |
---|
97 | { |
---|
98 | syserr("553 5.3.5 Cannot parse Postmaster!"); |
---|
99 | finis(true, true, EX_SOFTWARE); |
---|
100 | } |
---|
101 | } |
---|
102 | e->e_to = NULL; |
---|
103 | |
---|
104 | /* |
---|
105 | ** Basic state machine. |
---|
106 | ** |
---|
107 | ** This machine runs through the following states: |
---|
108 | ** |
---|
109 | ** ESM_QUIET Errors have already been printed iff the |
---|
110 | ** sender is local. |
---|
111 | ** ESM_REPORT Report directly to the sender's terminal. |
---|
112 | ** ESM_MAIL Mail response to the sender. |
---|
113 | ** ESM_DEADLETTER Save response in ~/dead.letter. |
---|
114 | ** ESM_POSTMASTER Mail response to the postmaster. |
---|
115 | ** ESM_DEADLETTERDROP |
---|
116 | ** If DeadLetterDrop set, save it there. |
---|
117 | ** ESM_PANIC Save response anywhere possible. |
---|
118 | */ |
---|
119 | |
---|
120 | /* determine starting state */ |
---|
121 | switch (e->e_errormode) |
---|
122 | { |
---|
123 | case EM_WRITE: |
---|
124 | state = ESM_REPORT; |
---|
125 | break; |
---|
126 | |
---|
127 | case EM_BERKNET: |
---|
128 | case EM_MAIL: |
---|
129 | state = ESM_MAIL; |
---|
130 | break; |
---|
131 | |
---|
132 | case EM_PRINT: |
---|
133 | case '\0': |
---|
134 | state = ESM_QUIET; |
---|
135 | break; |
---|
136 | |
---|
137 | case EM_QUIET: |
---|
138 | /* no need to return anything at all */ |
---|
139 | return panic; |
---|
140 | |
---|
141 | default: |
---|
142 | syserr("554 5.3.0 savemail: bogus errormode x%x", |
---|
143 | e->e_errormode); |
---|
144 | state = ESM_MAIL; |
---|
145 | break; |
---|
146 | } |
---|
147 | |
---|
148 | /* if this is already an error response, send to postmaster */ |
---|
149 | if (bitset(EF_RESPONSE, e->e_flags)) |
---|
150 | { |
---|
151 | if (e->e_parent != NULL && |
---|
152 | bitset(EF_RESPONSE, e->e_parent->e_flags)) |
---|
153 | { |
---|
154 | /* got an error sending a response -- can it */ |
---|
155 | return panic; |
---|
156 | } |
---|
157 | state = ESM_POSTMASTER; |
---|
158 | } |
---|
159 | |
---|
160 | while (state != ESM_DONE) |
---|
161 | { |
---|
162 | if (tTd(6, 5)) |
---|
163 | sm_dprintf(" state %d\n", state); |
---|
164 | |
---|
165 | switch (state) |
---|
166 | { |
---|
167 | case ESM_QUIET: |
---|
168 | if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) |
---|
169 | state = ESM_DEADLETTER; |
---|
170 | else |
---|
171 | state = ESM_MAIL; |
---|
172 | break; |
---|
173 | |
---|
174 | case ESM_REPORT: |
---|
175 | |
---|
176 | /* |
---|
177 | ** If the user is still logged in on the same terminal, |
---|
178 | ** then write the error messages back to hir (sic). |
---|
179 | */ |
---|
180 | |
---|
181 | p = ttypath(); |
---|
182 | if (p == NULL || sm_io_reopen(SmFtStdio, |
---|
183 | SM_TIME_DEFAULT, |
---|
184 | p, SM_IO_WRONLY, NULL, |
---|
185 | smioout) == NULL) |
---|
186 | { |
---|
187 | state = ESM_MAIL; |
---|
188 | break; |
---|
189 | } |
---|
190 | |
---|
191 | expand("\201n", buf, sizeof buf, e); |
---|
192 | (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, |
---|
193 | "\r\nMessage from %s...\r\n", buf); |
---|
194 | (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, |
---|
195 | "Errors occurred while sending mail.\r\n"); |
---|
196 | if (e->e_xfp != NULL) |
---|
197 | { |
---|
198 | (void) bfrewind(e->e_xfp); |
---|
199 | (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, |
---|
200 | "Transcript follows:\r\n"); |
---|
201 | while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT, |
---|
202 | buf, sizeof buf) != NULL && |
---|
203 | !sm_io_error(smioout)) |
---|
204 | (void) sm_io_fputs(smioout, |
---|
205 | SM_TIME_DEFAULT, |
---|
206 | buf); |
---|
207 | } |
---|
208 | else |
---|
209 | { |
---|
210 | syserr("Cannot open %s", |
---|
211 | queuename(e, XSCRPT_LETTER)); |
---|
212 | (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, |
---|
213 | "Transcript of session is unavailable.\r\n"); |
---|
214 | } |
---|
215 | (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, |
---|
216 | "Original message will be saved in dead.letter.\r\n"); |
---|
217 | state = ESM_DEADLETTER; |
---|
218 | break; |
---|
219 | |
---|
220 | case ESM_MAIL: |
---|
221 | /* |
---|
222 | ** If mailing back, do it. |
---|
223 | ** Throw away all further output. Don't alias, |
---|
224 | ** since this could cause loops, e.g., if joe |
---|
225 | ** mails to joe@x, and for some reason the network |
---|
226 | ** for @x is down, then the response gets sent to |
---|
227 | ** joe@x, which gives a response, etc. Also force |
---|
228 | ** the mail to be delivered even if a version of |
---|
229 | ** it has already been sent to the sender. |
---|
230 | ** |
---|
231 | ** If this is a configuration or local software |
---|
232 | ** error, send to the local postmaster as well, |
---|
233 | ** since the originator can't do anything |
---|
234 | ** about it anyway. Note that this is a full |
---|
235 | ** copy of the message (intentionally) so that |
---|
236 | ** the Postmaster can forward things along. |
---|
237 | */ |
---|
238 | |
---|
239 | if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) |
---|
240 | { |
---|
241 | (void) sendtolist("postmaster", NULLADDR, |
---|
242 | &e->e_errorqueue, 0, e); |
---|
243 | } |
---|
244 | if (!emptyaddr(&e->e_from)) |
---|
245 | { |
---|
246 | char from[TOBUFSIZE]; |
---|
247 | |
---|
248 | if (sm_strlcpy(from, e->e_from.q_paddr, |
---|
249 | sizeof from) >= sizeof from) |
---|
250 | { |
---|
251 | state = ESM_POSTMASTER; |
---|
252 | break; |
---|
253 | } |
---|
254 | |
---|
255 | if (!DontPruneRoutes) |
---|
256 | (void) pruneroute(from); |
---|
257 | |
---|
258 | (void) sendtolist(from, NULLADDR, |
---|
259 | &e->e_errorqueue, 0, e); |
---|
260 | } |
---|
261 | |
---|
262 | /* |
---|
263 | ** Deliver a non-delivery report to the |
---|
264 | ** Postmaster-designate (not necessarily |
---|
265 | ** Postmaster). This does not include the |
---|
266 | ** body of the message, for privacy reasons. |
---|
267 | ** You really shouldn't need this. |
---|
268 | */ |
---|
269 | |
---|
270 | e->e_flags |= EF_PM_NOTIFY; |
---|
271 | |
---|
272 | /* check to see if there are any good addresses */ |
---|
273 | for (q = e->e_errorqueue; q != NULL; q = q->q_next) |
---|
274 | { |
---|
275 | if (QS_IS_SENDABLE(q->q_state)) |
---|
276 | break; |
---|
277 | } |
---|
278 | if (q == NULL) |
---|
279 | { |
---|
280 | /* this is an error-error */ |
---|
281 | state = ESM_POSTMASTER; |
---|
282 | break; |
---|
283 | } |
---|
284 | if (returntosender(e->e_message, e->e_errorqueue, |
---|
285 | sendbody ? RTSF_SEND_BODY |
---|
286 | : RTSF_NO_BODY, |
---|
287 | e) == 0) |
---|
288 | { |
---|
289 | state = ESM_DONE; |
---|
290 | break; |
---|
291 | } |
---|
292 | |
---|
293 | /* didn't work -- return to postmaster */ |
---|
294 | state = ESM_POSTMASTER; |
---|
295 | break; |
---|
296 | |
---|
297 | case ESM_POSTMASTER: |
---|
298 | /* |
---|
299 | ** Similar to previous case, but to system postmaster. |
---|
300 | */ |
---|
301 | |
---|
302 | q = NULL; |
---|
303 | expand(DoubleBounceAddr, buf, sizeof buf, e); |
---|
304 | |
---|
305 | /* |
---|
306 | ** Just drop it on the floor if DoubleBounceAddr |
---|
307 | ** expands to an empty string. |
---|
308 | */ |
---|
309 | |
---|
310 | if (*buf == '\0') |
---|
311 | { |
---|
312 | state = ESM_DONE; |
---|
313 | break; |
---|
314 | } |
---|
315 | if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0) |
---|
316 | { |
---|
317 | syserr("553 5.3.0 cannot parse %s!", buf); |
---|
318 | ExitStat = EX_SOFTWARE; |
---|
319 | state = ESM_DEADLETTERDROP; |
---|
320 | break; |
---|
321 | } |
---|
322 | flags = RTSF_PM_BOUNCE; |
---|
323 | if (sendbody) |
---|
324 | flags |= RTSF_SEND_BODY; |
---|
325 | if (returntosender(e->e_message, q, flags, e) == 0) |
---|
326 | { |
---|
327 | state = ESM_DONE; |
---|
328 | break; |
---|
329 | } |
---|
330 | |
---|
331 | /* didn't work -- last resort */ |
---|
332 | state = ESM_DEADLETTERDROP; |
---|
333 | break; |
---|
334 | |
---|
335 | case ESM_DEADLETTER: |
---|
336 | /* |
---|
337 | ** Save the message in dead.letter. |
---|
338 | ** If we weren't mailing back, and the user is |
---|
339 | ** local, we should save the message in |
---|
340 | ** ~/dead.letter so that the poor person doesn't |
---|
341 | ** have to type it over again -- and we all know |
---|
342 | ** what poor typists UNIX users are. |
---|
343 | */ |
---|
344 | |
---|
345 | p = NULL; |
---|
346 | if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) |
---|
347 | { |
---|
348 | if (e->e_from.q_home != NULL) |
---|
349 | p = e->e_from.q_home; |
---|
350 | else if (sm_mbdb_lookup(e->e_from.q_user, &user) |
---|
351 | == EX_OK && |
---|
352 | *user.mbdb_homedir != '\0') |
---|
353 | p = user.mbdb_homedir; |
---|
354 | } |
---|
355 | if (p == NULL || e->e_dfp == NULL) |
---|
356 | { |
---|
357 | /* no local directory or no data file */ |
---|
358 | state = ESM_MAIL; |
---|
359 | break; |
---|
360 | } |
---|
361 | |
---|
362 | /* we have a home directory; write dead.letter */ |
---|
363 | macdefine(&e->e_macro, A_TEMP, 'z', p); |
---|
364 | |
---|
365 | /* get the sender for the UnixFromLine */ |
---|
366 | p = macvalue('g', e); |
---|
367 | macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); |
---|
368 | |
---|
369 | expand("\201z/dead.letter", dlbuf, sizeof dlbuf, e); |
---|
370 | sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; |
---|
371 | if (RealUid == 0) |
---|
372 | sff |= SFF_ROOTOK; |
---|
373 | e->e_to = dlbuf; |
---|
374 | if (writable(dlbuf, NULL, sff) && |
---|
375 | mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK) |
---|
376 | { |
---|
377 | int oldverb = Verbose; |
---|
378 | |
---|
379 | if (OpMode != MD_DAEMON && OpMode != MD_SMTP) |
---|
380 | Verbose = 1; |
---|
381 | if (Verbose > 0) |
---|
382 | message("Saved message in %s", dlbuf); |
---|
383 | Verbose = oldverb; |
---|
384 | macdefine(&e->e_macro, A_PERM, 'g', p); |
---|
385 | state = ESM_DONE; |
---|
386 | break; |
---|
387 | } |
---|
388 | macdefine(&e->e_macro, A_PERM, 'g', p); |
---|
389 | state = ESM_MAIL; |
---|
390 | break; |
---|
391 | |
---|
392 | case ESM_DEADLETTERDROP: |
---|
393 | /* |
---|
394 | ** Log the mail in DeadLetterDrop file. |
---|
395 | */ |
---|
396 | |
---|
397 | if (e->e_class < 0) |
---|
398 | { |
---|
399 | state = ESM_DONE; |
---|
400 | break; |
---|
401 | } |
---|
402 | |
---|
403 | if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || |
---|
404 | DeadLetterDrop == NULL || |
---|
405 | DeadLetterDrop[0] == '\0') |
---|
406 | { |
---|
407 | state = ESM_PANIC; |
---|
408 | break; |
---|
409 | } |
---|
410 | |
---|
411 | sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; |
---|
412 | if (!writable(DeadLetterDrop, NULL, sff) || |
---|
413 | (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, |
---|
414 | FileMode, sff)) == NULL) |
---|
415 | { |
---|
416 | state = ESM_PANIC; |
---|
417 | break; |
---|
418 | } |
---|
419 | |
---|
420 | memset(&mcibuf, '\0', sizeof mcibuf); |
---|
421 | mcibuf.mci_out = fp; |
---|
422 | mcibuf.mci_mailer = FileMailer; |
---|
423 | if (bitnset(M_7BITS, FileMailer->m_flags)) |
---|
424 | mcibuf.mci_flags |= MCIF_7BIT; |
---|
425 | |
---|
426 | /* get the sender for the UnixFromLine */ |
---|
427 | p = macvalue('g', e); |
---|
428 | macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); |
---|
429 | |
---|
430 | if (!putfromline(&mcibuf, e) || |
---|
431 | !(*e->e_puthdr)(&mcibuf, e->e_header, e, |
---|
432 | M87F_OUTER) || |
---|
433 | !(*e->e_putbody)(&mcibuf, e, NULL) || |
---|
434 | !putline("\n", &mcibuf) || |
---|
435 | sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF || |
---|
436 | sm_io_error(fp) || |
---|
437 | sm_io_close(fp, SM_TIME_DEFAULT) < 0) |
---|
438 | state = ESM_PANIC; |
---|
439 | else |
---|
440 | { |
---|
441 | int oldverb = Verbose; |
---|
442 | |
---|
443 | if (OpMode != MD_DAEMON && OpMode != MD_SMTP) |
---|
444 | Verbose = 1; |
---|
445 | if (Verbose > 0) |
---|
446 | message("Saved message in %s", |
---|
447 | DeadLetterDrop); |
---|
448 | Verbose = oldverb; |
---|
449 | if (LogLevel > 3) |
---|
450 | sm_syslog(LOG_NOTICE, e->e_id, |
---|
451 | "Saved message in %s", |
---|
452 | DeadLetterDrop); |
---|
453 | state = ESM_DONE; |
---|
454 | } |
---|
455 | macdefine(&e->e_macro, A_PERM, 'g', p); |
---|
456 | break; |
---|
457 | |
---|
458 | default: |
---|
459 | syserr("554 5.3.5 savemail: unknown state %d", state); |
---|
460 | /* FALLTHROUGH */ |
---|
461 | |
---|
462 | case ESM_PANIC: |
---|
463 | /* leave the locked queue & transcript files around */ |
---|
464 | loseqfile(e, "savemail panic"); |
---|
465 | panic = true; |
---|
466 | errno = 0; |
---|
467 | syserr("554 savemail: cannot save rejected email anywhere"); |
---|
468 | state = ESM_DONE; |
---|
469 | break; |
---|
470 | } |
---|
471 | } |
---|
472 | return panic; |
---|
473 | } |
---|
474 | /* |
---|
475 | ** RETURNTOSENDER -- return a message to the sender with an error. |
---|
476 | ** |
---|
477 | ** Parameters: |
---|
478 | ** msg -- the explanatory message. |
---|
479 | ** returnq -- the queue of people to send the message to. |
---|
480 | ** flags -- flags tweaking the operation: |
---|
481 | ** RTSF_SENDBODY -- include body of message (otherwise |
---|
482 | ** just send the header). |
---|
483 | ** RTSF_PMBOUNCE -- this is a postmaster bounce. |
---|
484 | ** e -- the current envelope. |
---|
485 | ** |
---|
486 | ** Returns: |
---|
487 | ** zero -- if everything went ok. |
---|
488 | ** else -- some error. |
---|
489 | ** |
---|
490 | ** Side Effects: |
---|
491 | ** Returns the current message to the sender via mail. |
---|
492 | */ |
---|
493 | |
---|
494 | #define MAXRETURNS 6 /* max depth of returning messages */ |
---|
495 | #define ERRORFUDGE 1024 /* nominal size of error message text */ |
---|
496 | |
---|
497 | int |
---|
498 | returntosender(msg, returnq, flags, e) |
---|
499 | char *msg; |
---|
500 | ADDRESS *returnq; |
---|
501 | int flags; |
---|
502 | register ENVELOPE *e; |
---|
503 | { |
---|
504 | register ENVELOPE *ee; |
---|
505 | ENVELOPE *oldcur = CurEnv; |
---|
506 | ENVELOPE errenvelope; |
---|
507 | static int returndepth = 0; |
---|
508 | register ADDRESS *q; |
---|
509 | char *p; |
---|
510 | char buf[MAXNAME + 1]; |
---|
511 | |
---|
512 | if (returnq == NULL) |
---|
513 | return -1; |
---|
514 | |
---|
515 | if (msg == NULL) |
---|
516 | msg = "Unable to deliver mail"; |
---|
517 | |
---|
518 | if (tTd(6, 1)) |
---|
519 | { |
---|
520 | sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=", |
---|
521 | msg, returndepth, e); |
---|
522 | printaddr(returnq, true); |
---|
523 | if (tTd(6, 20)) |
---|
524 | { |
---|
525 | sm_dprintf("Sendq="); |
---|
526 | printaddr(e->e_sendqueue, true); |
---|
527 | } |
---|
528 | } |
---|
529 | |
---|
530 | if (++returndepth >= MAXRETURNS) |
---|
531 | { |
---|
532 | if (returndepth != MAXRETURNS) |
---|
533 | syserr("554 5.3.0 returntosender: infinite recursion on %s", |
---|
534 | returnq->q_paddr); |
---|
535 | /* don't "unrecurse" and fake a clean exit */ |
---|
536 | /* returndepth--; */ |
---|
537 | return 0; |
---|
538 | } |
---|
539 | |
---|
540 | macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); |
---|
541 | macdefine(&e->e_macro, A_PERM, 'u', NULL); |
---|
542 | |
---|
543 | /* initialize error envelope */ |
---|
544 | ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL)); |
---|
545 | macdefine(&ee->e_macro, A_PERM, 'a', "\201b"); |
---|
546 | macdefine(&ee->e_macro, A_PERM, 'r', ""); |
---|
547 | macdefine(&ee->e_macro, A_PERM, 's', "localhost"); |
---|
548 | macdefine(&ee->e_macro, A_PERM, '_', "localhost"); |
---|
549 | clrsessenvelope(ee); |
---|
550 | |
---|
551 | ee->e_puthdr = putheader; |
---|
552 | ee->e_putbody = errbody; |
---|
553 | ee->e_flags |= EF_RESPONSE|EF_METOO; |
---|
554 | if (!bitset(EF_OLDSTYLE, e->e_flags)) |
---|
555 | ee->e_flags &= ~EF_OLDSTYLE; |
---|
556 | if (bitset(EF_DONT_MIME, e->e_flags)) |
---|
557 | { |
---|
558 | ee->e_flags |= EF_DONT_MIME; |
---|
559 | |
---|
560 | /* |
---|
561 | ** If we can't convert to MIME and we don't pass |
---|
562 | ** 8-bit, we can't send the body. |
---|
563 | */ |
---|
564 | |
---|
565 | if (bitset(EF_HAS8BIT, e->e_flags) && |
---|
566 | !bitset(MM_PASS8BIT, MimeMode)) |
---|
567 | flags &= ~RTSF_SEND_BODY; |
---|
568 | } |
---|
569 | |
---|
570 | ee->e_sendqueue = returnq; |
---|
571 | ee->e_msgsize = 0; |
---|
572 | if (bitset(RTSF_SEND_BODY, flags) && |
---|
573 | !bitset(PRIV_NOBODYRETN, PrivacyFlags)) |
---|
574 | ee->e_msgsize = ERRORFUDGE + e->e_msgsize; |
---|
575 | else |
---|
576 | ee->e_flags |= EF_NO_BODY_RETN; |
---|
577 | |
---|
578 | if (!setnewqueue(ee)) |
---|
579 | { |
---|
580 | syserr("554 5.3.0 returntosender: cannot select queue for %s", |
---|
581 | returnq->q_paddr); |
---|
582 | ExitStat = EX_UNAVAILABLE; |
---|
583 | returndepth--; |
---|
584 | return -1; |
---|
585 | } |
---|
586 | initsys(ee); |
---|
587 | |
---|
588 | #if NAMED_BIND |
---|
589 | _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; |
---|
590 | _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; |
---|
591 | #endif /* NAMED_BIND */ |
---|
592 | for (q = returnq; q != NULL; q = q->q_next) |
---|
593 | { |
---|
594 | if (QS_IS_BADADDR(q->q_state)) |
---|
595 | continue; |
---|
596 | |
---|
597 | q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); |
---|
598 | q->q_flags |= QPINGONFAILURE; |
---|
599 | |
---|
600 | if (!QS_IS_DEAD(q->q_state)) |
---|
601 | ee->e_nrcpts++; |
---|
602 | |
---|
603 | if (q->q_alias == NULL) |
---|
604 | addheader("To", q->q_paddr, 0, ee); |
---|
605 | } |
---|
606 | |
---|
607 | if (LogLevel > 5) |
---|
608 | { |
---|
609 | if (bitset(EF_RESPONSE, e->e_flags)) |
---|
610 | p = "return to sender"; |
---|
611 | else if (bitset(EF_WARNING, e->e_flags)) |
---|
612 | p = "sender notify"; |
---|
613 | else if (bitset(RTSF_PM_BOUNCE, flags)) |
---|
614 | p = "postmaster notify"; |
---|
615 | else |
---|
616 | p = "DSN"; |
---|
617 | sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s", |
---|
618 | ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); |
---|
619 | } |
---|
620 | |
---|
621 | if (SendMIMEErrors) |
---|
622 | { |
---|
623 | addheader("MIME-Version", "1.0", 0, ee); |
---|
624 | (void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s", |
---|
625 | ee->e_id, (long)curtime(), MyHostName); |
---|
626 | ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf); |
---|
627 | (void) sm_snprintf(buf, sizeof buf, |
---|
628 | #if DSN |
---|
629 | "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", |
---|
630 | #else /* DSN */ |
---|
631 | "multipart/mixed; boundary=\"%s\"", |
---|
632 | #endif /* DSN */ |
---|
633 | ee->e_msgboundary); |
---|
634 | addheader("Content-Type", buf, 0, ee); |
---|
635 | |
---|
636 | p = hvalue("Content-Transfer-Encoding", e->e_header); |
---|
637 | if (p != NULL && sm_strcasecmp(p, "binary") != 0) |
---|
638 | p = NULL; |
---|
639 | if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) |
---|
640 | p = "8bit"; |
---|
641 | if (p != NULL) |
---|
642 | addheader("Content-Transfer-Encoding", p, 0, ee); |
---|
643 | } |
---|
644 | if (strncmp(msg, "Warning:", 8) == 0) |
---|
645 | { |
---|
646 | addheader("Subject", msg, 0, ee); |
---|
647 | p = "warning-timeout"; |
---|
648 | } |
---|
649 | else if (strncmp(msg, "Postmaster warning:", 19) == 0) |
---|
650 | { |
---|
651 | addheader("Subject", msg, 0, ee); |
---|
652 | p = "postmaster-warning"; |
---|
653 | } |
---|
654 | else if (strcmp(msg, "Return receipt") == 0) |
---|
655 | { |
---|
656 | addheader("Subject", msg, 0, ee); |
---|
657 | p = "return-receipt"; |
---|
658 | } |
---|
659 | else if (bitset(RTSF_PM_BOUNCE, flags)) |
---|
660 | { |
---|
661 | (void) sm_snprintf(buf, sizeof buf, |
---|
662 | "Postmaster notify: see transcript for details"); |
---|
663 | addheader("Subject", buf, 0, ee); |
---|
664 | p = "postmaster-notification"; |
---|
665 | } |
---|
666 | else |
---|
667 | { |
---|
668 | (void) sm_snprintf(buf, sizeof buf, |
---|
669 | "Returned mail: see transcript for details"); |
---|
670 | addheader("Subject", buf, 0, ee); |
---|
671 | p = "failure"; |
---|
672 | } |
---|
673 | (void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p); |
---|
674 | addheader("Auto-Submitted", buf, 0, ee); |
---|
675 | |
---|
676 | /* fake up an address header for the from person */ |
---|
677 | expand("\201n", buf, sizeof buf, e); |
---|
678 | if (parseaddr(buf, &ee->e_from, |
---|
679 | RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL) |
---|
680 | { |
---|
681 | syserr("553 5.3.5 Can't parse myself!"); |
---|
682 | ExitStat = EX_SOFTWARE; |
---|
683 | returndepth--; |
---|
684 | return -1; |
---|
685 | } |
---|
686 | ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); |
---|
687 | ee->e_from.q_flags |= QPINGONFAILURE; |
---|
688 | ee->e_sender = ee->e_from.q_paddr; |
---|
689 | |
---|
690 | /* push state into submessage */ |
---|
691 | CurEnv = ee; |
---|
692 | macdefine(&ee->e_macro, A_PERM, 'f', "\201n"); |
---|
693 | macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem"); |
---|
694 | eatheader(ee, true, true); |
---|
695 | |
---|
696 | /* mark statistics */ |
---|
697 | markstats(ee, NULLADDR, STATS_NORMAL); |
---|
698 | |
---|
699 | /* actually deliver the error message */ |
---|
700 | sendall(ee, SM_DELIVER); |
---|
701 | |
---|
702 | /* restore state */ |
---|
703 | dropenvelope(ee, true, false); |
---|
704 | sm_rpool_free(ee->e_rpool); |
---|
705 | CurEnv = oldcur; |
---|
706 | returndepth--; |
---|
707 | |
---|
708 | /* check for delivery errors */ |
---|
709 | if (ee->e_parent == NULL || |
---|
710 | !bitset(EF_RESPONSE, ee->e_parent->e_flags)) |
---|
711 | return 0; |
---|
712 | for (q = ee->e_sendqueue; q != NULL; q = q->q_next) |
---|
713 | { |
---|
714 | if (QS_IS_ATTEMPTED(q->q_state)) |
---|
715 | return 0; |
---|
716 | } |
---|
717 | return -1; |
---|
718 | } |
---|
719 | /* |
---|
720 | ** ERRBODY -- output the body of an error message. |
---|
721 | ** |
---|
722 | ** Typically this is a copy of the transcript plus a copy of the |
---|
723 | ** original offending message. |
---|
724 | ** |
---|
725 | ** Parameters: |
---|
726 | ** mci -- the mailer connection information. |
---|
727 | ** e -- the envelope we are working in. |
---|
728 | ** separator -- any possible MIME separator (unused). |
---|
729 | ** |
---|
730 | ** Returns: |
---|
731 | ** success |
---|
732 | ** |
---|
733 | ** Side Effects: |
---|
734 | ** Outputs the body of an error message. |
---|
735 | */ |
---|
736 | |
---|
737 | /* ARGSUSED2 */ |
---|
738 | static bool |
---|
739 | errbody(mci, e, separator) |
---|
740 | register MCI *mci; |
---|
741 | register ENVELOPE *e; |
---|
742 | char *separator; |
---|
743 | { |
---|
744 | bool printheader; |
---|
745 | bool sendbody; |
---|
746 | bool pm_notify; |
---|
747 | int save_errno; |
---|
748 | register SM_FILE_T *xfile; |
---|
749 | char *p; |
---|
750 | register ADDRESS *q = NULL; |
---|
751 | char actual[MAXLINE]; |
---|
752 | char buf[MAXLINE]; |
---|
753 | |
---|
754 | if (bitset(MCIF_INHEADER, mci->mci_flags)) |
---|
755 | { |
---|
756 | if (!putline("", mci)) |
---|
757 | goto writeerr; |
---|
758 | mci->mci_flags &= ~MCIF_INHEADER; |
---|
759 | } |
---|
760 | if (e->e_parent == NULL) |
---|
761 | { |
---|
762 | syserr("errbody: null parent"); |
---|
763 | if (!putline(" ----- Original message lost -----\n", mci)) |
---|
764 | goto writeerr; |
---|
765 | return true; |
---|
766 | } |
---|
767 | |
---|
768 | /* |
---|
769 | ** Output MIME header. |
---|
770 | */ |
---|
771 | |
---|
772 | if (e->e_msgboundary != NULL) |
---|
773 | { |
---|
774 | (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); |
---|
775 | if (!putline("This is a MIME-encapsulated message", mci) || |
---|
776 | !putline("", mci) || |
---|
777 | !putline(buf, mci) || |
---|
778 | !putline("", mci)) |
---|
779 | goto writeerr; |
---|
780 | } |
---|
781 | |
---|
782 | /* |
---|
783 | ** Output introductory information. |
---|
784 | */ |
---|
785 | |
---|
786 | pm_notify = false; |
---|
787 | p = hvalue("subject", e->e_header); |
---|
788 | if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) |
---|
789 | pm_notify = true; |
---|
790 | else |
---|
791 | { |
---|
792 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
793 | { |
---|
794 | if (QS_IS_BADADDR(q->q_state)) |
---|
795 | break; |
---|
796 | } |
---|
797 | } |
---|
798 | if (!pm_notify && q == NULL && |
---|
799 | !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) |
---|
800 | { |
---|
801 | if (!putline(" **********************************************", |
---|
802 | mci) || |
---|
803 | !putline(" ** THIS IS A WARNING MESSAGE ONLY **", |
---|
804 | mci) || |
---|
805 | !putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", |
---|
806 | mci) || |
---|
807 | !putline(" **********************************************", |
---|
808 | mci) || |
---|
809 | !putline("", mci)) |
---|
810 | goto writeerr; |
---|
811 | } |
---|
812 | (void) sm_snprintf(buf, sizeof buf, |
---|
813 | "The original message was received at %s", |
---|
814 | arpadate(ctime(&e->e_parent->e_ctime))); |
---|
815 | if (!putline(buf, mci)) |
---|
816 | goto writeerr; |
---|
817 | expand("from \201_", buf, sizeof buf, e->e_parent); |
---|
818 | if (!putline(buf, mci)) |
---|
819 | goto writeerr; |
---|
820 | |
---|
821 | /* include id in postmaster copies */ |
---|
822 | if (pm_notify && e->e_parent->e_id != NULL) |
---|
823 | { |
---|
824 | (void) sm_strlcpyn(buf, sizeof buf, 2, "with id ", |
---|
825 | e->e_parent->e_id); |
---|
826 | if (!putline(buf, mci)) |
---|
827 | goto writeerr; |
---|
828 | } |
---|
829 | if (!putline("", mci)) |
---|
830 | goto writeerr; |
---|
831 | |
---|
832 | /* |
---|
833 | ** Output error message header (if specified and available). |
---|
834 | */ |
---|
835 | |
---|
836 | if (ErrMsgFile != NULL && |
---|
837 | !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) |
---|
838 | { |
---|
839 | if (*ErrMsgFile == '/') |
---|
840 | { |
---|
841 | long sff = SFF_ROOTOK|SFF_REGONLY; |
---|
842 | |
---|
843 | if (DontLockReadFiles) |
---|
844 | sff |= SFF_NOLOCK; |
---|
845 | if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH, |
---|
846 | DontBlameSendmail)) |
---|
847 | sff |= SFF_SAFEDIRPATH; |
---|
848 | xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); |
---|
849 | if (xfile != NULL) |
---|
850 | { |
---|
851 | while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf, |
---|
852 | sizeof buf) != NULL) |
---|
853 | { |
---|
854 | translate_dollars(buf); |
---|
855 | expand(buf, buf, sizeof buf, e); |
---|
856 | if (!putline(buf, mci)) |
---|
857 | goto writeerr; |
---|
858 | } |
---|
859 | (void) sm_io_close(xfile, SM_TIME_DEFAULT); |
---|
860 | if (!putline("\n", mci)) |
---|
861 | goto writeerr; |
---|
862 | } |
---|
863 | } |
---|
864 | else |
---|
865 | { |
---|
866 | expand(ErrMsgFile, buf, sizeof buf, e); |
---|
867 | if (!putline(buf, mci) || !putline("", mci)) |
---|
868 | goto writeerr; |
---|
869 | } |
---|
870 | } |
---|
871 | |
---|
872 | /* |
---|
873 | ** Output message introduction |
---|
874 | */ |
---|
875 | |
---|
876 | /* permanent fatal errors */ |
---|
877 | printheader = true; |
---|
878 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
879 | { |
---|
880 | if (!QS_IS_BADADDR(q->q_state) || |
---|
881 | !bitset(QPINGONFAILURE, q->q_flags)) |
---|
882 | continue; |
---|
883 | |
---|
884 | if (printheader) |
---|
885 | { |
---|
886 | if (!putline(" ----- The following addresses had permanent fatal errors -----", |
---|
887 | mci)) |
---|
888 | goto writeerr; |
---|
889 | printheader = false; |
---|
890 | } |
---|
891 | |
---|
892 | (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), |
---|
893 | sizeof buf); |
---|
894 | if (!putline(buf, mci)) |
---|
895 | goto writeerr; |
---|
896 | if (q->q_rstatus != NULL) |
---|
897 | { |
---|
898 | (void) sm_snprintf(buf, sizeof buf, |
---|
899 | " (reason: %s)", |
---|
900 | shortenstring(exitstat(q->q_rstatus), |
---|
901 | MAXSHORTSTR)); |
---|
902 | if (!putline(buf, mci)) |
---|
903 | goto writeerr; |
---|
904 | } |
---|
905 | if (q->q_alias != NULL) |
---|
906 | { |
---|
907 | (void) sm_snprintf(buf, sizeof buf, |
---|
908 | " (expanded from: %s)", |
---|
909 | shortenstring(q->q_alias->q_paddr, |
---|
910 | MAXSHORTSTR)); |
---|
911 | if (!putline(buf, mci)) |
---|
912 | goto writeerr; |
---|
913 | } |
---|
914 | } |
---|
915 | if (!printheader && !putline("", mci)) |
---|
916 | goto writeerr; |
---|
917 | |
---|
918 | /* transient non-fatal errors */ |
---|
919 | printheader = true; |
---|
920 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
921 | { |
---|
922 | if (QS_IS_BADADDR(q->q_state) || |
---|
923 | !bitset(QPRIMARY, q->q_flags) || |
---|
924 | !bitset(QBYNDELAY, q->q_flags) || |
---|
925 | !bitset(QDELAYED, q->q_flags)) |
---|
926 | continue; |
---|
927 | |
---|
928 | if (printheader) |
---|
929 | { |
---|
930 | if (!putline(" ----- The following addresses had transient non-fatal errors -----", |
---|
931 | mci)) |
---|
932 | goto writeerr; |
---|
933 | printheader = false; |
---|
934 | } |
---|
935 | |
---|
936 | (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), |
---|
937 | sizeof buf); |
---|
938 | if (!putline(buf, mci)) |
---|
939 | goto writeerr; |
---|
940 | if (q->q_alias != NULL) |
---|
941 | { |
---|
942 | (void) sm_snprintf(buf, sizeof buf, |
---|
943 | " (expanded from: %s)", |
---|
944 | shortenstring(q->q_alias->q_paddr, |
---|
945 | MAXSHORTSTR)); |
---|
946 | if (!putline(buf, mci)) |
---|
947 | goto writeerr; |
---|
948 | } |
---|
949 | } |
---|
950 | if (!printheader && !putline("", mci)) |
---|
951 | goto writeerr; |
---|
952 | |
---|
953 | /* successful delivery notifications */ |
---|
954 | printheader = true; |
---|
955 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
956 | { |
---|
957 | if (QS_IS_BADADDR(q->q_state) || |
---|
958 | !bitset(QPRIMARY, q->q_flags) || |
---|
959 | bitset(QBYNDELAY, q->q_flags) || |
---|
960 | bitset(QDELAYED, q->q_flags)) |
---|
961 | continue; |
---|
962 | else if (bitset(QBYNRELAY, q->q_flags)) |
---|
963 | p = "Deliver-By notify: relayed"; |
---|
964 | else if (bitset(QBYTRACE, q->q_flags)) |
---|
965 | p = "Deliver-By trace: relayed"; |
---|
966 | else if (!bitset(QPINGONSUCCESS, q->q_flags)) |
---|
967 | continue; |
---|
968 | else if (bitset(QRELAYED, q->q_flags)) |
---|
969 | p = "relayed to non-DSN-aware mailer"; |
---|
970 | else if (bitset(QDELIVERED, q->q_flags)) |
---|
971 | { |
---|
972 | if (bitset(QEXPANDED, q->q_flags)) |
---|
973 | p = "successfully delivered to mailing list"; |
---|
974 | else |
---|
975 | p = "successfully delivered to mailbox"; |
---|
976 | } |
---|
977 | else if (bitset(QEXPANDED, q->q_flags)) |
---|
978 | p = "expanded by alias"; |
---|
979 | else |
---|
980 | continue; |
---|
981 | |
---|
982 | if (printheader) |
---|
983 | { |
---|
984 | if (!putline(" ----- The following addresses had successful delivery notifications -----", |
---|
985 | mci)) |
---|
986 | goto writeerr; |
---|
987 | printheader = false; |
---|
988 | } |
---|
989 | |
---|
990 | (void) sm_snprintf(buf, sizeof buf, "%s (%s)", |
---|
991 | shortenstring(q->q_paddr, MAXSHORTSTR), p); |
---|
992 | if (!putline(buf, mci)) |
---|
993 | goto writeerr; |
---|
994 | if (q->q_alias != NULL) |
---|
995 | { |
---|
996 | (void) sm_snprintf(buf, sizeof buf, |
---|
997 | " (expanded from: %s)", |
---|
998 | shortenstring(q->q_alias->q_paddr, |
---|
999 | MAXSHORTSTR)); |
---|
1000 | if (!putline(buf, mci)) |
---|
1001 | goto writeerr; |
---|
1002 | } |
---|
1003 | } |
---|
1004 | if (!printheader && !putline("", mci)) |
---|
1005 | goto writeerr; |
---|
1006 | |
---|
1007 | /* |
---|
1008 | ** Output transcript of errors |
---|
1009 | */ |
---|
1010 | |
---|
1011 | (void) sm_io_flush(smioout, SM_TIME_DEFAULT); |
---|
1012 | if (e->e_parent->e_xfp == NULL) |
---|
1013 | { |
---|
1014 | if (!putline(" ----- Transcript of session is unavailable -----\n", |
---|
1015 | mci)) |
---|
1016 | goto writeerr; |
---|
1017 | } |
---|
1018 | else |
---|
1019 | { |
---|
1020 | printheader = true; |
---|
1021 | (void) bfrewind(e->e_parent->e_xfp); |
---|
1022 | if (e->e_xfp != NULL) |
---|
1023 | (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); |
---|
1024 | while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf, |
---|
1025 | sizeof buf) != NULL) |
---|
1026 | { |
---|
1027 | if (printheader && !putline(" ----- Transcript of session follows -----\n", |
---|
1028 | mci)) |
---|
1029 | goto writeerr; |
---|
1030 | printheader = false; |
---|
1031 | if (!putline(buf, mci)) |
---|
1032 | goto writeerr; |
---|
1033 | } |
---|
1034 | } |
---|
1035 | errno = 0; |
---|
1036 | |
---|
1037 | #if DSN |
---|
1038 | /* |
---|
1039 | ** Output machine-readable version. |
---|
1040 | */ |
---|
1041 | |
---|
1042 | if (e->e_msgboundary != NULL) |
---|
1043 | { |
---|
1044 | (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); |
---|
1045 | if (!putline("", mci) || |
---|
1046 | !putline(buf, mci) || |
---|
1047 | !putline("Content-Type: message/delivery-status", mci) || |
---|
1048 | !putline("", mci)) |
---|
1049 | goto writeerr; |
---|
1050 | |
---|
1051 | /* |
---|
1052 | ** Output per-message information. |
---|
1053 | */ |
---|
1054 | |
---|
1055 | /* original envelope id from MAIL FROM: line */ |
---|
1056 | if (e->e_parent->e_envid != NULL) |
---|
1057 | { |
---|
1058 | (void) sm_snprintf(buf, sizeof buf, |
---|
1059 | "Original-Envelope-Id: %.800s", |
---|
1060 | xuntextify(e->e_parent->e_envid)); |
---|
1061 | if (!putline(buf, mci)) |
---|
1062 | goto writeerr; |
---|
1063 | } |
---|
1064 | |
---|
1065 | /* Reporting-MTA: is us (required) */ |
---|
1066 | (void) sm_snprintf(buf, sizeof buf, |
---|
1067 | "Reporting-MTA: dns; %.800s", MyHostName); |
---|
1068 | if (!putline(buf, mci)) |
---|
1069 | goto writeerr; |
---|
1070 | |
---|
1071 | /* DSN-Gateway: not relevant since we are not translating */ |
---|
1072 | |
---|
1073 | /* Received-From-MTA: shows where we got this message from */ |
---|
1074 | if (RealHostName != NULL) |
---|
1075 | { |
---|
1076 | /* XXX use $s for type? */ |
---|
1077 | if (e->e_parent->e_from.q_mailer == NULL || |
---|
1078 | (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) |
---|
1079 | p = "dns"; |
---|
1080 | (void) sm_snprintf(buf, sizeof buf, |
---|
1081 | "Received-From-MTA: %s; %.800s", |
---|
1082 | p, RealHostName); |
---|
1083 | if (!putline(buf, mci)) |
---|
1084 | goto writeerr; |
---|
1085 | } |
---|
1086 | |
---|
1087 | /* Arrival-Date: -- when it arrived here */ |
---|
1088 | (void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ", |
---|
1089 | arpadate(ctime(&e->e_parent->e_ctime))); |
---|
1090 | if (!putline(buf, mci)) |
---|
1091 | goto writeerr; |
---|
1092 | |
---|
1093 | /* Deliver-By-Date: -- when it should have been delivered */ |
---|
1094 | if (IS_DLVR_BY(e->e_parent)) |
---|
1095 | { |
---|
1096 | time_t dbyd; |
---|
1097 | |
---|
1098 | dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by; |
---|
1099 | (void) sm_strlcpyn(buf, sizeof buf, 2, |
---|
1100 | "Deliver-By-Date: ", |
---|
1101 | arpadate(ctime(&dbyd))); |
---|
1102 | if (!putline(buf, mci)) |
---|
1103 | goto writeerr; |
---|
1104 | } |
---|
1105 | |
---|
1106 | /* |
---|
1107 | ** Output per-address information. |
---|
1108 | */ |
---|
1109 | |
---|
1110 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
1111 | { |
---|
1112 | char *action; |
---|
1113 | |
---|
1114 | if (QS_IS_BADADDR(q->q_state)) |
---|
1115 | { |
---|
1116 | /* RFC 1891, 6.2.6 (b) */ |
---|
1117 | if (bitset(QHASNOTIFY, q->q_flags) && |
---|
1118 | !bitset(QPINGONFAILURE, q->q_flags)) |
---|
1119 | continue; |
---|
1120 | action = "failed"; |
---|
1121 | } |
---|
1122 | else if (!bitset(QPRIMARY, q->q_flags)) |
---|
1123 | continue; |
---|
1124 | else if (bitset(QDELIVERED, q->q_flags)) |
---|
1125 | { |
---|
1126 | if (bitset(QEXPANDED, q->q_flags)) |
---|
1127 | action = "delivered (to mailing list)"; |
---|
1128 | else |
---|
1129 | action = "delivered (to mailbox)"; |
---|
1130 | } |
---|
1131 | else if (bitset(QRELAYED, q->q_flags)) |
---|
1132 | action = "relayed (to non-DSN-aware mailer)"; |
---|
1133 | else if (bitset(QEXPANDED, q->q_flags)) |
---|
1134 | action = "expanded (to multi-recipient alias)"; |
---|
1135 | else if (bitset(QDELAYED, q->q_flags)) |
---|
1136 | action = "delayed"; |
---|
1137 | else if (bitset(QBYTRACE, q->q_flags)) |
---|
1138 | action = "relayed (Deliver-By trace mode)"; |
---|
1139 | else if (bitset(QBYNDELAY, q->q_flags)) |
---|
1140 | action = "delayed (Deliver-By notify mode)"; |
---|
1141 | else if (bitset(QBYNRELAY, q->q_flags)) |
---|
1142 | action = "relayed (Deliver-By notify mode)"; |
---|
1143 | else |
---|
1144 | continue; |
---|
1145 | |
---|
1146 | if (!putline("", mci)) |
---|
1147 | goto writeerr; |
---|
1148 | |
---|
1149 | /* Original-Recipient: -- passed from on high */ |
---|
1150 | if (q->q_orcpt != NULL) |
---|
1151 | { |
---|
1152 | (void) sm_snprintf(buf, sizeof buf, |
---|
1153 | "Original-Recipient: %.800s", |
---|
1154 | q->q_orcpt); |
---|
1155 | if (!putline(buf, mci)) |
---|
1156 | goto writeerr; |
---|
1157 | } |
---|
1158 | |
---|
1159 | /* Figure out actual recipient */ |
---|
1160 | actual[0] = '\0'; |
---|
1161 | if (q->q_user[0] != '\0') |
---|
1162 | { |
---|
1163 | if (q->q_mailer != NULL && |
---|
1164 | q->q_mailer->m_addrtype != NULL) |
---|
1165 | p = q->q_mailer->m_addrtype; |
---|
1166 | else |
---|
1167 | p = "rfc822"; |
---|
1168 | |
---|
1169 | if (sm_strcasecmp(p, "rfc822") == 0 && |
---|
1170 | strchr(q->q_user, '@') == NULL) |
---|
1171 | { |
---|
1172 | (void) sm_snprintf(actual, |
---|
1173 | sizeof actual, |
---|
1174 | "%s; %.700s@%.100s", |
---|
1175 | p, q->q_user, |
---|
1176 | MyHostName); |
---|
1177 | } |
---|
1178 | else |
---|
1179 | { |
---|
1180 | (void) sm_snprintf(actual, |
---|
1181 | sizeof actual, |
---|
1182 | "%s; %.800s", |
---|
1183 | p, q->q_user); |
---|
1184 | } |
---|
1185 | } |
---|
1186 | |
---|
1187 | /* Final-Recipient: -- the name from the RCPT command */ |
---|
1188 | if (q->q_finalrcpt == NULL) |
---|
1189 | { |
---|
1190 | /* should never happen */ |
---|
1191 | sm_syslog(LOG_ERR, e->e_id, |
---|
1192 | "returntosender: q_finalrcpt is NULL"); |
---|
1193 | |
---|
1194 | /* try to fall back to the actual recipient */ |
---|
1195 | if (actual[0] != '\0') |
---|
1196 | q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, |
---|
1197 | actual); |
---|
1198 | } |
---|
1199 | |
---|
1200 | if (q->q_finalrcpt != NULL) |
---|
1201 | { |
---|
1202 | (void) sm_snprintf(buf, sizeof buf, |
---|
1203 | "Final-Recipient: %s", |
---|
1204 | q->q_finalrcpt); |
---|
1205 | if (!putline(buf, mci)) |
---|
1206 | goto writeerr; |
---|
1207 | } |
---|
1208 | |
---|
1209 | /* X-Actual-Recipient: -- the real problem address */ |
---|
1210 | if (actual[0] != '\0' && |
---|
1211 | q->q_finalrcpt != NULL && |
---|
1212 | strcmp(actual, q->q_finalrcpt) != 0) |
---|
1213 | { |
---|
1214 | (void) sm_snprintf(buf, sizeof buf, |
---|
1215 | "X-Actual-Recipient: %s", |
---|
1216 | actual); |
---|
1217 | if (!putline(buf, mci)) |
---|
1218 | goto writeerr; |
---|
1219 | } |
---|
1220 | |
---|
1221 | /* Action: -- what happened? */ |
---|
1222 | (void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ", |
---|
1223 | action); |
---|
1224 | if (!putline(buf, mci)) |
---|
1225 | goto writeerr; |
---|
1226 | |
---|
1227 | /* Status: -- what _really_ happened? */ |
---|
1228 | if (q->q_status != NULL) |
---|
1229 | p = q->q_status; |
---|
1230 | else if (QS_IS_BADADDR(q->q_state)) |
---|
1231 | p = "5.0.0"; |
---|
1232 | else if (QS_IS_QUEUEUP(q->q_state)) |
---|
1233 | p = "4.0.0"; |
---|
1234 | else |
---|
1235 | p = "2.0.0"; |
---|
1236 | (void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p); |
---|
1237 | if (!putline(buf, mci)) |
---|
1238 | goto writeerr; |
---|
1239 | |
---|
1240 | /* Remote-MTA: -- who was I talking to? */ |
---|
1241 | if (q->q_statmta != NULL) |
---|
1242 | { |
---|
1243 | if (q->q_mailer == NULL || |
---|
1244 | (p = q->q_mailer->m_mtatype) == NULL) |
---|
1245 | p = "dns"; |
---|
1246 | (void) sm_snprintf(buf, sizeof buf, |
---|
1247 | "Remote-MTA: %s; %.800s", |
---|
1248 | p, q->q_statmta); |
---|
1249 | p = &buf[strlen(buf) - 1]; |
---|
1250 | if (*p == '.') |
---|
1251 | *p = '\0'; |
---|
1252 | if (!putline(buf, mci)) |
---|
1253 | goto writeerr; |
---|
1254 | } |
---|
1255 | |
---|
1256 | /* Diagnostic-Code: -- actual result from other end */ |
---|
1257 | if (q->q_rstatus != NULL) |
---|
1258 | { |
---|
1259 | p = q->q_mailer->m_diagtype; |
---|
1260 | if (p == NULL) |
---|
1261 | p = "smtp"; |
---|
1262 | (void) sm_snprintf(buf, sizeof buf, |
---|
1263 | "Diagnostic-Code: %s; %.800s", |
---|
1264 | p, q->q_rstatus); |
---|
1265 | if (!putline(buf, mci)) |
---|
1266 | goto writeerr; |
---|
1267 | } |
---|
1268 | |
---|
1269 | /* Last-Attempt-Date: -- fine granularity */ |
---|
1270 | if (q->q_statdate == (time_t) 0L) |
---|
1271 | q->q_statdate = curtime(); |
---|
1272 | (void) sm_strlcpyn(buf, sizeof buf, 2, |
---|
1273 | "Last-Attempt-Date: ", |
---|
1274 | arpadate(ctime(&q->q_statdate))); |
---|
1275 | if (!putline(buf, mci)) |
---|
1276 | goto writeerr; |
---|
1277 | |
---|
1278 | /* Will-Retry-Until: -- for delayed messages only */ |
---|
1279 | if (QS_IS_QUEUEUP(q->q_state)) |
---|
1280 | { |
---|
1281 | time_t xdate; |
---|
1282 | |
---|
1283 | xdate = e->e_parent->e_ctime + |
---|
1284 | TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; |
---|
1285 | (void) sm_strlcpyn(buf, sizeof buf, 2, |
---|
1286 | "Will-Retry-Until: ", |
---|
1287 | arpadate(ctime(&xdate))); |
---|
1288 | if (!putline(buf, mci)) |
---|
1289 | goto writeerr; |
---|
1290 | } |
---|
1291 | } |
---|
1292 | } |
---|
1293 | #endif /* DSN */ |
---|
1294 | |
---|
1295 | /* |
---|
1296 | ** Output text of original message |
---|
1297 | */ |
---|
1298 | |
---|
1299 | if (!putline("", mci)) |
---|
1300 | goto writeerr; |
---|
1301 | if (bitset(EF_HAS_DF, e->e_parent->e_flags)) |
---|
1302 | { |
---|
1303 | sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && |
---|
1304 | !bitset(EF_NO_BODY_RETN, e->e_flags); |
---|
1305 | |
---|
1306 | if (e->e_msgboundary == NULL) |
---|
1307 | { |
---|
1308 | if (!putline( |
---|
1309 | sendbody |
---|
1310 | ? " ----- Original message follows -----\n" |
---|
1311 | : " ----- Message header follows -----\n", |
---|
1312 | mci)) |
---|
1313 | { |
---|
1314 | goto writeerr; |
---|
1315 | } |
---|
1316 | } |
---|
1317 | else |
---|
1318 | { |
---|
1319 | (void) sm_strlcpyn(buf, sizeof buf, 2, "--", |
---|
1320 | e->e_msgboundary); |
---|
1321 | |
---|
1322 | if (!putline(buf, mci)) |
---|
1323 | goto writeerr; |
---|
1324 | (void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ", |
---|
1325 | sendbody ? "message/rfc822" |
---|
1326 | : "text/rfc822-headers"); |
---|
1327 | if (!putline(buf, mci)) |
---|
1328 | goto writeerr; |
---|
1329 | |
---|
1330 | p = hvalue("Content-Transfer-Encoding", |
---|
1331 | e->e_parent->e_header); |
---|
1332 | if (p != NULL && sm_strcasecmp(p, "binary") != 0) |
---|
1333 | p = NULL; |
---|
1334 | if (p == NULL && |
---|
1335 | bitset(EF_HAS8BIT, e->e_parent->e_flags)) |
---|
1336 | p = "8bit"; |
---|
1337 | if (p != NULL) |
---|
1338 | { |
---|
1339 | (void) sm_snprintf(buf, sizeof buf, |
---|
1340 | "Content-Transfer-Encoding: %s", |
---|
1341 | p); |
---|
1342 | if (!putline(buf, mci)) |
---|
1343 | goto writeerr; |
---|
1344 | } |
---|
1345 | } |
---|
1346 | if (!putline("", mci)) |
---|
1347 | goto writeerr; |
---|
1348 | save_errno = errno; |
---|
1349 | if (!putheader(mci, e->e_parent->e_header, e->e_parent, |
---|
1350 | M87F_OUTER)) |
---|
1351 | goto writeerr; |
---|
1352 | errno = save_errno; |
---|
1353 | if (sendbody) |
---|
1354 | { |
---|
1355 | if (!putbody(mci, e->e_parent, e->e_msgboundary)) |
---|
1356 | goto writeerr; |
---|
1357 | } |
---|
1358 | else if (e->e_msgboundary == NULL) |
---|
1359 | { |
---|
1360 | if (!putline("", mci) || |
---|
1361 | !putline(" ----- Message body suppressed -----", |
---|
1362 | mci)) |
---|
1363 | { |
---|
1364 | goto writeerr; |
---|
1365 | } |
---|
1366 | } |
---|
1367 | } |
---|
1368 | else if (e->e_msgboundary == NULL) |
---|
1369 | { |
---|
1370 | if (!putline(" ----- No message was collected -----\n", mci)) |
---|
1371 | goto writeerr; |
---|
1372 | } |
---|
1373 | |
---|
1374 | if (e->e_msgboundary != NULL) |
---|
1375 | { |
---|
1376 | (void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary, |
---|
1377 | "--"); |
---|
1378 | if (!putline("", mci) || !putline(buf, mci)) |
---|
1379 | goto writeerr; |
---|
1380 | } |
---|
1381 | if (!putline("", mci) || |
---|
1382 | sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF) |
---|
1383 | goto writeerr; |
---|
1384 | |
---|
1385 | /* |
---|
1386 | ** Cleanup and exit |
---|
1387 | */ |
---|
1388 | |
---|
1389 | if (errno != 0) |
---|
1390 | { |
---|
1391 | writeerr: |
---|
1392 | syserr("errbody: I/O error"); |
---|
1393 | return false; |
---|
1394 | } |
---|
1395 | return true; |
---|
1396 | } |
---|
1397 | |
---|
1398 | /* |
---|
1399 | ** SMTPTODSN -- convert SMTP to DSN status code |
---|
1400 | ** |
---|
1401 | ** Parameters: |
---|
1402 | ** smtpstat -- the smtp status code (e.g., 550). |
---|
1403 | ** |
---|
1404 | ** Returns: |
---|
1405 | ** The DSN version of the status code. |
---|
1406 | ** |
---|
1407 | ** Storage Management: |
---|
1408 | ** smtptodsn() returns a pointer to a character string literal, |
---|
1409 | ** which will remain valid forever, and thus does not need to |
---|
1410 | ** be copied. Current code relies on this property. |
---|
1411 | */ |
---|
1412 | |
---|
1413 | char * |
---|
1414 | smtptodsn(smtpstat) |
---|
1415 | int smtpstat; |
---|
1416 | { |
---|
1417 | if (smtpstat < 0) |
---|
1418 | return "4.4.2"; |
---|
1419 | |
---|
1420 | switch (smtpstat) |
---|
1421 | { |
---|
1422 | case 450: /* Req mail action not taken: mailbox unavailable */ |
---|
1423 | return "4.2.0"; |
---|
1424 | |
---|
1425 | case 451: /* Req action aborted: local error in processing */ |
---|
1426 | return "4.3.0"; |
---|
1427 | |
---|
1428 | case 452: /* Req action not taken: insufficient sys storage */ |
---|
1429 | return "4.3.1"; |
---|
1430 | |
---|
1431 | case 500: /* Syntax error, command unrecognized */ |
---|
1432 | return "5.5.2"; |
---|
1433 | |
---|
1434 | case 501: /* Syntax error in parameters or arguments */ |
---|
1435 | return "5.5.4"; |
---|
1436 | |
---|
1437 | case 502: /* Command not implemented */ |
---|
1438 | return "5.5.1"; |
---|
1439 | |
---|
1440 | case 503: /* Bad sequence of commands */ |
---|
1441 | return "5.5.1"; |
---|
1442 | |
---|
1443 | case 504: /* Command parameter not implemented */ |
---|
1444 | return "5.5.4"; |
---|
1445 | |
---|
1446 | case 550: /* Req mail action not taken: mailbox unavailable */ |
---|
1447 | return "5.2.0"; |
---|
1448 | |
---|
1449 | case 551: /* User not local; please try <...> */ |
---|
1450 | return "5.1.6"; |
---|
1451 | |
---|
1452 | case 552: /* Req mail action aborted: exceeded storage alloc */ |
---|
1453 | return "5.2.2"; |
---|
1454 | |
---|
1455 | case 553: /* Req action not taken: mailbox name not allowed */ |
---|
1456 | return "5.1.0"; |
---|
1457 | |
---|
1458 | case 554: /* Transaction failed */ |
---|
1459 | return "5.0.0"; |
---|
1460 | } |
---|
1461 | |
---|
1462 | if ((smtpstat / 100) == 2) |
---|
1463 | return "2.0.0"; |
---|
1464 | if ((smtpstat / 100) == 4) |
---|
1465 | return "4.0.0"; |
---|
1466 | return "5.0.0"; |
---|
1467 | } |
---|
1468 | /* |
---|
1469 | ** XTEXTIFY -- take regular text and turn it into DSN-style xtext |
---|
1470 | ** |
---|
1471 | ** Parameters: |
---|
1472 | ** t -- the text to convert. |
---|
1473 | ** taboo -- additional characters that must be encoded. |
---|
1474 | ** |
---|
1475 | ** Returns: |
---|
1476 | ** The xtext-ified version of the same string. |
---|
1477 | */ |
---|
1478 | |
---|
1479 | char * |
---|
1480 | xtextify(t, taboo) |
---|
1481 | register char *t; |
---|
1482 | char *taboo; |
---|
1483 | { |
---|
1484 | register char *p; |
---|
1485 | int l; |
---|
1486 | int nbogus; |
---|
1487 | static char *bp = NULL; |
---|
1488 | static int bplen = 0; |
---|
1489 | |
---|
1490 | if (taboo == NULL) |
---|
1491 | taboo = ""; |
---|
1492 | |
---|
1493 | /* figure out how long this xtext will have to be */ |
---|
1494 | nbogus = l = 0; |
---|
1495 | for (p = t; *p != '\0'; p++) |
---|
1496 | { |
---|
1497 | register int c = (*p & 0xff); |
---|
1498 | |
---|
1499 | /* ASCII dependence here -- this is the way the spec words it */ |
---|
1500 | if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || |
---|
1501 | strchr(taboo, c) != NULL) |
---|
1502 | nbogus++; |
---|
1503 | l++; |
---|
1504 | } |
---|
1505 | if (nbogus < 0) |
---|
1506 | { |
---|
1507 | /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */ |
---|
1508 | syserr("!xtextify string too long"); |
---|
1509 | } |
---|
1510 | if (nbogus == 0) |
---|
1511 | return t; |
---|
1512 | l += nbogus * 2 + 1; |
---|
1513 | |
---|
1514 | /* now allocate space if necessary for the new string */ |
---|
1515 | if (l > bplen) |
---|
1516 | { |
---|
1517 | if (bp != NULL) |
---|
1518 | sm_free(bp); /* XXX */ |
---|
1519 | bp = sm_pmalloc_x(l); |
---|
1520 | bplen = l; |
---|
1521 | } |
---|
1522 | |
---|
1523 | /* ok, copy the text with byte expansion */ |
---|
1524 | for (p = bp; *t != '\0'; ) |
---|
1525 | { |
---|
1526 | register int c = (*t++ & 0xff); |
---|
1527 | |
---|
1528 | /* ASCII dependence here -- this is the way the spec words it */ |
---|
1529 | if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || |
---|
1530 | strchr(taboo, c) != NULL) |
---|
1531 | { |
---|
1532 | *p++ = '+'; |
---|
1533 | *p++ = "0123456789ABCDEF"[c >> 4]; |
---|
1534 | *p++ = "0123456789ABCDEF"[c & 0xf]; |
---|
1535 | } |
---|
1536 | else |
---|
1537 | *p++ = c; |
---|
1538 | } |
---|
1539 | *p = '\0'; |
---|
1540 | return bp; |
---|
1541 | } |
---|
1542 | /* |
---|
1543 | ** XUNTEXTIFY -- take xtext and turn it into plain text |
---|
1544 | ** |
---|
1545 | ** Parameters: |
---|
1546 | ** t -- the xtextified text. |
---|
1547 | ** |
---|
1548 | ** Returns: |
---|
1549 | ** The decoded text. No attempt is made to deal with |
---|
1550 | ** null strings in the resulting text. |
---|
1551 | */ |
---|
1552 | |
---|
1553 | char * |
---|
1554 | xuntextify(t) |
---|
1555 | register char *t; |
---|
1556 | { |
---|
1557 | register char *p; |
---|
1558 | int l; |
---|
1559 | static char *bp = NULL; |
---|
1560 | static int bplen = 0; |
---|
1561 | |
---|
1562 | /* heuristic -- if no plus sign, just return the input */ |
---|
1563 | if (strchr(t, '+') == NULL) |
---|
1564 | return t; |
---|
1565 | |
---|
1566 | /* xtext is always longer than decoded text */ |
---|
1567 | l = strlen(t); |
---|
1568 | if (l > bplen) |
---|
1569 | { |
---|
1570 | if (bp != NULL) |
---|
1571 | sm_free(bp); /* XXX */ |
---|
1572 | bp = xalloc(l); |
---|
1573 | bplen = l; |
---|
1574 | } |
---|
1575 | |
---|
1576 | /* ok, copy the text with byte compression */ |
---|
1577 | for (p = bp; *t != '\0'; t++) |
---|
1578 | { |
---|
1579 | register int c = *t & 0xff; |
---|
1580 | |
---|
1581 | if (c != '+') |
---|
1582 | { |
---|
1583 | *p++ = c; |
---|
1584 | continue; |
---|
1585 | } |
---|
1586 | |
---|
1587 | c = *++t & 0xff; |
---|
1588 | if (!isascii(c) || !isxdigit(c)) |
---|
1589 | { |
---|
1590 | /* error -- first digit is not hex */ |
---|
1591 | usrerr("bogus xtext: +%c", c); |
---|
1592 | t--; |
---|
1593 | continue; |
---|
1594 | } |
---|
1595 | if (isdigit(c)) |
---|
1596 | c -= '0'; |
---|
1597 | else if (isupper(c)) |
---|
1598 | c -= 'A' - 10; |
---|
1599 | else |
---|
1600 | c -= 'a' - 10; |
---|
1601 | *p = c << 4; |
---|
1602 | |
---|
1603 | c = *++t & 0xff; |
---|
1604 | if (!isascii(c) || !isxdigit(c)) |
---|
1605 | { |
---|
1606 | /* error -- second digit is not hex */ |
---|
1607 | usrerr("bogus xtext: +%x%c", *p >> 4, c); |
---|
1608 | t--; |
---|
1609 | continue; |
---|
1610 | } |
---|
1611 | if (isdigit(c)) |
---|
1612 | c -= '0'; |
---|
1613 | else if (isupper(c)) |
---|
1614 | c -= 'A' - 10; |
---|
1615 | else |
---|
1616 | c -= 'a' - 10; |
---|
1617 | *p++ |= c; |
---|
1618 | } |
---|
1619 | *p = '\0'; |
---|
1620 | return bp; |
---|
1621 | } |
---|
1622 | /* |
---|
1623 | ** XTEXTOK -- check if a string is legal xtext |
---|
1624 | ** |
---|
1625 | ** Xtext is used in Delivery Status Notifications. The spec was |
---|
1626 | ** taken from RFC 1891, ``SMTP Service Extension for Delivery |
---|
1627 | ** Status Notifications''. |
---|
1628 | ** |
---|
1629 | ** Parameters: |
---|
1630 | ** s -- the string to check. |
---|
1631 | ** |
---|
1632 | ** Returns: |
---|
1633 | ** true -- if 's' is legal xtext. |
---|
1634 | ** false -- if it has any illegal characters in it. |
---|
1635 | */ |
---|
1636 | |
---|
1637 | bool |
---|
1638 | xtextok(s) |
---|
1639 | char *s; |
---|
1640 | { |
---|
1641 | int c; |
---|
1642 | |
---|
1643 | while ((c = *s++) != '\0') |
---|
1644 | { |
---|
1645 | if (c == '+') |
---|
1646 | { |
---|
1647 | c = *s++; |
---|
1648 | if (!isascii(c) || !isxdigit(c)) |
---|
1649 | return false; |
---|
1650 | c = *s++; |
---|
1651 | if (!isascii(c) || !isxdigit(c)) |
---|
1652 | return false; |
---|
1653 | } |
---|
1654 | else if (c < '!' || c > '~' || c == '=') |
---|
1655 | return false; |
---|
1656 | } |
---|
1657 | return true; |
---|
1658 | } |
---|
1659 | /* |
---|
1660 | ** PRUNEROUTE -- prune an RFC-822 source route |
---|
1661 | ** |
---|
1662 | ** Trims down a source route to the last internet-registered hop. |
---|
1663 | ** This is encouraged by RFC 1123 section 5.3.3. |
---|
1664 | ** |
---|
1665 | ** Parameters: |
---|
1666 | ** addr -- the address |
---|
1667 | ** |
---|
1668 | ** Returns: |
---|
1669 | ** true -- address was modified |
---|
1670 | ** false -- address could not be pruned |
---|
1671 | ** |
---|
1672 | ** Side Effects: |
---|
1673 | ** modifies addr in-place |
---|
1674 | */ |
---|
1675 | |
---|
1676 | static bool |
---|
1677 | pruneroute(addr) |
---|
1678 | char *addr; |
---|
1679 | { |
---|
1680 | #if NAMED_BIND |
---|
1681 | char *start, *at, *comma; |
---|
1682 | char c; |
---|
1683 | int braclev; |
---|
1684 | int rcode; |
---|
1685 | int i; |
---|
1686 | char hostbuf[BUFSIZ]; |
---|
1687 | char *mxhosts[MAXMXHOSTS + 1]; |
---|
1688 | |
---|
1689 | /* check to see if this is really a route-addr */ |
---|
1690 | if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') |
---|
1691 | return false; |
---|
1692 | |
---|
1693 | /* |
---|
1694 | ** Can't simply find the first ':' is the address might be in the |
---|
1695 | ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside |
---|
1696 | ** the IPv6 address. |
---|
1697 | */ |
---|
1698 | |
---|
1699 | start = addr; |
---|
1700 | braclev = 0; |
---|
1701 | while (*start != '\0') |
---|
1702 | { |
---|
1703 | if (*start == ':' && braclev <= 0) |
---|
1704 | break; |
---|
1705 | else if (*start == '[') |
---|
1706 | braclev++; |
---|
1707 | else if (*start == ']' && braclev > 0) |
---|
1708 | braclev--; |
---|
1709 | start++; |
---|
1710 | } |
---|
1711 | if (braclev > 0 || *start != ':') |
---|
1712 | return false; |
---|
1713 | |
---|
1714 | at = strrchr(addr, '@'); |
---|
1715 | if (at == NULL || at < start) |
---|
1716 | return false; |
---|
1717 | |
---|
1718 | /* slice off the angle brackets */ |
---|
1719 | i = strlen(at + 1); |
---|
1720 | if (i >= sizeof hostbuf) |
---|
1721 | return false; |
---|
1722 | (void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf); |
---|
1723 | hostbuf[i - 1] = '\0'; |
---|
1724 | |
---|
1725 | while (start != NULL) |
---|
1726 | { |
---|
1727 | if (getmxrr(hostbuf, mxhosts, NULL, false, |
---|
1728 | &rcode, true, NULL) > 0) |
---|
1729 | { |
---|
1730 | (void) sm_strlcpy(addr + 1, start + 1, |
---|
1731 | strlen(addr) - 1); |
---|
1732 | return true; |
---|
1733 | } |
---|
1734 | c = *start; |
---|
1735 | *start = '\0'; |
---|
1736 | comma = strrchr(addr, ','); |
---|
1737 | if (comma != NULL && comma[1] == '@' && |
---|
1738 | strlen(comma + 2) < sizeof hostbuf) |
---|
1739 | (void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf); |
---|
1740 | else |
---|
1741 | comma = NULL; |
---|
1742 | *start = c; |
---|
1743 | start = comma; |
---|
1744 | } |
---|
1745 | #endif /* NAMED_BIND */ |
---|
1746 | return false; |
---|
1747 | } |
---|