1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)quit.c 8.2 (Berkeley) 4/28/95 34 * $FreeBSD: src/usr.bin/mail/quit.c,v 1.2.6.3 2003/01/06 05:46:03 mikeh Exp $ 35 * $DragonFly: src/usr.bin/mail/quit.c,v 1.5 2004/09/08 03:01:11 joerg Exp $ 36 */ 37 38 #include "rcv.h" 39 #include <fcntl.h> 40 #include "extern.h" 41 42 static void edstop(void); 43 static int writeback(FILE *res); 44 45 /* 46 * Rcv -- receive mail rationally. 47 * 48 * Termination processing. 49 */ 50 51 /* 52 * The "quit" command. 53 */ 54 int 55 quitcmd(void) 56 { 57 /* 58 * If we are sourcing, then return 1 so execute() can handle it. 59 * Otherwise, return -1 to abort command loop. 60 */ 61 if (sourcing) 62 return (1); 63 return (-1); 64 } 65 66 /* 67 * Save all of the undetermined messages at the top of "mbox" 68 * Save all untouched messages back in the system mailbox. 69 * Remove the system mailbox, if none saved there. 70 */ 71 void 72 quit(void) 73 { 74 int mcount, p, modify, autohold, anystat, holdbit, nohold; 75 FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf; 76 struct message *mp; 77 int c, fd; 78 struct stat minfo; 79 char *mbox, tempname[PATHSIZE]; 80 81 /* 82 * If we are read only, we can't do anything, 83 * so just return quickly. 84 */ 85 if (readonly) 86 return; 87 /* 88 * If editing (not reading system mail box), then do the work 89 * in edstop() 90 */ 91 if (edit) { 92 edstop(); 93 return; 94 } 95 96 /* 97 * See if there any messages to save in mbox. If no, we 98 * can save copying mbox to /tmp and back. 99 * 100 * Check also to see if any files need to be preserved. 101 * Delete all untouched messages to keep them out of mbox. 102 * If all the messages are to be preserved, just exit with 103 * a message. 104 */ 105 106 fbuf = Fopen(mailname, "r"); 107 if (fbuf == NULL) 108 goto newmail; 109 flock(fileno(fbuf), LOCK_EX); 110 rbuf = NULL; 111 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { 112 printf("New mail has arrived.\n"); 113 snprintf(tempname, sizeof(tempname), "%s/mail.RqXXXXXXXXXX", 114 tmpdir); 115 if ((fd = mkstemp(tempname)) == -1 || 116 (rbuf = Fdopen(fd, "w")) == NULL) 117 goto newmail; 118 #ifdef APPEND 119 fseeko(fbuf, mailsize, SEEK_SET); 120 while ((c = getc(fbuf)) != EOF) 121 putc(c, rbuf); 122 #else 123 p = minfo.st_size - mailsize; 124 while (p-- > 0) { 125 c = getc(fbuf); 126 if (c == EOF) 127 goto newmail; 128 putc(c, rbuf); 129 } 130 #endif 131 Fclose(rbuf); 132 if ((rbuf = Fopen(tempname, "r")) == NULL) 133 goto newmail; 134 rm(tempname); 135 } 136 137 /* 138 * Adjust the message flags in each message. 139 */ 140 141 anystat = 0; 142 autohold = value("hold") != NULL; 143 holdbit = autohold ? MPRESERVE : MBOX; 144 nohold = MBOX|MSAVED|MDELETED|MPRESERVE; 145 if (value("keepsave") != NULL) 146 nohold &= ~MSAVED; 147 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 148 if (mp->m_flag & MNEW) { 149 mp->m_flag &= ~MNEW; 150 mp->m_flag |= MSTATUS; 151 } 152 if (mp->m_flag & MSTATUS) 153 anystat++; 154 if ((mp->m_flag & MTOUCH) == 0) 155 mp->m_flag |= MPRESERVE; 156 if ((mp->m_flag & nohold) == 0) 157 mp->m_flag |= holdbit; 158 } 159 modify = 0; 160 if (Tflag != NULL) { 161 if ((readstat = Fopen(Tflag, "w")) == NULL) 162 Tflag = NULL; 163 } 164 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { 165 if (mp->m_flag & MBOX) 166 c++; 167 if (mp->m_flag & MPRESERVE) 168 p++; 169 if (mp->m_flag & MODIFY) 170 modify++; 171 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { 172 char *id; 173 174 if ((id = hfield("article-id", mp)) != NULL) 175 fprintf(readstat, "%s\n", id); 176 } 177 } 178 if (Tflag != NULL) 179 Fclose(readstat); 180 if (p == msgCount && !modify && !anystat) { 181 printf("Held %d message%s in %s\n", 182 p, p == 1 ? "" : "s", mailname); 183 Fclose(fbuf); 184 return; 185 } 186 if (c == 0) { 187 if (p != 0) { 188 writeback(rbuf); 189 Fclose(fbuf); 190 return; 191 } 192 goto cream; 193 } 194 195 /* 196 * Create another temporary file and copy user's mbox file 197 * darin. If there is no mbox, copy nothing. 198 * If he has specified "append" don't copy his mailbox, 199 * just copy saveable entries at the end. 200 */ 201 202 mbox = expand("&"); 203 mcount = c; 204 if (value("append") == NULL) { 205 snprintf(tempname, sizeof(tempname), "%s/mail.RmXXXXXXXXXX", 206 tmpdir); 207 if ((fd = mkstemp(tempname)) == -1 || 208 (obuf = Fdopen(fd, "w")) == NULL) { 209 warn("%s", tempname); 210 Fclose(fbuf); 211 return; 212 } 213 if ((ibuf = Fopen(tempname, "r")) == NULL) { 214 warn("%s", tempname); 215 rm(tempname); 216 Fclose(obuf); 217 Fclose(fbuf); 218 return; 219 } 220 rm(tempname); 221 if ((abuf = Fopen(mbox, "r")) != NULL) { 222 while ((c = getc(abuf)) != EOF) 223 putc(c, obuf); 224 Fclose(abuf); 225 } 226 if (ferror(obuf)) { 227 warnx("%s", tempname); 228 Fclose(ibuf); 229 Fclose(obuf); 230 Fclose(fbuf); 231 return; 232 } 233 Fclose(obuf); 234 close(open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)); 235 if ((obuf = Fopen(mbox, "r+")) == NULL) { 236 warn("%s", mbox); 237 Fclose(ibuf); 238 Fclose(fbuf); 239 return; 240 } 241 } 242 if (value("append") != NULL) { 243 if ((obuf = Fopen(mbox, "a")) == NULL) { 244 warn("%s", mbox); 245 Fclose(fbuf); 246 return; 247 } 248 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 Fclose(ibuf); 255 Fclose(obuf); 256 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 putc(c, obuf); 271 if (ferror(obuf)) 272 break; 273 c = getc(ibuf); 274 } 275 Fclose(ibuf); 276 } 277 fflush(obuf); 278 trunc(obuf); 279 if (ferror(obuf)) { 280 warn("%s", mbox); 281 Fclose(obuf); 282 Fclose(fbuf); 283 return; 284 } 285 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 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 putc(c, abuf); 314 Fclose(rbuf); 315 trunc(abuf); 316 Fclose(abuf); 317 alter(mailname); 318 Fclose(fbuf); 319 return; 320 } 321 demail(); 322 Fclose(fbuf); 323 return; 324 325 newmail: 326 printf("Thou hast new mail.\n"); 327 if (fbuf != NULL) 328 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 static 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 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 Fclose(obuf); 360 return (-1); 361 } 362 } 363 #ifdef APPEND 364 if (res != NULL) 365 while ((c = getc(res)) != EOF) 366 putc(c, obuf); 367 #endif 368 fflush(obuf); 369 trunc(obuf); 370 if (ferror(obuf)) { 371 warn("%s", mailname); 372 Fclose(obuf); 373 return (-1); 374 } 375 if (res != NULL) 376 Fclose(res); 377 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 static 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 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 snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX", 429 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 Fclose(obuf); 439 rm(tempname); 440 relsesigs(); 441 reset(0); 442 } 443 fseeko(ibuf, mailsize, SEEK_SET); 444 while ((c = getc(ibuf)) != EOF) 445 putc(c, obuf); 446 Fclose(ibuf); 447 Fclose(obuf); 448 if ((ibuf = Fopen(tempname, "r")) == NULL) { 449 warn("%s", tempname); 450 rm(tempname); 451 relsesigs(); 452 reset(0); 453 } 454 rm(tempname); 455 } 456 printf("\"%s\" ", mailname); 457 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 putc(c, obuf); 479 Fclose(ibuf); 480 } 481 fflush(obuf); 482 if (ferror(obuf)) { 483 warn("%s", mailname); 484 relsesigs(); 485 reset(0); 486 } 487 Fclose(obuf); 488 if (gotcha) { 489 rm(mailname); 490 printf("removed\n"); 491 } else 492 printf("complete\n"); 493 fflush(stdout); 494 495 done: 496 relsesigs(); 497 } 498