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 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved. 34 * @(#)msgs.c 8.2 (Berkeley) 4/28/95 35 * $FreeBSD: src/usr.bin/msgs/msgs.c,v 1.15.2.2 2003/02/11 21:31:56 mike Exp $ 36 * $DragonFly: src/usr.bin/msgs/msgs.c,v 1.6 2005/08/09 20:05:55 joerg Exp $ 37 */ 38 39 /* 40 * msgs - a user bulletin board program 41 * 42 * usage: 43 * msgs [fhlopq] [[-]number] to read messages 44 * msgs -s to place messages 45 * msgs -c [-days] to clean up the bulletin board 46 * 47 * prompt commands are: 48 * y print message 49 * n flush message, go to next message 50 * q flush message, quit 51 * p print message, turn on 'pipe thru more' mode 52 * P print message, turn off 'pipe thru more' mode 53 * - reprint last message 54 * s[-][<num>] [<filename>] save message 55 * m[-][<num>] mail with message in temp mbox 56 * x exit without flushing this message 57 * <num> print message number <num> 58 */ 59 60 #define V7 /* will look for TERM in the environment */ 61 #define OBJECT /* will object to messages without Subjects */ 62 /* #define REJECT */ /* will reject messages without Subjects 63 (OBJECT must be defined also) */ 64 /* #define UNBUFFERED *//* use unbuffered output */ 65 66 #include <sys/param.h> 67 #include <sys/stat.h> 68 #include <ctype.h> 69 #include <dirent.h> 70 #include <err.h> 71 #include <errno.h> 72 #include <fcntl.h> 73 #include <locale.h> 74 #include <pwd.h> 75 #include <setjmp.h> 76 #include <termcap.h> 77 #include <termios.h> 78 #include <signal.h> 79 #include <stdio.h> 80 #include <stdlib.h> 81 #include <string.h> 82 #include <time.h> 83 #include <unistd.h> 84 #include "pathnames.h" 85 86 #define CMODE 0644 /* bounds file creation mode */ 87 #define NO 0 88 #define YES 1 89 #define SUPERUSER 0 /* superuser uid */ 90 #define DAEMON 1 /* daemon uid */ 91 #define NLINES 24 /* default number of lines/crt screen */ 92 #define NDAYS 21 /* default keep time for messages */ 93 #define DAYS *24*60*60 /* seconds/day */ 94 #define MSGSRC ".msgsrc" /* user's rc file */ 95 #define BOUNDS "bounds" /* message bounds file */ 96 #define NEXT "Next message? [yq]" 97 #define MORE "More? [ynq]" 98 #define NOMORE "(No more) [q] ?" 99 100 typedef char bool; 101 102 FILE *msgsrc; 103 FILE *newmsg; 104 const char *sep = "-"; 105 char inbuf[BUFSIZ]; 106 char fname[MAXPATHLEN]; 107 char cmdbuf[MAXPATHLEN + MAXPATHLEN]; 108 char subj[128]; 109 char from[128]; 110 char date[128]; 111 char *ptr; 112 char *in; 113 bool local; 114 bool ruptible; 115 bool totty; 116 bool seenfrom; 117 bool seensubj; 118 bool blankline; 119 bool printing = NO; 120 bool mailing = NO; 121 bool quitit = NO; 122 bool sending = NO; 123 bool intrpflg = NO; 124 int uid; 125 int msg; 126 int prevmsg; 127 int lct; 128 int nlines; 129 int Lpp = 0; 130 time_t t; 131 time_t keep; 132 133 /* option initialization */ 134 bool hdrs = NO; 135 bool qopt = NO; 136 bool hush = NO; 137 bool send_msg = NO; 138 bool locomode = NO; 139 bool use_pager = NO; 140 bool clean = NO; 141 bool lastcmd = NO; 142 jmp_buf tstpbuf; 143 144 145 void ask(const char *); 146 void gfrsub(FILE *); 147 int linecnt(FILE *); 148 int next(char *); 149 char *nxtfld(unsigned char *); 150 void onsusp(int); 151 void onintr(int); 152 void prmesg(int); 153 static void usage(void); 154 155 int 156 main(int argc, char **argv) 157 { 158 bool newrc, already; 159 int rcfirst = 0; /* first message to print (from .rc) */ 160 int rcback = 0; /* amount to back off of rcfirst */ 161 int firstmsg = 0, nextmsg = 0, lastmsg = 0; 162 int blast = 0; 163 struct stat buf; /* stat to check access of bounds */ 164 FILE *bounds; 165 166 #ifdef UNBUFFERED 167 setbuf(stdout, NULL); 168 #endif 169 setlocale(LC_ALL, ""); 170 171 time(&t); 172 setuid(uid = getuid()); 173 ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL); 174 if (ruptible) 175 signal(SIGINT, SIG_DFL); 176 177 argc--, argv++; 178 while (argc > 0) { 179 if (isdigit(argv[0][0])) { /* starting message # */ 180 rcfirst = atoi(argv[0]); 181 } 182 else if (isdigit(argv[0][1])) { /* backward offset */ 183 rcback = atoi( &( argv[0][1] ) ); 184 } 185 else { 186 ptr = *argv; 187 while (*ptr) switch (*ptr++) { 188 189 case '-': 190 break; 191 192 case 'c': 193 if (uid != SUPERUSER && uid != DAEMON) { 194 fprintf(stderr, "Sorry\n"); 195 exit(1); 196 } 197 clean = YES; 198 break; 199 200 case 'f': /* silently */ 201 hush = YES; 202 break; 203 204 case 'h': /* headers only */ 205 hdrs = YES; 206 break; 207 208 case 'l': /* local msgs only */ 209 locomode = YES; 210 break; 211 212 case 'o': /* option to save last message */ 213 lastcmd = YES; 214 break; 215 216 case 'p': /* pipe thru 'more' during long msgs */ 217 use_pager = YES; 218 break; 219 220 case 'q': /* query only */ 221 qopt = YES; 222 break; 223 224 case 's': /* sending TO msgs */ 225 send_msg = YES; 226 break; 227 228 default: 229 usage(); 230 } 231 } 232 argc--, argv++; 233 } 234 235 /* 236 * determine current message bounds 237 */ 238 snprintf(fname, sizeof(fname), "%s/%s", _PATH_MSGS, BOUNDS); 239 240 /* 241 * Test access rights to the bounds file 242 * This can be a little tricky. if(send_msg), then 243 * we will create it. We assume that if(send_msg), 244 * then you have write permission there. 245 * Else, it better be there, or we bail. 246 */ 247 if (send_msg != YES) { 248 if (stat(fname, &buf) < 0) { 249 if (hush != YES) { 250 err(errno, "%s", fname); 251 } else { 252 exit(1); 253 } 254 } 255 } 256 bounds = fopen(fname, "r"); 257 258 if (bounds != NULL) { 259 fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg); 260 fclose(bounds); 261 blast = lastmsg; /* save upper bound */ 262 } 263 264 if (clean) 265 keep = t - (rcback? rcback : NDAYS) DAYS; 266 267 if (clean || bounds == NULL) { /* relocate message bounds */ 268 struct dirent *dp; 269 struct stat stbuf; 270 bool seenany = NO; 271 DIR *dirp; 272 273 dirp = opendir(_PATH_MSGS); 274 if (dirp == NULL) 275 err(errno, "%s", _PATH_MSGS); 276 277 firstmsg = 32767; 278 lastmsg = 0; 279 280 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){ 281 char *cp = dp->d_name; 282 int i = 0; 283 284 if (dp->d_ino == 0) 285 continue; 286 287 if (clean) 288 snprintf(inbuf, sizeof(inbuf), "%s/%s", _PATH_MSGS, cp); 289 290 while (isdigit(*cp)) 291 i = i * 10 + *cp++ - '0'; 292 if (*cp) 293 continue; /* not a message! */ 294 295 if (clean) { 296 if (stat(inbuf, &stbuf) != 0) 297 continue; 298 if (stbuf.st_mtime < keep 299 && stbuf.st_mode&S_IWRITE) { 300 unlink(inbuf); 301 continue; 302 } 303 } 304 305 if (i > lastmsg) 306 lastmsg = i; 307 if (i < firstmsg) 308 firstmsg = i; 309 seenany = YES; 310 } 311 closedir(dirp); 312 313 if (!seenany) { 314 if (blast != 0) /* never lower the upper bound! */ 315 lastmsg = blast; 316 firstmsg = lastmsg + 1; 317 } 318 else if (blast > lastmsg) 319 lastmsg = blast; 320 321 if (!send_msg) { 322 bounds = fopen(fname, "w"); 323 if (bounds == NULL) 324 err(errno, "%s", fname); 325 chmod(fname, CMODE); 326 fprintf(bounds, "%d %d\n", firstmsg, lastmsg); 327 fclose(bounds); 328 } 329 } 330 331 if (send_msg) { 332 /* 333 * Send mode - place msgs in _PATH_MSGS 334 */ 335 bounds = fopen(fname, "w"); 336 if (bounds == NULL) 337 err(errno, "%s", fname); 338 339 nextmsg = lastmsg + 1; 340 snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, nextmsg); 341 newmsg = fopen(fname, "w"); 342 if (newmsg == NULL) 343 err(errno, "%s", fname); 344 chmod(fname, CMODE); 345 346 fprintf(bounds, "%d %d\n", firstmsg, nextmsg); 347 fclose(bounds); 348 349 sending = YES; 350 if (ruptible) 351 signal(SIGINT, onintr); 352 353 if (isatty(fileno(stdin))) { 354 ptr = getpwuid(uid)->pw_name; 355 printf("Message %d:\nFrom %s %sSubject: ", 356 nextmsg, ptr, ctime(&t)); 357 fflush(stdout); 358 fgets(inbuf, sizeof inbuf, stdin); 359 putchar('\n'); 360 fflush(stdout); 361 fprintf(newmsg, "From %s %sSubject: %s\n", 362 ptr, ctime(&t), inbuf); 363 blankline = seensubj = YES; 364 } 365 else 366 blankline = seensubj = NO; 367 for (;;) { 368 fgets(inbuf, sizeof inbuf, stdin); 369 if (feof(stdin) || ferror(stdin)) 370 break; 371 blankline = (blankline || (inbuf[0] == '\n')); 372 seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0))); 373 fputs(inbuf, newmsg); 374 } 375 #ifdef OBJECT 376 if (!seensubj) { 377 printf("NOTICE: Messages should have a Subject field!\n"); 378 #ifdef REJECT 379 unlink(fname); 380 #endif 381 exit(1); 382 } 383 #endif 384 exit(ferror(stdin)); 385 } 386 if (clean) 387 exit(0); 388 389 /* 390 * prepare to display messages 391 */ 392 totty = (isatty(fileno(stdout)) != 0); 393 use_pager = use_pager && totty; 394 395 snprintf(fname, sizeof(fname), "%s/%s", getenv("HOME"), MSGSRC); 396 msgsrc = fopen(fname, "r"); 397 if (msgsrc) { 398 newrc = NO; 399 fscanf(msgsrc, "%d\n", &nextmsg); 400 fclose(msgsrc); 401 if (nextmsg > lastmsg+1) { 402 printf("Warning: bounds have been reset (%d, %d)\n", 403 firstmsg, lastmsg); 404 truncate(fname, (off_t)0); 405 newrc = YES; 406 } 407 else if (!rcfirst) 408 rcfirst = nextmsg - rcback; 409 } 410 else 411 newrc = YES; 412 msgsrc = fopen(fname, "r+"); 413 if (msgsrc == NULL) 414 msgsrc = fopen(fname, "w"); 415 if (msgsrc == NULL) 416 err(errno, "%s", fname); 417 if (rcfirst) { 418 if (rcfirst > lastmsg+1) { 419 printf("Warning: the last message is number %d.\n", 420 lastmsg); 421 rcfirst = nextmsg; 422 } 423 if (rcfirst > firstmsg) 424 firstmsg = rcfirst; /* don't set below first msg */ 425 } 426 if (newrc) { 427 nextmsg = firstmsg; 428 fseek(msgsrc, 0L, 0); 429 fprintf(msgsrc, "%d\n", nextmsg); 430 fflush(msgsrc); 431 } 432 433 #ifdef V7 434 if (totty) { 435 struct winsize win; 436 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) 437 Lpp = win.ws_row; 438 if (Lpp <= 0) { 439 if (tgetent(inbuf, getenv("TERM")) <= 0 440 || (Lpp = tgetnum("li")) <= 0) { 441 Lpp = NLINES; 442 } 443 } 444 } 445 #endif 446 Lpp -= 6; /* for headers, etc. */ 447 448 already = NO; 449 prevmsg = firstmsg; 450 printing = YES; 451 if (ruptible) 452 signal(SIGINT, onintr); 453 454 /* 455 * Main program loop 456 */ 457 for (msg = firstmsg; msg <= lastmsg; msg++) { 458 459 snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, msg); 460 newmsg = fopen(fname, "r"); 461 if (newmsg == NULL) 462 continue; 463 464 gfrsub(newmsg); /* get From and Subject fields */ 465 if (locomode && !local) { 466 fclose(newmsg); 467 continue; 468 } 469 470 if (qopt) { /* This has to be located here */ 471 printf("There are new messages.\n"); 472 exit(0); 473 } 474 475 if (already && !hdrs) 476 putchar('\n'); 477 478 /* 479 * Print header 480 */ 481 if (totty) 482 signal(SIGTSTP, onsusp); 483 (void) setjmp(tstpbuf); 484 already = YES; 485 nlines = 2; 486 if (seenfrom) { 487 printf("Message %d:\nFrom %s %s", msg, from, date); 488 nlines++; 489 } 490 if (seensubj) { 491 printf("Subject: %s", subj); 492 nlines++; 493 } 494 else { 495 if (seenfrom) { 496 putchar('\n'); 497 nlines++; 498 } 499 while (nlines < 6 500 && fgets(inbuf, sizeof inbuf, newmsg) 501 && inbuf[0] != '\n') { 502 fputs(inbuf, stdout); 503 nlines++; 504 } 505 } 506 507 lct = linecnt(newmsg); 508 if (lct) 509 printf("(%d%sline%s) ", lct, seensubj? " " : " more ", 510 (lct == 1) ? "" : "s"); 511 512 if (hdrs) { 513 printf("\n-----\n"); 514 fclose(newmsg); 515 continue; 516 } 517 518 /* 519 * Ask user for command 520 */ 521 if (totty) 522 ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT)); 523 else 524 inbuf[0] = 'y'; 525 if (totty) 526 signal(SIGTSTP, SIG_DFL); 527 cmnd: 528 in = inbuf; 529 switch (*in) { 530 case 'x': 531 case 'X': 532 exit(0); 533 534 case 'q': 535 case 'Q': 536 quitit = YES; 537 printf("--Postponed--\n"); 538 exit(0); 539 /* intentional fall-thru */ 540 case 'n': 541 case 'N': 542 if (msg >= nextmsg) sep = "Flushed"; 543 prevmsg = msg; 544 break; 545 546 case 'p': 547 case 'P': 548 use_pager = (*in++ == 'p'); 549 /* intentional fallthru */ 550 case '\n': 551 case 'y': 552 default: 553 if (*in == '-') { 554 msg = prevmsg-1; 555 sep = "replay"; 556 break; 557 } 558 if (isdigit(*in)) { 559 msg = next(in); 560 sep = in; 561 break; 562 } 563 564 prmesg(nlines + lct + (seensubj? 1 : 0)); 565 prevmsg = msg; 566 567 } 568 569 printf("--%s--\n", sep); 570 sep = "-"; 571 if (msg >= nextmsg) { 572 nextmsg = msg + 1; 573 fseek(msgsrc, 0L, 0); 574 fprintf(msgsrc, "%d\n", nextmsg); 575 fflush(msgsrc); 576 } 577 if (newmsg) 578 fclose(newmsg); 579 if (quitit) 580 break; 581 } 582 583 /* 584 * Make sure .rc file gets updated 585 */ 586 if (--msg >= nextmsg) { 587 nextmsg = msg + 1; 588 fseek(msgsrc, 0L, 0); 589 fprintf(msgsrc, "%d\n", nextmsg); 590 fflush(msgsrc); 591 } 592 if (already && !quitit && lastcmd && totty) { 593 /* 594 * save or reply to last message? 595 */ 596 msg = prevmsg; 597 ask(NOMORE); 598 if (inbuf[0] == '-' || isdigit(inbuf[0])) 599 goto cmnd; 600 } 601 if (!(already || hush || qopt)) 602 printf("No new messages.\n"); 603 exit(0); 604 } 605 606 static void 607 usage(void) 608 { 609 fprintf(stderr, "usage: msgs [fhlopq] [[-]number]\n"); 610 exit(1); 611 } 612 613 void 614 prmesg(int length) 615 { 616 FILE *outf; 617 char *env_pager; 618 619 if (use_pager && length > Lpp) { 620 signal(SIGPIPE, SIG_IGN); 621 signal(SIGQUIT, SIG_IGN); 622 if ((env_pager = getenv("PAGER")) == NULL) { 623 snprintf(cmdbuf, sizeof(cmdbuf), _PATH_PAGER, Lpp); 624 } else { 625 snprintf(cmdbuf, sizeof(cmdbuf), "%s", env_pager); 626 } 627 outf = popen(cmdbuf, "w"); 628 if (!outf) 629 outf = stdout; 630 else 631 setbuf(outf, (char *)NULL); 632 } 633 else 634 outf = stdout; 635 636 if (seensubj) 637 putc('\n', outf); 638 639 while (fgets(inbuf, sizeof inbuf, newmsg)) { 640 fputs(inbuf, outf); 641 if (ferror(outf)) { 642 clearerr(outf); 643 break; 644 } 645 } 646 647 if (outf != stdout) { 648 pclose(outf); 649 signal(SIGPIPE, SIG_DFL); 650 signal(SIGQUIT, SIG_DFL); 651 } 652 else { 653 fflush(stdout); 654 } 655 656 /* force wait on output */ 657 tcdrain(fileno(stdout)); 658 } 659 660 void 661 onintr(int unused __unused) 662 { 663 signal(SIGINT, onintr); 664 if (mailing) 665 unlink(fname); 666 if (sending) { 667 unlink(fname); 668 puts("--Killed--"); 669 exit(1); 670 } 671 if (printing) { 672 putchar('\n'); 673 if (hdrs) 674 exit(0); 675 sep = "Interrupt"; 676 if (newmsg) 677 fseek(newmsg, 0L, 2); 678 intrpflg = YES; 679 } 680 } 681 682 /* 683 * We have just gotten a susp. Suspend and prepare to resume. 684 */ 685 void 686 onsusp(int unused __unused) 687 { 688 signal(SIGTSTP, SIG_DFL); 689 sigsetmask(0); 690 kill(0, SIGTSTP); 691 signal(SIGTSTP, onsusp); 692 if (!mailing) 693 longjmp(tstpbuf, 0); 694 } 695 696 int 697 linecnt(FILE *f) 698 { 699 off_t oldpos = ftell(f); 700 int l = 0; 701 char lbuf[BUFSIZ]; 702 703 while (fgets(lbuf, sizeof lbuf, f)) 704 l++; 705 clearerr(f); 706 fseek(f, oldpos, 0); 707 return (l); 708 } 709 710 int 711 next(char *buf) 712 { 713 int i; 714 sscanf(buf, "%d", &i); 715 sprintf(buf, "Goto %d", i); 716 return(--i); 717 } 718 719 void 720 ask(const char *prompt) 721 { 722 char inch; 723 int n, cmsg, fd; 724 off_t oldpos; 725 FILE *cpfrom, *cpto; 726 727 printf("%s ", prompt); 728 fflush(stdout); 729 intrpflg = NO; 730 (void) fgets(inbuf, sizeof inbuf, stdin); 731 if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n') 732 inbuf[n - 1] = '\0'; 733 if (intrpflg) 734 inbuf[0] = 'x'; 735 736 /* 737 * Handle 'mail' and 'save' here. 738 */ 739 if ((inch = inbuf[0]) == 's' || inch == 'm') { 740 if (inbuf[1] == '-') 741 cmsg = prevmsg; 742 else if (isdigit(inbuf[1])) 743 cmsg = atoi(&inbuf[1]); 744 else 745 cmsg = msg; 746 snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, cmsg); 747 748 oldpos = ftell(newmsg); 749 750 cpfrom = fopen(fname, "r"); 751 if (!cpfrom) { 752 printf("Message %d not found\n", cmsg); 753 ask (prompt); 754 return; 755 } 756 757 if (inch == 's') { 758 in = nxtfld(inbuf); 759 if (*in) { 760 for (n=0; in[n] > ' '; n++) { /* sizeof fname? */ 761 fname[n] = in[n]; 762 } 763 fname[n] = NULL; 764 } 765 else 766 strcpy(fname, "Messages"); 767 fd = open(fname, O_RDWR|O_EXCL|O_CREAT|O_APPEND); 768 } 769 else { 770 strcpy(fname, _PATH_TMP); 771 fd = mkstemp(fname); 772 if (fd != -1) { 773 snprintf(cmdbuf, sizeof(cmdbuf), _PATH_MAIL, 774 fname); 775 mailing = YES; 776 } 777 } 778 if (fd == -1 || (cpto = fdopen(fd, "a")) == NULL) { 779 if (fd != -1) 780 close(fd); 781 warn("%s", fname); 782 mailing = NO; 783 fseek(newmsg, oldpos, 0); 784 ask(prompt); 785 return; 786 } 787 788 while ((n = fread(inbuf, 1, sizeof inbuf, cpfrom))) 789 fwrite(inbuf, 1, n, cpto); 790 791 fclose(cpfrom); 792 fclose(cpto); 793 fseek(newmsg, oldpos, 0); /* reposition current message */ 794 if (inch == 's') 795 printf("Message %d saved in \"%s\"\n", cmsg, fname); 796 else { 797 system(cmdbuf); 798 unlink(fname); 799 mailing = NO; 800 } 801 ask(prompt); 802 } 803 } 804 805 void 806 gfrsub(FILE *infile) 807 { 808 off_t frompos; 809 int count; 810 811 seensubj = seenfrom = NO; 812 local = YES; 813 subj[0] = from[0] = date[0] = NULL; 814 815 /* 816 * Is this a normal message? 817 */ 818 if (fgets(inbuf, sizeof inbuf, infile)) { 819 if (strncmp(inbuf, "From", 4)==0) { 820 /* 821 * expected form starts with From 822 */ 823 seenfrom = YES; 824 frompos = ftell(infile); 825 ptr = from; 826 in = nxtfld(inbuf); 827 if (*in) { 828 count = sizeof(from) - 1; 829 while (*in && *in > ' ' && count-- > 0) { 830 if (*in == ':' || *in == '@' || 831 *in == '!') 832 local = NO; 833 *ptr++ = *in++; 834 } 835 } 836 *ptr = NULL; 837 if (*(in = nxtfld(in))) 838 strncpy(date, in, sizeof date); 839 else { 840 date[0] = '\n'; 841 date[1] = NULL; 842 } 843 } 844 else { 845 /* 846 * not the expected form 847 */ 848 fseek(infile, 0L, 0); 849 return; 850 } 851 } 852 else 853 /* 854 * empty file ? 855 */ 856 return; 857 858 /* 859 * look for Subject line until EOF or a blank line 860 */ 861 while (fgets(inbuf, sizeof inbuf, infile) 862 && !(blankline = (inbuf[0] == '\n'))) { 863 /* 864 * extract Subject line 865 */ 866 if (!seensubj && strncmp(inbuf, "Subj", 4)==0) { 867 seensubj = YES; 868 frompos = ftell(infile); 869 strncpy(subj, nxtfld(inbuf), sizeof subj); 870 } 871 } 872 if (!blankline) 873 /* 874 * ran into EOF 875 */ 876 fseek(infile, frompos, 0); 877 878 if (!seensubj) 879 /* 880 * for possible use with Mail 881 */ 882 strncpy(subj, "(No Subject)\n", sizeof subj); 883 } 884 885 char * 886 nxtfld(unsigned char *s) 887 { 888 if (*s) while (*s && !isspace(*s)) s++; /* skip over this field */ 889 if (*s) while (*s && isspace(*s)) s++; /* find start of next field */ 890 return (s); 891 } 892