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