Review: auxlily.c
[s-mailx.git] / quit.c
blob75b3ece3d702ecf1e447fb638e47606348eea444
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 - 2014 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 enum quitflags {
48 QUITFLAG_HOLD = 001,
49 QUITFLAG_KEEPSAVE = 002,
50 QUITFLAG_APPEND = 004,
51 QUITFLAG_EMPTYBOX = 010
54 struct quitnames {
55 enum quitflags flag;
56 enum okeys okey;
59 static struct quitnames const _quitnames[] = {
60 {QUITFLAG_HOLD, ok_b_hold},
61 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
62 {QUITFLAG_APPEND, ok_b_append},
63 {QUITFLAG_EMPTYBOX, ok_b_emptybox}
66 static char _mboxname[PATH_MAX]; /* Name of mbox */
68 /* Touch the indicated file */
69 static void _alter(char const *name);
71 /* Preserve all the appropriate messages back in the system mailbox, and print
72 * a nice message indicated how many were saved. On any error, just return -1.
73 * Else return 0. Incorporate the any new mail that we found */
74 static int writeback(FILE *res, FILE *obuf);
76 /* Terminate an editing session by attempting to write out the user's file from
77 * the temporary. Save any new stuff appended to the file */
78 static void edstop(void);
80 static void
81 _alter(char const *name)
83 struct stat sb;
84 struct utimbuf utb;
85 NYD_ENTER;
87 if (!stat(name, &sb)) {
88 utb.actime = time(NULL) + 1;
89 utb.modtime = sb.st_mtime;
90 utime(name, &utb);
92 NYD_LEAVE;
95 static int
96 writeback(FILE *res, FILE *obuf)
98 struct message *mp;
99 int rv = -1, p, c;
100 NYD_ENTER;
102 if (fseek(obuf, 0L, SEEK_SET) == -1)
103 goto jleave;
105 #ifndef APPEND
106 if (res != NULL)
107 while ((c = getc(res)) != EOF)
108 putc(c, obuf);
109 #endif
110 srelax_hold();
111 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
112 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
113 ++p;
114 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
115 srelax_rele();
116 goto jerror;
118 srelax();
120 srelax_rele();
121 #ifdef APPEND
122 if (res != NULL)
123 while ((c = getc(res)) != EOF)
124 putc(c, obuf);
125 #endif
126 fflush(obuf);
127 ftrunc(obuf);
129 if (ferror(obuf)) {
130 jerror:
131 perror(mailname);
132 fseek(obuf, 0L, SEEK_SET);
133 goto jleave;
135 if (fseek(obuf, 0L, SEEK_SET) == -1)
136 goto jleave;
138 _alter(mailname);
139 if (p == 1)
140 printf(tr(155, "Held 1 message in %s\n"), displayname);
141 else
142 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
143 rv = 0;
144 jleave:
145 if (res != NULL)
146 Fclose(res);
147 NYD_LEAVE;
148 return rv;
151 static void
152 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
154 int gotcha, c;
155 struct message *mp;
156 FILE *obuf = NULL, *ibuf = NULL;
157 struct stat statb;
158 bool_t doreset;
159 NYD_ENTER;
161 hold_sigs();
162 doreset = FAL0;
164 if (mb.mb_perm == 0)
165 goto jleave;
167 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
168 if (mp->m_flag & MNEW) {
169 mp->m_flag &= ~MNEW;
170 mp->m_flag |= MSTATUS;
172 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
173 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
174 ++gotcha;
176 if (!gotcha)
177 goto jleave;
179 doreset = TRU1;
181 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
182 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER,
183 0600)) == NULL) {
184 perror(tr(167, "tmpfile"));
185 goto jleave;
187 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
188 perror(mailname);
189 Fclose(obuf);
190 goto jleave;
192 fseek(ibuf, (long)mailsize, SEEK_SET);
193 while ((c = getc(ibuf)) != EOF)
194 putc(c, obuf);
195 Fclose(ibuf);
196 ibuf = obuf;
197 fflush_rewind(obuf);
200 printf(tr(168, "\"%s\" "), displayname);
201 fflush(stdout);
202 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
203 perror(mailname);
204 goto jleave;
206 ftrunc(obuf);
208 srelax_hold();
209 c = 0;
210 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
211 if ((mp->m_flag & MDELETED) != 0)
212 continue;
213 ++c;
214 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
215 perror(mailname);
216 srelax_rele();
217 goto jleave;
219 srelax();
221 srelax_rele();
223 gotcha = (c == 0 && ibuf == NULL);
224 if (ibuf != NULL) {
225 while ((c = getc(ibuf)) != EOF)
226 putc(c, obuf);
228 fflush(obuf);
229 if (ferror(obuf)) {
230 perror(mailname);
231 goto jleave;
233 Fclose(obuf);
235 doreset = FAL0;
237 if (gotcha && !ok_blook(emptybox)) {
238 rm(mailname);
239 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
240 ? tr(169, "removed\n") : tr(211, "removed.\n"));
241 } else
242 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
243 ? tr(170, "complete\n") : tr(212, "updated.\n"));
244 fflush(stdout);
245 jleave:
246 if (ibuf != NULL)
247 Fclose(ibuf);
248 rele_sigs();
249 NYD_LEAVE;
250 if (doreset)
251 reset(0);
254 FL int
255 c_quit(void *v)
257 int rv;
258 NYD_ENTER;
259 UNUSED(v);
261 /* If we are sourcing, then return 1 so evaluate() can handle it.
262 * Otherwise return -1 to abort command loop */
263 rv = sourcing ? 1 : -1;
264 NYD_LEAVE;
265 return rv;
268 FL void
269 quit(void)
271 int p, modify, anystat, c;
272 FILE *fbuf = NULL, *rbuf, *abuf;
273 struct message *mp;
274 char *tempResid;
275 struct stat minfo;
276 NYD_ENTER;
278 /* If we are read only, we can't do anything, so just return quickly. IMAP
279 * can set some flags (e.g. "\\Seen") so imap_quit must be called anyway */
280 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
281 goto jleave;
283 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
284 * TODO us, but this causes uninterruptible hangs due to blocked sigs
285 * TODO anywhere except for MB_FILE (all others install their own
286 * TODO handlers, as it seems, properly); marked YYY */
287 switch (mb.mb_type) {
288 case MB_FILE:
289 break;
290 case MB_MAILDIR:
291 rele_sigs(); /* YYY */
292 maildir_quit();
293 hold_sigs(); /* YYY */
294 goto jleave;
295 #ifdef HAVE_POP3
296 case MB_POP3:
297 rele_sigs(); /* YYY */
298 pop3_quit();
299 hold_sigs(); /* YYY */
300 goto jleave;
301 #endif
302 #ifdef HAVE_IMAP
303 case MB_IMAP:
304 case MB_CACHE:
305 rele_sigs(); /* YYY */
306 imap_quit();
307 hold_sigs(); /* YYY */
308 goto jleave;
309 #endif
310 case MB_VOID:
311 default:
312 goto jleave;
315 /* If editing (not reading system mail box), then do the work in edstop() */
316 if (edit) {
317 edstop();
318 goto jleave;
321 /* See if there any messages to save in mbox. If no, we
322 * can save copying mbox to /tmp and back.
324 * Check also to see if any files need to be preserved.
325 * Delete all untouched messages to keep them out of mbox.
326 * If all the messages are to be preserved, just exit with
327 * a message */
328 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
329 if (fbuf == NULL) {
330 if (errno == ENOENT)
331 goto jleave;
332 jnewmail:
333 printf(tr(166, "Thou hast new mail.\n"));
334 goto jleave;
337 if (fcntl_lock(fileno(fbuf), FLOCK_WRITE) == -1 ||
338 dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1) {
339 perror(tr(157, "Unable to lock mailbox"));
340 Fclose(fbuf);
341 fbuf = NULL;
342 goto jleave;
345 rbuf = NULL;
346 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
347 printf(tr(158, "New mail has arrived.\n"));
348 rbuf = Ftmp(&tempResid, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600);
349 if (rbuf == NULL || fbuf == NULL)
350 goto jnewmail;
351 #ifdef APPEND
352 fseek(fbuf, (long)mailsize, SEEK_SET);
353 while ((c = getc(fbuf)) != EOF)
354 putc(c, rbuf);
355 #else
356 p = minfo.st_size - mailsize;
357 while (p-- > 0) {
358 c = getc(fbuf);
359 if (c == EOF)
360 goto jnewmail;
361 putc(c, rbuf);
363 #endif
364 fflush_rewind(rbuf);
367 anystat = holdbits();
368 modify = 0;
369 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
370 if (mp->m_flag & MBOX)
371 c++;
372 if (mp->m_flag & MPRESERVE)
373 p++;
374 if (mp->m_flag & MODIFY)
375 modify++;
377 if (p == msgCount && !modify && !anystat) {
378 if (p == 1)
379 printf(tr(155, "Held 1 message in %s\n"), displayname);
380 else if (p > 1)
381 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
382 goto jleave;
384 if (c == 0) {
385 if (p != 0) {
386 writeback(rbuf, fbuf);
387 goto jleave;
389 goto jcream;
392 if (makembox() == STOP)
393 goto jleave;
395 /* Now we are ready to copy back preserved files to the system mailbox, if
396 * any were requested */
397 if (p != 0) {
398 writeback(rbuf, fbuf);
399 goto jleave;
402 /* Finally, remove his file. If new mail has arrived, copy it back */
403 jcream:
404 if (rbuf != NULL) {
405 abuf = fbuf;
406 fseek(abuf, 0L, SEEK_SET);
407 while ((c = getc(rbuf)) != EOF)
408 putc(c, abuf);
409 Fclose(rbuf);
410 ftrunc(abuf);
411 _alter(mailname);
412 goto jleave;
414 demail();
415 jleave:
416 if (fbuf != NULL) {
417 Fclose(fbuf);
418 dot_unlock(mailname);
420 NYD_LEAVE;
423 FL int
424 holdbits(void)
426 struct message *mp;
427 int anystat, autohold, holdbit, nohold;
428 NYD_ENTER;
430 anystat = 0;
431 autohold = ok_blook(hold);
432 holdbit = autohold ? MPRESERVE : MBOX;
433 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
434 if (ok_blook(keepsave))
435 nohold &= ~MSAVED;
436 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
437 if (mp->m_flag & MNEW) {
438 mp->m_flag &= ~MNEW;
439 mp->m_flag |= MSTATUS;
441 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
442 MDRAFT | MUNDRAFT))
443 ++anystat;
444 if (!(mp->m_flag & MTOUCH))
445 mp->m_flag |= MPRESERVE;
446 if (!(mp->m_flag & nohold))
447 mp->m_flag |= holdbit;
449 NYD_LEAVE;
450 return anystat;
453 FL enum okay
454 makembox(void) /* TODO oh my god */
456 struct message *mp;
457 char *mbox, *tempQuit;
458 int mcount, c;
459 FILE *ibuf = NULL, *obuf, *abuf;
460 enum protocol prot;
461 enum okay rv = STOP;
462 NYD_ENTER;
464 mbox = _mboxname;
465 mcount = 0;
466 if (!ok_blook(append)) {
467 if ((obuf = Ftmp(&tempQuit, "makembox",
468 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
469 perror(tr(163, "temporary mail quit file"));
470 goto jleave;
472 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
473 perror(tempQuit);
474 Ftmp_release(&tempQuit);
475 if (ibuf == NULL) {
476 Fclose(obuf);
477 goto jleave;
480 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
481 while ((c = getc(abuf)) != EOF)
482 putc(c, obuf);
483 Fclose(abuf);
485 if (ferror(obuf)) {
486 perror(tr(163, "temporary mail quit file"));
487 Fclose(ibuf);
488 Fclose(obuf);
489 goto jleave;
491 Fclose(obuf);
493 if ((c = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0)
494 close(c);
495 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
496 perror(mbox);
497 Fclose(ibuf);
498 goto jleave;
500 } else {
501 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
502 perror(mbox);
503 goto jleave;
505 fchmod(fileno(obuf), 0600);
508 srelax_hold();
509 prot = which_protocol(mbox);
510 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
511 if (mp->m_flag & MBOX) {
512 ++mcount;
513 if (prot == PROTO_IMAP &&
514 saveignore[0].i_count == 0 && saveignore[1].i_count == 0
515 #ifdef HAVE_IMAP /* TODO revisit */
516 && imap_thisaccount(mbox)
517 #endif
519 #ifdef HAVE_IMAP
520 if (imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
521 #endif
522 goto jerr;
523 } else if (sendmp(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) {
524 perror(mbox);
525 jerr:
526 if (ibuf != NULL)
527 Fclose(ibuf);
528 Fclose(obuf);
529 srelax_rele();
530 goto jleave;
532 mp->m_flag |= MBOXED;
533 srelax();
536 srelax_rele();
538 /* Copy the user's old mbox contents back to the end of the stuff we just
539 * saved. If we are appending, this is unnecessary */
540 if (!ok_blook(append)) {
541 rewind(ibuf);
542 c = getc(ibuf);
543 while (c != EOF) {
544 putc(c, obuf);
545 if (ferror(obuf))
546 break;
547 c = getc(ibuf);
549 Fclose(ibuf);
550 fflush(obuf);
552 ftrunc(obuf);
553 if (ferror(obuf)) {
554 perror(mbox);
555 Fclose(obuf);
556 goto jleave;
558 if (Fclose(obuf) != 0) {
559 if (prot != PROTO_IMAP)
560 perror(mbox);
561 goto jleave;
563 if (mcount == 1)
564 printf(tr(164, "Saved 1 message in mbox\n"));
565 else
566 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
567 rv = OKAY;
568 jleave:
569 NYD_LEAVE;
570 return rv;
573 FL void
574 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
576 char const *cp;
577 NYD_ENTER;
579 if ((cp = expand("&")) == NULL)
580 cp = "";
581 n_strlcpy(_mboxname, cp, sizeof _mboxname);
582 NYD_LEAVE;
585 FL int
586 savequitflags(void)
588 enum quitflags qf = 0;
589 size_t i;
590 NYD_ENTER;
592 for (i = 0; i < NELEM(_quitnames); ++i)
593 if (_var_oklook(_quitnames[i].okey) != NULL)
594 qf |= _quitnames[i].flag;
595 NYD_LEAVE;
596 return qf;
599 FL void
600 restorequitflags(int qf)
602 size_t i;
603 NYD_ENTER;
605 for (i = 0; i < NELEM(_quitnames); ++i) {
606 char *x = _var_oklook(_quitnames[i].okey);
607 if (qf & _quitnames[i].flag) {
608 if (x == NULL)
609 _var_okset(_quitnames[i].okey, TRU1);
610 } else if (x != NULL)
611 _var_okclear(_quitnames[i].okey);
613 NYD_LEAVE;
616 /* vim:set fenc=utf-8:s-it-mode */