junk.c: file_expand() may fail
[s-mailx.git] / quit.c
blob874471299c535c8087a526af34ff2e47f8b8d593
1 /*
2 * S-nail - 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 #include "rcv.h"
41 #include "extern.h"
42 #include <stdio.h>
43 #include <errno.h>
44 #include <sys/file.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <fcntl.h>
50 * Rcv -- receive mail rationally.
52 * Termination processing.
55 static int writeback(FILE *res, FILE *obuf);
56 static void edstop(void);
59 * The "quit" command.
61 /*ARGSUSED*/
62 int
63 quitcmd(void *v)
65 (void)v;
67 * If we are sourcing, then return 1 so execute() can handle it.
68 * Otherwise, return -1 to abort command loop.
70 if (sourcing)
71 return 1;
72 return -1;
76 * Preserve all the appropriate messages back in the system
77 * mailbox, and print a nice message indicated how many were
78 * saved. On any error, just return -1. Else return 0.
79 * Incorporate the any new mail that we found.
81 static int
82 writeback(FILE *res, FILE *obuf)
84 struct message *mp;
85 int p, c;
87 p = 0;
88 fseek(obuf, 0L, SEEK_SET);
89 #ifndef APPEND
90 if (res != NULL)
91 while ((c = getc(res)) != EOF)
92 putc(c, obuf);
93 #endif
94 for (mp = &message[0]; mp < &message[msgCount]; mp++)
95 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
96 p++;
97 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
98 perror(mailname);
99 fseek(obuf, 0L, SEEK_SET);
100 return(-1);
103 #ifdef APPEND
104 if (res != NULL)
105 while ((c = getc(res)) != EOF)
106 putc(c, obuf);
107 #endif
108 fflush(obuf);
109 trunc(obuf);
110 if (ferror(obuf)) {
111 perror(mailname);
112 fseek(obuf, 0L, SEEK_SET);
113 return(-1);
115 if (res != NULL)
116 Fclose(res);
117 fseek(obuf, 0L, SEEK_SET);
118 alter(mailname);
119 if (p == 1)
120 printf(catgets(catd, CATSET, 155,
121 "Held 1 message in %s\n"), mailname);
122 else
123 printf(catgets(catd, CATSET, 156,
124 "Held %d messages in %s\n"), p, mailname);
125 return(0);
129 * Save all of the undetermined messages at the top of "mbox"
130 * Save all untouched messages back in the system mailbox.
131 * Remove the system mailbox, if none saved there.
133 void
134 quit(void)
136 int p, modify, anystat;
137 FILE *fbuf, *rbuf, *readstat = NULL, *abuf;
138 struct message *mp;
139 int c;
140 char *tempResid;
141 struct stat minfo;
144 * If we are read only, we can't do anything,
145 * so just return quickly. IMAP can set some
146 * flags (e.g. "\\Seen") so imap_quit must be
147 * called even then.
149 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
150 return;
151 switch (mb.mb_type) {
152 case MB_FILE:
153 break;
154 case MB_MAILDIR:
155 maildir_quit();
156 return;
157 case MB_POP3:
158 pop3_quit();
159 return;
160 case MB_IMAP:
161 case MB_CACHE:
162 imap_quit();
163 return;
164 case MB_VOID:
165 return;
168 * If editing (not reading system mail box), then do the work
169 * in edstop()
171 if (edit) {
172 edstop();
173 return;
177 * See if there any messages to save in mbox. If no, we
178 * can save copying mbox to /tmp and back.
180 * Check also to see if any files need to be preserved.
181 * Delete all untouched messages to keep them out of mbox.
182 * If all the messages are to be preserved, just exit with
183 * a message.
186 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
187 if (fbuf == NULL) {
188 if (errno == ENOENT)
189 return;
190 goto newmail;
192 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
193 nolock:
194 perror(catgets(catd, CATSET, 157, "Unable to lock mailbox"));
195 Fclose(fbuf);
196 return;
198 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
199 goto nolock;
200 rbuf = NULL;
201 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
202 printf(catgets(catd, CATSET, 158, "New mail has arrived.\n"));
203 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
204 if (rbuf == NULL || fbuf == NULL)
205 goto newmail;
206 #ifdef APPEND
207 fseek(fbuf, (long)mailsize, SEEK_SET);
208 while ((c = getc(fbuf)) != EOF)
209 putc(c, rbuf);
210 #else
211 p = minfo.st_size - mailsize;
212 while (p-- > 0) {
213 c = getc(fbuf);
214 if (c == EOF)
215 goto newmail;
216 putc(c, rbuf);
218 #endif
219 Fclose(rbuf);
220 if ((rbuf = Fopen(tempResid, "r")) == NULL)
221 goto newmail;
222 rm(tempResid);
223 Ftfree(&tempResid);
226 anystat = holdbits();
227 modify = 0;
228 if (Tflag != NULL) {
229 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
230 Tflag = NULL;
232 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
233 if (mp->m_flag & MBOX)
234 c++;
235 if (mp->m_flag & MPRESERVE)
236 p++;
237 if (mp->m_flag & MODIFY)
238 modify++;
239 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
240 char *id;
242 if ((id = hfield1("message-id", mp)) != NULL ||
243 (id = hfield1("article-id",mp)) != NULL)
244 fprintf(readstat, "%s\n", id);
247 if (readstat != NULL)
248 Fclose(readstat);
249 if (p == msgCount && !modify && !anystat) {
250 if (p == 1)
251 printf(catgets(catd, CATSET, 155,
252 "Held 1 message in %s\n"), mailname);
253 else if (p > 1)
254 printf(catgets(catd, CATSET, 156,
255 "Held %d messages in %s\n"), p, mailname);
256 Fclose(fbuf);
257 dot_unlock(mailname);
258 return;
260 if (c == 0) {
261 if (p != 0) {
262 writeback(rbuf, fbuf);
263 Fclose(fbuf);
264 dot_unlock(mailname);
265 return;
267 goto cream;
270 if (makembox() == STOP) {
271 Fclose(fbuf);
272 dot_unlock(mailname);
273 return;
276 * Now we are ready to copy back preserved files to
277 * the system mailbox, if any were requested.
280 if (p != 0) {
281 writeback(rbuf, fbuf);
282 Fclose(fbuf);
283 dot_unlock(mailname);
284 return;
288 * Finally, remove his /usr/mail file.
289 * If new mail has arrived, copy it back.
292 cream:
293 if (rbuf != NULL) {
294 abuf = fbuf;
295 fseek(abuf, 0L, SEEK_SET);
296 while ((c = getc(rbuf)) != EOF)
297 putc(c, abuf);
298 Fclose(rbuf);
299 trunc(abuf);
300 alter(mailname);
301 Fclose(fbuf);
302 dot_unlock(mailname);
303 return;
305 demail();
306 Fclose(fbuf);
307 dot_unlock(mailname);
308 return;
310 newmail:
311 printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n"));
312 if (fbuf != NULL) {
313 Fclose(fbuf);
314 dot_unlock(mailname);
319 * Adjust the message flags in each message.
321 int
322 holdbits(void)
324 struct message *mp;
325 int anystat, autohold, holdbit, nohold;
327 anystat = 0;
328 autohold = value("hold") != NULL;
329 holdbit = autohold ? MPRESERVE : MBOX;
330 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
331 if (value("keepsave") != NULL)
332 nohold &= ~MSAVED;
333 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
334 if (mp->m_flag & MNEW) {
335 mp->m_flag &= ~MNEW;
336 mp->m_flag |= MSTATUS;
338 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
339 MDRAFT|MUNDRAFT))
340 anystat++;
341 if ((mp->m_flag & MTOUCH) == 0)
342 mp->m_flag |= MPRESERVE;
343 if ((mp->m_flag & nohold) == 0)
344 mp->m_flag |= holdbit;
346 return anystat;
350 * Create another temporary file and copy user's mbox file
351 * darin. If there is no mbox, copy nothing.
352 * If he has specified "append" don't copy his mailbox,
353 * just copy saveable entries at the end.
356 enum okay
357 makembox(void)
359 struct message *mp;
360 char *mbox, *tempQuit;
361 int mcount, c;
362 FILE *ibuf = NULL, *obuf, *abuf;
363 enum protocol prot;
365 mbox = mboxname;
366 mcount = 0;
367 if (value("append") == NULL) {
368 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
369 perror(catgets(catd, CATSET, 162,
370 "temporary mail quit file"));
371 return STOP;
373 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
374 perror(tempQuit);
375 rm(tempQuit);
376 Ftfree(&tempQuit);
377 Fclose(obuf);
378 return STOP;
380 rm(tempQuit);
381 Ftfree(&tempQuit);
382 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
383 while ((c = getc(abuf)) != EOF)
384 putc(c, obuf);
385 Fclose(abuf);
387 if (ferror(obuf)) {
388 perror(catgets(catd, CATSET, 163,
389 "temporary mail quit file"));
390 Fclose(ibuf);
391 Fclose(obuf);
392 return STOP;
394 Fclose(obuf);
395 close(creat(mbox, 0600));
396 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
397 perror(mbox);
398 Fclose(ibuf);
399 return STOP;
402 else {
403 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
404 perror(mbox);
405 return STOP;
407 fchmod(fileno(obuf), 0600);
409 prot = which_protocol(mbox);
410 for (mp = &message[0]; mp < &message[msgCount]; mp++)
411 if (mp->m_flag & MBOX) {
412 mcount++;
413 if (prot == PROTO_IMAP &&
414 saveignore[0].i_count == 0 &&
415 saveignore[1].i_count == 0 &&
416 imap_thisaccount(mbox)) {
417 if (imap_copy(mp, mp-message+1, mbox) == STOP)
418 goto err;
419 } else if (send(mp, obuf, saveignore,
420 NULL, SEND_MBOX, NULL) < 0) {
421 perror(mbox);
422 err: if (ibuf)
423 Fclose(ibuf);
424 Fclose(obuf);
425 return STOP;
427 mp->m_flag |= MBOXED;
431 * Copy the user's old mbox contents back
432 * to the end of the stuff we just saved.
433 * If we are appending, this is unnecessary.
436 if (value("append") == NULL) {
437 rewind(ibuf);
438 c = getc(ibuf);
439 while (c != EOF) {
440 putc(c, obuf);
441 if (ferror(obuf))
442 break;
443 c = getc(ibuf);
445 Fclose(ibuf);
446 fflush(obuf);
448 trunc(obuf);
449 if (ferror(obuf)) {
450 perror(mbox);
451 Fclose(obuf);
452 return STOP;
454 if (Fclose(obuf) != 0) {
455 if (prot != PROTO_IMAP)
456 perror(mbox);
457 return STOP;
459 if (mcount == 1)
460 printf(catgets(catd, CATSET, 164, "Saved 1 message in mbox\n"));
461 else
462 printf(catgets(catd, CATSET, 165,
463 "Saved %d messages in mbox\n"), mcount);
464 return OKAY;
468 * Terminate an editing session by attempting to write out the user's
469 * file from the temporary. Save any new stuff appended to the file.
471 static void
472 edstop(void)
474 int gotcha, c;
475 struct message *mp;
476 FILE *obuf, *ibuf = NULL, *readstat = NULL;
477 struct stat statb;
479 if (mb.mb_perm == 0)
480 return;
481 holdsigs();
482 if (Tflag != NULL) {
483 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
484 Tflag = NULL;
486 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
487 if (mp->m_flag & MNEW) {
488 mp->m_flag &= ~MNEW;
489 mp->m_flag |= MSTATUS;
491 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
492 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
493 gotcha++;
494 if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
495 char *id;
497 if ((id = hfield1("message-id", mp)) != NULL ||
498 (id = hfield1("article-id",mp)) != NULL)
499 fprintf(readstat, "%s\n", id);
502 if (readstat != NULL)
503 Fclose(readstat);
504 if (!gotcha || Tflag != NULL)
505 goto done;
506 ibuf = NULL;
507 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
508 char *tempname;
510 if ((obuf = Ftemp(&tempname, "mbox.", "w", 0600, 1)) == NULL) {
511 perror(catgets(catd, CATSET, 167, "tmpfile"));
512 relsesigs();
513 reset(0);
515 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
516 perror(mailname);
517 Fclose(obuf);
518 rm(tempname);
519 Ftfree(&tempname);
520 relsesigs();
521 reset(0);
523 fseek(ibuf, (long)mailsize, SEEK_SET);
524 while ((c = getc(ibuf)) != EOF)
525 putc(c, obuf);
526 Fclose(ibuf);
527 Fclose(obuf);
528 if ((ibuf = Fopen(tempname, "r")) == NULL) {
529 perror(tempname);
530 rm(tempname);
531 Ftfree(&tempname);
532 relsesigs();
533 reset(0);
535 rm(tempname);
536 Ftfree(&tempname);
538 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
539 fflush(stdout);
540 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
541 perror(mailname);
542 relsesigs();
543 reset(0);
545 trunc(obuf);
546 c = 0;
547 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
548 if ((mp->m_flag & MDELETED) != 0)
549 continue;
550 c++;
551 if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
552 perror(mailname);
553 relsesigs();
554 reset(0);
557 gotcha = (c == 0 && ibuf == NULL);
558 if (ibuf != NULL) {
559 while ((c = getc(ibuf)) != EOF)
560 putc(c, obuf);
561 Fclose(ibuf);
563 fflush(obuf);
564 if (ferror(obuf)) {
565 perror(mailname);
566 relsesigs();
567 reset(0);
569 Fclose(obuf);
570 if (gotcha && value("emptybox") == NULL) {
571 rm(mailname);
572 printf(value("bsdcompat") || value("bsdmsgs") ?
573 catgets(catd, CATSET, 169, "removed\n") :
574 catgets(catd, CATSET, 211, "removed.\n"));
575 } else
576 printf(value("bsdcompat") || value("bsdmsgs") ?
577 catgets(catd, CATSET, 170, "complete\n") :
578 catgets(catd, CATSET, 212, "updated.\n"));
579 fflush(stdout);
581 done:
582 relsesigs();
585 enum quitflags {
586 QUITFLAG_HOLD = 001,
587 QUITFLAG_KEEPSAVE = 002,
588 QUITFLAG_APPEND = 004,
589 QUITFLAG_EMPTYBOX = 010
592 static const struct quitnames {
593 enum quitflags flag;
594 const char *name;
595 } quitnames[] = {
596 { QUITFLAG_HOLD, "hold" },
597 { QUITFLAG_KEEPSAVE, "keepsave" },
598 { QUITFLAG_APPEND, "append" },
599 { QUITFLAG_EMPTYBOX, "emptybox" },
600 { 0, NULL }
604 savequitflags(void)
606 enum quitflags qf = 0;
607 int i;
609 for (i = 0; quitnames[i].name; i++)
610 if (value(quitnames[i].name))
611 qf |= quitnames[i].flag;
612 return qf;
615 void
616 restorequitflags(int qf)
618 int i;
620 for (i = 0; quitnames[i].name; i++)
621 if (qf & quitnames[i].flag) {
622 if (value(quitnames[i].name) == NULL)
623 assign(quitnames[i].name, "");
624 } else if (value(quitnames[i].name))
625 unset_internal(quitnames[i].name);