1: /*
2: * recnews [to newsgroup] [from user]
3: *
4: * Process a news article which has been mailed to some group like msgs.
5: * Such articles are in normal mail format and have never seen the insides
6: * of netnews. If the "to newsgroup" is included, the article is posted
7: * to this newsgroup instead of trying to intuit it from the headers.
8: * If the "from user" is included, the return address is forged to look
9: * like that user instead of what getuid or a from line says.
10: *
11: * It is recommended that you always include the to newsgroup, since the
12: * intuition code is flakey and out of date. The from user is probably
13: * appropriate for arpanet mailing lists being funnelled at ucbvax but
14: * not otherwise. Sample lines in /usr/lib/aliases (if you run delivermail):
15: * worldnews: "|/usr/lib/news/recnews net.general"
16: * Allows you to mail to worldnews rather than using inews.
17: * Intended for humans to mail to.
18: * post-unix-wizards: "|/usr/lib/news/recnews fa.unix-wizards unix-wizards"
19: * Causes mail to post-unix-wizards to be fed into fa.unix-wizards
20: * and the return address forged as unix-wizards on the local
21: * machine. post-unix-wizards (on the local machine) should
22: * be part of the master mailing list somewhere (on a different
23: * machine.)
24: *
25: * Recnews is primarily useful in remote places on the usenet which collect
26: * mail from mailing lists and funnel them into the network. It is also
27: * useful if you like to send mail to some user instead of invoking
28: * inews -t .. -n .. when you want to submit an article. (Many mailers give
29: * you nice facilities like editing the message.) It is not, however,
30: * essential to use recnews to be able to join usenet.
31: *
32: * WARNING: recnews disables the "recording" check - it has to because
33: * by the time inews is run, it's in the background and too late to
34: * ask permission. If you depend heavily on recordings you probably
35: * should not allow recnews (and thus the mail interface) to be used.
36: */
37:
38: #ifdef SCCSID
39: static char *SccsId = "@(#)recnews.c 2.11 3/19/86";
40: #endif /* SCCSID */
41:
42: #include "defs.h"
43:
44: #include <stdio.h>
45: #include <ctype.h>
46:
47: /*
48: * Note: we assume there are 2 kinds of hosts using recnews:
49: * Those that have delivermail (and hence this program will never
50: * have to deal with more than one message at a time) and those on the arpanet
51: * that do not (and hence all messages end with a sentinel). It is
52: * supposed that regular v7 type systems without delivermail or some
53: * other automatic forwarding device will just use rnews. We do
54: * not attempt to tell where a message ends on all systems due to the
55: * different conventions in effect. (This COULD be fixed, I suppose.)
56: */
57:
58: /*
59: * Kinds of lines in a message.
60: */
61: #define FROM 001 /* From line */
62: #define SUBJ 002 /* Subject */
63: #define TO 003 /* To (newgroup based on this) */
64: #define BLANK 004 /* blank line */
65: #define EOM 005 /* End of message (4 ctrl A's) */
66: #define 006 /* any unrecognized header */
67: #define TEXT 007 /* anything unrecognized */
68: #define INCLUSIVE 010 /* newsgroup is already in header */
69:
70: /*
71: * Possible states program can be in.
72: */
73: #define SKIPPING 0100 /* In header of message */
74: #define READING 0200 /* In body of message */
75:
76: #define BFSZ 250
77:
78: #define EOT '\004'
79:
80: char from[BFSZ]; /* mailing address for replies */
81: char sender[BFSZ]; /* mailing address of author, if different */
82: char to[BFSZ]; /* Destination of mail (msgs, etc) */
83: char subject[BFSZ]; /* subject of message */
84: char newsgroup[BFSZ]; /* newsgroups of message */
85: char cmdbuf[BFSZ]; /* command to popen */
86:
87: extern char *strcat(), *strcpy();
88: extern FILE *popen();
89: char *any();
90:
91: main(argc, argv)
92: int argc;
93: char **argv;
94: {
95: char buf[BFSZ], inews[BFSZ];
96: register char *p, *q;
97: register FILE *pipe = NULL;
98: register int state;
99:
100: /* build inews command */
101: #ifdef IHCC
102: sprintf(inews, "%s/%s/%s", logdir(HOME), LIBDIR, "inews");
103: #else
104: sprintf(inews, "%s/%s", LIBDIR, "inews");
105: #endif
106:
107: if (argc > 1)
108: strcpy(to, argv[1]);
109: if (argc > 2)
110: strcpy(from, argv[2]);
111: #ifdef debug
112: printf("argv[0] is <%s>, argv[1] is <%s>, argv[2] is <%s>\n",
113: argv[0], argv[1], argv[2]);
114: #endif
115: state = SKIPPING;
116: while (fgets(buf, BFSZ, stdin) != NULL) {
117: if (state == READING) {
118: fputs(buf,pipe);
119: continue;
120: }
121: switch (type(buf)) {
122:
123: case FROM:
124: frombreak(buf, from);
125: break;
126:
127: case SUBJ:
128: p = any(buf, " \t");
129: if (p == NULL)
130: p = buf + 8;
131: q = subject;
132: while (*++p) {
133: if (*p == '"')
134: *q++ = '\\';
135: *q++ = *p;
136: }
137: q[-1] = '\0';
138: break;
139:
140: case TO:
141: if (to[0])
142: break; /* already have one */
143: p = any(buf, " \t");
144: if (p == NULL)
145: p = buf + 3;
146: q = to;
147: while (*++p) {
148: if (*p == '"')
149: *q++ = '\\';
150: *q++ = *p;
151: }
152: q[-1] = '\0';
153: break;
154:
155: case INCLUSIVE:
156: sprintf(cmdbuf,"exec %s -p", inews);
157: pipe = popen(cmdbuf,"w");
158: if (pipe == NULL){
159: perror("recnews: open failed");
160: exit(1);
161: }
162: state = READING;
163: fputs(buf,pipe);
164: break;
165:
166: /*
167: * Kludge to compensate for messages without real headers
168: */
169: case HEADER:
170: break;
171:
172: case BLANK:
173: state = READING;
174: findgroup(to, newsgroup);
175: sprintf(cmdbuf, "exec %s -t \"%s\" -n \"%s\" -f \"%s\"",
176: inews, *subject ? subject : "(none)",
177: newsgroup, from);
178: #ifdef debug
179: pipe = stdout;
180: printf("BLANK: %s\n", cmdbuf);
181: #else
182: pipe = popen(cmdbuf, "w");
183: if (pipe == NULL) {
184: perror("recnews: popen failed");
185: exit(1);
186: }
187: #endif
188: if (sender[0]) {
189: fputs(sender, pipe);
190: putc('\n', pipe);
191: }
192: break;
193:
194: case TEXT:
195: findgroup(to, newsgroup);
196: state = READING;
197: if (subject[0] == 0) {
198: strcpy(subject, buf);
199: if (subject[strlen(subject)-1] == '\n')
200: subject[strlen(subject)-1] = '\0';
201: }
202: sprintf(cmdbuf, "exec \"%s\" -t \"%s\" -n \"%s\" -f \"%s\"",
203: inews, subject, newsgroup, from);
204: #ifdef debug
205: pipe = stdout;
206: printf("TEXT: %s\n", cmdbuf);
207: #else
208: pipe = popen(cmdbuf, "w");
209: if (pipe == NULL) {
210: perror("pipe failed");
211: exit(1);
212: }
213: #endif
214: if (sender[0]){
215: fputs(sender, pipe);
216: putc('\n',pipe);
217: }
218: break;
219: }
220: }
221: exit(0);
222: }
223:
224: type(p)
225: register char *p;
226: {
227: char *firstbl;
228: static char lasthdr; /* prev line was a header */
229:
230: lasthdr = 1;
231: if ((*p == ' ' || *p == '\t') && lasthdr)
232: return HEADER; /* continuation line */
233: firstbl = any(p, " \t");
234: while (*p == ' ' || *p == '?' || *p == '\t')
235: ++p;
236:
237: if (*p == '\n' || *p == 0)
238: return BLANK;
239: if (strncmp(p, ">From", 5) == 0 || strncmp(p, "From", 4) == 0)
240: return FROM;
241: if (strncmp(p, "Subj", 4)==0 || strncmp(p, "Re:", 3)==0 ||
242: strncmp(p, "re:", 3)==0)
243: return SUBJ;
244: if (strncmp(p, "To", 2)==0)
245: return TO;
246: if (strncmp(p, "\1\1\1\1", 4)==0)
247: return EOM;
248: if (firstbl && firstbl[-1] == ':' && isalpha(*p))
249: return HEADER;
250: lasthdr = 0;
251: return TEXT;
252: }
253:
254: /*
255: * Figure out who a message is from.
256: */
257: frombreak(buf, fbuf)
258: register char *buf, *fbuf;
259: {
260: char wordfrom[BFSZ], uname[BFSZ], at[BFSZ], site[BFSZ];
261:
262: if (fbuf[0]) { /* we already know who it's from */
263: if (sender[0] == 0 || buf[4] == ':') {
264: #ifdef debug
265: printf("sender set to: %s", buf);
266: #endif
267: strcpy(sender, buf);
268: }
269: return;
270: }
271: /* break the line into tokens. */
272: sscanf(buf, "%s %s %s %s", wordfrom, uname, at, site);
273: if (isat(at))
274: /*
275: * Some arpanet mail comes from "joe at mit-dms"
276: * instead of "joe@mit-dms", so handle it here.
277: */
278: sprintf(fbuf, "%s@%s", uname, site);
279: else
280: strcpy(fbuf, uname);
281: }
282:
283: isat(str)
284: char *str;
285: {
286: if (strcmp(str, "@")==0) return TRUE;
287: if (strcmp(str, "at")==0) return TRUE;
288: if (strcmp(str, "AT")==0) return TRUE;
289: return FALSE;
290: }
291:
292: findgroup(dest, group)
293: char *dest ;
294: char *group;
295: {
296: #ifdef debug
297: printf("findgroup(%s)\n", dest);
298: #endif
299: #ifdef fussy
300: /*
301: * Default unknown "to" fields to "general". This gives you
302: * tight control over which newsgroups exist.
303: */
304: if (strcmp(dest, "msgs")==0)
305: strcpy(group, "msgs");
306: else if (strcmp(dest, "allmsgs")==0)
307: strcpy(group, "NET.allmsgs");
308: else if (strcmp(dest, "csmsgs")==0)
309: strcpy(group, "NET.csmsgs");
310: else
311: strcpy(group, "general");
312: #else
313: /*
314: * Allow any newsgroup. This way you don't have to recompile
315: * recnews every time you add a newsgroup.
316: */
317: strcpy(group, dest);
318: #endif
319: }
320:
321: /*
322: * Return the ptr in sp at which a character in sq appears;
323: * NULL if not found
324: *
325: */
326:
327: char *
328: any(sp, sq)
329: char *sp, *sq;
330: {
331: register c1, c2;
332: register char *q;
333:
334: while (c1 = *sp++) {
335: q = sq;
336: while (c2 = *q++)
337: if (c1 == c2)
338: return(--sp);
339: }
340: return(NULL);
341: }