mime-types.c: (just) (satisfy) scan-build
[s-mailx.git] / quit.c
blobcec754997cb07ae482a8a230932e5a32d972ee2f
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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
51 struct quitnames {
52 enum quitflags flag;
53 enum okeys okey;
56 static struct quitnames const _quitnames[] = {
57 {QUITFLAG_HOLD, ok_b_hold},
58 {QUITFLAG_KEEP, ok_b_keep},
59 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
60 {QUITFLAG_APPEND, ok_b_append}
63 static char _mboxname[PATH_MAX]; /* Name of mbox */
65 /* Touch the indicated file */
66 static void _alter(char const *name);
68 /* Preserve all the appropriate messages back in the system mailbox, and print
69 * a nice message indicated how many were saved. On any error, just return -1.
70 * Else return 0. Incorporate the any new mail that we found */
71 static int writeback(FILE *res, FILE *obuf);
73 /* Terminate an editing session by attempting to write out the user's file from
74 * the temporary. Save any new stuff appended to the file */
75 static bool_t edstop(void);
77 static void
78 _alter(char const *name) /* TODO error handling */
80 #ifdef HAVE_UTIMENSAT
81 struct timespec tsa[2];
82 #else
83 struct stat sb;
84 struct utimbuf utb;
85 #endif
86 struct n_timespec const *tsp;
87 NYD_ENTER;
89 tsp = n_time_now(TRU1); /* TODO -> eventloop */
91 #ifdef HAVE_UTIMENSAT
92 tsa[0].tv_sec = tsp->ts_sec + 1;
93 tsa[0].tv_nsec = tsp->ts_nsec;
94 tsa[1].tv_nsec = UTIME_OMIT;
95 utimensat(AT_FDCWD, name, tsa, 0);
96 #else
97 if (!stat(name, &sb)) {
98 utb.actime = tsp->ts_sec;
99 utb.modtime = sb.st_mtime;
100 utime(name, &utb);
102 #endif
103 NYD_LEAVE;
106 static int
107 writeback(FILE *res, FILE *obuf)
109 struct message *mp;
110 int rv = -1, p, c;
111 NYD_ENTER;
113 if (fseek(obuf, 0L, SEEK_SET) == -1)
114 goto jleave;
116 #ifndef APPEND
117 if (res != NULL)
118 while ((c = getc(res)) != EOF)
119 putc(c, obuf);
120 #endif
121 srelax_hold();
122 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
123 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
124 ++p;
125 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
126 n_perr(mailname, 0);
127 srelax_rele();
128 goto jerror;
130 srelax();
132 srelax_rele();
133 #ifdef APPEND
134 if (res != NULL)
135 while ((c = getc(res)) != EOF)
136 putc(c, obuf);
137 #endif
138 ftrunc(obuf);
140 if (ferror(obuf)) {
141 n_perr(mailname, 0);
142 jerror:
143 fseek(obuf, 0L, SEEK_SET);
144 goto jleave;
146 if (fseek(obuf, 0L, SEEK_SET) == -1)
147 goto jleave;
149 _alter(mailname);
150 if (p == 1)
151 fprintf(n_stdout, _("Held 1 message in %s\n"), displayname);
152 else
153 fprintf(n_stdout, _("Held %d messages in %s\n"), p, displayname);
154 rv = 0;
155 jleave:
156 if (res != NULL)
157 Fclose(res);
158 NYD_LEAVE;
159 return rv;
162 static bool_t
163 edstop(void) /* TODO oh my god */
165 int gotcha, c;
166 struct message *mp;
167 FILE *obuf = NULL, *ibuf = NULL;
168 struct stat statb;
169 bool_t rv;
170 NYD_ENTER;
172 rv = TRU1;
174 if (mb.mb_perm == 0)
175 goto j_leave;
177 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
178 if (mp->m_flag & MNEW) {
179 mp->m_flag &= ~MNEW;
180 mp->m_flag |= MSTATUS;
182 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
183 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
184 ++gotcha;
186 if (!gotcha)
187 goto jleave;
189 rv = FAL0;
191 /* TODO This is too simple minded? We should regenerate an index file
192 * TODO to be able to truly tell whether *anything* has changed!
193 * TODO (Or better: only come here.. then! It is an *object method!* */
194 /* TODO Ignoring stat error is easy, huh? */
195 if (!stat(mailname, &statb) && statb.st_size > mailsize) {
196 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
197 NULL) {
198 n_perr(_("tmpfile"), 0);
199 goto jleave;
201 if ((ibuf = n_fopen_any(mailname, "r", NULL)) == NULL) {
202 n_perr(mailname, 0);
203 goto jleave;
206 n_file_lock(fileno(ibuf), FLT_READ, 0,0, UIZ_MAX); /* TODO ign. lock err*/
207 fseek(ibuf, (long)mailsize, SEEK_SET);
208 while ((c = getc(ibuf)) != EOF) /* xxx bytewise??? TODO ... I/O error? */
209 putc(c, obuf);
210 Fclose(ibuf);
211 ibuf = obuf;
212 fflush_rewind(obuf);
213 /*obuf = NULL;*/
216 fprintf(n_stdout, _("%s "), n_shexp_quote_cp(displayname, FAL0));
217 fflush(n_stdout);
219 if ((obuf = n_fopen_any(mailname, "r+", NULL)) == NULL) {
220 int e = n_err_no;
221 n_perr(n_shexp_quote_cp(mailname, FAL0), e);
222 goto jleave;
224 n_file_lock(fileno(obuf), FLT_WRITE, 0,0, UIZ_MAX); /* TODO ign. lock err! */
225 ftrunc(obuf);
227 srelax_hold();
228 c = 0;
229 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
230 if (mp->m_flag & MDELETED)
231 continue;
232 ++c;
233 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
234 srelax_rele();
235 n_err(_("Failed to finalize %s\n"), n_shexp_quote_cp(mailname, FAL0));
236 goto jleave;
238 srelax();
240 srelax_rele();
242 gotcha = (c == 0 && ibuf == NULL);
243 if (ibuf != NULL) {
244 while ((c = getc(ibuf)) != EOF)
245 putc(c, obuf);
247 fflush(obuf);
248 if (ferror(obuf)) {
249 n_err(_("Failed to finalize %s\n"), n_shexp_quote_cp(mailname, FAL0));
250 goto jleave;
253 if(gotcha){
254 /* Non-system boxes are never removed except forced via POSIX mode */
255 #ifdef HAVE_FTRUNCATE
256 ftruncate(fileno(obuf), 0);
257 #else
258 int fd;
260 if((fd = open(mailname, (O_WRONLY | O_CREAT | n_O_NOFOLLOW | O_TRUNC),
261 0600)) != -1)
262 close(fd);
263 #endif
265 if(ok_blook(posix) && !ok_blook(keep) && n_path_rm(mailname))
266 fputs(_("removed\n"), n_stdout);
267 else
268 fputs(_("truncated\n"), n_stdout);
269 } else
270 fputs((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
271 ? _("complete\n") : _("updated.\n"), n_stdout);
272 fflush(n_stdout);
274 rv = TRU1;
275 jleave:
276 if (obuf != NULL)
277 Fclose(obuf);
278 if (ibuf != NULL)
279 Fclose(ibuf);
280 if(!rv){
281 /* TODO The codebase aborted by jumping to the main loop here.
282 * TODO The OpenBSD mailx simply ignores this error.
283 * TODO For now we follow the latter unless we are interactive,
284 * TODO in which case we ask the user whether the error is to be
285 * TODO ignored or not. More of this around here in this file! */
286 rv = getapproval(_("Continue, possibly loosing changes"), TRU1);
288 j_leave:
289 NYD_LEAVE;
290 return rv;
293 FL bool_t
294 quit(bool_t hold_sigs_on)
296 int p, modify, anystat, c;
297 FILE *fbuf = NULL, *lckfp = NULL, *rbuf, *abuf;
298 struct message *mp;
299 struct stat minfo;
300 bool_t rv;
301 NYD_ENTER;
303 if(!hold_sigs_on)
304 hold_sigs();
306 rv = FAL0;
307 temporary_folder_hook_unroll();
309 /* If we are read only, we can't do anything, so just return quickly */
310 /* TODO yet we cannot return quickly if resources have to be released!
311 * TODO somewhen it'll be mailbox->quit() anyway, for now do it by hand
312 *if (mb.mb_perm == 0)
313 * goto jleave;*/
314 p = (mb.mb_perm == 0);
316 switch (mb.mb_type) {
317 case MB_FILE:
318 break;
319 case MB_MAILDIR:
320 rv = maildir_quit(TRU1);
321 goto jleave;
322 #ifdef HAVE_POP3
323 case MB_POP3:
324 rv = pop3_quit(TRU1);
325 goto jleave;
326 #endif
327 #ifdef HAVE_IMAP
328 case MB_IMAP:
329 case MB_CACHE:
330 rv = imap_quit(TRU1);
331 goto jleave;
332 #endif
333 case MB_VOID:
334 rv = TRU1;
335 /* FALLTHRU */
336 default:
337 goto jleave;
339 if (p) {
340 rv = TRU1;
341 goto jleave; /* TODO */
344 /* If editing (not reading system mail box), then do the work in edstop() */
345 if (n_pstate & n_PS_EDIT) {
346 rv = edstop();
347 goto jleave;
350 /* See if there any messages to save in mbox. If no, we
351 * can save copying mbox to /tmp and back.
353 * Check also to see if any files need to be preserved.
354 * Delete all untouched messages to keep them out of mbox.
355 * If all the messages are to be preserved, just exit with
356 * a message */
357 fbuf = n_fopen_any(mailname, "r+", NULL);
358 if (fbuf == NULL) {
359 if (n_err_no != n_ERR_NOENT)
360 jnewmail:
361 fprintf(n_stdout, _("Thou hast new mail.\n"));
362 rv = TRU1;
363 goto jleave;
366 if ((lckfp = n_dotlock(mailname, fileno(fbuf), FLT_WRITE, 0,0, UIZ_MAX)
367 ) == NULL) {
368 n_perr(_("Unable to (dot) lock mailbox"), 0);
369 Fclose(fbuf);
370 fbuf = NULL;
371 rv = getapproval(_("Continue, possibly loosing changes"), TRU1);
372 goto jleave;
375 rbuf = NULL;
376 if (!fstat(fileno(fbuf), &minfo) && minfo.st_size > mailsize) {
377 fprintf(n_stdout, _("New mail has arrived.\n"));
378 rbuf = Ftmp(NULL, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER);
379 if (rbuf == NULL || fbuf == NULL)
380 goto jnewmail;
381 #ifdef APPEND
382 fseek(fbuf, (long)mailsize, SEEK_SET);
383 while ((c = getc(fbuf)) != EOF)
384 putc(c, rbuf);
385 #else
386 p = minfo.st_size - mailsize;
387 while (p-- > 0) {
388 c = getc(fbuf);
389 if (c == EOF)
390 goto jnewmail;
391 putc(c, rbuf);
393 #endif
394 fflush_rewind(rbuf);
397 anystat = holdbits();
398 modify = 0;
399 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
400 if (mp->m_flag & MBOX)
401 c++;
402 if (mp->m_flag & MPRESERVE)
403 p++;
404 if (mp->m_flag & MODIFY)
405 modify++;
407 if (p == msgCount && !modify && !anystat) {
408 rv = TRU1;
409 if (p == 1)
410 fprintf(n_stdout, _("Held 1 message in %s\n"), displayname);
411 else if (p > 1)
412 fprintf(n_stdout, _("Held %d messages in %s\n"), p, displayname);
413 goto jleave;
416 if (c == 0) {
417 if (p != 0) {
418 if (writeback(rbuf, fbuf) >= 0)
419 rv = TRU1;
420 else
421 rv = getapproval(_("Continue, possibly loosing changes"), TRU1);
422 goto jleave;
424 goto jcream;
427 if (makembox() == STOP) {
428 rv = getapproval(_("Continue, possibly loosing changes"), TRU1);
429 goto jleave;
432 /* Now we are ready to copy back preserved files to the system mailbox, if
433 * any were requested */
434 if (p != 0) {
435 if (writeback(rbuf, fbuf) < 0)
436 rv = getapproval(_("Continue, possibly loosing changes"), TRU1);
437 goto jleave;
440 /* Finally, remove his file. If new mail has arrived, copy it back */
441 jcream:
442 if (rbuf != NULL) {
443 abuf = fbuf;
444 fseek(abuf, 0L, SEEK_SET);
445 while ((c = getc(rbuf)) != EOF)
446 putc(c, abuf);
447 Fclose(rbuf);
448 ftrunc(abuf);
449 _alter(mailname);
450 rv = TRU1;
451 } else {
452 #ifdef HAVE_FTRUNCATE
453 ftruncate(fileno(fbuf), 0);
454 #else
455 int fd;
457 if((fd = open(mailname, (O_WRONLY | O_CREAT | n_O_NOFOLLOW | O_TRUNC),
458 0600)) != -1)
459 close(fd);
460 #endif
461 if(!ok_blook(keep))
462 n_path_rm(mailname);
463 rv = TRU1;
465 jleave:
466 if (fbuf != NULL) {
467 Fclose(fbuf);
468 if (lckfp != NULL && lckfp != (FILE*)-1)
469 Pclose(lckfp, FAL0);
471 if(!hold_sigs_on)
472 rele_sigs();
473 NYD_LEAVE;
474 return rv;
477 FL int
478 holdbits(void)
480 struct message *mp;
481 int anystat, autohold, holdbit, nohold;
482 NYD_ENTER;
484 anystat = 0;
485 autohold = ok_blook(hold);
486 holdbit = autohold ? MPRESERVE : MBOX;
487 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
488 if (ok_blook(keepsave))
489 nohold &= ~MSAVED;
490 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
491 if (mp->m_flag & MNEW) {
492 mp->m_flag &= ~MNEW;
493 mp->m_flag |= MSTATUS;
495 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
496 MDRAFT | MUNDRAFT))
497 ++anystat;
498 if (!(mp->m_flag & MTOUCH))
499 mp->m_flag |= MPRESERVE;
500 if (!(mp->m_flag & nohold))
501 mp->m_flag |= holdbit;
503 NYD_LEAVE;
504 return anystat;
507 FL enum okay
508 makembox(void) /* TODO oh my god */
510 struct message *mp;
511 char *mbox, *tempQuit;
512 int mcount, c;
513 FILE *ibuf = NULL, *obuf, *abuf;
514 enum n_fopen_state fs;
515 enum okay rv = STOP;
516 NYD_ENTER;
518 mbox = _mboxname;
519 mcount = 0;
520 if (!ok_blook(append)) {
521 if ((obuf = Ftmp(&tempQuit, "makembox",
522 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER)) == NULL) {
523 n_perr(_("temporary mail quit file"), 0);
524 goto jleave;
526 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
527 n_perr(tempQuit, 0);
528 Ftmp_release(&tempQuit);
529 if (ibuf == NULL) {
530 Fclose(obuf);
531 goto jleave;
534 if ((abuf = n_fopen_any(mbox, "r", NULL)) != NULL) {
535 while ((c = getc(abuf)) != EOF)
536 putc(c, obuf);
537 Fclose(abuf);
539 if (ferror(obuf)) {
540 n_perr(_("temporary mail quit file"), 0);
541 Fclose(ibuf);
542 Fclose(obuf);
543 goto jleave;
545 Fclose(obuf);
547 if ((c = open(mbox, (O_WRONLY | O_CREAT | n_O_NOFOLLOW | O_TRUNC), 0666)
548 ) != -1)
549 close(c);
550 if ((obuf = n_fopen_any(mbox, "r+", &fs)) == NULL) {
551 n_perr(mbox, 0);
552 Fclose(ibuf);
553 goto jleave;
555 } else {
556 if ((obuf = n_fopen_any(mbox, "a", &fs)) == NULL) {
557 n_perr(mbox, 0);
558 goto jleave;
562 srelax_hold();
563 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
564 if (mp->m_flag & MBOX) {
565 ++mcount;
566 #ifdef HAVE_IMAP
567 if((fs & n_PROTO_MASK) == n_PROTO_IMAP &&
568 !n_ignore_is_any(n_IGNORE_SAVE) && imap_thisaccount(mbox)){
569 if(imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
570 goto jcopyerr;
571 }else
572 #endif
573 if (sendmp(mp, obuf, n_IGNORE_SAVE, NULL, SEND_MBOX, NULL) < 0) {
574 #ifdef HAVE_IMAP
575 jcopyerr:
576 #endif
577 n_perr(mbox, 0);
578 srelax_rele();
579 if (ibuf != NULL)
580 Fclose(ibuf);
581 Fclose(obuf);
582 goto jleave;
584 mp->m_flag |= MBOXED;
585 srelax();
588 srelax_rele();
590 /* Copy the user's old mbox contents back to the end of the stuff we just
591 * saved. If we are appending, this is unnecessary */
592 if (!ok_blook(append)) {
593 rewind(ibuf);
594 c = getc(ibuf);
595 while (c != EOF) {
596 putc(c, obuf);
597 if (ferror(obuf))
598 break;
599 c = getc(ibuf);
601 Fclose(ibuf);
602 fflush(obuf);
604 ftrunc(obuf);
605 if (ferror(obuf)) {
606 n_perr(mbox, 0);
607 Fclose(obuf);
608 goto jleave;
610 if (Fclose(obuf) != 0) {
611 #ifdef HAVE_IMAP
612 if((fs & n_PROTO_MASK) != n_PROTO_IMAP)
613 #endif
614 n_perr(mbox, 0);
615 goto jleave;
617 if (mcount == 1)
618 fprintf(n_stdout, _("Saved 1 message in mbox\n"));
619 else
620 fprintf(n_stdout, _("Saved %d messages in mbox\n"), mcount);
621 rv = OKAY;
622 jleave:
623 NYD_LEAVE;
624 return rv;
627 FL void
628 save_mbox_for_possible_quitstuff(void){ /* TODO try to get rid of that */
629 char const *cp;
630 NYD2_ENTER;
632 if((cp = fexpand("&", FEXP_FULL)) == NULL)
633 cp = n_empty;
634 n_strscpy(_mboxname, cp, sizeof _mboxname);
635 NYD2_LEAVE;
638 FL int
639 savequitflags(void)
641 enum quitflags qf = 0;
642 size_t i;
643 NYD_ENTER;
645 for (i = 0; i < n_NELEM(_quitnames); ++i)
646 if (n_var_oklook(_quitnames[i].okey) != NULL)
647 qf |= _quitnames[i].flag;
648 NYD_LEAVE;
649 return qf;
652 FL void
653 restorequitflags(int qf)
655 size_t i;
656 NYD_ENTER;
658 for (i = 0; i < n_NELEM(_quitnames); ++i) {
659 char *x = n_var_oklook(_quitnames[i].okey);
660 if (qf & _quitnames[i].flag) {
661 if (x == NULL)
662 n_var_okset(_quitnames[i].okey, TRU1);
663 } else if (x != NULL)
664 n_var_okclear(_quitnames[i].okey);
666 NYD_LEAVE;
669 /* s-it-mode */