cc-test.sh: do a simple \`Resend' test
[s-mailx.git] / quit.c
blobe9e346fe08e04ff6bffd8b50c874819397048648
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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE quit
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <utime.h>
44 enum quitflags {
45 QUITFLAG_HOLD = 1<<0,
46 QUITFLAG_KEEP = 1<<1,
47 QUITFLAG_KEEPSAVE = 1<<2,
48 QUITFLAG_APPEND = 1<<3,
49 QUITFLAG_EMPTYBOX = 1<<4
52 struct quitnames {
53 enum quitflags flag;
54 enum okeys okey;
57 static struct quitnames const _quitnames[] = {
58 {QUITFLAG_HOLD, ok_b_hold},
59 {QUITFLAG_KEEP, ok_b_keep},
60 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
61 {QUITFLAG_APPEND, ok_b_append},
62 {QUITFLAG_EMPTYBOX, ok_b_emptybox} /* TODO obsolete emptybox */
65 static char _mboxname[PATH_MAX]; /* Name of mbox */
67 /* Touch the indicated file */
68 static void _alter(char const *name);
70 /* Preserve all the appropriate messages back in the system mailbox, and print
71 * a nice message indicated how many were saved. On any error, just return -1.
72 * Else return 0. Incorporate the any new mail that we found */
73 static int writeback(FILE *res, FILE *obuf);
75 /* Terminate an editing session by attempting to write out the user's file from
76 * the temporary. Save any new stuff appended to the file */
77 static void edstop(void);
79 /* Remove "mailname", unless *keep* says otherwise; force truncation, then */
80 static void _demail(void);
82 static void
83 _alter(char const *name)
85 struct stat sb;
86 struct utimbuf utb;
87 NYD_ENTER;
89 if (!stat(name, &sb)) {
90 utb.actime = time(NULL) + 1;
91 utb.modtime = sb.st_mtime;
92 utime(name, &utb);
94 NYD_LEAVE;
97 static int
98 writeback(FILE *res, FILE *obuf)
100 struct message *mp;
101 int rv = -1, p, c;
102 NYD_ENTER;
104 if (fseek(obuf, 0L, SEEK_SET) == -1)
105 goto jleave;
107 #ifndef APPEND
108 if (res != NULL)
109 while ((c = getc(res)) != EOF)
110 putc(c, obuf);
111 #endif
112 srelax_hold();
113 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
114 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
115 ++p;
116 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
117 n_perr(mailname, 0);
118 srelax_rele();
119 goto jerror;
121 srelax();
123 srelax_rele();
124 #ifdef APPEND
125 if (res != NULL)
126 while ((c = getc(res)) != EOF)
127 putc(c, obuf);
128 #endif
129 ftrunc(obuf);
131 if (ferror(obuf)) {
132 n_perr(mailname, 0);
133 jerror:
134 fseek(obuf, 0L, SEEK_SET);
135 goto jleave;
137 if (fseek(obuf, 0L, SEEK_SET) == -1)
138 goto jleave;
140 _alter(mailname);
141 if (p == 1)
142 printf(_("Held 1 message in %s\n"), displayname);
143 else
144 printf(_("Held %d messages in %s\n"), p, displayname);
145 rv = 0;
146 jleave:
147 if (res != NULL)
148 Fclose(res);
149 NYD_LEAVE;
150 return rv;
153 static void
154 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
156 int gotcha, c;
157 struct message *mp;
158 FILE *obuf = NULL, *ibuf = NULL;
159 struct stat statb;
160 bool_t doreset;
161 NYD_ENTER;
163 hold_sigs();
164 doreset = FAL0;
166 if (mb.mb_perm == 0)
167 goto jleave;
169 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
170 if (mp->m_flag & MNEW) {
171 mp->m_flag &= ~MNEW;
172 mp->m_flag |= MSTATUS;
174 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
175 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
176 ++gotcha;
178 if (!gotcha)
179 goto jleave;
181 doreset = TRU1;
183 /* TODO This is too simple minded? We should regenerate an index file
184 * TODO to be able to truly tell wether *anything* has changed!
185 * TODO (Or better: only come here.. then! It is an *object method!* */
186 /* TODO Ignoring stat error is easy, huh? */
187 if (!stat(mailname, &statb) && statb.st_size > mailsize) {
188 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER,
189 0600)) == NULL) {
190 n_perr(_("tmpfile"), 0);
191 goto jleave;
193 if ((ibuf = Zopen(mailname, "r")) == NULL) {
194 n_perr(mailname, 0);
195 Fclose(obuf);
196 goto jleave;
199 file_lock(fileno(ibuf), FLT_READ, 0,0, 1); /* TODO ignoring lock error! */
200 fseek(ibuf, (long)mailsize, SEEK_SET);
201 while ((c = getc(ibuf)) != EOF) /* xxx bytewise??? TODO ... I/O error? */
202 putc(c, obuf);
203 Fclose(ibuf);
204 ibuf = obuf;
205 fflush_rewind(obuf);
208 printf(_("\"%s\" "), displayname);
209 fflush(stdout);
210 if ((obuf = Zopen(mailname, "r+")) == NULL) {
211 n_perr(mailname, 0);
212 goto jleave;
215 file_lock(fileno(obuf), FLT_WRITE, 0,0, 1); /* TODO ignoring lock error! */
216 ftrunc(obuf);
218 srelax_hold();
219 c = 0;
220 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
221 if (mp->m_flag & MDELETED)
222 continue;
223 ++c;
224 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
225 n_perr(mailname, 0);
226 srelax_rele();
227 goto jleave;
229 srelax();
231 srelax_rele();
233 gotcha = (c == 0 && ibuf == NULL);
234 if (ibuf != NULL) {
235 while ((c = getc(ibuf)) != EOF)
236 putc(c, obuf);
238 fflush(obuf);
239 if (ferror(obuf)) {
240 n_perr(mailname, 0);
241 goto jleave;
243 Fclose(obuf);
245 doreset = FAL0;
247 if (gotcha && !ok_blook(keep) && !ok_blook(emptybox)/* TODO obsolete eb*/) {
248 rm(mailname);
249 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
250 ? _("removed\n") : _("removed.\n"));
251 } else
252 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
253 ? _("complete\n") : _("updated.\n"));
254 fflush(stdout);
255 jleave:
256 if (ibuf != NULL)
257 Fclose(ibuf);
258 rele_sigs();
259 NYD_LEAVE;
260 if (doreset)
261 reset(0);
264 static void
265 _demail(void)
267 NYD2_ENTER;
268 if (ok_blook(keep) || rm(mailname) < 0) {
269 /* TODO demail(): try use f?truncate(2) instead?! */
270 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
271 if (fd >= 0)
272 close(fd);
274 NYD2_LEAVE;
277 FL void
278 quit(void)
280 int p, modify, anystat, c;
281 FILE *fbuf = NULL, *lckfp = NULL, *rbuf, *abuf;
282 struct message *mp;
283 struct stat minfo;
284 NYD_ENTER;
286 temporary_localopts_folder_hook_unroll();
288 /* If we are read only, we can't do anything, so just return quickly */
289 /* TODO yet we cannot return quickly if resources have to be released!
290 * TODO somewhen it'll be mailbox->quit() anyway, for now do it by hand
291 *if (mb.mb_perm == 0)
292 * goto jleave;*/
293 p = (mb.mb_perm == 0);
295 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
296 * TODO us, but this causes uninterruptible hangs due to blocked sigs
297 * TODO anywhere except for MB_FILE (all others install their own
298 * TODO handlers, as it seems, properly); marked YYY */
299 switch (mb.mb_type) {
300 case MB_FILE:
301 break;
302 case MB_MAILDIR:
303 rele_sigs(); /* YYY */
304 maildir_quit();
305 hold_sigs(); /* YYY */
306 goto jleave;
307 #ifdef HAVE_POP3
308 case MB_POP3:
309 rele_sigs(); /* YYY */
310 pop3_quit();
311 hold_sigs(); /* YYY */
312 goto jleave;
313 #endif
314 #ifdef HAVE_IMAP
315 case MB_IMAP:
316 case MB_CACHE:
317 rele_sigs(); /* YYY */
318 imap_quit();
319 hold_sigs(); /* YYY */
320 goto jleave;
321 #endif
322 case MB_VOID:
323 default:
324 goto jleave;
326 if (p) goto jleave; /* TODO */
328 /* If editing (not reading system mail box), then do the work in edstop() */
329 if (pstate & PS_EDIT) {
330 edstop();
331 goto jleave;
334 /* See if there any messages to save in mbox. If no, we
335 * can save copying mbox to /tmp and back.
337 * Check also to see if any files need to be preserved.
338 * Delete all untouched messages to keep them out of mbox.
339 * If all the messages are to be preserved, just exit with
340 * a message */
341 fbuf = Zopen(mailname, "r+");
342 if (fbuf == NULL) {
343 if (errno == ENOENT)
344 goto jleave;
345 jnewmail:
346 printf(_("Thou hast new mail.\n"));
347 goto jleave;
350 if ((lckfp = dot_lock(mailname, fileno(fbuf), FLT_WRITE, 0,0, 1)) == NULL) {
351 n_perr(_("Unable to (dot) lock mailbox, aborting operation"), 0);
352 Fclose(fbuf);
353 fbuf = NULL;
354 goto jleave;
357 rbuf = NULL;
358 if (!fstat(fileno(fbuf), &minfo) && minfo.st_size > mailsize) {
359 printf(_("New mail has arrived.\n"));
360 rbuf = Ftmp(NULL, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600);
361 if (rbuf == NULL || fbuf == NULL)
362 goto jnewmail;
363 #ifdef APPEND
364 fseek(fbuf, (long)mailsize, SEEK_SET);
365 while ((c = getc(fbuf)) != EOF)
366 putc(c, rbuf);
367 #else
368 p = minfo.st_size - mailsize;
369 while (p-- > 0) {
370 c = getc(fbuf);
371 if (c == EOF)
372 goto jnewmail;
373 putc(c, rbuf);
375 #endif
376 fflush_rewind(rbuf);
379 anystat = holdbits();
380 modify = 0;
381 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
382 if (mp->m_flag & MBOX)
383 c++;
384 if (mp->m_flag & MPRESERVE)
385 p++;
386 if (mp->m_flag & MODIFY)
387 modify++;
389 if (p == msgCount && !modify && !anystat) {
390 if (p == 1)
391 printf(_("Held 1 message in %s\n"), displayname);
392 else if (p > 1)
393 printf(_("Held %d messages in %s\n"), p, displayname);
394 goto jleave;
396 if (c == 0) {
397 if (p != 0) {
398 writeback(rbuf, fbuf);
399 goto jleave;
401 goto jcream;
404 if (makembox() == STOP)
405 goto jleave;
407 /* Now we are ready to copy back preserved files to the system mailbox, if
408 * any were requested */
409 if (p != 0) {
410 writeback(rbuf, fbuf);
411 goto jleave;
414 /* Finally, remove his file. If new mail has arrived, copy it back */
415 jcream:
416 if (rbuf != NULL) {
417 abuf = fbuf;
418 fseek(abuf, 0L, SEEK_SET);
419 while ((c = getc(rbuf)) != EOF)
420 putc(c, abuf);
421 Fclose(rbuf);
422 ftrunc(abuf);
423 _alter(mailname);
424 goto jleave;
426 _demail();
427 jleave:
428 if (fbuf != NULL) {
429 Fclose(fbuf);
430 if (lckfp != NULL && lckfp != (FILE*)-1)
431 Pclose(lckfp, FAL0);
433 NYD_LEAVE;
436 FL int
437 holdbits(void)
439 struct message *mp;
440 int anystat, autohold, holdbit, nohold;
441 NYD_ENTER;
443 anystat = 0;
444 autohold = ok_blook(hold);
445 holdbit = autohold ? MPRESERVE : MBOX;
446 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
447 if (ok_blook(keepsave))
448 nohold &= ~MSAVED;
449 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
450 if (mp->m_flag & MNEW) {
451 mp->m_flag &= ~MNEW;
452 mp->m_flag |= MSTATUS;
454 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
455 MDRAFT | MUNDRAFT))
456 ++anystat;
457 if (!(mp->m_flag & MTOUCH))
458 mp->m_flag |= MPRESERVE;
459 if (!(mp->m_flag & nohold))
460 mp->m_flag |= holdbit;
462 NYD_LEAVE;
463 return anystat;
466 FL enum okay
467 makembox(void) /* TODO oh my god */
469 struct message *mp;
470 char *mbox, *tempQuit;
471 int mcount, c;
472 FILE *ibuf = NULL, *obuf, *abuf;
473 enum protocol prot;
474 enum okay rv = STOP;
475 NYD_ENTER;
477 mbox = _mboxname;
478 mcount = 0;
479 if (!ok_blook(append)) {
480 if ((obuf = Ftmp(&tempQuit, "makembox",
481 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
482 n_perr(_("temporary mail quit file"), 0);
483 goto jleave;
485 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
486 n_perr(tempQuit, 0);
487 Ftmp_release(&tempQuit);
488 if (ibuf == NULL) {
489 Fclose(obuf);
490 goto jleave;
493 if ((abuf = Zopen(mbox, "r")) != NULL) {
494 while ((c = getc(abuf)) != EOF)
495 putc(c, obuf);
496 Fclose(abuf);
498 if (ferror(obuf)) {
499 n_perr(_("temporary mail quit file"), 0);
500 Fclose(ibuf);
501 Fclose(obuf);
502 goto jleave;
504 Fclose(obuf);
506 if ((c = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1)
507 close(c);
508 if ((obuf = Zopen(mbox, "r+")) == NULL) {
509 n_perr(mbox, 0);
510 Fclose(ibuf);
511 goto jleave;
513 } else {
514 if ((obuf = Zopen(mbox, "a")) == NULL) {
515 n_perr(mbox, 0);
516 goto jleave;
518 fchmod(fileno(obuf), 0600);
521 srelax_hold();
522 prot = which_protocol(mbox);
523 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
524 if (mp->m_flag & MBOX) {
525 ++mcount;
526 if (prot == PROTO_IMAP &&
527 saveignore[0].i_count == 0 && saveignore[1].i_count == 0
528 #ifdef HAVE_IMAP /* TODO revisit */
529 && imap_thisaccount(mbox)
530 #endif
532 #ifdef HAVE_IMAP
533 if (imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
534 #endif
535 goto jerr;
536 } else if (sendmp(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) {
537 n_perr(mbox, 0);
538 jerr:
539 srelax_rele();
540 if (ibuf != NULL)
541 Fclose(ibuf);
542 Fclose(obuf);
543 goto jleave;
545 mp->m_flag |= MBOXED;
546 srelax();
549 srelax_rele();
551 /* Copy the user's old mbox contents back to the end of the stuff we just
552 * saved. If we are appending, this is unnecessary */
553 if (!ok_blook(append)) {
554 rewind(ibuf);
555 c = getc(ibuf);
556 while (c != EOF) {
557 putc(c, obuf);
558 if (ferror(obuf))
559 break;
560 c = getc(ibuf);
562 Fclose(ibuf);
563 fflush(obuf);
565 ftrunc(obuf);
566 if (ferror(obuf)) {
567 n_perr(mbox, 0);
568 Fclose(obuf);
569 goto jleave;
571 if (Fclose(obuf) != 0) {
572 if (prot != PROTO_IMAP)
573 n_perr(mbox, 0);
574 goto jleave;
576 if (mcount == 1)
577 printf(_("Saved 1 message in mbox\n"));
578 else
579 printf(_("Saved %d messages in mbox\n"), mcount);
580 rv = OKAY;
581 jleave:
582 NYD_LEAVE;
583 return rv;
586 FL void
587 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
589 char const *cp;
590 NYD_ENTER;
592 if ((cp = expand("&")) == NULL)
593 cp = "";
594 n_strlcpy(_mboxname, cp, sizeof _mboxname);
595 NYD_LEAVE;
598 FL int
599 savequitflags(void)
601 enum quitflags qf = 0;
602 size_t i;
603 NYD_ENTER;
605 for (i = 0; i < NELEM(_quitnames); ++i)
606 if (_var_oklook(_quitnames[i].okey) != NULL)
607 qf |= _quitnames[i].flag;
608 NYD_LEAVE;
609 return qf;
612 FL void
613 restorequitflags(int qf)
615 size_t i;
616 NYD_ENTER;
618 for (i = 0; i < NELEM(_quitnames); ++i) {
619 char *x = _var_oklook(_quitnames[i].okey);
620 if (qf & _quitnames[i].flag) {
621 if (x == NULL)
622 _var_okset(_quitnames[i].okey, TRU1);
623 } else if (x != NULL)
624 _var_okclear(_quitnames[i].okey);
626 NYD_LEAVE;
629 /* s-it-mode */