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