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