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