1 /* $OpenBSD: quit.c,v 1.17 2003/06/03 02:56:11 millert Exp $ */
2 /* $NetBSD: quit.c,v 1.6 1996/12/28 07:11:07 tls Exp $ */
3
4 /*
5 * Copyright (c) 1980, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)quit.c 8.2 (Berkeley) 4/28/95";
36 #else
37 static const char rcsid[] = "$OpenBSD: quit.c,v 1.17 2003/06/03 02:56:11 millert Exp $";
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include <fcntl.h>
43 #include "extern.h"
44
45 /*
46 * Rcv -- receive mail rationally.
47 *
48 * Termination processing.
49 */
50
51 /*
52 * The "quit" command.
53 */
54 int
quitcmd(void * v)55 quitcmd(void *v)
56 {
57 /*
58 * If we are sourcing, then return 1 so execute() can handle it.
59 * Otherwise, return -1 to abort command loop.
60 */
61 if (sourcing)
62 return(1);
63 return(-1);
64 }
65
66 /*
67 * Save all of the undetermined messages at the top of "mbox"
68 * Save all untouched messages back in the system mailbox.
69 * Remove the system mailbox, if none saved there.
70 */
71 int
quit(void)72 quit(void)
73 {
74 int mcount, p, modify, autohold, anystat, holdbit, nohold;
75 FILE *ibuf = NULL, *obuf, *fbuf, *rbuf, *readstat = NULL, *abuf;
76 struct message *mp;
77 int c, fd;
78 struct stat minfo;
79 char *mbox, tempname[PATHSIZE];
80
81 /*
82 * If we are read only, we can't do anything,
83 * so just return quickly.
84 */
85 if (readonly)
86 return(0);
87
88 /*
89 * If editing (not reading system mail box), then do the work
90 * in edstop()
91 */
92 if (edit)
93 return(edstop());
94
95 /*
96 * See if there any messages to save in mbox. If no, we
97 * can save copying mbox to /tmp and back.
98 *
99 * Check also to see if any files need to be preserved.
100 * Delete all untouched messages to keep them out of mbox.
101 * If all the messages are to be preserved, just exit with
102 * a message.
103 */
104 fbuf = Fopen(mailname, "r+");
105 if (fbuf == NULL)
106 goto newmail;
107 if (flock(fileno(fbuf), LOCK_EX) == -1) {
108 warn("Unable to lock mailbox");
109 (void)Fclose(fbuf);
110 return(-1);
111 }
112 if (!spool_lock()) {
113 (void)Fclose(fbuf);
114 return(-1); /* lockspool printed the error for us */
115 }
116 rbuf = NULL;
117 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
118 puts("New mail has arrived.");
119 (void)snprintf(tempname, sizeof(tempname),
120 "%s/mail.RqXXXXXXXXXX", tmpdir);
121 if ((fd = mkstemp(tempname)) == -1 ||
122 (rbuf = Fdopen(fd, "w")) == NULL)
123 goto newmail;
124 #ifdef APPEND
125 fseek(fbuf, (long)mailsize, 0);
126 while ((c = getc(fbuf)) != EOF)
127 (void)putc(c, rbuf);
128 #else
129 p = minfo.st_size - mailsize;
130 while (p-- > 0) {
131 c = getc(fbuf);
132 if (c == EOF)
133 goto newmail;
134 (void)putc(c, rbuf);
135 }
136 #endif
137 (void)Fclose(rbuf);
138 if ((rbuf = Fopen(tempname, "r")) == NULL)
139 goto newmail;
140 (void)rm(tempname);
141 }
142
143 /*
144 * Adjust the message flags in each message.
145 */
146 anystat = 0;
147 autohold = value("hold") != NULL;
148 holdbit = autohold ? MPRESERVE : MBOX;
149 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
150 if (value("keepsave") != NULL)
151 nohold &= ~MSAVED;
152 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
153 if (mp->m_flag & MNEW) {
154 mp->m_flag &= ~MNEW;
155 mp->m_flag |= MSTATUS;
156 }
157 if (mp->m_flag & MSTATUS)
158 anystat++;
159 if ((mp->m_flag & MTOUCH) == 0)
160 mp->m_flag |= MPRESERVE;
161 if ((mp->m_flag & nohold) == 0)
162 mp->m_flag |= holdbit;
163 }
164 modify = 0;
165 if (Tflag != NULL) {
166 if ((readstat = Fopen(Tflag, "w")) == NULL)
167 Tflag = NULL;
168 }
169 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
170 if (mp->m_flag & MBOX)
171 c++;
172 if (mp->m_flag & MPRESERVE)
173 p++;
174 if (mp->m_flag & MODIFY)
175 modify++;
176 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
177 char *id;
178
179 if ((id = hfield("article-id", mp)) != NULL)
180 fprintf(readstat, "%s\n", id);
181 }
182 }
183 if (Tflag != NULL)
184 (void)Fclose(readstat);
185 if (p == msgCount && !modify && !anystat) {
186 printf("Held %d message%s in %s\n",
187 p, p == 1 ? "" : "s", mailname);
188 (void)Fclose(fbuf);
189 spool_unlock();
190 return(0);
191 }
192 if (c == 0) {
193 if (p != 0) {
194 writeback(rbuf);
195 (void)Fclose(fbuf);
196 spool_unlock();
197 return(0);
198 }
199 goto cream;
200 }
201
202 /*
203 * Create another temporary file and copy user's mbox file
204 * darin. If there is no mbox, copy nothing.
205 * If he has specified "append" don't copy his mailbox,
206 * just copy saveable entries at the end.
207 */
208 mbox = expand("&");
209 mcount = c;
210 if (value("append") == NULL) {
211 (void)snprintf(tempname, sizeof(tempname),
212 "%s/mail.RmXXXXXXXXXX", tmpdir);
213 if ((fd = mkstemp(tempname)) == -1 ||
214 (obuf = Fdopen(fd, "w")) == NULL) {
215 warn("%s", tempname);
216 (void)Fclose(fbuf);
217 spool_unlock();
218 return(-1);
219 }
220 if ((ibuf = Fopen(tempname, "r")) == NULL) {
221 warn("%s", tempname);
222 (void)rm(tempname);
223 (void)Fclose(obuf);
224 (void)Fclose(fbuf);
225 spool_unlock();
226 return(-1);
227 }
228 (void)rm(tempname);
229 if ((abuf = Fopen(mbox, "r")) != NULL) {
230 while ((c = getc(abuf)) != EOF)
231 (void)putc(c, obuf);
232 (void)Fclose(abuf);
233 }
234 if (ferror(obuf)) {
235 warn("%s", tempname);
236 (void)Fclose(ibuf);
237 (void)Fclose(obuf);
238 (void)Fclose(fbuf);
239 spool_unlock();
240 return(-1);
241 }
242 (void)Fclose(obuf);
243 (void)close(creat(mbox, 0600));
244 if ((obuf = Fopen(mbox, "r+")) == NULL) {
245 warn("%s", mbox);
246 (void)Fclose(ibuf);
247 (void)Fclose(fbuf);
248 spool_unlock();
249 return(-1);
250 }
251 } else {
252 if ((obuf = Fopen(mbox, "a")) == NULL) {
253 warn("%s", mbox);
254 (void)Fclose(fbuf);
255 spool_unlock();
256 return(-1);
257 }
258 fchmod(fileno(obuf), 0600);
259 }
260 for (mp = &message[0]; mp < &message[msgCount]; mp++)
261 if (mp->m_flag & MBOX)
262 if (sendmessage(mp, obuf, saveignore, NULL) < 0) {
263 warn("%s", mbox);
264 (void)Fclose(ibuf);
265 (void)Fclose(obuf);
266 (void)Fclose(fbuf);
267 spool_unlock();
268 return(-1);
269 }
270
271 /*
272 * Copy the user's old mbox contents back
273 * to the end of the stuff we just saved.
274 * If we are appending, this is unnecessary.
275 */
276 if (value("append") == NULL) {
277 rewind(ibuf);
278 c = getc(ibuf);
279 while (c != EOF) {
280 (void)putc(c, obuf);
281 if (ferror(obuf))
282 break;
283 c = getc(ibuf);
284 }
285 (void)Fclose(ibuf);
286 fflush(obuf);
287 }
288 trunc(obuf);
289 if (ferror(obuf)) {
290 warn("%s", mbox);
291 (void)Fclose(obuf);
292 (void)Fclose(fbuf);
293 spool_unlock();
294 return(-1);
295 }
296 (void)Fclose(obuf);
297 if (mcount == 1)
298 puts("Saved 1 message in mbox");
299 else
300 printf("Saved %d messages in mbox\n", mcount);
301
302 /*
303 * Now we are ready to copy back preserved files to
304 * the system mailbox, if any were requested.
305 */
306 if (p != 0) {
307 writeback(rbuf);
308 (void)Fclose(fbuf);
309 spool_unlock();
310 return(0);
311 }
312
313 /*
314 * Finally, remove his /var/mail file.
315 * If new mail has arrived, copy it back.
316 */
317 cream:
318 if (rbuf != NULL) {
319 abuf = Fopen(mailname, "r+");
320 if (abuf == NULL)
321 goto newmail;
322 while ((c = getc(rbuf)) != EOF)
323 (void)putc(c, abuf);
324 (void)Fclose(rbuf);
325 trunc(abuf);
326 (void)Fclose(abuf);
327 alter(mailname);
328 (void)Fclose(fbuf);
329 spool_unlock();
330 return(0);
331 }
332 demail();
333 (void)Fclose(fbuf);
334 spool_unlock();
335 return(0);;
336
337 newmail:
338 puts("Thou hast new mail.");
339 if (fbuf != NULL) {
340 (void)Fclose(fbuf);
341 spool_unlock();
342 }
343 return(0);
344 }
345
346 /*
347 * Preserve all the appropriate messages back in the system
348 * mailbox, and print a nice message indicated how many were
349 * saved. On any error, just return -1. Else return 0.
350 * Incorporate the any new mail that we found.
351 */
352 int
writeback(FILE * res)353 writeback(FILE *res)
354 {
355 struct message *mp;
356 int p, c;
357 FILE *obuf;
358
359 p = 0;
360 if ((obuf = Fopen(mailname, "r+")) == NULL) {
361 warn("%s", mailname);
362 return(-1);
363 }
364 #ifndef APPEND
365 if (res != NULL)
366 while ((c = getc(res)) != EOF)
367 (void)putc(c, obuf);
368 #endif
369 for (mp = &message[0]; mp < &message[msgCount]; mp++)
370 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
371 p++;
372 if (sendmessage(mp, obuf, NULL, NULL) < 0) {
373 warn("%s", mailname);
374 (void)Fclose(obuf);
375 return(-1);
376 }
377 }
378 #ifdef APPEND
379 if (res != NULL)
380 while ((c = getc(res)) != EOF)
381 (void)putc(c, obuf);
382 #endif
383 fflush(obuf);
384 trunc(obuf);
385 if (ferror(obuf)) {
386 warn("%s", mailname);
387 (void)Fclose(obuf);
388 return(-1);
389 }
390 if (res != NULL)
391 (void)Fclose(res);
392 (void)Fclose(obuf);
393 alter(mailname);
394 if (p == 1)
395 printf("Held 1 message in %s\n", mailname);
396 else
397 printf("Held %d messages in %s\n", p, mailname);
398 return(0);
399 }
400
401 /*
402 * Terminate an editing session by attempting to write out the user's
403 * file from the temporary. Save any new stuff appended to the file.
404 */
405 int
edstop(void)406 edstop(void)
407 {
408 int gotcha, c;
409 struct message *mp;
410 FILE *obuf, *ibuf, *readstat = NULL;
411 struct stat statb;
412 char tempname[PATHSIZE];
413
414 if (readonly)
415 return(0);
416 holdsigs();
417 if (Tflag != NULL) {
418 if ((readstat = Fopen(Tflag, "w")) == NULL)
419 Tflag = NULL;
420 }
421 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
422 if (mp->m_flag & MNEW) {
423 mp->m_flag &= ~MNEW;
424 mp->m_flag |= MSTATUS;
425 }
426 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
427 gotcha++;
428 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
429 char *id;
430
431 if ((id = hfield("article-id", mp)) != NULL)
432 fprintf(readstat, "%s\n", id);
433 }
434 }
435 if (Tflag != NULL)
436 (void)Fclose(readstat);
437 if (!gotcha || Tflag != NULL)
438 goto done;
439 ibuf = NULL;
440 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
441 int fd;
442
443 (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX",
444 tmpdir);
445 if ((fd = mkstemp(tempname)) == -1 ||
446 (obuf = Fdopen(fd, "w")) == NULL) {
447 warn("%s", tempname);
448 if (fd != -1)
449 close(fd);
450 relsesigs();
451 return(-1);
452 }
453 if ((ibuf = Fopen(mailname, "r")) == NULL) {
454 warn("%s", mailname);
455 (void)Fclose(obuf);
456 (void)rm(tempname);
457 relsesigs();
458 return(-1);
459 }
460 fseek(ibuf, (long)mailsize, 0);
461 while ((c = getc(ibuf)) != EOF)
462 (void)putc(c, obuf);
463 (void)Fclose(ibuf);
464 (void)Fclose(obuf);
465 if ((ibuf = Fopen(tempname, "r")) == NULL) {
466 warn("%s", tempname);
467 (void)rm(tempname);
468 relsesigs();
469 return(-1);
470 }
471 (void)rm(tempname);
472 }
473 printf("\"%s\" ", mailname);
474 fflush(stdout);
475 if ((obuf = Fopen(mailname, "r+")) == NULL) {
476 warn("%s", mailname);
477 relsesigs();
478 return(-1);
479 }
480 trunc(obuf);
481 c = 0;
482 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
483 if ((mp->m_flag & MDELETED) != 0)
484 continue;
485 c++;
486 if (sendmessage(mp, obuf, NULL, NULL) < 0) {
487 warn("%s", mailname);
488 relsesigs();
489 return(-1);
490 }
491 }
492 gotcha = (c == 0 && ibuf == NULL);
493 if (ibuf != NULL) {
494 while ((c = getc(ibuf)) != EOF)
495 (void)putc(c, obuf);
496 (void)Fclose(ibuf);
497 }
498 fflush(obuf);
499 if (ferror(obuf)) {
500 warn("%s", mailname);
501 relsesigs();
502 return(-1);
503 }
504 (void)Fclose(obuf);
505 if (gotcha) {
506 (void)rm(mailname);
507 puts("removed");
508 } else
509 puts("complete");
510 fflush(stdout);
511
512 done:
513 relsesigs();
514 return(0);
515 }
516