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