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