Rename occurrences of USE_ to HAVE_ (again)
[s-mailx.git] / quit.c
blobbd0dbe92b91f8372fe2a3c33e157fa569ce2b84f
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 #include "rcv.h"
42 #include <sys/file.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <time.h>
48 #include <unistd.h>
49 #include <utime.h>
51 #include "extern.h"
53 /* Touch the indicated file */
54 static void alter(char const *name);
56 static int writeback(FILE *res, FILE *obuf);
57 static void edstop(void);
59 static void
60 alter(char const *name)
62 struct stat sb;
63 struct utimbuf utb;
65 if (stat(name, &sb))
66 return;
67 utb.actime = time((time_t *)0) + 1;
68 utb.modtime = sb.st_mtime;
69 utime(name, &utb);
73 * The "quit" command.
75 /*ARGSUSED*/
76 int
77 quitcmd(void *v)
79 (void)v;
81 * If we are sourcing, then return 1 so execute() can handle it.
82 * Otherwise, return -1 to abort command loop.
84 if (sourcing)
85 return 1;
86 return -1;
90 * Preserve all the appropriate messages back in the system
91 * mailbox, and print a nice message indicated how many were
92 * saved. On any error, just return -1. Else return 0.
93 * Incorporate the any new mail that we found.
95 static int
96 writeback(FILE *res, FILE *obuf)
98 struct message *mp;
99 int p, c;
101 p = 0;
102 if (fseek(obuf, 0L, SEEK_SET) < 0)
103 return -1;
104 #ifndef APPEND
105 if (res != NULL)
106 while ((c = getc(res)) != EOF)
107 putc(c, obuf);
108 #endif
109 for (mp = &message[0]; mp < &message[msgCount]; mp++)
110 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
111 p++;
112 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
113 perror(mailname);
114 (void)fseek(obuf, 0L, SEEK_SET);
115 return(-1);
118 #ifdef APPEND
119 if (res != NULL)
120 while ((c = getc(res)) != EOF)
121 putc(c, obuf);
122 #endif
123 fflush(obuf);
124 ftrunc(obuf);
125 if (ferror(obuf)) {
126 perror(mailname);
127 (void)fseek(obuf, 0L, SEEK_SET);
128 return(-1);
130 if (res != NULL)
131 Fclose(res);
132 if (fseek(obuf, 0L, SEEK_SET) < 0)
133 return -1;
134 alter(mailname);
135 if (p == 1)
136 printf(tr(155, "Held 1 message in %s\n"), displayname);
137 else
138 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
139 return 0;
143 * Save all of the undetermined messages at the top of "mbox"
144 * Save all untouched messages back in the system mailbox.
145 * Remove the system mailbox, if none saved there.
147 void
148 quit(void)
150 int p, modify, anystat;
151 FILE *fbuf, *rbuf, *abuf;
152 struct message *mp;
153 int c;
154 char *tempResid;
155 struct stat minfo;
158 * If we are read only, we can't do anything,
159 * so just return quickly. IMAP can set some
160 * flags (e.g. "\\Seen") so imap_quit must be
161 * called even then.
163 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
164 return;
165 switch (mb.mb_type) {
166 case MB_FILE:
167 break;
168 case MB_MAILDIR:
169 maildir_quit();
170 return;
171 #ifdef HAVE_POP3
172 case MB_POP3:
173 pop3_quit();
174 return;
175 #endif
176 #ifdef HAVE_IMAP
177 case MB_IMAP:
178 case MB_CACHE:
179 imap_quit();
180 return;
181 #endif
182 case MB_VOID:
183 default:
184 return;
187 * If editing (not reading system mail box), then do the work
188 * in edstop()
190 if (edit) {
191 edstop();
192 return;
196 * See if there any messages to save in mbox. If no, we
197 * can save copying mbox to /tmp and back.
199 * Check also to see if any files need to be preserved.
200 * Delete all untouched messages to keep them out of mbox.
201 * If all the messages are to be preserved, just exit with
202 * a message.
205 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
206 if (fbuf == NULL) {
207 if (errno == ENOENT)
208 return;
209 goto newmail;
211 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
212 nolock:
213 perror(catgets(catd, CATSET, 157, "Unable to lock mailbox"));
214 Fclose(fbuf);
215 return;
217 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
218 goto nolock;
219 rbuf = NULL;
220 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
221 printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
222 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
223 if (rbuf == NULL || fbuf == NULL)
224 goto newmail;
225 #ifdef APPEND
226 fseek(fbuf, (long)mailsize, SEEK_SET);
227 while ((c = getc(fbuf)) != EOF)
228 putc(c, rbuf);
229 #else
230 p = minfo.st_size - mailsize;
231 while (p-- > 0) {
232 c = getc(fbuf);
233 if (c == EOF)
234 goto newmail;
235 putc(c, rbuf);
237 #endif
238 Fclose(rbuf);
239 if ((rbuf = Fopen(tempResid, "r")) == NULL)
240 goto newmail;
241 rm(tempResid);
242 Ftfree(&tempResid);
245 anystat = holdbits();
246 modify = 0;
247 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
248 if (mp->m_flag & MBOX)
249 c++;
250 if (mp->m_flag & MPRESERVE)
251 p++;
252 if (mp->m_flag & MODIFY)
253 modify++;
255 if (p == msgCount && !modify && !anystat) {
256 if (p == 1)
257 printf(tr(155, "Held 1 message in %s\n"), displayname);
258 else if (p > 1)
259 printf(tr(156, "Held %d messages in %s\n"),
260 p, displayname);
261 Fclose(fbuf);
262 dot_unlock(mailname);
263 return;
265 if (c == 0) {
266 if (p != 0) {
267 writeback(rbuf, fbuf);
268 Fclose(fbuf);
269 dot_unlock(mailname);
270 return;
272 goto cream;
275 if (makembox() == STOP) {
276 Fclose(fbuf);
277 dot_unlock(mailname);
278 return;
281 * Now we are ready to copy back preserved files to
282 * the system mailbox, if any were requested.
285 if (p != 0) {
286 writeback(rbuf, fbuf);
287 Fclose(fbuf);
288 dot_unlock(mailname);
289 return;
293 * Finally, remove his /usr/mail file.
294 * If new mail has arrived, copy it back.
297 cream:
298 if (rbuf != NULL) {
299 abuf = fbuf;
300 fseek(abuf, 0L, SEEK_SET);
301 while ((c = getc(rbuf)) != EOF)
302 putc(c, abuf);
303 Fclose(rbuf);
304 ftrunc(abuf);
305 alter(mailname);
306 Fclose(fbuf);
307 dot_unlock(mailname);
308 return;
310 demail();
311 Fclose(fbuf);
312 dot_unlock(mailname);
313 return;
315 newmail:
316 printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n"));
317 if (fbuf != NULL) {
318 Fclose(fbuf);
319 dot_unlock(mailname);
324 * Adjust the message flags in each message.
326 int
327 holdbits(void)
329 struct message *mp;
330 int anystat, autohold, holdbit, nohold;
332 anystat = 0;
333 autohold = value("hold") != NULL;
334 holdbit = autohold ? MPRESERVE : MBOX;
335 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
336 if (value("keepsave") != NULL)
337 nohold &= ~MSAVED;
338 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
339 if (mp->m_flag & MNEW) {
340 mp->m_flag &= ~MNEW;
341 mp->m_flag |= MSTATUS;
343 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
344 MDRAFT|MUNDRAFT))
345 anystat++;
346 if ((mp->m_flag & MTOUCH) == 0)
347 mp->m_flag |= MPRESERVE;
348 if ((mp->m_flag & nohold) == 0)
349 mp->m_flag |= holdbit;
351 return anystat;
355 * Create another temporary file and copy user's mbox file
356 * darin. If there is no mbox, copy nothing.
357 * If he has specified "append" don't copy his mailbox,
358 * just copy saveable entries at the end.
361 enum okay
362 makembox(void)
364 struct message *mp;
365 char *mbox, *tempQuit;
366 int mcount, c;
367 FILE *ibuf = NULL, *obuf, *abuf;
368 enum protocol prot;
370 mbox = mboxname;
371 mcount = 0;
372 if (value("append") == NULL) {
373 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
374 perror(tr(163, "temporary mail quit file"));
375 return STOP;
377 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
378 perror(tempQuit);
379 Fclose(obuf);
381 rm(tempQuit);
382 Ftfree(&tempQuit);
383 if (ibuf == NULL)
384 return STOP;
386 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
387 while ((c = getc(abuf)) != EOF)
388 putc(c, obuf);
389 Fclose(abuf);
391 if (ferror(obuf)) {
392 perror(tr(163, "temporary mail quit file"));
393 Fclose(ibuf);
394 Fclose(obuf);
395 return STOP;
397 Fclose(obuf);
399 if ((c = open(mbox, O_CREAT|O_TRUNC|O_WRONLY, 0600)) >= 0)
400 close(c);
401 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
402 perror(mbox);
403 Fclose(ibuf);
404 return STOP;
407 else {
408 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
409 perror(mbox);
410 return STOP;
412 fchmod(fileno(obuf), 0600);
414 prot = which_protocol(mbox);
415 for (mp = &message[0]; mp < &message[msgCount]; mp++)
416 if (mp->m_flag & MBOX) {
417 mcount++;
418 if (prot == PROTO_IMAP &&
419 saveignore[0].i_count == 0 &&
420 saveignore[1].i_count == 0
421 #ifdef HAVE_IMAP /* TODO revisit */
422 && imap_thisaccount(mbox)
423 #endif
425 #ifdef HAVE_IMAP
426 if (imap_copy(mp, mp-message+1, mbox) == STOP)
427 #endif
428 goto err;
429 } else if (send(mp, obuf, saveignore,
430 NULL, SEND_MBOX, NULL) < 0) {
431 perror(mbox);
432 err: if (ibuf)
433 Fclose(ibuf);
434 Fclose(obuf);
435 return STOP;
437 mp->m_flag |= MBOXED;
441 * Copy the user's old mbox contents back
442 * to the end of the stuff we just saved.
443 * If we are appending, this is unnecessary.
446 if (value("append") == NULL) {
447 rewind(ibuf);
448 c = getc(ibuf);
449 while (c != EOF) {
450 putc(c, obuf);
451 if (ferror(obuf))
452 break;
453 c = getc(ibuf);
455 Fclose(ibuf);
456 fflush(obuf);
458 ftrunc(obuf);
459 if (ferror(obuf)) {
460 perror(mbox);
461 Fclose(obuf);
462 return STOP;
464 if (Fclose(obuf) != 0) {
465 if (prot != PROTO_IMAP)
466 perror(mbox);
467 return STOP;
469 if (mcount == 1)
470 printf(catgets(catd, CATSET, 164, "Saved 1 message in mbox\n"));
471 else
472 printf(catgets(catd, CATSET, 165,
473 "Saved %d messages in mbox\n"), mcount);
474 return OKAY;
478 * Terminate an editing session by attempting to write out the user's
479 * file from the temporary. Save any new stuff appended to the file.
481 static void
482 edstop(void)
484 int gotcha, c;
485 struct message *mp;
486 FILE *obuf, *ibuf = NULL;
487 struct stat statb;
489 if (mb.mb_perm == 0)
490 return;
491 holdsigs();
492 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
493 if (mp->m_flag & MNEW) {
494 mp->m_flag &= ~MNEW;
495 mp->m_flag |= MSTATUS;
497 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
498 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
499 gotcha++;
501 if (!gotcha)
502 goto done;
503 ibuf = NULL;
504 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
505 char *tempname;
507 if ((obuf = Ftemp(&tempname, "edstop", "w", 0600, 1)) == NULL) {
508 perror(catgets(catd, CATSET, 167, "tmpfile"));
509 relsesigs();
510 reset(0);
512 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
513 perror(mailname);
514 Fclose(obuf);
515 rm(tempname);
516 Ftfree(&tempname);
517 relsesigs();
518 reset(0);
520 fseek(ibuf, (long)mailsize, SEEK_SET);
521 while ((c = getc(ibuf)) != EOF)
522 putc(c, obuf);
523 Fclose(ibuf);
524 Fclose(obuf);
525 if ((ibuf = Fopen(tempname, "r")) == NULL) {
526 perror(tempname);
527 rm(tempname);
528 Ftfree(&tempname);
529 relsesigs();
530 reset(0);
532 rm(tempname);
533 Ftfree(&tempname);
535 printf(tr(168, "\"%s\" "), displayname);
536 fflush(stdout);
537 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
538 perror(mailname);
539 relsesigs();
540 reset(0);
542 ftrunc(obuf);
543 c = 0;
544 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
545 if ((mp->m_flag & MDELETED) != 0)
546 continue;
547 c++;
548 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
549 perror(mailname);
550 relsesigs();
551 reset(0);
554 gotcha = (c == 0 && ibuf == NULL);
555 if (ibuf != NULL) {
556 while ((c = getc(ibuf)) != EOF)
557 putc(c, obuf);
558 Fclose(ibuf);
560 fflush(obuf);
561 if (ferror(obuf)) {
562 perror(mailname);
563 relsesigs();
564 reset(0);
566 Fclose(obuf);
567 if (gotcha && value("emptybox") == NULL) {
568 rm(mailname);
569 printf(value("bsdcompat") || value("bsdmsgs") ?
570 catgets(catd, CATSET, 169, "removed\n") :
571 catgets(catd, CATSET, 211, "removed.\n"));
572 } else
573 printf(value("bsdcompat") || value("bsdmsgs") ?
574 catgets(catd, CATSET, 170, "complete\n") :
575 catgets(catd, CATSET, 212, "updated.\n"));
576 fflush(stdout);
578 done:
579 relsesigs();
582 enum quitflags {
583 QUITFLAG_HOLD = 001,
584 QUITFLAG_KEEPSAVE = 002,
585 QUITFLAG_APPEND = 004,
586 QUITFLAG_EMPTYBOX = 010
589 static const struct quitnames {
590 enum quitflags flag;
591 const char *name;
592 } quitnames[] = {
593 { QUITFLAG_HOLD, "hold" },
594 { QUITFLAG_KEEPSAVE, "keepsave" },
595 { QUITFLAG_APPEND, "append" },
596 { QUITFLAG_EMPTYBOX, "emptybox" },
597 { 0, NULL }
601 savequitflags(void)
603 enum quitflags qf = 0;
604 int i;
606 for (i = 0; quitnames[i].name; i++)
607 if (value(quitnames[i].name))
608 qf |= quitnames[i].flag;
609 return qf;
612 void
613 restorequitflags(int qf)
615 int i;
617 for (i = 0; quitnames[i].name; i++)
618 if (qf & quitnames[i].flag) {
619 if (value(quitnames[i].name) == NULL)
620 assign(quitnames[i].name, "");
621 } else if (value(quitnames[i].name))
622 unset_internal(quitnames[i].name);