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