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