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.2 (Berkeley) 4/10/86"; 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 = 0; 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: struct winsize win; 400: if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) 401: Lpp = win.ws_row; 402: if (Lpp <= 0) { 403: if (tgetent(inbuf, getenv("TERM")) <= 0 404: || (Lpp = tgetnum("li")) <= 0) { 405: Lpp = NLINES; 406: } 407: } 408: } 409: #endif 410: Lpp -= 6; /* for headers, etc. */ 411: 412: already = NO; 413: prevmsg = firstmsg; 414: printing = YES; 415: if (ruptible) 416: signal(SIGINT, onintr); 417: 418: /* 419: * Main program loop 420: */ 421: for (msg = firstmsg; msg <= lastmsg; msg++) { 422: 423: sprintf(fname, "%s/%d", USRMSGS, msg); 424: newmsg = fopen(fname, "r"); 425: if (newmsg == NULL) 426: continue; 427: 428: gfrsub(newmsg); /* get From and Subject fields */ 429: if (locomode && !local) { 430: fclose(newmsg); 431: continue; 432: } 433: 434: if (qopt) { /* This has to be located here */ 435: printf("There are new messages.\n"); 436: exit(0); 437: } 438: 439: if (already && !hdrs) 440: putchar('\n'); 441: already = YES; 442: 443: /* 444: * Print header 445: */ 446: again: 447: if (totty) 448: signal(SIGTSTP, onsusp); 449: (void) setjmp(tstpbuf); 450: nlines = 2; 451: if (seenfrom) { 452: printf("Message %d:\nFrom %s %s", msg, from, date); 453: nlines++; 454: } 455: if (seensubj) { 456: printf("Subject: %s", subj); 457: nlines++; 458: } 459: else { 460: if (seenfrom) { 461: putchar('\n'); 462: nlines++; 463: } 464: while (nlines < 6 465: && fgets(inbuf, sizeof inbuf, newmsg) 466: && inbuf[0] != '\n') { 467: fputs(inbuf, stdout); 468: nlines++; 469: } 470: } 471: 472: lct = linecnt(newmsg); 473: if (lct) 474: printf("(%d%slines) ", lct, seensubj? " " : " more "); 475: 476: if (hdrs) { 477: printf("\n-----\n"); 478: fclose(newmsg); 479: continue; 480: } 481: 482: /* 483: * Ask user for command 484: */ 485: if (totty) 486: ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT)); 487: else 488: inbuf[0] = 'y'; 489: if (totty) 490: signal(SIGTSTP, SIG_DFL); 491: cmnd: 492: in = inbuf; 493: switch (*in) { 494: case 'x': 495: case 'X': 496: exit(0); 497: 498: case 'q': 499: case 'Q': 500: quitit = YES; 501: printf("--Postponed--\n"); 502: exit(0); 503: /* intentional fall-thru */ 504: case 'n': 505: case 'N': 506: if (msg >= nextmsg) sep = "Flushed"; 507: prevmsg = msg; 508: break; 509: 510: case 'p': 511: case 'P': 512: pause = (*in++ == 'p'); 513: /* intentional fallthru */ 514: case '\n': 515: case 'y': 516: default: 517: if (*in == '-') { 518: msg = prevmsg-1; 519: sep = "replay"; 520: break; 521: } 522: if (isdigit(*in)) { 523: msg = next(in); 524: sep = in; 525: break; 526: } 527: 528: prmesg(nlines + lct + (seensubj? 1 : 0)); 529: prevmsg = msg; 530: 531: } 532: 533: printf("--%s--\n", sep); 534: sep = "-"; 535: if (msg >= nextmsg) { 536: nextmsg = msg + 1; 537: fseek(msgsrc, 0L, 0); 538: fprintf(msgsrc, "%d\n", nextmsg); 539: fflush(msgsrc); 540: } 541: if (newmsg) 542: fclose(newmsg); 543: if (quitit) 544: break; 545: } 546: 547: /* 548: * Make sure .rc file gets updated 549: */ 550: if (--msg >= nextmsg) { 551: nextmsg = msg + 1; 552: fseek(msgsrc, 0L, 0); 553: fprintf(msgsrc, "%d\n", nextmsg); 554: fflush(msgsrc); 555: } 556: if (already && !quitit && lastcmd && totty) { 557: /* 558: * save or reply to last message? 559: */ 560: msg = prevmsg; 561: ask(NOMORE); 562: if (inbuf[0] == '-' || isdigit(inbuf[0])) 563: goto cmnd; 564: } 565: if (!(already || hush || qopt)) 566: printf("No new messages.\n"); 567: exit(0); 568: } 569: 570: prmesg(length) 571: int length; 572: { 573: FILE *outf, *inf; 574: int c; 575: 576: if (pause && length > Lpp) { 577: signal(SIGPIPE, SIG_IGN); 578: signal(SIGQUIT, SIG_IGN); 579: sprintf(cmdbuf, PAGE, Lpp); 580: outf = popen(cmdbuf, "w"); 581: if (!outf) 582: outf = stdout; 583: else 584: setbuf(outf, NULL); 585: } 586: else 587: outf = stdout; 588: 589: if (seensubj) 590: putc('\n', outf); 591: 592: while (fgets(inbuf, sizeof inbuf, newmsg)) { 593: fputs(inbuf, outf); 594: if (ferror(outf)) { 595: clearerr(outf); 596: break; 597: } 598: } 599: 600: if (outf != stdout) { 601: pclose(outf); 602: signal(SIGPIPE, SIG_DFL); 603: signal(SIGQUIT, SIG_DFL); 604: } 605: else { 606: fflush(stdout); 607: } 608: 609: /* trick to force wait on output */ 610: stty(fileno(stdout), &otty); 611: } 612: 613: onintr() 614: { 615: signal(SIGINT, onintr); 616: if (mailing) 617: unlink(fname); 618: if (sending) { 619: unlink(fname); 620: puts("--Killed--"); 621: exit(1); 622: } 623: if (printing) { 624: putchar('\n'); 625: if (hdrs) 626: exit(0); 627: sep = "Interrupt"; 628: if (newmsg) 629: fseek(newmsg, 0L, 2); 630: intrpflg = YES; 631: } 632: } 633: 634: /* 635: * We have just gotten a susp. Suspend and prepare to resume. 636: */ 637: onsusp() 638: { 639: 640: signal(SIGTSTP, SIG_DFL); 641: sigsetmask(0); 642: kill(0, SIGTSTP); 643: signal(SIGTSTP, onsusp); 644: if (!mailing) 645: longjmp(tstpbuf); 646: } 647: 648: linecnt(f) 649: FILE *f; 650: { 651: off_t oldpos = ftell(f); 652: int l = 0; 653: char lbuf[BUFSIZ]; 654: 655: while (fgets(lbuf, sizeof lbuf, f)) 656: l++; 657: clearerr(f); 658: fseek(f, oldpos, 0); 659: return (l); 660: } 661: 662: next(buf) 663: char *buf; 664: { 665: int i; 666: sscanf(buf, "%d", &i); 667: sprintf(buf, "Goto %d", i); 668: return(--i); 669: } 670: 671: ask(prompt) 672: char *prompt; 673: { 674: char inch; 675: int n, cmsg; 676: off_t oldpos; 677: FILE *cpfrom, *cpto; 678: 679: printf("%s ", prompt); 680: fflush(stdout); 681: intrpflg = NO; 682: gets(inbuf); 683: if (intrpflg) 684: inbuf[0] = 'x'; 685: 686: /* 687: * Handle 'mail' and 'save' here. 688: */ 689: if ((inch = inbuf[0]) == 's' || inch == 'm') { 690: if (inbuf[1] == '-') 691: cmsg = prevmsg; 692: else if (isdigit(inbuf[1])) 693: cmsg = atoi(&inbuf[1]); 694: else 695: cmsg = msg; 696: sprintf(fname, "%s/%d", USRMSGS, cmsg); 697: 698: oldpos = ftell(newmsg); 699: 700: cpfrom = fopen(fname, "r"); 701: if (!cpfrom) { 702: printf("Message %d not found\n", cmsg); 703: ask (prompt); 704: return; 705: } 706: 707: if (inch == 's') { 708: in = nxtfld(inbuf); 709: if (*in) { 710: for (n=0; in[n] > ' '; n++) { /* sizeof fname? */ 711: fname[n] = in[n]; 712: } 713: fname[n] = NULL; 714: } 715: else 716: strcpy(fname, "Messages"); 717: } 718: else { 719: strcpy(fname, TEMP); 720: mktemp(fname); 721: sprintf(cmdbuf, MAIL, fname); 722: mailing = YES; 723: } 724: cpto = fopen(fname, "a"); 725: if (!cpto) { 726: perror(fname); 727: mailing = NO; 728: fseek(newmsg, oldpos, 0); 729: ask(prompt); 730: return; 731: } 732: 733: while (n = fread(inbuf, 1, sizeof inbuf, cpfrom)) 734: fwrite(inbuf, 1, n, cpto); 735: 736: fclose(cpfrom); 737: fclose(cpto); 738: fseek(newmsg, oldpos, 0); /* reposition current message */ 739: if (inch == 's') 740: printf("Message %d saved in \"%s\"\n", cmsg, fname); 741: else { 742: system(cmdbuf); 743: unlink(fname); 744: mailing = NO; 745: } 746: ask(prompt); 747: } 748: } 749: 750: gfrsub(infile) 751: FILE *infile; 752: { 753: off_t frompos; 754: 755: seensubj = seenfrom = NO; 756: local = YES; 757: subj[0] = from[0] = date[0] = NULL; 758: 759: /* 760: * Is this a normal message? 761: */ 762: if (fgets(inbuf, sizeof inbuf, infile)) { 763: if (strncmp(inbuf, "From", 4)==0) { 764: /* 765: * expected form starts with From 766: */ 767: seenfrom = YES; 768: frompos = ftell(infile); 769: ptr = from; 770: in = nxtfld(inbuf); 771: if (*in) while (*in && *in > ' ') { 772: if (*in == ':' || *in == '@' || *in == '!') 773: local = NO; 774: *ptr++ = *in++; 775: /* what about sizeof from ? */ 776: } 777: *ptr = NULL; 778: if (*(in = nxtfld(in))) 779: strncpy(date, in, sizeof date); 780: else { 781: date[0] = '\n'; 782: date[1] = NULL; 783: } 784: } 785: else { 786: /* 787: * not the expected form 788: */ 789: fseek(infile, 0L, 0); 790: return; 791: } 792: } 793: else 794: /* 795: * empty file ? 796: */ 797: return; 798: 799: /* 800: * look for Subject line until EOF or a blank line 801: */ 802: while (fgets(inbuf, sizeof inbuf, infile) 803: && !(blankline = (inbuf[0] == '\n'))) { 804: /* 805: * extract Subject line 806: */ 807: if (!seensubj && strncmp(inbuf, "Subj", 4)==0) { 808: seensubj = YES; 809: frompos = ftell(infile); 810: strncpy(subj, nxtfld(inbuf), sizeof subj); 811: } 812: } 813: if (!blankline) 814: /* 815: * ran into EOF 816: */ 817: fseek(infile, frompos, 0); 818: 819: if (!seensubj) 820: /* 821: * for possible use with Mail 822: */ 823: strncpy(subj, "(No Subject)\n", sizeof subj); 824: } 825: 826: char * 827: nxtfld(s) 828: char *s; 829: { 830: if (*s) while (*s && *s > ' ') s++; /* skip over this field */ 831: if (*s) while (*s && *s <= ' ') s++; /* find start of next field */ 832: return (s); 833: }