* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)savemail.c 5.9 (Berkeley) 6/30/88";
** SAVEMAIL -- Save mail on error
** If mailing back errors, mail it back to the originator
** together with an error message; otherwise, just put it in
** dead.letter in the user's home directory (if he exists on
** e -- the envelope containing the message in error.
** Saves the letter, by writing or mailing it back to the
** sender, or by putting it in dead.letter in her home
/* defines for state machine */
# define ESM_REPORT 0 /* report to sender's terminal */
# define ESM_MAIL 1 /* mail back to sender */
# define ESM_QUIET 2 /* messages have already been returned */
# define ESM_DEADLETTER 3 /* save in ~/dead.letter */
# define ESM_POSTMASTER 4 /* return to postmaster */
# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */
# define ESM_PANIC 6 /* leave the locked queue/transcript files */
# define ESM_DONE 7 /* the message is successfully delivered */
register struct passwd
*pw
;
extern struct passwd
*getpwnam();
printf("\nsavemail, ErrorMode = %c\n", ErrorMode
);
if (bitset(EF_RESPONSE
, e
->e_flags
))
message(Arpa_Info
, "Dumping junk mail");
e
->e_flags
&= ~EF_FATALERRS
;
** In the unhappy event we don't know who to return the mail
if (e
->e_from
.q_paddr
== NULL
)
if (parseaddr("root", &e
->e_from
, 0, '\0') == NULL
)
syserr("Cannot parse root!");
** This machine runs through the following states:
** ESM_QUIET Errors have already been printed iff the
** ESM_REPORT Report directly to the sender's terminal.
** ESM_MAIL Mail response to the sender.
** ESM_DEADLETTER Save response in ~/dead.letter.
** ESM_POSTMASTER Mail response to the postmaster.
** ESM_PANIC Save response anywhere possible.
/* determine starting state */
/* mail back, but return o.k. exit status */
/* no need to return anything at all */
syserr("savemail: ErrorMode x%x\n");
while (state
!= ESM_DONE
)
printf(" state %d\n", state
);
if (e
->e_from
.q_mailer
== LocalMailer
)
** If the user is still logged in on the same terminal,
** then write the error messages back to hir (sic).
if (p
== NULL
|| freopen(p
, "w", stdout
) == NULL
)
expand("\001n", buf
, &buf
[sizeof buf
- 1], e
);
printf("\r\nMessage from %s...\r\n", buf
);
printf("Errors occurred while sending mail.\r\n");
fp
= fopen(queuename(e
, 'x'), "r");
syserr("Cannot open %s", queuename(e
, 'x'));
printf("Transcript of session is unavailable.\r\n");
printf("Transcript follows:\r\n");
while (fgets(buf
, sizeof buf
, fp
) != NULL
&&
printf("Original message will be saved in dead.letter.\r\n");
(void) syserr("savemail: stdout: write err");
** If mailing back, do it.
** Throw away all further output. Don't alias,
** since this could cause loops, e.g., if joe
** mails to joe@x, and for some reason the network
** for @x is down, then the response gets sent to
** joe@x, which gives a response, etc. Also force
** the mail to be delivered even if a version of
** it has already been sent to the sender.
if (e
->e_errorqueue
== NULL
)
sendtolist(e
->e_from
.q_paddr
,
/* deliver a cc: to the postmaster if desired */
if (PostMasterCopy
!= NULL
)
sendtolist(PostMasterCopy
,
if (parseaddr("postmaster", q
, 0, '\0') == NULL
)
syserr("cannot parse postmaster!");
if (returntosender(e
->e_message
!= NULL
? e
->e_message
:
"Unable to deliver mail",
state
= state
== ESM_MAIL
? ESM_POSTMASTER
: ESM_USRTMP
;
** Save the message in dead.letter.
** If we weren't mailing back, and the user is
** local, we should save the message in
** ~/dead.letter so that the poor person doesn't
** have to type it over again -- and we all know
** what poor typists UNIX users are.
if (e
->e_from
.q_mailer
== LocalMailer
)
if (e
->e_from
.q_home
!= NULL
)
else if ((pw
= getpwnam(e
->e_from
.q_user
)) != NULL
)
syserr("Can't return mail to %s", e
->e_from
.q_paddr
);
/* we have a home directory; open dead.letter */
expand("\001z/dead.letter", buf
, &buf
[sizeof buf
- 1], e
);
message(Arpa_Info
, "Saving message in %s", buf
);
sendtolist(buf
, (ADDRESS
*) NULL
, &q
);
/* no data file -- try mailing back */
** Log the mail in /usr/tmp/dead.letter.
fp
= dfopen("/usr/tmp/dead.letter", "a");
putfromline(fp
, ProgMailer
);
(*e
->e_puthdr
)(fp
, ProgMailer
, e
);
putline("\n", fp
, ProgMailer
);
(*e
->e_putbody
)(fp
, ProgMailer
, e
);
putline("\n", fp
, ProgMailer
);
state
= ferror(fp
) ? ESM_PANIC
: ESM_DONE
;
syserr("savemail: unknown state %d", state
);
syserr("savemail: HELP!!!!");
syslog(LOG_ALERT
, "savemail: HELP!!!!");
/* leave the locked queue & transcript files around */
** RETURNTOSENDER -- return a message to the sender with an error.
** msg -- the explanatory message.
** returnq -- the queue of people to send the message to.
** sendbody -- if TRUE, also send back the body of the
** message; otherwise just send the header.
** zero -- if everything went ok.
** Returns the current message to the sender via
#define MAXRETURNS 6 /* max depth of returning messages */
returntosender(msg
, returnq
, sendbody
)
extern putheader(), errbody();
extern ENVELOPE
*newenvelope();
printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
msg
, returndepth
, CurEnv
);
printaddr(returnq
, TRUE
);
if (++returndepth
>= MAXRETURNS
)
if (returndepth
!= MAXRETURNS
)
syserr("returntosender: infinite recursion on %s", returnq
->q_paddr
);
/* don't "unrecurse" and fake a clean exit */
define('g', "\001f", CurEnv
);
ee
= newenvelope(&errenvelope
);
define('a', "\001b", ee
);
ee
->e_puthdr
= putheader
;
ee
->e_flags
|= EF_RESPONSE
;
ee
->e_sendqueue
= returnq
;
for (q
= returnq
; q
!= NULL
; q
= q
->q_next
)
addheader("to", q
->q_paddr
, ee
);
(void) sprintf(buf
, "Returned mail: %s", msg
);
addheader("subject", buf
, ee
);
/* fake up an address header for the from person */
expand("\001n", buf
, &buf
[sizeof buf
- 1], CurEnv
);
if (parseaddr(buf
, &ee
->e_from
, -1, '\0') == NULL
)
syserr("Can't parse myself!");
/* push state into submessage */
define('f', "\001n", ee
);
define('x', "Mail Delivery Subsystem", ee
);
/* actually deliver the error message */
CurEnv
= CurEnv
->e_parent
;
/* should check for delivery errors here */
** ERRBODY -- output the body of an error message.
** Typically this is a copy of the transcript plus a copy of the
** original offending message.
** fp -- the output file.
** m -- the mailer to output to.
** e -- the envelope we are working in.
** Outputs the body of an error message.
register struct mailer
*m
;
** Output transcript of errors
p
= queuename(e
->e_parent
, 'x');
if ((xfile
= fopen(p
, "r")) == NULL
)
syserr("Cannot open %s", p
);
fprintf(fp
, " ----- Transcript of session is unavailable -----\n");
fprintf(fp
, " ----- Transcript of session follows -----\n");
while (fgets(buf
, sizeof buf
, xfile
) != NULL
)
** Output text of original message
fprintf(fp
, "\n ----- Return message suppressed -----\n\n");
else if (e
->e_parent
->e_dfp
!= NULL
)
putline(" ----- Unsent message follows -----\n", fp
, m
);
putheader(fp
, m
, e
->e_parent
);
putbody(fp
, m
, e
->e_parent
);
putline(" ----- Message header follows -----\n", fp
, m
);
putheader(fp
, m
, e
->e_parent
);
putline(" ----- No message was collected -----\n", fp
, m
);
syserr("errbody: I/O error");