1N/A * Copyright (c) 1998-2003, 2006 Sendmail, Inc. and its suppliers. 1N/A * All rights reserved. 1N/A * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 1N/A * Copyright (c) 1988, 1993 1N/A * The Regents of the University of California. All rights reserved. 1N/A * By using this file, you agree to the terms and conditions set 1N/A * forth in the LICENSE file which can be found at the top level of 1N/A * the sendmail distribution. 1N/A** SAVEMAIL -- Save mail on error 1N/A** If mailing back errors, mail it back to the originator 1N/A** together with an error message; otherwise, just put it in 1N/A** dead.letter in the user's home directory (if he exists on 1N/A** e -- the envelope containing the message in error. 1N/A** sendbody -- if true, also send back the body of the 1N/A** message; otherwise just send the header. 1N/A** true if savemail panic'ed, (i.e., the data file should 1N/A** be preserved by dropenvelope()) 1N/A** Saves the letter, by writing or mailing it back to the 1N/A** sender, or by putting it in dead.letter in her home 1N/A/* defines for state machine */ 1N/A#
define ESM_DONE 7 /* message is successfully delivered */ 1N/A sm_dprintf(
"\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
1N/A /* can't return a message with no id */ 1N/A ** In the unhappy event we don't know who to return the mail 1N/A ** to, make someone up. 1N/A syserr(
"553 5.3.5 Cannot parse Postmaster!");
1N/A ** Basic state machine. 1N/A ** This machine runs through the following states: 1N/A ** ESM_QUIET Errors have already been printed iff the 1N/A ** ESM_REPORT Report directly to the sender's terminal. 1N/A ** ESM_MAIL Mail response to the sender. 1N/A ** ESM_DEADLETTER Save response in ~/dead.letter. 1N/A ** ESM_POSTMASTER Mail response to the postmaster. 1N/A ** ESM_DEADLETTERDROP 1N/A ** If DeadLetterDrop set, save it there. 1N/A ** ESM_PANIC Save response anywhere possible. 1N/A /* determine starting state */ 1N/A /* no need to return anything at all */ 1N/A syserr(
"554 5.3.0 savemail: bogus errormode x%x",
1N/A /* if this is already an error response, send to postmaster */ 1N/A /* got an error sending a response -- can it */ 1N/A ** If the user is still logged in on the same terminal, 1N/A ** then write the error messages back to hir (sic). 1N/A#
else /* USE_TTYPATH */ 1N/A#
endif /* USE_TTYPATH */ 1N/A "\r\nMessage from %s...\r\n",
buf);
1N/A "Errors occurred while sending mail.\r\n");
1N/A "Transcript follows:\r\n");
1N/A "Transcript of session is unavailable.\r\n");
1N/A "Original message will be saved in dead.letter.\r\n");
1N/A ** If mailing back, do it. 1N/A ** Throw away all further output. Don't alias, 1N/A ** since this could cause loops, e.g., if joe 1N/A ** mails to joe@x, and for some reason the network 1N/A ** for @x is down, then the response gets sent to 1N/A ** joe@x, which gives a response, etc. Also force 1N/A ** the mail to be delivered even if a version of 1N/A ** it has already been sent to the sender. 1N/A ** If this is a configuration or local software 1N/A ** error, send to the local postmaster as well, 1N/A ** since the originator can't do anything 1N/A ** about it anyway. Note that this is a full 1N/A ** copy of the message (intentionally) so that 1N/A ** the Postmaster can forward things along. 1N/A ** Deliver a non-delivery report to the 1N/A ** Postmaster-designate (not necessarily 1N/A ** Postmaster). This does not include the 1N/A ** body of the message, for privacy reasons. 1N/A ** You really shouldn't need this. 1N/A /* check to see if there are any good addresses */ 1N/A /* this is an error-error */ 1N/A /* didn't work -- return to postmaster */ 1N/A ** Similar to previous case, but to system postmaster. 1N/A ** Just drop it on the floor if DoubleBounceAddr 1N/A ** expands to an empty string. 1N/A /* didn't work -- last resort */ 1N/A ** Save the message in dead.letter. 1N/A ** If we weren't mailing back, and the user is 1N/A ** local, we should save the message in 1N/A ** ~/dead.letter so that the poor person doesn't 1N/A ** have to type it over again -- and we all know 1N/A ** what poor typists UNIX users are. 1N/A /* no local directory or no data file */ 1N/A /* we have a home directory; write dead.letter */ 1N/A /* get the sender for the UnixFromLine */ 1N/A ** Log the mail in DeadLetterDrop file. 1N/A /* get the sender for the UnixFromLine */ 1N/A "Saved message in %s",
1N/A /* leave the locked queue & transcript files around */ 1N/A syserr(
"554 savemail: cannot save rejected email anywhere");
1N/A** RETURNTOSENDER -- return a message to the sender with an error. 1N/A** msg -- the explanatory message. 1N/A** returnq -- the queue of people to send the message to. 1N/A** flags -- flags tweaking the operation: 1N/A** RTSF_SENDBODY -- include body of message (otherwise 1N/A** just send the header). 1N/A** RTSF_PMBOUNCE -- this is a postmaster bounce. 1N/A** e -- the current envelope. 1N/A** zero -- if everything went ok. 1N/A** else -- some error. 1N/A** Returns the current message to the sender via mail. 1N/A msg =
"Unable to deliver mail";
1N/A sm_dprintf(
"\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
1N/A syserr(
"554 5.3.0 returntosender: infinite recursion on %s",
1N/A /* don't "unrecurse" and fake a clean exit */ 1N/A /* returndepth--; */ 1N/A /* initialize error envelope */ 1N/A ** If we can't convert to MIME and we don't pass 1N/A ** 8-bit, we can't send the body. 1N/A syserr(
"554 5.3.0 returntosender: cannot select queue for %s",
1N/A#
endif /* NAMED_BIND */ 1N/A p =
"return to sender";
1N/A p =
"sender notify";
1N/A p =
"postmaster notify";
1N/A p =
"warning-timeout";
1N/A p =
"postmaster-warning";
1N/A p =
"return-receipt";
1N/A "Postmaster notify: see transcript for details");
1N/A p =
"postmaster-notification";
1N/A "Returned mail: see transcript for details");
1N/A /* fake up an address header for the from person */ 1N/A /* push state into submessage */ 1N/A /* mark statistics */ 1N/A /* actually deliver the error message */ 1N/A /* check for delivery errors */ 1N/A** ERRBODY -- output the body of an error message. 1N/A** Typically this is a copy of the transcript plus a copy of the 1N/A** original offending message. 1N/A** mci -- the mailer connection information. 1N/A** e -- the envelope we are working in. 1N/A** separator -- any possible MIME separator (unused). 1N/A** true iff body was written successfully 1N/A** Outputs the body of an error message. 1N/A ** Output MIME header. 1N/A ** Output introductory information. 1N/A if (!
putline(
" **********************************************",
1N/A !
putline(
" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
1N/A !
putline(
" **********************************************",
1N/A "The original message was received at %s",
1N/A /* include id in postmaster copies */ 1N/A ** Output error message header (if specified and available). 1N/A ** Output message introduction 1N/A /* permanent fatal errors */ 1N/A if (!
putline(
" ----- The following addresses had permanent fatal errors -----",
1N/A " (expanded from: %s)",
1N/A /* transient non-fatal errors */ 1N/A if (!
putline(
" ----- The following addresses had transient non-fatal errors -----",
1N/A " (expanded from: %s)",
1N/A /* successful delivery notifications */ 1N/A p =
"Deliver-By notify: relayed";
1N/A p =
"Deliver-By trace: relayed";
1N/A p =
"relayed to non-DSN-aware mailer";
1N/A p =
"successfully delivered to mailing list";
1N/A p =
"successfully delivered to mailbox";
1N/A p =
"expanded by alias";
1N/A if (!
putline(
" ----- The following addresses had successful delivery notifications -----",
1N/A " (expanded from: %s)",
1N/A ** Output transcript of errors 1N/A if (!
putline(
" ----- Transcript of session is unavailable -----\n",
1N/A ** Output machine-readable version. 1N/A ** Output per-message information. 1N/A /* original envelope id from MAIL FROM: line */ 1N/A "Original-Envelope-Id: %.800s",
1N/A /* Reporting-MTA: is us (required) */ 1N/A /* DSN-Gateway: not relevant since we are not translating */ 1N/A /* Received-From-MTA: shows where we got this message from */ 1N/A /* XXX use $s for type? */ 1N/A "Received-From-MTA: %s; %.800s",
1N/A /* Arrival-Date: -- when it arrived here */ 1N/A /* Deliver-By-Date: -- when it should have been delivered */ 1N/A "Deliver-By-Date: ",
1N/A ** Output per-address information. 1N/A /* RFC 1891, 6.2.6 (b) */ 1N/A action =
"relayed (to non-DSN-aware mailer)";
1N/A action =
"expanded (to multi-recipient alias)";
1N/A /* Original-Recipient: -- passed from on high */ 1N/A "Original-Recipient: %.800s",
1N/A /* Figure out actual recipient */ 1N/A "%s; %.700s@%.100s",
1N/A /* Final-Recipient: -- the name from the RCPT command */ 1N/A /* should never happen */ 1N/A "returntosender: q_finalrcpt is NULL");
1N/A /* try to fall back to the actual recipient */ 1N/A "Final-Recipient: %s",
1N/A /* X-Actual-Recipient: -- the real problem address */ 1N/A "X-Actual-Recipient: %s",
1N/A /* Action: -- what happened? */ 1N/A /* Status: -- what _really_ happened? */ 1N/A /* Remote-MTA: -- who was I talking to? */ 1N/A "Remote-MTA: %s; %.800s",
1N/A /* Diagnostic-Code: -- actual result from other end */ 1N/A "Diagnostic-Code: %s; %.800s",
1N/A /* Last-Attempt-Date: -- fine granularity */ 1N/A "Last-Attempt-Date: ",
1N/A /* Will-Retry-Until: -- for delayed messages only */ 1N/A "Will-Retry-Until: ",
1N/A ** Output text of original message 1N/A ?
" ----- Original message follows -----\n" 1N/A :
" ----- Message header follows -----\n",
1N/A "Content-Transfer-Encoding: %s",
1N/A** SMTPTODSN -- convert SMTP to DSN status code 1N/A** smtpstat -- the smtp status code (e.g., 550). 1N/A** The DSN version of the status code. 1N/A** Storage Management: 1N/A** smtptodsn() returns a pointer to a character string literal, 1N/A** which will remain valid forever, and thus does not need to 1N/A** be copied. Current code relies on this property. 1N/A case 450:
/* Req mail action not taken: mailbox unavailable */ 1N/A case 451:
/* Req action aborted: local error in processing */ 1N/A case 452:
/* Req action not taken: insufficient sys storage */ 1N/A case 500:
/* Syntax error, command unrecognized */ 1N/A case 501:
/* Syntax error in parameters or arguments */ 1N/A case 502:
/* Command not implemented */ 1N/A case 503:
/* Bad sequence of commands */ 1N/A case 504:
/* Command parameter not implemented */ 1N/A case 550:
/* Req mail action not taken: mailbox unavailable */ 1N/A case 551:
/* User not local; please try <...> */ 1N/A case 552:
/* Req mail action aborted: exceeded storage alloc */ 1N/A case 553:
/* Req action not taken: mailbox name not allowed */ 1N/A case 554:
/* Transaction failed */ 1N/A** XTEXTIFY -- take regular text and turn it into DSN-style xtext 1N/A** t -- the text to convert. 1N/A** taboo -- additional characters that must be encoded. 1N/A** The xtext-ified version of the same string. 1N/A /* figure out how long this xtext will have to be */ 1N/A for (p = t; *p !=
'\0'; p++)
1N/A register int c = (*p &
0xff);
1N/A /* ASCII dependence here -- this is the way the spec words it */ 1N/A if (c <
'!' || c >
'~' || c ==
'+' || c ==
'\\' || c ==
'(' ||
1N/A /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */ 1N/A /* now allocate space if necessary for the new string */ 1N/A /* ok, copy the text with byte expansion */ 1N/A for (p =
bp; *t !=
'\0'; )
1N/A register int c = (*t++ &
0xff);
1N/A /* ASCII dependence here -- this is the way the spec words it */ 1N/A if (c <
'!' || c >
'~' || c ==
'+' || c ==
'\\' || c ==
'(' ||
1N/A *p++ =
"0123456789ABCDEF"[c >>
4];
1N/A *p++ =
"0123456789ABCDEF"[c &
0xf];
1N/A** XUNTEXTIFY -- take xtext and turn it into plain text 1N/A** t -- the xtextified text. 1N/A** The decoded text. No attempt is made to deal with 1N/A** null strings in the resulting text. 1N/A /* heuristic -- if no plus sign, just return the input */ 1N/A /* xtext is always longer than decoded text */ 1N/A /* ok, copy the text with byte compression */ 1N/A for (p =
bp; *t !=
'\0'; t++)
1N/A register int c = *t &
0xff;
1N/A /* error -- first digit is not hex */ 1N/A /* error -- second digit is not hex */ 1N/A** XTEXTOK -- check if a string is legal xtext 1N/A** Xtext is used in Delivery Status Notifications. The spec was 1N/A** taken from RFC 1891, ``SMTP Service Extension for Delivery 1N/A** Status Notifications''. 1N/A** s -- the string to check. 1N/A** true -- if 's' is legal xtext. 1N/A** false -- if it has any illegal characters in it. 1N/A while ((c = *s++) !=
'\0')
1N/A else if (c <
'!' || c >
'~' || c ==
'=')
1N/A** PRUNEROUTE -- prune an RFC-822 source route 1N/A** Trims down a source route to the last internet-registered hop. 1N/A** This is encouraged by RFC 1123 section 5.3.3. 1N/A** addr -- the address 1N/A** true -- address was modified 1N/A** false -- address could not be pruned 1N/A** modifies addr in-place 1N/A /* check to see if this is really a route-addr */ 1N/A ** Can't simply find the first ':' is the address might be in the 1N/A ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside 1N/A ** the IPv6 address. 1N/A /* slice off the angle brackets */ 1N/A#
endif /* NAMED_BIND */