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