1 /* $OpenBSD: quit.c,v 1.21 2014/11/24 20:01:43 millert Exp $ */ 2 /* $NetBSD: quit.c,v 1.6 1996/12/28 07:11:07 tls Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include "rcv.h" 34 #include <fcntl.h> 35 #include "extern.h" 36 37 /* 38 * Rcv -- receive mail rationally. 39 * 40 * Termination processing. 41 */ 42 43 /* 44 * The "quit" command. 45 */ 46 int 47 quitcmd(void *v) 48 { 49 /* 50 * If we are sourcing, then return 1 so execute() can handle it. 51 * Otherwise, return -1 to abort command loop. 52 */ 53 if (sourcing) 54 return(1); 55 return(-1); 56 } 57 58 /* 59 * Save all of the undetermined messages at the top of "mbox" 60 * Save all untouched messages back in the system mailbox. 61 * Remove the system mailbox, if none saved there. 62 */ 63 int 64 quit(void) 65 { 66 int mcount, p, modify, autohold, anystat, holdbit, nohold; 67 FILE *ibuf = NULL, *obuf, *fbuf, *rbuf, *abuf; 68 struct message *mp; 69 int c, fd; 70 struct stat minfo; 71 char *mbox, tempname[PATHSIZE]; 72 73 /* 74 * If we are read only, we can't do anything, 75 * so just return quickly. 76 */ 77 if (readonly) 78 return(0); 79 80 /* 81 * If editing (not reading system mail box), then do the work 82 * in edstop() 83 */ 84 if (edit) 85 return(edstop()); 86 87 /* 88 * See if there any messages to save in mbox. If no, we 89 * can save copying mbox to /tmp and back. 90 * 91 * Check also to see if any files need to be preserved. 92 * Delete all untouched messages to keep them out of mbox. 93 * If all the messages are to be preserved, just exit with 94 * a message. 95 */ 96 fbuf = Fopen(mailname, "r+"); 97 if (fbuf == NULL) 98 goto newmail; 99 if (flock(fileno(fbuf), LOCK_EX) == -1) { 100 warn("Unable to lock mailbox"); 101 (void)Fclose(fbuf); 102 return(-1); 103 } 104 if (!spool_lock()) { 105 (void)Fclose(fbuf); 106 return(-1); /* lockspool printed the error for us */ 107 } 108 rbuf = NULL; 109 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { 110 puts("New mail has arrived."); 111 (void)snprintf(tempname, sizeof(tempname), 112 "%s/mail.RqXXXXXXXXXX", tmpdir); 113 if ((fd = mkstemp(tempname)) == -1 || 114 (rbuf = Fdopen(fd, "w")) == NULL) 115 goto newmail; 116 #ifdef APPEND 117 fseek(fbuf, (long)mailsize, SEEK_SET); 118 while ((c = getc(fbuf)) != EOF) 119 (void)putc(c, rbuf); 120 #else 121 p = minfo.st_size - mailsize; 122 while (p-- > 0) { 123 c = getc(fbuf); 124 if (c == EOF) 125 goto newmail; 126 (void)putc(c, rbuf); 127 } 128 #endif 129 (void)Fclose(rbuf); 130 if ((rbuf = Fopen(tempname, "r")) == NULL) 131 goto newmail; 132 (void)rm(tempname); 133 } 134 135 /* 136 * Adjust the message flags in each message. 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 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { 158 if (mp->m_flag & MBOX) 159 c++; 160 if (mp->m_flag & MPRESERVE) 161 p++; 162 if (mp->m_flag & MODIFY) 163 modify++; 164 } 165 if (p == msgCount && !modify && !anystat) { 166 printf("Held %d message%s in %s\n", 167 p, p == 1 ? "" : "s", mailname); 168 (void)Fclose(fbuf); 169 spool_unlock(); 170 return(0); 171 } 172 if (c == 0) { 173 if (p != 0) { 174 writeback(rbuf); 175 (void)Fclose(fbuf); 176 spool_unlock(); 177 return(0); 178 } 179 goto cream; 180 } 181 182 /* 183 * Create another temporary file and copy user's mbox file 184 * darin. If there is no mbox, copy nothing. 185 * If he has specified "append" don't copy his mailbox, 186 * just copy saveable entries at the end. 187 */ 188 mbox = expand("&"); 189 mcount = c; 190 if (value("append") == NULL) { 191 (void)snprintf(tempname, sizeof(tempname), 192 "%s/mail.RmXXXXXXXXXX", tmpdir); 193 if ((fd = mkstemp(tempname)) == -1 || 194 (obuf = Fdopen(fd, "w")) == NULL) { 195 warn("%s", tempname); 196 (void)Fclose(fbuf); 197 spool_unlock(); 198 return(-1); 199 } 200 if ((ibuf = Fopen(tempname, "r")) == NULL) { 201 warn("%s", tempname); 202 (void)rm(tempname); 203 (void)Fclose(obuf); 204 (void)Fclose(fbuf); 205 spool_unlock(); 206 return(-1); 207 } 208 (void)rm(tempname); 209 if ((abuf = Fopen(mbox, "r")) != NULL) { 210 while ((c = getc(abuf)) != EOF) 211 (void)putc(c, obuf); 212 (void)Fclose(abuf); 213 } 214 if (ferror(obuf)) { 215 warn("%s", tempname); 216 (void)Fclose(ibuf); 217 (void)Fclose(obuf); 218 (void)Fclose(fbuf); 219 spool_unlock(); 220 return(-1); 221 } 222 (void)Fclose(obuf); 223 (void)close(creat(mbox, 0600)); 224 if ((obuf = Fopen(mbox, "r+")) == NULL) { 225 warn("%s", mbox); 226 (void)Fclose(ibuf); 227 (void)Fclose(fbuf); 228 spool_unlock(); 229 return(-1); 230 } 231 } else { 232 if ((obuf = Fopen(mbox, "a")) == NULL) { 233 warn("%s", mbox); 234 (void)Fclose(fbuf); 235 spool_unlock(); 236 return(-1); 237 } 238 fchmod(fileno(obuf), 0600); 239 } 240 for (mp = &message[0]; mp < &message[msgCount]; mp++) 241 if (mp->m_flag & MBOX) 242 if (sendmessage(mp, obuf, saveignore, NULL) < 0) { 243 warn("%s", mbox); 244 (void)Fclose(ibuf); 245 (void)Fclose(obuf); 246 (void)Fclose(fbuf); 247 spool_unlock(); 248 return(-1); 249 } 250 251 /* 252 * Copy the user's old mbox contents back 253 * to the end of the stuff we just saved. 254 * If we are appending, this is unnecessary. 255 */ 256 if (value("append") == NULL) { 257 rewind(ibuf); 258 c = getc(ibuf); 259 while (c != EOF) { 260 (void)putc(c, obuf); 261 if (ferror(obuf)) 262 break; 263 c = getc(ibuf); 264 } 265 (void)Fclose(ibuf); 266 fflush(obuf); 267 } 268 trunc(obuf); 269 if (ferror(obuf)) { 270 warn("%s", mbox); 271 (void)Fclose(obuf); 272 (void)Fclose(fbuf); 273 spool_unlock(); 274 return(-1); 275 } 276 (void)Fclose(obuf); 277 if (mcount == 1) 278 puts("Saved 1 message in mbox"); 279 else 280 printf("Saved %d messages in mbox\n", mcount); 281 282 /* 283 * Now we are ready to copy back preserved files to 284 * the system mailbox, if any were requested. 285 */ 286 if (p != 0) { 287 writeback(rbuf); 288 (void)Fclose(fbuf); 289 spool_unlock(); 290 return(0); 291 } 292 293 /* 294 * Finally, remove his /var/mail file. 295 * If new mail has arrived, copy it back. 296 */ 297 cream: 298 if (rbuf != NULL) { 299 abuf = Fopen(mailname, "r+"); 300 if (abuf == NULL) 301 goto newmail; 302 while ((c = getc(rbuf)) != EOF) 303 (void)putc(c, abuf); 304 (void)Fclose(rbuf); 305 trunc(abuf); 306 (void)Fclose(abuf); 307 alter(mailname); 308 (void)Fclose(fbuf); 309 spool_unlock(); 310 return(0); 311 } 312 demail(); 313 (void)Fclose(fbuf); 314 spool_unlock(); 315 return(0); 316 317 newmail: 318 puts("Thou hast new mail."); 319 if (fbuf != NULL) { 320 (void)Fclose(fbuf); 321 spool_unlock(); 322 } 323 return(0); 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 warn("%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 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 int 386 edstop(void) 387 { 388 int gotcha, c; 389 struct message *mp; 390 FILE *obuf, *ibuf; 391 struct stat statb; 392 char tempname[PATHSIZE]; 393 394 if (readonly) 395 return(0); 396 holdsigs(); 397 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 398 if (mp->m_flag & MNEW) { 399 mp->m_flag &= ~MNEW; 400 mp->m_flag |= MSTATUS; 401 } 402 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 403 gotcha++; 404 } 405 if (!gotcha) 406 goto done; 407 ibuf = NULL; 408 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { 409 int fd; 410 411 (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX", 412 tmpdir); 413 if ((fd = mkstemp(tempname)) == -1 || 414 (obuf = Fdopen(fd, "w")) == NULL) { 415 warn("%s", tempname); 416 if (fd != -1) 417 close(fd); 418 relsesigs(); 419 return(-1); 420 } 421 if ((ibuf = Fopen(mailname, "r")) == NULL) { 422 warn("%s", mailname); 423 (void)Fclose(obuf); 424 (void)rm(tempname); 425 relsesigs(); 426 return(-1); 427 } 428 fseek(ibuf, (long)mailsize, SEEK_SET); 429 while ((c = getc(ibuf)) != EOF) 430 (void)putc(c, obuf); 431 (void)Fclose(ibuf); 432 (void)Fclose(obuf); 433 if ((ibuf = Fopen(tempname, "r")) == NULL) { 434 warn("%s", tempname); 435 (void)rm(tempname); 436 relsesigs(); 437 return(-1); 438 } 439 (void)rm(tempname); 440 } 441 printf("\"%s\" ", mailname); 442 fflush(stdout); 443 if ((obuf = Fopen(mailname, "r+")) == NULL) { 444 warn("%s", mailname); 445 relsesigs(); 446 return(-1); 447 } 448 trunc(obuf); 449 c = 0; 450 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 451 if ((mp->m_flag & MDELETED) != 0) 452 continue; 453 c++; 454 if (sendmessage(mp, obuf, NULL, NULL) < 0) { 455 warn("%s", mailname); 456 relsesigs(); 457 return(-1); 458 } 459 } 460 gotcha = (c == 0 && ibuf == NULL); 461 if (ibuf != NULL) { 462 while ((c = getc(ibuf)) != EOF) 463 (void)putc(c, obuf); 464 (void)Fclose(ibuf); 465 } 466 fflush(obuf); 467 if (ferror(obuf)) { 468 warn("%s", mailname); 469 relsesigs(); 470 return(-1); 471 } 472 (void)Fclose(obuf); 473 if (gotcha) { 474 (void)rm(mailname); 475 puts("removed"); 476 } else 477 puts("complete"); 478 fflush(stdout); 479 480 done: 481 relsesigs(); 482 return(0); 483 } 484