1 /* $NetBSD: quit.c,v 1.21 2005/07/19 23:07:10 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.21 2005/07/19 23:07:10 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) 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 (void)close(creat(mbox, 0600)); 265 if ((obuf = Fopen(mbox, "r+")) == NULL) { 266 warn("%s", mbox); 267 (void)Fclose(ibuf); 268 (void)Fclose(fbuf); 269 dot_unlock(mailname); 270 return; 271 } 272 } 273 else { 274 if ((obuf = Fopen(mbox, "a")) == NULL) { 275 warn("%s", mbox); 276 (void)Fclose(fbuf); 277 dot_unlock(mailname); 278 return; 279 } 280 (void)fchmod(fileno(obuf), 0600); 281 } 282 for (mp = &message[0]; mp < &message[msgCount]; mp++) 283 if (mp->m_flag & MBOX) 284 if (sendmessage(mp, obuf, saveignore, NULL) < 0) { 285 warn("%s", mbox); 286 (void)Fclose(ibuf); 287 (void)Fclose(obuf); 288 (void)Fclose(fbuf); 289 dot_unlock(mailname); 290 return; 291 } 292 293 /* 294 * Copy the user's old mbox contents back 295 * to the end of the stuff we just saved. 296 * If we are appending, this is unnecessary. 297 */ 298 299 if (value("append") == NULL) { 300 rewind(ibuf); 301 c = getc(ibuf); 302 while (c != EOF) { 303 (void)putc(c, obuf); 304 if (ferror(obuf)) 305 break; 306 c = getc(ibuf); 307 } 308 (void)Fclose(ibuf); 309 } 310 (void)fflush(obuf); 311 if (!ferror(obuf)) 312 trunc(obuf); /* XXX or should we truncate? */ 313 if (ferror(obuf)) { 314 warn("%s", mbox); 315 (void)Fclose(obuf); 316 (void)Fclose(fbuf); 317 dot_unlock(mailname); 318 return; 319 } 320 (void)Fclose(obuf); 321 if (mcount == 1) 322 (void)printf("Saved 1 message in mbox\n"); 323 else 324 (void)printf("Saved %d messages in mbox\n", mcount); 325 326 /* 327 * Now we are ready to copy back preserved files to 328 * the system mailbox, if any were requested. 329 */ 330 331 if (p != 0) { 332 (void)writeback(rbuf); 333 (void)Fclose(fbuf); 334 dot_unlock(mailname); 335 return; 336 } 337 338 /* 339 * Finally, remove his /var/mail file. 340 * If new mail has arrived, copy it back. 341 */ 342 343 cream: 344 if (rbuf != NULL) { 345 abuf = Fopen(mailname, "r+"); 346 if (abuf == NULL) 347 goto newmail; 348 while ((c = getc(rbuf)) != EOF) 349 (void)putc(c, abuf); 350 (void)fflush(abuf); 351 if (ferror(abuf)) { 352 warn("%s", mailname); 353 (void)Fclose(abuf); 354 (void)Fclose(fbuf); 355 dot_unlock(mailname); 356 return; 357 } 358 (void)Fclose(rbuf); 359 trunc(abuf); 360 (void)Fclose(abuf); 361 alter(mailname); 362 (void)Fclose(fbuf); 363 dot_unlock(mailname); 364 return; 365 } 366 demail(); 367 (void)Fclose(fbuf); 368 dot_unlock(mailname); 369 return; 370 371 newmail: 372 (void)printf("Thou hast new mail.\n"); 373 if (fbuf != NULL) { 374 (void)Fclose(fbuf); 375 dot_unlock(mailname); 376 } 377 } 378 379 /* 380 * Preserve all the appropriate messages back in the system 381 * mailbox, and print a nice message indicated how many were 382 * saved. On any error, just return -1. Else return 0. 383 * Incorporate the any new mail that we found. 384 */ 385 int 386 writeback(FILE *res) 387 { 388 struct message *mp; 389 int p, c; 390 FILE *obuf; 391 392 p = 0; 393 if ((obuf = Fopen(mailname, "r+")) == NULL) { 394 warn("%s", mailname); 395 return(-1); 396 } 397 #ifndef APPEND 398 if (res != NULL) { 399 while ((c = getc(res)) != EOF) 400 (void)putc(c, obuf); 401 (void)fflush(obuf); 402 if (ferror(obuf)) { 403 warn("%s", mailname); 404 (void)Fclose(obuf); 405 return(-1); 406 } 407 } 408 #endif 409 for (mp = &message[0]; mp < &message[msgCount]; mp++) 410 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { 411 p++; 412 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 413 warn("%s", mailname); 414 (void)Fclose(obuf); 415 return(-1); 416 } 417 } 418 #ifdef APPEND 419 if (res != NULL) 420 while ((c = getc(res)) != EOF) 421 (void)putc(c, obuf); 422 #endif 423 (void)fflush(obuf); 424 if (!ferror(obuf)) 425 trunc(obuf); /* XXX or should we truncate? */ 426 if (ferror(obuf)) { 427 warn("%s", mailname); 428 (void)Fclose(obuf); 429 return(-1); 430 } 431 if (res != NULL) 432 (void)Fclose(res); 433 (void)Fclose(obuf); 434 alter(mailname); 435 if (p == 1) 436 (void)printf("Held 1 message in %s\n", mailname); 437 else 438 (void)printf("Held %d messages in %s\n", p, mailname); 439 return(0); 440 } 441 442 /* 443 * Terminate an editing session by attempting to write out the user's 444 * file from the temporary. Save any new stuff appended to the file. 445 */ 446 void 447 edstop(void) 448 { 449 int gotcha, c; 450 struct message *mp; 451 FILE *obuf, *ibuf, *readstat = NULL; 452 struct stat statb; 453 char tempname[PATHSIZE]; 454 int fd; 455 456 if (readonly) 457 return; 458 holdsigs(); 459 if (Tflag != NULL) { 460 if ((readstat = Fopen(Tflag, "w")) == NULL) 461 Tflag = NULL; 462 } 463 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 464 if (mp->m_flag & MNEW) { 465 mp->m_flag &= ~MNEW; 466 mp->m_flag |= MSTATUS; 467 } 468 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 469 gotcha++; 470 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { 471 char *id; 472 473 if ((id = hfield("article-id", mp)) != NULL) 474 (void)fprintf(readstat, "%s\n", id); 475 } 476 } 477 if (Tflag != NULL) 478 (void)Fclose(readstat); 479 if (!gotcha || Tflag != NULL) 480 goto done; 481 ibuf = NULL; 482 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { 483 (void)snprintf(tempname, sizeof(tempname), 484 "%s/mbox.XXXXXXXXXX", tmpdir); 485 if ((fd = mkstemp(tempname)) == -1 || 486 (obuf = Fdopen(fd, "w")) == NULL) { 487 warn("%s", tempname); 488 if (fd != -1) 489 (void)close(fd); 490 relsesigs(); 491 reset(0); 492 } 493 if ((ibuf = Fopen(mailname, "r")) == NULL) { 494 warn("%s", mailname); 495 (void)Fclose(obuf); 496 (void)rm(tempname); 497 relsesigs(); 498 reset(0); 499 } 500 (void)fseek(ibuf, (long)mailsize, 0); 501 while ((c = getc(ibuf)) != EOF) 502 (void)putc(c, obuf); 503 (void)fflush(obuf); 504 if (ferror(obuf)) { 505 warn("%s", tempname); 506 (void)Fclose(obuf); 507 (void)Fclose(ibuf); 508 (void)rm(tempname); 509 relsesigs(); 510 reset(0); 511 } 512 (void)Fclose(ibuf); 513 (void)Fclose(obuf); 514 if ((ibuf = Fopen(tempname, "r")) == NULL) { 515 warn("%s", tempname); 516 (void)rm(tempname); 517 relsesigs(); 518 reset(0); 519 } 520 (void)rm(tempname); 521 } 522 (void)printf("\"%s\" ", mailname); 523 (void)fflush(stdout); 524 if ((obuf = Fopen(mailname, "r+")) == NULL) { 525 warn("%s", mailname); 526 relsesigs(); 527 reset(0); 528 } 529 trunc(obuf); 530 c = 0; 531 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 532 if ((mp->m_flag & MDELETED) != 0) 533 continue; 534 c++; 535 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 536 warn("%s", mailname); 537 relsesigs(); 538 reset(0); 539 } 540 } 541 gotcha = (c == 0 && ibuf == NULL); 542 if (ibuf != NULL) { 543 while ((c = getc(ibuf)) != EOF) 544 (void)putc(c, obuf); 545 (void)Fclose(ibuf); 546 } 547 (void)fflush(obuf); 548 if (ferror(obuf)) { 549 warn("%s", mailname); 550 relsesigs(); 551 reset(0); 552 } 553 (void)Fclose(obuf); 554 if (gotcha) { 555 (void)rm(mailname); 556 (void)printf("removed\n"); 557 } else 558 (void)printf("complete\n"); 559 (void)fflush(stdout); 560 561 done: 562 relsesigs(); 563 } 564