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