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