send.c: fix compiler warnings..
[s-mailx.git] / quit.c
blob8ab473357cdd306eff9b10aa0378162787afc3fe
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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 lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)quit.c 2.30 (gritter) 11/11/08";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
47 #include "extern.h"
48 #include <stdio.h>
49 #include <errno.h>
50 #include <sys/file.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53 #include <fcntl.h>
56 * Rcv -- receive mail rationally.
58 * Termination processing.
61 static int writeback(FILE *res, FILE *obuf);
62 static void edstop(void);
65 * The "quit" command.
67 /*ARGSUSED*/
68 int
69 quitcmd(void *v)
71 (void)v;
73 * If we are sourcing, then return 1 so execute() can handle it.
74 * Otherwise, return -1 to abort command loop.
76 if (sourcing)
77 return 1;
78 return -1;
82 * Preserve all the appropriate messages back in the system
83 * mailbox, and print a nice message indicated how many were
84 * saved. On any error, just return -1. Else return 0.
85 * Incorporate the any new mail that we found.
87 static int
88 writeback(FILE *res, FILE *obuf)
90 struct message *mp;
91 int p, c;
93 p = 0;
94 fseek(obuf, 0L, SEEK_SET);
95 #ifndef APPEND
96 if (res != NULL)
97 while ((c = getc(res)) != EOF)
98 putc(c, obuf);
99 #endif
100 for (mp = &message[0]; mp < &message[msgCount]; mp++)
101 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
102 p++;
103 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
104 perror(mailname);
105 fseek(obuf, 0L, SEEK_SET);
106 return(-1);
109 #ifdef APPEND
110 if (res != NULL)
111 while ((c = getc(res)) != EOF)
112 putc(c, obuf);
113 #endif
114 fflush(obuf);
115 trunc(obuf);
116 if (ferror(obuf)) {
117 perror(mailname);
118 fseek(obuf, 0L, SEEK_SET);
119 return(-1);
121 if (res != NULL)
122 Fclose(res);
123 fseek(obuf, 0L, SEEK_SET);
124 alter(mailname);
125 if (p == 1)
126 printf(catgets(catd, CATSET, 155,
127 "Held 1 message in %s\n"), mailname);
128 else
129 printf(catgets(catd, CATSET, 156,
130 "Held %d messages in %s\n"), p, mailname);
131 return(0);
135 * Save all of the undetermined messages at the top of "mbox"
136 * Save all untouched messages back in the system mailbox.
137 * Remove the system mailbox, if none saved there.
139 void
140 quit(void)
142 int p, modify, anystat;
143 FILE *fbuf, *rbuf, *readstat = NULL, *abuf;
144 struct message *mp;
145 int c;
146 char *tempResid;
147 struct stat minfo;
150 * If we are read only, we can't do anything,
151 * so just return quickly. IMAP can set some
152 * flags (e.g. "\\Seen") so imap_quit must be
153 * called even then.
155 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
156 return;
157 switch (mb.mb_type) {
158 case MB_FILE:
159 break;
160 case MB_MAILDIR:
161 maildir_quit();
162 return;
163 case MB_POP3:
164 pop3_quit();
165 return;
166 case MB_IMAP:
167 case MB_CACHE:
168 imap_quit();
169 return;
170 case MB_VOID:
171 return;
174 * If editing (not reading system mail box), then do the work
175 * in edstop()
177 if (edit) {
178 edstop();
179 return;
183 * See if there any messages to save in mbox. If no, we
184 * can save copying mbox to /tmp and back.
186 * Check also to see if any files need to be preserved.
187 * Delete all untouched messages to keep them out of mbox.
188 * If all the messages are to be preserved, just exit with
189 * a message.
192 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
193 if (fbuf == NULL) {
194 if (errno == ENOENT)
195 return;
196 goto newmail;
198 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
199 nolock:
200 perror(catgets(catd, CATSET, 157, "Unable to lock mailbox"));
201 Fclose(fbuf);
202 return;
204 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
205 goto nolock;
206 rbuf = NULL;
207 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
208 printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
209 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
210 if (rbuf == NULL || fbuf == NULL)
211 goto newmail;
212 #ifdef APPEND
213 fseek(fbuf, (long)mailsize, SEEK_SET);
214 while ((c = getc(fbuf)) != EOF)
215 putc(c, rbuf);
216 #else
217 p = minfo.st_size - mailsize;
218 while (p-- > 0) {
219 c = getc(fbuf);
220 if (c == EOF)
221 goto newmail;
222 putc(c, rbuf);
224 #endif
225 Fclose(rbuf);
226 if ((rbuf = Fopen(tempResid, "r")) == NULL)
227 goto newmail;
228 rm(tempResid);
229 Ftfree(&tempResid);
232 anystat = holdbits();
233 modify = 0;
234 if (Tflag != NULL) {
235 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
236 Tflag = NULL;
238 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
239 if (mp->m_flag & MBOX)
240 c++;
241 if (mp->m_flag & MPRESERVE)
242 p++;
243 if (mp->m_flag & MODIFY)
244 modify++;
245 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
246 char *id;
248 if ((id = hfield("message-id", mp)) != NULL ||
249 (id = hfield("article-id", mp)) != NULL)
250 fprintf(readstat, "%s\n", id);
253 if (readstat != NULL)
254 Fclose(readstat);
255 if (p == msgCount && !modify && !anystat) {
256 if (p == 1)
257 printf(catgets(catd, CATSET, 155,
258 "Held 1 message in %s\n"), mailname);
259 else if (p > 1)
260 printf(catgets(catd, CATSET, 156,
261 "Held %d messages in %s\n"), p, mailname);
262 Fclose(fbuf);
263 dot_unlock(mailname);
264 return;
266 if (c == 0) {
267 if (p != 0) {
268 writeback(rbuf, fbuf);
269 Fclose(fbuf);
270 dot_unlock(mailname);
271 return;
273 goto cream;
276 if (makembox() == STOP) {
277 Fclose(fbuf);
278 dot_unlock(mailname);
279 return;
282 * Now we are ready to copy back preserved files to
283 * the system mailbox, if any were requested.
286 if (p != 0) {
287 writeback(rbuf, fbuf);
288 Fclose(fbuf);
289 dot_unlock(mailname);
290 return;
294 * Finally, remove his /usr/mail file.
295 * If new mail has arrived, copy it back.
298 cream:
299 if (rbuf != NULL) {
300 abuf = fbuf;
301 fseek(abuf, 0L, SEEK_SET);
302 while ((c = getc(rbuf)) != EOF)
303 putc(c, abuf);
304 Fclose(rbuf);
305 trunc(abuf);
306 alter(mailname);
307 Fclose(fbuf);
308 dot_unlock(mailname);
309 return;
311 demail();
312 Fclose(fbuf);
313 dot_unlock(mailname);
314 return;
316 newmail:
317 printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n"));
318 if (fbuf != NULL) {
319 Fclose(fbuf);
320 dot_unlock(mailname);
325 * Adjust the message flags in each message.
327 int
328 holdbits(void)
330 struct message *mp;
331 int anystat, autohold, holdbit, nohold;
333 anystat = 0;
334 autohold = value("hold") != NULL;
335 holdbit = autohold ? MPRESERVE : MBOX;
336 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
337 if (value("keepsave") != NULL)
338 nohold &= ~MSAVED;
339 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
340 if (mp->m_flag & MNEW) {
341 mp->m_flag &= ~MNEW;
342 mp->m_flag |= MSTATUS;
344 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
345 MDRAFT|MUNDRAFT))
346 anystat++;
347 if ((mp->m_flag & MTOUCH) == 0)
348 mp->m_flag |= MPRESERVE;
349 if ((mp->m_flag & nohold) == 0)
350 mp->m_flag |= holdbit;
352 return anystat;
356 * Create another temporary file and copy user's mbox file
357 * darin. If there is no mbox, copy nothing.
358 * If he has specified "append" don't copy his mailbox,
359 * just copy saveable entries at the end.
362 enum okay
363 makembox(void)
365 struct message *mp;
366 char *mbox, *tempQuit;
367 int mcount, c;
368 FILE *ibuf = NULL, *obuf, *abuf;
369 enum protocol prot;
371 mbox = mboxname;
372 mcount = 0;
373 if (value("append") == NULL) {
374 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
375 perror(catgets(catd, CATSET, 162,
376 "temporary mail quit file"));
377 return STOP;
379 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
380 perror(tempQuit);
381 rm(tempQuit);
382 Ftfree(&tempQuit);
383 Fclose(obuf);
384 return STOP;
386 rm(tempQuit);
387 Ftfree(&tempQuit);
388 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
389 while ((c = getc(abuf)) != EOF)
390 putc(c, obuf);
391 Fclose(abuf);
393 if (ferror(obuf)) {
394 perror(catgets(catd, CATSET, 163,
395 "temporary mail quit file"));
396 Fclose(ibuf);
397 Fclose(obuf);
398 return STOP;
400 Fclose(obuf);
401 close(creat(mbox, 0600));
402 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
403 perror(mbox);
404 Fclose(ibuf);
405 return STOP;
408 else {
409 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
410 perror(mbox);
411 return STOP;
413 fchmod(fileno(obuf), 0600);
415 prot = which_protocol(mbox);
416 for (mp = &message[0]; mp < &message[msgCount]; mp++)
417 if (mp->m_flag & MBOX) {
418 mcount++;
419 if (prot == PROTO_IMAP &&
420 saveignore[0].i_count == 0 &&
421 saveignore[1].i_count == 0 &&
422 imap_thisaccount(mbox)) {
423 if (imap_copy(mp, mp-message+1, mbox) == STOP)
424 goto err;
425 } else if (send(mp, obuf, saveignore,
426 NULL, SEND_MBOX, NULL) < 0) {
427 perror(mbox);
428 err: if (ibuf)
429 Fclose(ibuf);
430 Fclose(obuf);
431 return STOP;
433 mp->m_flag |= MBOXED;
437 * Copy the user's old mbox contents back
438 * to the end of the stuff we just saved.
439 * If we are appending, this is unnecessary.
442 if (value("append") == NULL) {
443 rewind(ibuf);
444 c = getc(ibuf);
445 while (c != EOF) {
446 putc(c, obuf);
447 if (ferror(obuf))
448 break;
449 c = getc(ibuf);
451 Fclose(ibuf);
452 fflush(obuf);
454 trunc(obuf);
455 if (ferror(obuf)) {
456 perror(mbox);
457 Fclose(obuf);
458 return STOP;
460 if (Fclose(obuf) != 0) {
461 if (prot != PROTO_IMAP)
462 perror(mbox);
463 return STOP;
465 if (mcount == 1)
466 printf(catgets(catd, CATSET, 164, "Saved 1 message in mbox\n"));
467 else
468 printf(catgets(catd, CATSET, 165,
469 "Saved %d messages in mbox\n"), mcount);
470 return OKAY;
474 * Terminate an editing session by attempting to write out the user's
475 * file from the temporary. Save any new stuff appended to the file.
477 static void
478 edstop(void)
480 int gotcha, c;
481 struct message *mp;
482 FILE *obuf, *ibuf = NULL, *readstat = NULL;
483 struct stat statb;
485 if (mb.mb_perm == 0)
486 return;
487 holdsigs();
488 if (Tflag != NULL) {
489 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
490 Tflag = NULL;
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++;
500 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
501 char *id;
503 if ((id = hfield("message-id", mp)) != NULL ||
504 (id = hfield("article-id", mp)) != NULL)
505 fprintf(readstat, "%s\n", id);
508 if (readstat != NULL)
509 Fclose(readstat);
510 if (!gotcha || Tflag != NULL)
511 goto done;
512 ibuf = NULL;
513 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
514 char *tempname;
516 if ((obuf = Ftemp(&tempname, "mbox.", "w", 0600, 1)) == NULL) {
517 perror(catgets(catd, CATSET, 167, "tmpfile"));
518 relsesigs();
519 reset(0);
521 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
522 perror(mailname);
523 Fclose(obuf);
524 rm(tempname);
525 Ftfree(&tempname);
526 relsesigs();
527 reset(0);
529 fseek(ibuf, (long)mailsize, SEEK_SET);
530 while ((c = getc(ibuf)) != EOF)
531 putc(c, obuf);
532 Fclose(ibuf);
533 Fclose(obuf);
534 if ((ibuf = Fopen(tempname, "r")) == NULL) {
535 perror(tempname);
536 rm(tempname);
537 Ftfree(&tempname);
538 relsesigs();
539 reset(0);
541 rm(tempname);
542 Ftfree(&tempname);
544 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
545 fflush(stdout);
546 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
547 perror(mailname);
548 relsesigs();
549 reset(0);
551 trunc(obuf);
552 c = 0;
553 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
554 if ((mp->m_flag & MDELETED) != 0)
555 continue;
556 c++;
557 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
558 perror(mailname);
559 relsesigs();
560 reset(0);
563 gotcha = (c == 0 && ibuf == NULL);
564 if (ibuf != NULL) {
565 while ((c = getc(ibuf)) != EOF)
566 putc(c, obuf);
567 Fclose(ibuf);
569 fflush(obuf);
570 if (ferror(obuf)) {
571 perror(mailname);
572 relsesigs();
573 reset(0);
575 Fclose(obuf);
576 if (gotcha && value("emptybox") == NULL) {
577 rm(mailname);
578 printf(value("bsdcompat") || value("bsdmsgs") ?
579 catgets(catd, CATSET, 169, "removed\n") :
580 catgets(catd, CATSET, 211, "removed.\n"));
581 } else
582 printf(value("bsdcompat") || value("bsdmsgs") ?
583 catgets(catd, CATSET, 170, "complete\n") :
584 catgets(catd, CATSET, 212, "updated.\n"));
585 fflush(stdout);
587 done:
588 relsesigs();
591 enum quitflags {
592 QUITFLAG_HOLD = 001,
593 QUITFLAG_KEEPSAVE = 002,
594 QUITFLAG_APPEND = 004,
595 QUITFLAG_EMPTYBOX = 010
598 static const struct quitnames {
599 enum quitflags flag;
600 const char *name;
601 } quitnames[] = {
602 { QUITFLAG_HOLD, "hold" },
603 { QUITFLAG_KEEPSAVE, "keepsave" },
604 { QUITFLAG_APPEND, "append" },
605 { QUITFLAG_EMPTYBOX, "emptybox" },
606 { 0, NULL }
610 savequitflags(void)
612 enum quitflags qf = 0;
613 int i;
615 for (i = 0; quitnames[i].name; i++)
616 if (value(quitnames[i].name))
617 qf |= quitnames[i].flag;
618 return qf;
621 void
622 restorequitflags(int qf)
624 int i;
626 for (i = 0; quitnames[i].name; i++)
627 if (qf & quitnames[i].flag) {
628 if (value(quitnames[i].name) == NULL)
629 assign(quitnames[i].name, "");
630 } else if (value(quitnames[i].name))
631 unset_internal(quitnames[i].name);