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