1 /* $OpenBSD: quit.c,v 1.23 2016/07/19 06:43:27 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, *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 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { 158 if (mp->m_flag & MBOX) 159 c++; 160 if (mp->m_flag & MPRESERVE) 161 p++; 162 if (mp->m_flag & MODIFY) 163 modify++; 164 } 165 if (p == msgCount && !modify && !anystat) { 166 printf("Held %d message%s in %s\n", 167 p, p == 1 ? "" : "s", mailname); 168 (void)Fclose(fbuf); 169 spool_unlock(); 170 return(0); 171 } 172 if (c == 0) { 173 if (p != 0) { 174 writeback(rbuf); 175 (void)Fclose(fbuf); 176 spool_unlock(); 177 return(0); 178 } 179 goto cream; 180 } 181 182 /* 183 * Create another temporary file and copy user's mbox file 184 * darin. If there is no mbox, copy nothing. 185 * If he has specified "append" don't copy his mailbox, 186 * just copy saveable entries at the end. 187 */ 188 mbox = expand("&"); 189 mcount = c; 190 if (value("append") == NULL) { 191 int fdx; 192 193 (void)snprintf(tempname, sizeof(tempname), 194 "%s/mail.RmXXXXXXXXXX", tmpdir); 195 if ((fd = mkstemp(tempname)) == -1 || 196 (obuf = Fdopen(fd, "w")) == NULL) { 197 warn("%s", tempname); 198 (void)Fclose(fbuf); 199 spool_unlock(); 200 return(-1); 201 } 202 if ((ibuf = Fopen(tempname, "r")) == NULL) { 203 warn("%s", tempname); 204 (void)rm(tempname); 205 (void)Fclose(obuf); 206 (void)Fclose(fbuf); 207 spool_unlock(); 208 return(-1); 209 } 210 (void)rm(tempname); 211 if ((abuf = Fopen(mbox, "r")) != NULL) { 212 while ((c = getc(abuf)) != EOF) 213 (void)putc(c, obuf); 214 (void)Fclose(abuf); 215 } 216 if (ferror(obuf)) { 217 warn("%s", tempname); 218 (void)Fclose(ibuf); 219 (void)Fclose(obuf); 220 (void)Fclose(fbuf); 221 spool_unlock(); 222 return(-1); 223 } 224 (void)Fclose(obuf); 225 if ((fdx = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) 226 close(fdx); 227 if ((obuf = Fopen(mbox, "r+")) == NULL) { 228 warn("%s", mbox); 229 (void)Fclose(ibuf); 230 (void)Fclose(fbuf); 231 spool_unlock(); 232 return(-1); 233 } 234 } else { 235 if ((obuf = Fopen(mbox, "a")) == NULL) { 236 warn("%s", mbox); 237 (void)Fclose(fbuf); 238 spool_unlock(); 239 return(-1); 240 } 241 fchmod(fileno(obuf), 0600); 242 } 243 for (mp = &message[0]; mp < &message[msgCount]; mp++) 244 if (mp->m_flag & MBOX) 245 if (sendmessage(mp, obuf, saveignore, NULL) < 0) { 246 warn("%s", mbox); 247 (void)Fclose(ibuf); 248 (void)Fclose(obuf); 249 (void)Fclose(fbuf); 250 spool_unlock(); 251 return(-1); 252 } 253 254 /* 255 * Copy the user's old mbox contents back 256 * to the end of the stuff we just saved. 257 * If we are appending, this is unnecessary. 258 */ 259 if (value("append") == NULL) { 260 rewind(ibuf); 261 c = getc(ibuf); 262 while (c != EOF) { 263 (void)putc(c, obuf); 264 if (ferror(obuf)) 265 break; 266 c = getc(ibuf); 267 } 268 (void)Fclose(ibuf); 269 fflush(obuf); 270 } 271 trunc(obuf); 272 if (ferror(obuf)) { 273 warn("%s", mbox); 274 (void)Fclose(obuf); 275 (void)Fclose(fbuf); 276 spool_unlock(); 277 return(-1); 278 } 279 (void)Fclose(obuf); 280 if (mcount == 1) 281 puts("Saved 1 message in mbox"); 282 else 283 printf("Saved %d messages in mbox\n", mcount); 284 285 /* 286 * Now we are ready to copy back preserved files to 287 * the system mailbox, if any were requested. 288 */ 289 if (p != 0) { 290 writeback(rbuf); 291 (void)Fclose(fbuf); 292 spool_unlock(); 293 return(0); 294 } 295 296 /* 297 * Finally, remove his /var/mail file. 298 * If new mail has arrived, copy it back. 299 */ 300 cream: 301 if (rbuf != NULL) { 302 abuf = Fopen(mailname, "r+"); 303 if (abuf == NULL) 304 goto newmail; 305 while ((c = getc(rbuf)) != EOF) 306 (void)putc(c, abuf); 307 (void)Fclose(rbuf); 308 trunc(abuf); 309 (void)Fclose(abuf); 310 alter(mailname); 311 (void)Fclose(fbuf); 312 spool_unlock(); 313 return(0); 314 } 315 demail(); 316 (void)Fclose(fbuf); 317 spool_unlock(); 318 return(0); 319 320 newmail: 321 puts("Thou hast new mail."); 322 if (fbuf != NULL) { 323 (void)Fclose(fbuf); 324 spool_unlock(); 325 } 326 return(0); 327 } 328 329 /* 330 * Preserve all the appropriate messages back in the system 331 * mailbox, and print a nice message indicated how many were 332 * saved. On any error, just return -1. Else return 0. 333 * Incorporate the any new mail that we found. 334 */ 335 int 336 writeback(FILE *res) 337 { 338 struct message *mp; 339 int p, c; 340 FILE *obuf; 341 342 p = 0; 343 if ((obuf = Fopen(mailname, "r+")) == NULL) { 344 warn("%s", mailname); 345 return(-1); 346 } 347 #ifndef APPEND 348 if (res != NULL) 349 while ((c = getc(res)) != EOF) 350 (void)putc(c, obuf); 351 #endif 352 for (mp = &message[0]; mp < &message[msgCount]; mp++) 353 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { 354 p++; 355 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 356 warn("%s", mailname); 357 (void)Fclose(obuf); 358 return(-1); 359 } 360 } 361 #ifdef APPEND 362 if (res != NULL) 363 while ((c = getc(res)) != EOF) 364 (void)putc(c, obuf); 365 #endif 366 fflush(obuf); 367 trunc(obuf); 368 if (ferror(obuf)) { 369 warn("%s", mailname); 370 (void)Fclose(obuf); 371 return(-1); 372 } 373 if (res != NULL) 374 (void)Fclose(res); 375 (void)Fclose(obuf); 376 alter(mailname); 377 if (p == 1) 378 printf("Held 1 message in %s\n", mailname); 379 else 380 printf("Held %d messages in %s\n", p, mailname); 381 return(0); 382 } 383 384 /* 385 * Terminate an editing session by attempting to write out the user's 386 * file from the temporary. Save any new stuff appended to the file. 387 */ 388 int 389 edstop(void) 390 { 391 int gotcha, c; 392 struct message *mp; 393 FILE *obuf, *ibuf; 394 struct stat statb; 395 char tempname[PATHSIZE]; 396 397 if (readonly) 398 return(0); 399 holdsigs(); 400 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 401 if (mp->m_flag & MNEW) { 402 mp->m_flag &= ~MNEW; 403 mp->m_flag |= MSTATUS; 404 } 405 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 406 gotcha++; 407 } 408 if (!gotcha) 409 goto done; 410 ibuf = NULL; 411 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { 412 int fd; 413 414 (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX", 415 tmpdir); 416 if ((fd = mkstemp(tempname)) == -1 || 417 (obuf = Fdopen(fd, "w")) == NULL) { 418 warn("%s", tempname); 419 if (fd != -1) 420 close(fd); 421 relsesigs(); 422 return(-1); 423 } 424 if ((ibuf = Fopen(mailname, "r")) == NULL) { 425 warn("%s", mailname); 426 (void)Fclose(obuf); 427 (void)rm(tempname); 428 relsesigs(); 429 return(-1); 430 } 431 fseek(ibuf, (long)mailsize, SEEK_SET); 432 while ((c = getc(ibuf)) != EOF) 433 (void)putc(c, obuf); 434 (void)Fclose(ibuf); 435 (void)Fclose(obuf); 436 if ((ibuf = Fopen(tempname, "r")) == NULL) { 437 warn("%s", tempname); 438 (void)rm(tempname); 439 relsesigs(); 440 return(-1); 441 } 442 (void)rm(tempname); 443 } 444 printf("\"%s\" ", mailname); 445 fflush(stdout); 446 if ((obuf = Fopen(mailname, "r+")) == NULL) { 447 warn("%s", mailname); 448 relsesigs(); 449 return(-1); 450 } 451 trunc(obuf); 452 c = 0; 453 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 454 if ((mp->m_flag & MDELETED) != 0) 455 continue; 456 c++; 457 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 458 warn("%s", mailname); 459 relsesigs(); 460 return(-1); 461 } 462 } 463 gotcha = (c == 0 && ibuf == NULL); 464 if (ibuf != NULL) { 465 while ((c = getc(ibuf)) != EOF) 466 (void)putc(c, obuf); 467 (void)Fclose(ibuf); 468 } 469 fflush(obuf); 470 if (ferror(obuf)) { 471 warn("%s", mailname); 472 relsesigs(); 473 return(-1); 474 } 475 (void)Fclose(obuf); 476 if (gotcha) { 477 (void)rm(mailname); 478 puts("removed"); 479 } else 480 puts("complete"); 481 fflush(stdout); 482 483 done: 484 relsesigs(); 485 return(0); 486 } 487