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