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