xref: /dragonfly/usr.bin/mail/quit.c (revision dc71b7ab81c4f5270d3668e1625d94a58895fa7a)
1 /*
2  * Copyright (c) 1980, 1993
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)quit.c       8.2 (Berkeley) 4/28/95
30  * $FreeBSD: src/usr.bin/mail/quit.c,v 1.2.6.3 2003/01/06 05:46:03 mikeh Exp $
31  * $DragonFly: src/usr.bin/mail/quit.c,v 1.5 2004/09/08 03:01:11 joerg Exp $
32  */
33 
34 #include "rcv.h"
35 #include <fcntl.h>
36 #include "extern.h"
37 
38 static void         edstop(void);
39 static int          writeback(FILE *res);
40 
41 /*
42  * Rcv -- receive mail rationally.
43  *
44  * Termination processing.
45  */
46 
47 /*
48  * The "quit" command.
49  */
50 int
quitcmd(void)51 quitcmd(void)
52 {
53           /*
54            * If we are sourcing, then return 1 so execute() can handle it.
55            * Otherwise, return -1 to abort command loop.
56            */
57           if (sourcing)
58                     return (1);
59           return (-1);
60 }
61 
62 /*
63  * Save all of the undetermined messages at the top of "mbox"
64  * Save all untouched messages back in the system mailbox.
65  * Remove the system mailbox, if none saved there.
66  */
67 void
quit(void)68 quit(void)
69 {
70           int mcount, p, modify, autohold, anystat, holdbit, nohold;
71           FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
72           struct message *mp;
73           int c, fd;
74           struct stat minfo;
75           char *mbox, tempname[PATHSIZE];
76 
77           /*
78            * If we are read only, we can't do anything,
79            * so just return quickly.
80            */
81           if (readonly)
82                     return;
83           /*
84            * If editing (not reading system mail box), then do the work
85            * in edstop()
86            */
87           if (edit) {
88                     edstop();
89                     return;
90           }
91 
92           /*
93            * See if there any messages to save in mbox.  If no, we
94            * can save copying mbox to /tmp and back.
95            *
96            * Check also to see if any files need to be preserved.
97            * Delete all untouched messages to keep them out of mbox.
98            * If all the messages are to be preserved, just exit with
99            * a message.
100            */
101 
102           fbuf = Fopen(mailname, "r");
103           if (fbuf == NULL)
104                     goto newmail;
105           flock(fileno(fbuf), LOCK_EX);
106           rbuf = NULL;
107           if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
108                     printf("New mail has arrived.\n");
109                     snprintf(tempname, sizeof(tempname), "%s/mail.RqXXXXXXXXXX",
110                                tmpdir);
111                     if ((fd = mkstemp(tempname)) == -1 ||
112                         (rbuf = Fdopen(fd, "w")) == NULL)
113                               goto newmail;
114 #ifdef APPEND
115                     fseeko(fbuf, mailsize, SEEK_SET);
116                     while ((c = getc(fbuf)) != EOF)
117                               putc(c, rbuf);
118 #else
119                     p = minfo.st_size - mailsize;
120                     while (p-- > 0) {
121                               c = getc(fbuf);
122                               if (c == EOF)
123                                         goto newmail;
124                               putc(c, rbuf);
125                     }
126 #endif
127                     Fclose(rbuf);
128                     if ((rbuf = Fopen(tempname, "r")) == NULL)
129                               goto newmail;
130                     rm(tempname);
131           }
132 
133           /*
134            * Adjust the message flags in each message.
135            */
136 
137           anystat = 0;
138           autohold = value("hold") != NULL;
139           holdbit = autohold ? MPRESERVE : MBOX;
140           nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
141           if (value("keepsave") != NULL)
142                     nohold &= ~MSAVED;
143           for (mp = &message[0]; mp < &message[msgCount]; mp++) {
144                     if (mp->m_flag & MNEW) {
145                               mp->m_flag &= ~MNEW;
146                               mp->m_flag |= MSTATUS;
147                     }
148                     if (mp->m_flag & MSTATUS)
149                               anystat++;
150                     if ((mp->m_flag & MTOUCH) == 0)
151                               mp->m_flag |= MPRESERVE;
152                     if ((mp->m_flag & nohold) == 0)
153                               mp->m_flag |= holdbit;
154           }
155           modify = 0;
156           if (Tflag != NULL) {
157                     if ((readstat = Fopen(Tflag, "w")) == NULL)
158                               Tflag = NULL;
159           }
160           for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
161                     if (mp->m_flag & MBOX)
162                               c++;
163                     if (mp->m_flag & MPRESERVE)
164                               p++;
165                     if (mp->m_flag & MODIFY)
166                               modify++;
167                     if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
168                               char *id;
169 
170                               if ((id = hfield("article-id", mp)) != NULL)
171                                         fprintf(readstat, "%s\n", id);
172                     }
173           }
174           if (Tflag != NULL)
175                     Fclose(readstat);
176           if (p == msgCount && !modify && !anystat) {
177                     printf("Held %d message%s in %s\n",
178                               p, p == 1 ? "" : "s", mailname);
179                     Fclose(fbuf);
180                     return;
181           }
182           if (c == 0) {
183                     if (p != 0) {
184                               writeback(rbuf);
185                               Fclose(fbuf);
186                               return;
187                     }
188                     goto cream;
189           }
190 
191           /*
192            * Create another temporary file and copy user's mbox file
193            * darin.  If there is no mbox, copy nothing.
194            * If he has specified "append" don't copy his mailbox,
195            * just copy saveable entries at the end.
196            */
197 
198           mbox = expand("&");
199           mcount = c;
200           if (value("append") == NULL) {
201                     snprintf(tempname, sizeof(tempname), "%s/mail.RmXXXXXXXXXX",
202                                tmpdir);
203                     if ((fd = mkstemp(tempname)) == -1 ||
204                         (obuf = Fdopen(fd, "w")) == NULL) {
205                               warn("%s", tempname);
206                               Fclose(fbuf);
207                               return;
208                     }
209                     if ((ibuf = Fopen(tempname, "r")) == NULL) {
210                               warn("%s", tempname);
211                               rm(tempname);
212                               Fclose(obuf);
213                               Fclose(fbuf);
214                               return;
215                     }
216                     rm(tempname);
217                     if ((abuf = Fopen(mbox, "r")) != NULL) {
218                               while ((c = getc(abuf)) != EOF)
219                                         putc(c, obuf);
220                               Fclose(abuf);
221                     }
222                     if (ferror(obuf)) {
223                               warnx("%s", tempname);
224                               Fclose(ibuf);
225                               Fclose(obuf);
226                               Fclose(fbuf);
227                               return;
228                     }
229                     Fclose(obuf);
230                     close(open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600));
231                     if ((obuf = Fopen(mbox, "r+")) == NULL) {
232                               warn("%s", mbox);
233                               Fclose(ibuf);
234                               Fclose(fbuf);
235                               return;
236                     }
237           }
238           if (value("append") != NULL) {
239                     if ((obuf = Fopen(mbox, "a")) == NULL) {
240                               warn("%s", mbox);
241                               Fclose(fbuf);
242                               return;
243                     }
244                     fchmod(fileno(obuf), 0600);
245           }
246           for (mp = &message[0]; mp < &message[msgCount]; mp++)
247                     if (mp->m_flag & MBOX)
248                               if (sendmessage(mp, obuf, saveignore, NULL) < 0) {
249                                         warnx("%s", mbox);
250                                         Fclose(ibuf);
251                                         Fclose(obuf);
252                                         Fclose(fbuf);
253                                         return;
254                               }
255 
256           /*
257            * Copy the user's old mbox contents back
258            * to the end of the stuff we just saved.
259            * If we are appending, this is unnecessary.
260            */
261 
262           if (value("append") == NULL) {
263                     rewind(ibuf);
264                     c = getc(ibuf);
265                     while (c != EOF) {
266                               putc(c, obuf);
267                               if (ferror(obuf))
268                                         break;
269                               c = getc(ibuf);
270                     }
271                     Fclose(ibuf);
272           }
273           fflush(obuf);
274           trunc(obuf);
275           if (ferror(obuf)) {
276                     warn("%s", mbox);
277                     Fclose(obuf);
278                     Fclose(fbuf);
279                     return;
280           }
281           Fclose(obuf);
282           if (mcount == 1)
283                     printf("Saved 1 message in mbox\n");
284           else
285                     printf("Saved %d messages in mbox\n", mcount);
286 
287           /*
288            * Now we are ready to copy back preserved files to
289            * the system mailbox, if any were requested.
290            */
291 
292           if (p != 0) {
293                     writeback(rbuf);
294                     Fclose(fbuf);
295                     return;
296           }
297 
298           /*
299            * Finally, remove his /var/mail file.
300            * If new mail has arrived, copy it back.
301            */
302 
303 cream:
304           if (rbuf != NULL) {
305                     abuf = Fopen(mailname, "r+");
306                     if (abuf == NULL)
307                               goto newmail;
308                     while ((c = getc(rbuf)) != EOF)
309                               putc(c, abuf);
310                     Fclose(rbuf);
311                     trunc(abuf);
312                     Fclose(abuf);
313                     alter(mailname);
314                     Fclose(fbuf);
315                     return;
316           }
317           demail();
318           Fclose(fbuf);
319           return;
320 
321 newmail:
322           printf("Thou hast new mail.\n");
323           if (fbuf != NULL)
324                     Fclose(fbuf);
325 }
326 
327 /*
328  * Preserve all the appropriate messages back in the system
329  * mailbox, and print a nice message indicated how many were
330  * saved.  On any error, just return -1.  Else return 0.
331  * Incorporate the any new mail that we found.
332  */
333 static int
writeback(FILE * res)334 writeback(FILE *res)
335 {
336           struct message *mp;
337           int p, c;
338           FILE *obuf;
339 
340           p = 0;
341           if ((obuf = Fopen(mailname, "r+")) == NULL) {
342                     warn("%s", mailname);
343                     return (-1);
344           }
345 #ifndef APPEND
346           if (res != NULL)
347                     while ((c = getc(res)) != EOF)
348                               putc(c, obuf);
349 #endif
350           for (mp = &message[0]; mp < &message[msgCount]; mp++)
351                     if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
352                               p++;
353                               if (sendmessage(mp, obuf, NULL, NULL) < 0) {
354                                         warnx("%s", mailname);
355                                         Fclose(obuf);
356                                         return (-1);
357                               }
358                     }
359 #ifdef APPEND
360           if (res != NULL)
361                     while ((c = getc(res)) != EOF)
362                               putc(c, obuf);
363 #endif
364           fflush(obuf);
365           trunc(obuf);
366           if (ferror(obuf)) {
367                     warn("%s", mailname);
368                     Fclose(obuf);
369                     return (-1);
370           }
371           if (res != NULL)
372                     Fclose(res);
373           Fclose(obuf);
374           alter(mailname);
375           if (p == 1)
376                     printf("Held 1 message in %s\n", mailname);
377           else
378                     printf("Held %d messages in %s\n", p, mailname);
379           return (0);
380 }
381 
382 /*
383  * Terminate an editing session by attempting to write out the user's
384  * file from the temporary.  Save any new stuff appended to the file.
385  */
386 static void
edstop(void)387 edstop(void)
388 {
389           int gotcha, c;
390           struct message *mp;
391           FILE *obuf, *ibuf, *readstat;
392           struct stat statb;
393           char tempname[PATHSIZE];
394 
395           if (readonly)
396                     return;
397           holdsigs();
398           if (Tflag != NULL) {
399                     if ((readstat = Fopen(Tflag, "w")) == NULL)
400                               Tflag = NULL;
401           }
402           for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
403                     if (mp->m_flag & MNEW) {
404                               mp->m_flag &= ~MNEW;
405                               mp->m_flag |= MSTATUS;
406                     }
407                     if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
408                               gotcha++;
409                     if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
410                               char *id;
411 
412                               if ((id = hfield("article-id", mp)) != NULL)
413                                         fprintf(readstat, "%s\n", id);
414                     }
415           }
416           if (Tflag != NULL)
417                     Fclose(readstat);
418           if (!gotcha || Tflag != NULL)
419                     goto done;
420           ibuf = NULL;
421           if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
422                     int fd;
423 
424                     snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX",
425                                tmpdir);
426                     if ((fd = mkstemp(tempname)) == -1 ||
427                         (obuf = Fdopen(fd, "w")) == NULL) {
428                               warn("%s", tempname);
429                               relsesigs();
430                               reset(0);
431                     }
432                     if ((ibuf = Fopen(mailname, "r")) == NULL) {
433                               warn("%s", mailname);
434                               Fclose(obuf);
435                               rm(tempname);
436                               relsesigs();
437                               reset(0);
438                     }
439                     fseeko(ibuf, mailsize, SEEK_SET);
440                     while ((c = getc(ibuf)) != EOF)
441                               putc(c, obuf);
442                     Fclose(ibuf);
443                     Fclose(obuf);
444                     if ((ibuf = Fopen(tempname, "r")) == NULL) {
445                               warn("%s", tempname);
446                               rm(tempname);
447                               relsesigs();
448                               reset(0);
449                     }
450                     rm(tempname);
451           }
452           printf("\"%s\" ", mailname);
453           fflush(stdout);
454           if ((obuf = Fopen(mailname, "r+")) == NULL) {
455                     warn("%s", mailname);
456                     relsesigs();
457                     reset(0);
458           }
459           trunc(obuf);
460           c = 0;
461           for (mp = &message[0]; mp < &message[msgCount]; mp++) {
462                     if ((mp->m_flag & MDELETED) != 0)
463                               continue;
464                     c++;
465                     if (sendmessage(mp, obuf, NULL, NULL) < 0) {
466                               warnx("%s", mailname);
467                               relsesigs();
468                               reset(0);
469                     }
470           }
471           gotcha = (c == 0 && ibuf == NULL);
472           if (ibuf != NULL) {
473                     while ((c = getc(ibuf)) != EOF)
474                               putc(c, obuf);
475                     Fclose(ibuf);
476           }
477           fflush(obuf);
478           if (ferror(obuf)) {
479                     warn("%s", mailname);
480                     relsesigs();
481                     reset(0);
482           }
483           Fclose(obuf);
484           if (gotcha) {
485                     rm(mailname);
486                     printf("removed\n");
487           } else
488                     printf("complete\n");
489           fflush(stdout);
490 
491 done:
492           relsesigs();
493 }
494