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