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 #include "rcv.h" 39 #include <fcntl.h> 40 #include "extern.h" 41 42 /* 43 * Rcv -- receive mail rationally. 44 * 45 * Termination processing. 46 */ 47 48 /* 49 * The "quit" command. 50 */ 51 int 52 quitcmd(void *arg __unused) 53 { 54 /* 55 * If we are sourcing, then return 1 so execute() can handle it. 56 * Otherwise, return -1 to abort command loop. 57 */ 58 if (sourcing) 59 return (1); 60 return (-1); 61 } 62 63 /* 64 * Save all of the undetermined messages at the top of "mbox" 65 * Save all untouched messages back in the system mailbox. 66 * Remove the system mailbox, if none saved there. 67 */ 68 void 69 quit(void) 70 { 71 int mcount, p, modify, autohold, anystat, holdbit, nohold; 72 FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf; 73 struct message *mp; 74 int c, fd; 75 struct stat minfo; 76 char *mbox, tempname[PATHSIZE]; 77 78 /* 79 * If we are read only, we can't do anything, 80 * so just return quickly. 81 */ 82 if (readonly) 83 return; 84 /* 85 * If editing (not reading system mail box), then do the work 86 * in edstop() 87 */ 88 if (edit) { 89 edstop(); 90 return; 91 } 92 93 /* 94 * See if there any messages to save in mbox. If no, we 95 * can save copying mbox to /tmp and back. 96 * 97 * Check also to see if any files need to be preserved. 98 * Delete all untouched messages to keep them out of mbox. 99 * If all the messages are to be preserved, just exit with 100 * a message. 101 */ 102 103 fbuf = Fopen(mailname, "r"); 104 if (fbuf == NULL) 105 goto newmail; 106 (void)flock(fileno(fbuf), LOCK_EX); 107 rbuf = NULL; 108 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { 109 printf("New mail has arrived.\n"); 110 (void)snprintf(tempname, sizeof(tempname), 111 "%s/mail.RqXXXXXXXXXX", tmpdir); 112 if ((fd = mkstemp(tempname)) == -1 || 113 (rbuf = Fdopen(fd, "w")) == NULL) 114 goto newmail; 115 #ifdef APPEND 116 (void)fseeko(fbuf, mailsize, SEEK_SET); 117 while ((c = getc(fbuf)) != EOF) 118 (void)putc(c, rbuf); 119 #else 120 p = minfo.st_size - mailsize; 121 while (p-- > 0) { 122 c = getc(fbuf); 123 if (c == EOF) 124 goto newmail; 125 (void)putc(c, rbuf); 126 } 127 #endif 128 (void)Fclose(rbuf); 129 if ((rbuf = Fopen(tempname, "r")) == NULL) 130 goto newmail; 131 (void)rm(tempname); 132 } 133 134 /* 135 * Adjust the message flags in each message. 136 */ 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 return; 182 } 183 if (c == 0) { 184 if (p != 0) { 185 writeback(rbuf); 186 (void)Fclose(fbuf); 187 return; 188 } 189 goto cream; 190 } 191 192 /* 193 * Create another temporary file and copy user's mbox file 194 * darin. If there is no mbox, copy nothing. 195 * If he has specified "append" don't copy his mailbox, 196 * just copy saveable entries at the end. 197 */ 198 199 mbox = expand("&"); 200 mcount = c; 201 if (value("append") == NULL) { 202 (void)snprintf(tempname, sizeof(tempname), 203 "%s/mail.RmXXXXXXXXXX", tmpdir); 204 if ((fd = mkstemp(tempname)) == -1 || 205 (obuf = Fdopen(fd, "w")) == NULL) { 206 warn("%s", tempname); 207 (void)Fclose(fbuf); 208 return; 209 } 210 if ((ibuf = Fopen(tempname, "r")) == NULL) { 211 warn("%s", tempname); 212 (void)rm(tempname); 213 (void)Fclose(obuf); 214 (void)Fclose(fbuf); 215 return; 216 } 217 (void)rm(tempname); 218 if ((abuf = Fopen(mbox, "r")) != NULL) { 219 while ((c = getc(abuf)) != EOF) 220 (void)putc(c, obuf); 221 (void)Fclose(abuf); 222 } 223 if (ferror(obuf)) { 224 warnx("%s", tempname); 225 (void)Fclose(ibuf); 226 (void)Fclose(obuf); 227 (void)Fclose(fbuf); 228 return; 229 } 230 (void)Fclose(obuf); 231 if ((fd = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0) 232 (void)close(fd); 233 if ((obuf = Fopen(mbox, "r+")) == NULL) { 234 warn("%s", mbox); 235 (void)Fclose(ibuf); 236 (void)Fclose(fbuf); 237 return; 238 } 239 } 240 if (value("append") != NULL) { 241 if ((obuf = Fopen(mbox, "a")) == NULL) { 242 warn("%s", mbox); 243 (void)Fclose(fbuf); 244 return; 245 } 246 (void)fchmod(fileno(obuf), 0600); 247 } 248 for (mp = &message[0]; mp < &message[msgCount]; mp++) 249 if (mp->m_flag & MBOX) 250 if (sendmessage(mp, obuf, saveignore, NULL) < 0) { 251 warnx("%s", mbox); 252 (void)Fclose(ibuf); 253 (void)Fclose(obuf); 254 (void)Fclose(fbuf); 255 return; 256 } 257 258 /* 259 * Copy the user's old mbox contents back 260 * to the end of the stuff we just saved. 261 * If we are appending, this is unnecessary. 262 */ 263 264 if (value("append") == NULL) { 265 rewind(ibuf); 266 c = getc(ibuf); 267 while (c != EOF) { 268 (void)putc(c, obuf); 269 if (ferror(obuf)) 270 break; 271 c = getc(ibuf); 272 } 273 (void)Fclose(ibuf); 274 } 275 (void)fflush(obuf); 276 trunc(obuf); 277 if (ferror(obuf)) { 278 warn("%s", mbox); 279 (void)Fclose(obuf); 280 (void)Fclose(fbuf); 281 return; 282 } 283 (void)Fclose(obuf); 284 if (mcount == 1) 285 printf("Saved 1 message in mbox\n"); 286 else 287 printf("Saved %d messages in mbox\n", mcount); 288 289 /* 290 * Now we are ready to copy back preserved files to 291 * the system mailbox, if any were requested. 292 */ 293 294 if (p != 0) { 295 writeback(rbuf); 296 (void)Fclose(fbuf); 297 return; 298 } 299 300 /* 301 * Finally, remove his /var/mail file. 302 * If new mail has arrived, copy it back. 303 */ 304 305 cream: 306 if (rbuf != NULL) { 307 abuf = Fopen(mailname, "r+"); 308 if (abuf == NULL) 309 goto newmail; 310 while ((c = getc(rbuf)) != EOF) 311 (void)putc(c, abuf); 312 (void)Fclose(rbuf); 313 trunc(abuf); 314 (void)Fclose(abuf); 315 alter(mailname); 316 (void)Fclose(fbuf); 317 return; 318 } 319 demail(); 320 (void)Fclose(fbuf); 321 return; 322 323 newmail: 324 printf("Thou hast new mail.\n"); 325 if (fbuf != NULL) 326 (void)Fclose(fbuf); 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 warnx("%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 (void)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 void 389 edstop(void) 390 { 391 int gotcha, c; 392 struct message *mp; 393 FILE *obuf, *ibuf, *readstat; 394 struct stat statb; 395 char tempname[PATHSIZE]; 396 397 if (readonly) 398 return; 399 holdsigs(); 400 if (Tflag != NULL) { 401 if ((readstat = Fopen(Tflag, "w")) == NULL) 402 Tflag = NULL; 403 } 404 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 405 if (mp->m_flag & MNEW) { 406 mp->m_flag &= ~MNEW; 407 mp->m_flag |= MSTATUS; 408 } 409 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 410 gotcha++; 411 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { 412 char *id; 413 414 if ((id = hfield("article-id", mp)) != NULL) 415 fprintf(readstat, "%s\n", id); 416 } 417 } 418 if (Tflag != NULL) 419 (void)Fclose(readstat); 420 if (!gotcha || Tflag != NULL) 421 goto done; 422 ibuf = NULL; 423 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { 424 int fd; 425 426 (void)snprintf(tempname, sizeof(tempname), 427 "%s/mbox.XXXXXXXXXX", tmpdir); 428 if ((fd = mkstemp(tempname)) == -1 || 429 (obuf = Fdopen(fd, "w")) == NULL) { 430 warn("%s", tempname); 431 relsesigs(); 432 reset(0); 433 } 434 if ((ibuf = Fopen(mailname, "r")) == NULL) { 435 warn("%s", mailname); 436 (void)Fclose(obuf); 437 (void)rm(tempname); 438 relsesigs(); 439 reset(0); 440 } 441 (void)fseeko(ibuf, mailsize, SEEK_SET); 442 while ((c = getc(ibuf)) != EOF) 443 (void)putc(c, obuf); 444 (void)Fclose(ibuf); 445 (void)Fclose(obuf); 446 if ((ibuf = Fopen(tempname, "r")) == NULL) { 447 warn("%s", tempname); 448 (void)rm(tempname); 449 relsesigs(); 450 reset(0); 451 } 452 (void)rm(tempname); 453 } 454 printf("\"%s\" ", mailname); 455 (void)fflush(stdout); 456 if ((obuf = Fopen(mailname, "r+")) == NULL) { 457 warn("%s", mailname); 458 relsesigs(); 459 reset(0); 460 } 461 trunc(obuf); 462 c = 0; 463 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 464 if ((mp->m_flag & MDELETED) != 0) 465 continue; 466 c++; 467 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 468 warnx("%s", mailname); 469 relsesigs(); 470 reset(0); 471 } 472 } 473 gotcha = (c == 0 && ibuf == NULL); 474 if (ibuf != NULL) { 475 while ((c = getc(ibuf)) != EOF) 476 (void)putc(c, obuf); 477 (void)Fclose(ibuf); 478 } 479 (void)fflush(obuf); 480 if (ferror(obuf)) { 481 warn("%s", mailname); 482 relsesigs(); 483 reset(0); 484 } 485 (void)Fclose(obuf); 486 if (gotcha) { 487 (void)rm(mailname); 488 printf("removed\n"); 489 } else 490 printf("complete\n"); 491 (void)fflush(stdout); 492 493 done: 494 relsesigs(); 495 } 496