lint
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
CommitLineData
b3cbe40f 1# include <pwd.h>
96faada8 2# include "sendmail.h"
b3cbe40f 3
f7eb07a3 4SCCSID(@(#)savemail.c 3.36 %G%);
916b3375 5
b3cbe40f
EA
6/*
7** SAVEMAIL -- Save mail on error
8**
9** If the MailBack flag is set, mail it back to the originator
10** together with an error message; otherwise, just put it in
11** dead.letter in the user's home directory (if he exists on
12** this machine).
13**
14** Parameters:
15** none
16**
17** Returns:
18** none
19**
20** Side Effects:
21** Saves the letter, by writing or mailing it back to the
22** sender, or by putting it in dead.letter in her home
23** directory.
b3cbe40f
EA
24*/
25
b3cbe40f
EA
26savemail()
27{
28 register struct passwd *pw;
29 register FILE *xfile;
30 char buf[MAXLINE+1];
b3cbe40f
EA
31 extern struct passwd *getpwnam();
32 register char *p;
b3cbe40f 33 extern char *ttypath();
b3cbe40f 34 static int exclusive;
40e27d12 35 typedef int (*fnptr)();
dd1fe05b 36 extern ENVELOPE *newenvelope();
b3cbe40f 37
dd1fe05b 38 if (exclusive++)
b3cbe40f 39 return;
dd1fe05b
EA
40 if (CurEnv->e_class <= PRI_JUNK)
41 {
91f69adf 42 message(Arpa_Info, "Dumping junk mail");
dd1fe05b
EA
43 return;
44 }
91f69adf 45 ForceMail = TRUE;
b3cbe40f
EA
46
47 /*
48 ** In the unhappy event we don't know who to return the mail
49 ** to, make someone up.
50 */
51
1bcdf0a2 52 if (CurEnv->e_returnto == NULL)
b3cbe40f 53 {
1bcdf0a2
EA
54 CurEnv->e_returnto = parse("root", (ADDRESS *) NULL, 0);
55 if (CurEnv->e_returnto == NULL)
b3cbe40f
EA
56 {
57 syserr("Cannot parse root!");
58 ExitStat = EX_SOFTWARE;
59 finis();
60 }
61 }
2654b031 62 CurEnv->e_to = NULL;
b3cbe40f
EA
63
64 /*
3b661a43
EA
65 ** If called from Eric Schmidt's network, do special mailback.
66 ** Fundamentally, this is the mailback case except that
67 ** it returns an OK exit status (assuming the return
68 ** worked).
1ff06f70 69 ** Also, if the from address is not local, mail it back.
b3cbe40f
EA
70 */
71
3b661a43 72 if (BerkNet)
b3cbe40f 73 {
3b661a43 74 ExitStat = EX_OK;
1ff06f70 75 MailBack = TRUE;
b3cbe40f 76 }
1bcdf0a2 77 if (!bitset(M_LOCAL, CurEnv->e_returnto->q_mailer->m_flags))
1ff06f70 78 MailBack = TRUE;
b3cbe40f
EA
79
80 /*
81 ** If writing back, do it.
82 ** If the user is still logged in on the same terminal,
83 ** then write the error messages back to hir (sic).
84 ** If not, set the MailBack flag so that it will get
85 ** mailed back instead.
86 */
87
88 if (WriteBack)
89 {
90 p = ttypath();
91 if (p == NULL || freopen(p, "w", stdout) == NULL)
92 {
1ff06f70 93 MailBack = TRUE;
b3cbe40f
EA
94 errno = 0;
95 }
96 else
97 {
49086753 98 (void) fflush(Xscript);
3b661a43
EA
99 xfile = fopen(Transcript, "r");
100 if (xfile == NULL)
101 syserr("Cannot open %s", Transcript);
dd1fe05b 102 expand("$n", buf, &buf[sizeof buf - 1], CurEnv);
dc39c568 103 printf("\r\nMessage from %s...\r\n", buf);
74c5fe7c 104 printf("Errors occurred while sending mail; transcript follows:\r\n");
29871fef 105 while (fgets(buf, sizeof buf, xfile) != NULL && !ferror(stdout))
b3cbe40f
EA
106 fputs(buf, stdout);
107 if (ferror(stdout))
29871fef
EA
108 (void) syserr("savemail: stdout: write err");
109 (void) fclose(xfile);
b3cbe40f
EA
110 }
111 }
112
b3cbe40f
EA
113 /*
114 ** If mailing back, do it.
115 ** Throw away all further output. Don't do aliases, since
116 ** this could cause loops, e.g., if joe mails to x:joe,
117 ** and for some reason the network for x: is down, then
118 ** the response gets sent to x:joe, which gives a
119 ** response, etc. Also force the mail to be delivered
120 ** even if a version of it has already been sent to the
121 ** sender.
122 */
123
6708a5e3 124 if (MailBack)
b3cbe40f 125 {
3aa1c35f 126 if (CurEnv->e_errorqueue == NULL)
f7eb07a3 127 sendto(CurEnv->e_from.q_paddr, 1, (ADDRESS *) NULL, &CurEnv->e_errorqueue);
1bcdf0a2 128 if (returntosender("Unable to deliver mail", CurEnv->e_returnto, TRUE) == 0)
b3cbe40f
EA
129 return;
130 }
131
132 /*
133 ** Save the message in dead.letter.
134 ** If we weren't mailing back, and the user is local, we
135 ** should save the message in dead.letter so that the
136 ** poor person doesn't have to type it over again --
137 ** and we all know what poor typists programmers are.
138 */
139
49086753 140 if (ArpaMode)
dc39c568 141 return;
4bb4503e 142 p = NULL;
1bcdf0a2 143 if (CurEnv->e_returnto->q_mailer == LocalMailer)
b3cbe40f 144 {
1bcdf0a2
EA
145 if (CurEnv->e_returnto->q_home != NULL)
146 p = CurEnv->e_returnto->q_home;
147 else if ((pw = getpwnam(CurEnv->e_returnto->q_user)) != NULL)
4bb4503e 148 p = pw->pw_dir;
b3cbe40f 149 }
4bb4503e 150 if (p == NULL)
b3cbe40f 151 {
1bcdf0a2 152 syserr("Can't return mail to %s", CurEnv->e_returnto->q_paddr);
b3cbe40f
EA
153# ifdef DEBUG
154 p = "/usr/tmp";
155# else
156 p = NULL;
157# endif
158 }
738043e8 159 if (p != NULL && TempFile != NULL)
b3cbe40f 160 {
de9d070c 161 auto ADDRESS *q;
91f69adf 162 bool oldverb = Verbose;
de9d070c 163
b3cbe40f 164 /* we have a home directory; open dead.letter */
91f69adf 165 Verbose = TRUE;
15b28570 166 message(Arpa_Info, "Saving message in dead.letter");
91f69adf 167 Verbose = oldverb;
4bb4503e 168 define('z', p);
dd1fe05b 169 expand("$z/dead.letter", buf, &buf[sizeof buf - 1], CurEnv);
2654b031 170 CurEnv->e_to = buf;
de9d070c 171 q = NULL;
40e27d12 172 sendto(buf, -1, (ADDRESS *) NULL, &q);
dd1fe05b 173 (void) deliver(q);
b3cbe40f 174 }
b3cbe40f
EA
175
176 /* add terminator to writeback message */
177 if (WriteBack)
178 printf("-----\r\n");
179}
180\f/*
aba51985
EA
181** RETURNTOSENDER -- return a message to the sender with an error.
182**
183** Parameters:
184** msg -- the explanatory message.
3c7fe765 185** returnto -- the queue of people to send the message to.
79bd7c07
EA
186** sendbody -- if TRUE, also send back the body of the
187** message; otherwise just send the header.
aba51985
EA
188**
189** Returns:
190** zero -- if everything went ok.
191** else -- some error.
192**
193** Side Effects:
194** Returns the current message to the sender via
195** mail.
196*/
197
79bd7c07 198static bool SendBody;
aba51985 199
3c7fe765
EA
200#define MAXRETURNS 6 /* max depth of returning messages */
201
dd1fe05b 202returntosender(msg, returnto, sendbody)
aba51985 203 char *msg;
dd1fe05b 204 ADDRESS *returnto;
79bd7c07 205 bool sendbody;
aba51985 206{
aba51985 207 char buf[MAXNAME];
dd1fe05b
EA
208 extern putheader(), errbody();
209 register ENVELOPE *ee;
210 extern ENVELOPE *newenvelope();
211 ENVELOPE errenvelope;
3c7fe765
EA
212 static int returndepth;
213
214 if (++returndepth >= MAXRETURNS)
215 {
216 if (returndepth != MAXRETURNS)
217 syserr("returntosender: infinite recursion on %s", returnto->q_paddr);
218 /* don't "unrecurse" and fake a clean exit */
219 /* returndepth--; */
220 return (0);
221 }
aba51985 222
1ff06f70 223 NoAlias = TRUE;
79bd7c07 224 SendBody = sendbody;
dd1fe05b
EA
225 ee = newenvelope(&errenvelope);
226 ee->e_puthdr = putheader;
227 ee->e_putbody = errbody;
228 addheader("date", "$b", ee);
229 addheader("from", "$g (Mail Delivery Subsystem)", ee);
230 addheader("to", returnto->q_paddr, ee);
231 addheader("subject", msg, ee);
aba51985
EA
232
233 /* fake up an address header for the from person */
dd1fe05b
EA
234 expand("$n", buf, &buf[sizeof buf - 1], CurEnv);
235 if (parse(buf, &ee->e_from, -1) == NULL)
aba51985
EA
236 {
237 syserr("Can't parse myself!");
238 ExitStat = EX_SOFTWARE;
3c7fe765 239 returndepth--;
aba51985
EA
240 return (-1);
241 }
3c7fe765 242 ee->e_sendqueue = returnto;
79bd7c07 243
dd1fe05b
EA
244 /* push state into submessage */
245 CurEnv = ee;
246 define('f', "$n");
247 define('x', "Mail Delivery Subsystem");
248
249 /* actually deliver the error message */
3c7fe765 250 sendall(ee, FALSE);
dd1fe05b 251
3c7fe765
EA
252 /* do any closing error processing */
253 checkerrors(ee);
dd1fe05b
EA
254
255 /* restore state */
256 CurEnv = CurEnv->e_parent;
3c7fe765 257 returndepth--;
79bd7c07 258
3c7fe765 259 /* should check for delivery errors here */
aba51985
EA
260 return (0);
261}
262\f/*
dd1fe05b 263** ERRBODY -- output the body of an error message.
b3cbe40f 264**
dd1fe05b
EA
265** Typically this is a copy of the transcript plus a copy of the
266** original offending message.
b3cbe40f 267**
b3cbe40f
EA
268** Parameters:
269** xfile -- the transcript file.
270** fp -- the output file.
dd1fe05b 271** xdot -- if set, use the SMTP hidden dot algorithm.
b3cbe40f
EA
272**
273** Returns:
274** none
275**
276** Side Effects:
dd1fe05b 277** Outputs the body of an error message.
b3cbe40f
EA
278*/
279
dd1fe05b 280errbody(fp, m, xdot)
b3cbe40f 281 register FILE *fp;
74c5fe7c 282 register struct mailer *m;
e6b0a75b 283 bool xdot;
b3cbe40f 284{
9e3c0a28 285 register FILE *xfile;
dd1fe05b 286 char buf[MAXLINE];
b85ad418 287 bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
b3cbe40f 288
29871fef 289 (void) fflush(stdout);
9e3c0a28
EA
290 if ((xfile = fopen(Transcript, "r")) == NULL)
291 syserr("Cannot open %s", Transcript);
b3cbe40f 292 errno = 0;
74c5fe7c 293
74c5fe7c
EA
294 /*
295 ** Output transcript of errors
296 */
297
dd1fe05b 298 fprintf(fp, " ----- Transcript of session follows -----\n");
49086753 299 (void) fflush(Xscript);
29871fef 300 while (fgets(buf, sizeof buf, xfile) != NULL)
b85ad418 301 putline(buf, fp, fullsmtp);
74c5fe7c
EA
302
303 /*
304 ** Output text of original message
305 */
306
46b14b48
EA
307 if (NoReturn)
308 fprintf(fp, "\n ----- Return message suppressed -----\n\n");
309 else if (TempFile != NULL)
738043e8 310 {
79bd7c07
EA
311 if (SendBody)
312 {
313 fprintf(fp, "\n ----- Unsent message follows -----\n");
314 (void) fflush(fp);
f9566d23 315 putheader(fp, m, CurEnv->e_parent);
dd1fe05b 316 fprintf(fp, "\n");
f9566d23 317 putbody(fp, m, xdot);
79bd7c07
EA
318 }
319 else
320 {
321 fprintf(fp, "\n ----- Message header follows -----\n");
322 (void) fflush(fp);
f9566d23 323 putheader(fp, m, CurEnv);
79bd7c07 324 }
738043e8
EA
325 }
326 else
327 fprintf(fp, "\n ----- No message was collected -----\n\n");
74c5fe7c
EA
328
329 /*
330 ** Cleanup and exit
331 */
332
29871fef 333 (void) fclose(xfile);
b3cbe40f 334 if (errno != 0)
dd1fe05b 335 syserr("errbody: I/O error");
b3cbe40f 336}