popen.c: some cleanup, use other wait_child() from now on
[s-mailx.git] / quit.c
blob1bfd3a200ceb10167e1151969ecbd98d7831ff13
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Termination processing.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
45 #include <utime.h>
47 static char _mboxname[MAXPATHLEN]; /* Name of mbox */
49 /* Touch the indicated file */
50 static void alter(char const *name);
52 static int writeback(FILE *res, FILE *obuf);
53 static void edstop(void);
55 static void
56 alter(char const *name)
58 struct stat sb;
59 struct utimbuf utb;
61 if (stat(name, &sb))
62 return;
63 utb.actime = time((time_t *)0) + 1;
64 utb.modtime = sb.st_mtime;
65 utime(name, &utb);
69 * The "quit" command.
71 /*ARGSUSED*/
72 FL int
73 quitcmd(void *v)
75 (void)v;
77 * If we are sourcing, then return 1 so execute() can handle it.
78 * Otherwise, return -1 to abort command loop.
80 if (sourcing)
81 return 1;
82 return -1;
86 * Preserve all the appropriate messages back in the system
87 * mailbox, and print a nice message indicated how many were
88 * saved. On any error, just return -1. Else return 0.
89 * Incorporate the any new mail that we found.
91 static int
92 writeback(FILE *res, FILE *obuf)
94 struct message *mp;
95 int p, c;
97 p = 0;
98 if (fseek(obuf, 0L, SEEK_SET) < 0)
99 return -1;
100 #ifndef APPEND
101 if (res != NULL)
102 while ((c = getc(res)) != EOF)
103 putc(c, obuf);
104 #endif
105 srelax_hold();
106 for (mp = &message[0]; mp < &message[msgCount]; mp++)
107 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
108 p++;
109 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
110 perror(mailname);
111 (void)fseek(obuf, 0L, SEEK_SET);
112 srelax_rele();
113 return(-1);
115 srelax();
117 srelax_rele();
119 #ifdef APPEND
120 if (res != NULL)
121 while ((c = getc(res)) != EOF)
122 putc(c, obuf);
123 #endif
124 fflush(obuf);
125 ftrunc(obuf);
126 if (ferror(obuf)) {
127 perror(mailname);
128 (void)fseek(obuf, 0L, SEEK_SET);
129 return(-1);
131 if (res != NULL)
132 Fclose(res);
133 if (fseek(obuf, 0L, SEEK_SET) < 0)
134 return -1;
135 alter(mailname);
136 if (p == 1)
137 printf(tr(155, "Held 1 message in %s\n"), displayname);
138 else
139 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
140 return 0;
144 * Save all of the undetermined messages at the top of "mbox"
145 * Save all untouched messages back in the system mailbox.
146 * Remove the system mailbox, if none saved there.
148 FL void
149 quit(void)
151 int p, modify, anystat;
152 FILE *fbuf, *rbuf, *abuf;
153 struct message *mp;
154 int c;
155 char *tempResid;
156 struct stat minfo;
159 * If we are read only, we can't do anything,
160 * so just return quickly. IMAP can set some
161 * flags (e.g. "\\Seen") so imap_quit must be
162 * called even then.
164 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
165 return;
166 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
167 * TODO us, but this causes uninterruptible hangs due to blocked sigs
168 * TODO anywhere except for MB_FILE (all others install their own
169 * TODO handlers, as it seems, properly); marked YYY */
170 switch (mb.mb_type) {
171 case MB_FILE:
172 break;
173 case MB_MAILDIR:
174 rele_sigs(); /* YYY */
175 maildir_quit();
176 hold_sigs(); /* YYY */
177 return;
178 #ifdef HAVE_POP3
179 case MB_POP3:
180 rele_sigs(); /* YYY */
181 pop3_quit();
182 hold_sigs(); /* YYY */
183 return;
184 #endif
185 #ifdef HAVE_IMAP
186 case MB_IMAP:
187 case MB_CACHE:
188 rele_sigs(); /* YYY */
189 imap_quit();
190 hold_sigs(); /* YYY */
191 return;
192 #endif
193 case MB_VOID:
194 default:
195 return;
198 * If editing (not reading system mail box), then do the work
199 * in edstop()
201 if (edit) {
202 edstop();
203 return;
207 * See if there any messages to save in mbox. If no, we
208 * can save copying mbox to /tmp and back.
210 * Check also to see if any files need to be preserved.
211 * Delete all untouched messages to keep them out of mbox.
212 * If all the messages are to be preserved, just exit with
213 * a message.
216 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
217 if (fbuf == NULL) {
218 if (errno == ENOENT)
219 return;
220 goto newmail;
222 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
223 nolock:
224 perror(tr(157, "Unable to lock mailbox"));
225 Fclose(fbuf);
226 return;
228 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
229 goto nolock;
230 rbuf = NULL;
231 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
232 printf(tr(158, "New mail has arrived.\n"));
233 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
234 if (rbuf == NULL || fbuf == NULL)
235 goto newmail;
236 #ifdef APPEND
237 fseek(fbuf, (long)mailsize, SEEK_SET);
238 while ((c = getc(fbuf)) != EOF)
239 putc(c, rbuf);
240 #else
241 p = minfo.st_size - mailsize;
242 while (p-- > 0) {
243 c = getc(fbuf);
244 if (c == EOF)
245 goto newmail;
246 putc(c, rbuf);
248 #endif
249 Fclose(rbuf);
250 if ((rbuf = Fopen(tempResid, "r")) == NULL)
251 goto newmail;
252 rm(tempResid);
253 Ftfree(&tempResid);
256 anystat = holdbits();
257 modify = 0;
258 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
259 if (mp->m_flag & MBOX)
260 c++;
261 if (mp->m_flag & MPRESERVE)
262 p++;
263 if (mp->m_flag & MODIFY)
264 modify++;
266 if (p == msgCount && !modify && !anystat) {
267 if (p == 1)
268 printf(tr(155, "Held 1 message in %s\n"), displayname);
269 else if (p > 1)
270 printf(tr(156, "Held %d messages in %s\n"),
271 p, displayname);
272 Fclose(fbuf);
273 dot_unlock(mailname);
274 return;
276 if (c == 0) {
277 if (p != 0) {
278 writeback(rbuf, fbuf);
279 Fclose(fbuf);
280 dot_unlock(mailname);
281 return;
283 goto cream;
286 if (makembox() == STOP) {
287 Fclose(fbuf);
288 dot_unlock(mailname);
289 return;
292 * Now we are ready to copy back preserved files to
293 * the system mailbox, if any were requested.
296 if (p != 0) {
297 writeback(rbuf, fbuf);
298 Fclose(fbuf);
299 dot_unlock(mailname);
300 return;
304 * Finally, remove his /usr/mail file.
305 * If new mail has arrived, copy it back.
308 cream:
309 if (rbuf != NULL) {
310 abuf = fbuf;
311 fseek(abuf, 0L, SEEK_SET);
312 while ((c = getc(rbuf)) != EOF)
313 putc(c, abuf);
314 Fclose(rbuf);
315 ftrunc(abuf);
316 alter(mailname);
317 Fclose(fbuf);
318 dot_unlock(mailname);
319 return;
321 demail();
322 Fclose(fbuf);
323 dot_unlock(mailname);
324 return;
326 newmail:
327 printf(tr(166, "Thou hast new mail.\n"));
328 if (fbuf != NULL) {
329 Fclose(fbuf);
330 dot_unlock(mailname);
335 * Adjust the message flags in each message.
337 FL int
338 holdbits(void)
340 struct message *mp;
341 int anystat, autohold, holdbit, nohold;
343 anystat = 0;
344 autohold = value("hold") != NULL;
345 holdbit = autohold ? MPRESERVE : MBOX;
346 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
347 if (value("keepsave") != NULL)
348 nohold &= ~MSAVED;
349 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
350 if (mp->m_flag & MNEW) {
351 mp->m_flag &= ~MNEW;
352 mp->m_flag |= MSTATUS;
354 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
355 MDRAFT|MUNDRAFT))
356 anystat++;
357 if ((mp->m_flag & MTOUCH) == 0)
358 mp->m_flag |= MPRESERVE;
359 if ((mp->m_flag & nohold) == 0)
360 mp->m_flag |= holdbit;
362 return anystat;
365 FL void
366 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
368 char const *cp;
370 if ((cp = expand("&")) == NULL)
371 cp = "";
372 n_strlcpy(_mboxname, cp, sizeof _mboxname);
376 * Create another temporary file and copy user's mbox file
377 * darin. If there is no mbox, copy nothing.
378 * If he has specified "append" don't copy his mailbox,
379 * just copy saveable entries at the end.
381 FL enum okay
382 makembox(void)
384 struct message *mp;
385 char *mbox, *tempQuit;
386 int mcount, c;
387 FILE *ibuf = NULL, *obuf, *abuf;
388 enum protocol prot;
390 mbox = _mboxname;
391 mcount = 0;
392 if (value("append") == NULL) {
393 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
394 perror(tr(163, "temporary mail quit file"));
395 return STOP;
397 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
398 perror(tempQuit);
399 Fclose(obuf);
401 rm(tempQuit);
402 Ftfree(&tempQuit);
403 if (ibuf == NULL)
404 return STOP;
406 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
407 while ((c = getc(abuf)) != EOF)
408 putc(c, obuf);
409 Fclose(abuf);
411 if (ferror(obuf)) {
412 perror(tr(163, "temporary mail quit file"));
413 Fclose(ibuf);
414 Fclose(obuf);
415 return STOP;
417 Fclose(obuf);
419 if ((c = open(mbox, O_CREAT|O_TRUNC|O_WRONLY, 0600)) >= 0)
420 close(c);
421 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
422 perror(mbox);
423 Fclose(ibuf);
424 return STOP;
427 else {
428 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
429 perror(mbox);
430 return STOP;
432 fchmod(fileno(obuf), 0600);
435 srelax_hold();
436 prot = which_protocol(mbox);
437 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
438 if (mp->m_flag & MBOX) {
439 mcount++;
440 if (prot == PROTO_IMAP &&
441 saveignore[0].i_count == 0 &&
442 saveignore[1].i_count == 0
443 #ifdef HAVE_IMAP /* TODO revisit */
444 && imap_thisaccount(mbox)
445 #endif
447 #ifdef HAVE_IMAP
448 if (imap_copy(mp, mp-message+1, mbox) == STOP)
449 #endif
450 goto jerr;
451 } else if (sendmp(mp, obuf, saveignore,
452 NULL, SEND_MBOX, NULL) < 0) {
453 perror(mbox);
454 jerr:
455 if (ibuf)
456 Fclose(ibuf);
457 Fclose(obuf);
458 srelax_rele();
459 return STOP;
461 mp->m_flag |= MBOXED;
462 srelax();
465 srelax_rele();
468 * Copy the user's old mbox contents back
469 * to the end of the stuff we just saved.
470 * If we are appending, this is unnecessary.
473 if (value("append") == NULL) {
474 rewind(ibuf);
475 c = getc(ibuf);
476 while (c != EOF) {
477 putc(c, obuf);
478 if (ferror(obuf))
479 break;
480 c = getc(ibuf);
482 Fclose(ibuf);
483 fflush(obuf);
485 ftrunc(obuf);
486 if (ferror(obuf)) {
487 perror(mbox);
488 Fclose(obuf);
489 return STOP;
491 if (Fclose(obuf) != 0) {
492 if (prot != PROTO_IMAP)
493 perror(mbox);
494 return STOP;
496 if (mcount == 1)
497 printf(tr(164, "Saved 1 message in mbox\n"));
498 else
499 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
500 return OKAY;
504 * Terminate an editing session by attempting to write out the user's
505 * file from the temporary. Save any new stuff appended to the file.
507 static void
508 edstop(void)
510 int gotcha, c;
511 struct message *mp;
512 FILE *obuf, *ibuf = NULL;
513 struct stat statb;
515 if (mb.mb_perm == 0)
516 return;
517 hold_sigs();
518 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
519 if (mp->m_flag & MNEW) {
520 mp->m_flag &= ~MNEW;
521 mp->m_flag |= MSTATUS;
523 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
524 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
525 gotcha++;
527 if (!gotcha)
528 goto done;
529 ibuf = NULL;
530 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
531 char *tempname;
533 if ((obuf = Ftemp(&tempname, "edstop", "w", 0600, 1)) == NULL) {
534 perror(tr(167, "tmpfile"));
535 rele_sigs();
536 reset(0);
538 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
539 perror(mailname);
540 Fclose(obuf);
541 rm(tempname);
542 Ftfree(&tempname);
543 rele_sigs();
544 reset(0);
546 fseek(ibuf, (long)mailsize, SEEK_SET);
547 while ((c = getc(ibuf)) != EOF)
548 putc(c, obuf);
549 Fclose(ibuf);
550 Fclose(obuf);
551 if ((ibuf = Fopen(tempname, "r")) == NULL) {
552 perror(tempname);
553 rm(tempname);
554 Ftfree(&tempname);
555 rele_sigs();
556 reset(0);
558 rm(tempname);
559 Ftfree(&tempname);
561 printf(tr(168, "\"%s\" "), displayname);
562 fflush(stdout);
563 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
564 perror(mailname);
565 rele_sigs();
566 reset(0);
568 ftrunc(obuf);
570 srelax_hold();
571 c = 0;
572 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
573 if ((mp->m_flag & MDELETED) != 0)
574 continue;
575 c++;
576 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
577 perror(mailname);
578 rele_sigs();
579 srelax_rele();
580 reset(0);/* XXX jump aways are terrible */
582 srelax();
584 srelax_rele();
586 gotcha = (c == 0 && ibuf == NULL);
587 if (ibuf != NULL) {
588 while ((c = getc(ibuf)) != EOF)
589 putc(c, obuf);
590 Fclose(ibuf);
592 fflush(obuf);
593 if (ferror(obuf)) {
594 perror(mailname);
595 rele_sigs();
596 reset(0);
598 Fclose(obuf);
599 if (gotcha && value("emptybox") == NULL) {
600 rm(mailname);
601 printf((value("bsdcompat") || value("bsdmsgs"))
602 ? tr(169, "removed\n") : tr(211, "removed.\n"));
603 } else
604 printf((value("bsdcompat") || value("bsdmsgs"))
605 ? tr(170, "complete\n") : tr(212, "updated.\n"));
606 fflush(stdout);
608 done:
609 rele_sigs();
612 enum quitflags {
613 QUITFLAG_HOLD = 001,
614 QUITFLAG_KEEPSAVE = 002,
615 QUITFLAG_APPEND = 004,
616 QUITFLAG_EMPTYBOX = 010
619 static const struct quitnames {
620 enum quitflags flag;
621 const char *name;
622 } quitnames[] = {
623 { QUITFLAG_HOLD, "hold" },
624 { QUITFLAG_KEEPSAVE, "keepsave" },
625 { QUITFLAG_APPEND, "append" },
626 { QUITFLAG_EMPTYBOX, "emptybox" },
627 { 0, NULL }
630 FL int
631 savequitflags(void)
633 enum quitflags qf = 0;
634 int i;
636 for (i = 0; quitnames[i].name; i++)
637 if (value(quitnames[i].name))
638 qf |= quitnames[i].flag;
639 return qf;
642 FL void
643 restorequitflags(int qf)
645 int i;
647 for (i = 0; quitnames[i].name; i++)
648 if (qf & quitnames[i].flag) {
649 if (value(quitnames[i].name) == NULL)
650 assign(quitnames[i].name, "");
651 } else if (value(quitnames[i].name))
652 unset_internal(quitnames[i].name);