1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3 *
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.
9 *
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.
25 *
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.
37 */
38
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)quit.c 2.28 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
44
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>
53
54 /*
55 * Rcv -- receive mail rationally.
56 *
57 * Termination processing.
58 */
59
60 static int writeback(FILE *res, FILE *obuf);
61 static void edstop(void);
62
63 /*
64 * The "quit" command.
65 */
66 /*ARGSUSED*/
67 int
quitcmd(void * v)68 quitcmd(void *v)
69 {
70 /*
71 * If we are sourcing, then return 1 so execute() can handle it.
72 * Otherwise, return -1 to abort command loop.
73 */
74 if (sourcing)
75 return 1;
76 return -1;
77 }
78
79 /*
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.
84 */
85 static int
writeback(FILE * res,FILE * obuf)86 writeback(FILE *res, FILE *obuf)
87 {
88 struct message *mp;
89 int p, c;
90
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);
105 }
106 }
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);
118 }
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);
130 }
131
132 /*
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.
136 */
137 void
quit(void)138 quit(void)
139 {
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;
146
147 /*
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.
152 */
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;
170 }
171 /*
172 * If editing (not reading system mail box), then do the work
173 * in edstop()
174 */
175 if (edit) {
176 edstop();
177 return;
178 }
179
180 /*
181 * See if there any messages to save in mbox. If no, we
182 * can save copying mbox to /tmp and back.
183 *
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.
188 */
189
190 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
191 if (fbuf == NULL) {
192 if (errno == ENOENT)
193 return;
194 goto newmail;
195 }
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;
201 }
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);
221 }
222 #endif
223 Fclose(rbuf);
224 if ((rbuf = Fopen(tempResid, "r")) == NULL)
225 goto newmail;
226 rm(tempResid);
227 Ftfree(&tempResid);
228 }
229
230 anystat = holdbits();
231 modify = 0;
232 if (Tflag != NULL) {
233 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
234 Tflag = NULL;
235 }
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;
245
246 if ((id = hfield("message-id", mp)) != NULL ||
247 (id = hfield("article-id", mp)) != NULL)
248 fprintf(readstat, "%s\n", id);
249 }
250 }
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;
263 }
264 if (c == 0) {
265 if (p != 0) {
266 writeback(rbuf, fbuf);
267 Fclose(fbuf);
268 dot_unlock(mailname);
269 return;
270 }
271 goto cream;
272 }
273
274 if (makembox() == STOP) {
275 Fclose(fbuf);
276 dot_unlock(mailname);
277 return;
278 }
279 /*
280 * Now we are ready to copy back preserved files to
281 * the system mailbox, if any were requested.
282 */
283
284 if (p != 0) {
285 writeback(rbuf, fbuf);
286 Fclose(fbuf);
287 dot_unlock(mailname);
288 return;
289 }
290
291 /*
292 * Finally, remove his /usr/mail file.
293 * If new mail has arrived, copy it back.
294 */
295
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;
308 }
309 demail();
310 Fclose(fbuf);
311 dot_unlock(mailname);
312 return;
313
314 newmail:
315 printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n"));
316 if (fbuf != NULL) {
317 Fclose(fbuf);
318 dot_unlock(mailname);
319 }
320 }
321
322 /*
323 * Adjust the message flags in each message.
324 */
325 int
holdbits(void)326 holdbits(void)
327 {
328 struct message *mp;
329 int anystat, autohold, holdbit, nohold;
330
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;
341 }
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;
349 }
350 return anystat;
351 }
352
353 /*
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.
358 */
359
360 enum okay
makembox(void)361 makembox(void)
362 {
363 struct message *mp;
364 char *mbox, *tempQuit;
365 int mcount, c;
366 FILE *ibuf = NULL, *obuf, *abuf;
367 enum protocol prot;
368
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;
376 }
377 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
378 perror(tempQuit);
379 rm(tempQuit);
380 Ftfree(&tempQuit);
381 Fclose(obuf);
382 return STOP;
383 }
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);
390 }
391 if (ferror(obuf)) {
392 perror(catgets(catd, CATSET, 163,
393 "temporary mail quit file"));
394 Fclose(ibuf);
395 Fclose(obuf);
396 return STOP;
397 }
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;
404 }
405 }
406 else {
407 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
408 perror(mbox);
409 return STOP;
410 }
411 fchmod(fileno(obuf), 0600);
412 }
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;
430 }
431 mp->m_flag |= MBOXED;
432 }
433
434 /*
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.
438 */
439
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);
448 }
449 Fclose(ibuf);
450 fflush(obuf);
451 }
452 trunc(obuf);
453 if (ferror(obuf)) {
454 perror(mbox);
455 Fclose(obuf);
456 return STOP;
457 }
458 if (Fclose(obuf) != 0) {
459 if (prot != PROTO_IMAP)
460 perror(mbox);
461 return STOP;
462 }
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;
469 }
470
471 /*
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.
474 */
475 static void
edstop(void)476 edstop(void)
477 {
478 int gotcha, c;
479 struct message *mp;
480 FILE *obuf, *ibuf = NULL, *readstat = NULL;
481 struct stat statb;
482
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;
489 }
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;
494 }
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;
500
501 if ((id = hfield("message-id", mp)) != NULL ||
502 (id = hfield("article-id", mp)) != NULL)
503 fprintf(readstat, "%s\n", id);
504 }
505 }
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;
513
514 if ((obuf = Ftemp(&tempname, "mbox.", "w", 0600, 1)) == NULL) {
515 perror(catgets(catd, CATSET, 167, "tmpfile"));
516 relsesigs();
517 reset(0);
518 }
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);
526 }
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);
538 }
539 rm(tempname);
540 Ftfree(&tempname);
541 }
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);
548 }
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);
559 }
560 }
561 gotcha = (c == 0 && ibuf == NULL);
562 if (ibuf != NULL) {
563 while ((c = getc(ibuf)) != EOF)
564 putc(c, obuf);
565 Fclose(ibuf);
566 }
567 fflush(obuf);
568 if (ferror(obuf)) {
569 perror(mailname);
570 relsesigs();
571 reset(0);
572 }
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);
584
585 done:
586 relsesigs();
587 }
588