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