list.c: fix compiler warnings
[s-mailx.git] / quit.c
blobf70aec27f666ea29db347f0cff370ad63aa77d0f
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)quit.c 2.30 (gritter) 11/11/08";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
46 #include "extern.h"
47 #include <stdio.h>
48 #include <errno.h>
49 #include <sys/file.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <fcntl.h>
55 * Rcv -- receive mail rationally.
57 * Termination processing.
60 static int writeback(FILE *res, FILE *obuf);
61 static void edstop(void);
64 * The "quit" command.
66 /*ARGSUSED*/
67 int
68 quitcmd(void *v)
71 * If we are sourcing, then return 1 so execute() can handle it.
72 * Otherwise, return -1 to abort command loop.
74 if (sourcing)
75 return 1;
76 return -1;
80 * Preserve all the appropriate messages back in the system
81 * mailbox, and print a nice message indicated how many were
82 * saved. On any error, just return -1. Else return 0.
83 * Incorporate the any new mail that we found.
85 static int
86 writeback(FILE *res, FILE *obuf)
88 struct message *mp;
89 int p, c;
91 p = 0;
92 fseek(obuf, 0L, SEEK_SET);
93 #ifndef APPEND
94 if (res != NULL)
95 while ((c = getc(res)) != EOF)
96 putc(c, obuf);
97 #endif
98 for (mp = &message[0]; mp < &message[msgCount]; mp++)
99 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
100 p++;
101 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
102 perror(mailname);
103 fseek(obuf, 0L, SEEK_SET);
104 return(-1);
107 #ifdef APPEND
108 if (res != NULL)
109 while ((c = getc(res)) != EOF)
110 putc(c, obuf);
111 #endif
112 fflush(obuf);
113 trunc(obuf);
114 if (ferror(obuf)) {
115 perror(mailname);
116 fseek(obuf, 0L, SEEK_SET);
117 return(-1);
119 if (res != NULL)
120 Fclose(res);
121 fseek(obuf, 0L, SEEK_SET);
122 alter(mailname);
123 if (p == 1)
124 printf(catgets(catd, CATSET, 155,
125 "Held 1 message in %s\n"), mailname);
126 else
127 printf(catgets(catd, CATSET, 156,
128 "Held %d messages in %s\n"), p, mailname);
129 return(0);
133 * Save all of the undetermined messages at the top of "mbox"
134 * Save all untouched messages back in the system mailbox.
135 * Remove the system mailbox, if none saved there.
137 void
138 quit(void)
140 int p, modify, anystat;
141 FILE *fbuf, *rbuf, *readstat = NULL, *abuf;
142 struct message *mp;
143 int c;
144 char *tempResid;
145 struct stat minfo;
148 * If we are read only, we can't do anything,
149 * so just return quickly. IMAP can set some
150 * flags (e.g. "\\Seen") so imap_quit must be
151 * called even then.
153 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
154 return;
155 switch (mb.mb_type) {
156 case MB_FILE:
157 break;
158 case MB_MAILDIR:
159 maildir_quit();
160 return;
161 case MB_POP3:
162 pop3_quit();
163 return;
164 case MB_IMAP:
165 case MB_CACHE:
166 imap_quit();
167 return;
168 case MB_VOID:
169 return;
172 * If editing (not reading system mail box), then do the work
173 * in edstop()
175 if (edit) {
176 edstop();
177 return;
181 * See if there any messages to save in mbox. If no, we
182 * can save copying mbox to /tmp and back.
184 * Check also to see if any files need to be preserved.
185 * Delete all untouched messages to keep them out of mbox.
186 * If all the messages are to be preserved, just exit with
187 * a message.
190 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
191 if (fbuf == NULL) {
192 if (errno == ENOENT)
193 return;
194 goto newmail;
196 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
197 nolock:
198 perror(catgets(catd, CATSET, 157, "Unable to lock mailbox"));
199 Fclose(fbuf);
200 return;
202 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
203 goto nolock;
204 rbuf = NULL;
205 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
206 printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
207 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
208 if (rbuf == NULL || fbuf == NULL)
209 goto newmail;
210 #ifdef APPEND
211 fseek(fbuf, (long)mailsize, SEEK_SET);
212 while ((c = getc(fbuf)) != EOF)
213 putc(c, rbuf);
214 #else
215 p = minfo.st_size - mailsize;
216 while (p-- > 0) {
217 c = getc(fbuf);
218 if (c == EOF)
219 goto newmail;
220 putc(c, rbuf);
222 #endif
223 Fclose(rbuf);
224 if ((rbuf = Fopen(tempResid, "r")) == NULL)
225 goto newmail;
226 rm(tempResid);
227 Ftfree(&tempResid);
230 anystat = holdbits();
231 modify = 0;
232 if (Tflag != NULL) {
233 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
234 Tflag = NULL;
236 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
237 if (mp->m_flag & MBOX)
238 c++;
239 if (mp->m_flag & MPRESERVE)
240 p++;
241 if (mp->m_flag & MODIFY)
242 modify++;
243 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
244 char *id;
246 if ((id = hfield("message-id", mp)) != NULL ||
247 (id = hfield("article-id", mp)) != NULL)
248 fprintf(readstat, "%s\n", id);
251 if (readstat != NULL)
252 Fclose(readstat);
253 if (p == msgCount && !modify && !anystat) {
254 if (p == 1)
255 printf(catgets(catd, CATSET, 155,
256 "Held 1 message in %s\n"), mailname);
257 else if (p > 1)
258 printf(catgets(catd, CATSET, 156,
259 "Held %d messages in %s\n"), p, mailname);
260 Fclose(fbuf);
261 dot_unlock(mailname);
262 return;
264 if (c == 0) {
265 if (p != 0) {
266 writeback(rbuf, fbuf);
267 Fclose(fbuf);
268 dot_unlock(mailname);
269 return;
271 goto cream;
274 if (makembox() == STOP) {
275 Fclose(fbuf);
276 dot_unlock(mailname);
277 return;
280 * Now we are ready to copy back preserved files to
281 * the system mailbox, if any were requested.
284 if (p != 0) {
285 writeback(rbuf, fbuf);
286 Fclose(fbuf);
287 dot_unlock(mailname);
288 return;
292 * Finally, remove his /usr/mail file.
293 * If new mail has arrived, copy it back.
296 cream:
297 if (rbuf != NULL) {
298 abuf = fbuf;
299 fseek(abuf, 0L, SEEK_SET);
300 while ((c = getc(rbuf)) != EOF)
301 putc(c, abuf);
302 Fclose(rbuf);
303 trunc(abuf);
304 alter(mailname);
305 Fclose(fbuf);
306 dot_unlock(mailname);
307 return;
309 demail();
310 Fclose(fbuf);
311 dot_unlock(mailname);
312 return;
314 newmail:
315 printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n"));
316 if (fbuf != NULL) {
317 Fclose(fbuf);
318 dot_unlock(mailname);
323 * Adjust the message flags in each message.
325 int
326 holdbits(void)
328 struct message *mp;
329 int anystat, autohold, holdbit, nohold;
331 anystat = 0;
332 autohold = value("hold") != NULL;
333 holdbit = autohold ? MPRESERVE : MBOX;
334 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
335 if (value("keepsave") != NULL)
336 nohold &= ~MSAVED;
337 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
338 if (mp->m_flag & MNEW) {
339 mp->m_flag &= ~MNEW;
340 mp->m_flag |= MSTATUS;
342 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
343 MDRAFT|MUNDRAFT))
344 anystat++;
345 if ((mp->m_flag & MTOUCH) == 0)
346 mp->m_flag |= MPRESERVE;
347 if ((mp->m_flag & nohold) == 0)
348 mp->m_flag |= holdbit;
350 return anystat;
354 * Create another temporary file and copy user's mbox file
355 * darin. If there is no mbox, copy nothing.
356 * If he has specified "append" don't copy his mailbox,
357 * just copy saveable entries at the end.
360 enum okay
361 makembox(void)
363 struct message *mp;
364 char *mbox, *tempQuit;
365 int mcount, c;
366 FILE *ibuf = NULL, *obuf, *abuf;
367 enum protocol prot;
369 mbox = mboxname;
370 mcount = 0;
371 if (value("append") == NULL) {
372 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
373 perror(catgets(catd, CATSET, 162,
374 "temporary mail quit file"));
375 return STOP;
377 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
378 perror(tempQuit);
379 rm(tempQuit);
380 Ftfree(&tempQuit);
381 Fclose(obuf);
382 return STOP;
384 rm(tempQuit);
385 Ftfree(&tempQuit);
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(catgets(catd, CATSET, 163,
393 "temporary mail quit file"));
394 Fclose(ibuf);
395 Fclose(obuf);
396 return STOP;
398 Fclose(obuf);
399 close(creat(mbox, 0600));
400 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
401 perror(mbox);
402 Fclose(ibuf);
403 return STOP;
406 else {
407 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
408 perror(mbox);
409 return STOP;
411 fchmod(fileno(obuf), 0600);
413 prot = which_protocol(mbox);
414 for (mp = &message[0]; mp < &message[msgCount]; mp++)
415 if (mp->m_flag & MBOX) {
416 mcount++;
417 if (prot == PROTO_IMAP &&
418 saveignore[0].i_count == 0 &&
419 saveignore[1].i_count == 0 &&
420 imap_thisaccount(mbox)) {
421 if (imap_copy(mp, mp-message+1, mbox) == STOP)
422 goto err;
423 } else if (send(mp, obuf, saveignore,
424 NULL, SEND_MBOX, NULL) < 0) {
425 perror(mbox);
426 err: if (ibuf)
427 Fclose(ibuf);
428 Fclose(obuf);
429 return STOP;
431 mp->m_flag |= MBOXED;
435 * Copy the user's old mbox contents back
436 * to the end of the stuff we just saved.
437 * If we are appending, this is unnecessary.
440 if (value("append") == NULL) {
441 rewind(ibuf);
442 c = getc(ibuf);
443 while (c != EOF) {
444 putc(c, obuf);
445 if (ferror(obuf))
446 break;
447 c = getc(ibuf);
449 Fclose(ibuf);
450 fflush(obuf);
452 trunc(obuf);
453 if (ferror(obuf)) {
454 perror(mbox);
455 Fclose(obuf);
456 return STOP;
458 if (Fclose(obuf) != 0) {
459 if (prot != PROTO_IMAP)
460 perror(mbox);
461 return STOP;
463 if (mcount == 1)
464 printf(catgets(catd, CATSET, 164, "Saved 1 message in mbox\n"));
465 else
466 printf(catgets(catd, CATSET, 165,
467 "Saved %d messages in mbox\n"), mcount);
468 return OKAY;
472 * Terminate an editing session by attempting to write out the user's
473 * file from the temporary. Save any new stuff appended to the file.
475 static void
476 edstop(void)
478 int gotcha, c;
479 struct message *mp;
480 FILE *obuf, *ibuf = NULL, *readstat = NULL;
481 struct stat statb;
483 if (mb.mb_perm == 0)
484 return;
485 holdsigs();
486 if (Tflag != NULL) {
487 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
488 Tflag = NULL;
490 for (mp = &message[0], gotcha = 0; 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 & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
496 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
497 gotcha++;
498 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
499 char *id;
501 if ((id = hfield("message-id", mp)) != NULL ||
502 (id = hfield("article-id", mp)) != NULL)
503 fprintf(readstat, "%s\n", id);
506 if (readstat != NULL)
507 Fclose(readstat);
508 if (!gotcha || Tflag != NULL)
509 goto done;
510 ibuf = NULL;
511 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
512 char *tempname;
514 if ((obuf = Ftemp(&tempname, "mbox.", "w", 0600, 1)) == NULL) {
515 perror(catgets(catd, CATSET, 167, "tmpfile"));
516 relsesigs();
517 reset(0);
519 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
520 perror(mailname);
521 Fclose(obuf);
522 rm(tempname);
523 Ftfree(&tempname);
524 relsesigs();
525 reset(0);
527 fseek(ibuf, (long)mailsize, SEEK_SET);
528 while ((c = getc(ibuf)) != EOF)
529 putc(c, obuf);
530 Fclose(ibuf);
531 Fclose(obuf);
532 if ((ibuf = Fopen(tempname, "r")) == NULL) {
533 perror(tempname);
534 rm(tempname);
535 Ftfree(&tempname);
536 relsesigs();
537 reset(0);
539 rm(tempname);
540 Ftfree(&tempname);
542 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
543 fflush(stdout);
544 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
545 perror(mailname);
546 relsesigs();
547 reset(0);
549 trunc(obuf);
550 c = 0;
551 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
552 if ((mp->m_flag & MDELETED) != 0)
553 continue;
554 c++;
555 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
556 perror(mailname);
557 relsesigs();
558 reset(0);
561 gotcha = (c == 0 && ibuf == NULL);
562 if (ibuf != NULL) {
563 while ((c = getc(ibuf)) != EOF)
564 putc(c, obuf);
565 Fclose(ibuf);
567 fflush(obuf);
568 if (ferror(obuf)) {
569 perror(mailname);
570 relsesigs();
571 reset(0);
573 Fclose(obuf);
574 if (gotcha && value("emptybox") == NULL) {
575 rm(mailname);
576 printf(value("bsdcompat") || value("bsdmsgs") ?
577 catgets(catd, CATSET, 169, "removed\n") :
578 catgets(catd, CATSET, 211, "removed.\n"));
579 } else
580 printf(value("bsdcompat") || value("bsdmsgs") ?
581 catgets(catd, CATSET, 170, "complete\n") :
582 catgets(catd, CATSET, 212, "updated.\n"));
583 fflush(stdout);
585 done:
586 relsesigs();
589 enum quitflags {
590 QUITFLAG_HOLD = 001,
591 QUITFLAG_KEEPSAVE = 002,
592 QUITFLAG_APPEND = 004,
593 QUITFLAG_EMPTYBOX = 010
596 static const struct quitnames {
597 enum quitflags flag;
598 const char *name;
599 } quitnames[] = {
600 { QUITFLAG_HOLD, "hold" },
601 { QUITFLAG_KEEPSAVE, "keepsave" },
602 { QUITFLAG_APPEND, "append" },
603 { QUITFLAG_EMPTYBOX, "emptybox" },
604 { 0, NULL }
608 savequitflags(void)
610 enum quitflags qf = 0;
611 int i;
613 for (i = 0; quitnames[i].name; i++)
614 if (value(quitnames[i].name))
615 qf |= quitnames[i].flag;
616 return qf;
619 void
620 restorequitflags(int qf)
622 int i;
624 for (i = 0; quitnames[i].name; i++)
625 if (qf & quitnames[i].flag) {
626 if (value(quitnames[i].name) == NULL)
627 assign(quitnames[i].name, "");
628 } else if (value(quitnames[i].name))
629 unset_internal(quitnames[i].name);