xref: /original-bsd/libexec/bugfiler/unixtomh.c (revision e59fb703)
1 #ifndef lint
2 static char sccsid[] = "@(#)unixtomh.c	5.1 86/11/25";
3 #endif not lint
4 
5 /*
6  * This program copies the mail file in standard unix format
7  * given as $1 to the file $2 in Rand Message Handler format.
8  * The change made is to bracket each message with a line
9  * containing 4 control-A's and to split the From line into
10  * a From: field and a Date: field, with the date in Arpanet
11  * standard format.
12  *
13  * This program is designed to be called from the rand mh program
14  * ``inc''
15  *
16  * Set SENDMAIL if you are running sendmail -- this guarantees that
17  * From: and Date: lines will appear already, and will put the info
18  * in the UNIX-From line into a Received-From: field.
19  */
20 
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/timeb.h>
24 #include <ctype.h>
25 
26 #define SENDMAIL
27 
28 struct headline {
29 	char	*l_from;	/* The name of the sender */
30 	char	*l_tty;		/* His tty string (if any) */
31 	char	*l_date;	/* The entire date string */
32 };
33 
34 char *savestr(), *copyin(), *copy(), *nextword(), *calloc();
35 char *index();
36 
37 #define	NOSTR		((char *) 0)
38 #define	UUCP			/* Undo strange uucp naming */
39 
40 main(argc, argv)
41 	char **argv;
42 {
43 	char linebuf[BUFSIZ];
44 	register int maybe;
45 	register FILE *inf, *outf;
46 	int inhdr, infld;
47 
48 	if (argc > 3) {
49 		fprintf(stderr, "Usage: unixtomh name1 name2\n");
50 		exit(1);
51 	}
52 	outf = inf = NULL;
53 	if (argc < 3)
54 		outf = stdout;
55 	if (argc < 2)
56 		inf = stdin;
57 	if (inf == NULL && (inf = fopen(argv[1], "r")) == NULL) {
58 		perror(argv[1]);
59 		exit(1);
60 	}
61 	if (outf == NULL && (outf = fopen(argv[2], "w")) == NULL) {
62 		perror(argv[2]);
63 		exit(1);
64 	}
65 	maybe = 1;
66 	inhdr = 0;
67 	infld = 0;
68 	while (nullgets(linebuf, BUFSIZ, inf) > 0) {
69 		if (maybe && ishead(linebuf)) {
70 			fputs("\1\1\1\1\n", outf);
71 			inhdr++;
72 			dohead(linebuf, inf, outf);
73 			continue;
74 		}
75 		if (strlen(linebuf) == 0) {
76 			maybe = 1;
77 			inhdr = 0;
78 			infld = 0;
79 			putc('\n', outf);
80 			continue;
81 		}
82 		else
83 			maybe = 0;
84 #ifndef SENDMAIL
85 		if (inhdr && strcmpn(linebuf, "Date: ", 6) == 0)
86 			continue;
87 		if (inhdr && strcmpn(linebuf, "From: ", 6) == 0)
88 			continue;
89 #endif SENDMAIL
90 		if (infld && isspace(linebuf[0])) {
91 			fputs(linebuf, outf);
92 			putc('\n', outf);
93 			continue;
94 		}
95 		if (inhdr && !isspace(linebuf[0])) {
96 			char *colp, *sp;
97 
98 			colp = index(linebuf, ':');
99 			sp = index(linebuf, ' ');
100 			if (colp == NOSTR || sp == NOSTR || sp < colp) {
101 				putc('\n', outf);
102 				inhdr = 0;
103 			}
104 			else
105 				infld = 1;
106 		}
107 		fputs(linebuf, outf);
108 		putc('\n', outf);
109 	}
110 	fputs("\1\1\1\1\n", outf);
111 	fflush(outf);
112 	if (ferror(outf)) {
113 		fprintf(stderr, "unixtomh: write: ");
114 		perror(argv[2]);
115 		exit(1);
116 	}
117 	exit(0);
118 }
119 
120 /*
121  * Get a line from the given file descriptor, don't return the
122  * terminating newline.
123  */
124 
125 nullgets(linebuf, sz, file)
126 	char linebuf[];
127 	register FILE *file;
128 {
129 	register char *cp;
130 	register int c, cnt;
131 
132 	cp = linebuf;
133 	cnt = sz;
134 	do {
135 		if (--cnt <= 0) {
136 			*cp = 0;
137 			return(1);
138 		}
139 		c = getc(file);
140 		*cp++ = c;
141 	} while (c != EOF && c != '\n');
142 	if (c == EOF && cp == linebuf+1)
143 		return(0);
144 	*--cp = 0;
145 	return(1);
146 }
147 
148 /*
149  * Output the fields extracted from the From line --
150  * From: and Date:  Untangle UUCP stuff if appropriate.
151  */
152 
153 dohead(line, infile, outfile)
154 	char line[];
155 	register FILE *infile, *outfile;
156 {
157 	register char *cp;
158 	struct headline hl;
159 	char parbuf[BUFSIZ];
160 #ifdef UUCP
161 	char *word();
162 	char namebuf[BUFSIZ];
163 	char linebuf[BUFSIZ];
164 	int first;
165 	long curoff;
166 #endif UUCP
167 
168 	parse(line, &hl, parbuf);
169 #ifndef SENDMAIL
170 	putdate(hl.l_date, outfile);
171 #endif SENDMAIL
172 #ifdef UUCP
173 	if (strcmp(hl.l_from, "uucp") == 0) {
174 		strcpy(namebuf, "");
175 		first = 1;
176 		for (;;) {
177 			curoff = ftell(infile);
178 			if (fgets(linebuf, BUFSIZ, infile) == NULL)
179 				break;
180 			if (strcmp(word(1, linebuf), ">From") != 0)
181 				break;
182 			if (strcmp(word(-3, linebuf), "remote") != 0)
183 				break;
184 			if (strcmp(word(-2, linebuf), "from") != 0)
185 				break;
186 			if (first) {
187 				strcpy(namebuf, word(-1, linebuf));
188 				strcat(namebuf, "!");
189 				strcat(namebuf, word(2, linebuf));
190 				first = 0;
191 			}
192 			else {
193 				strcpy(rindex(namebuf, '!')+1,
194 				    word(-1, linebuf));
195 				strcat(namebuf, "!");
196 				strcat(namebuf, word(2, linebuf));
197 			}
198 		}
199 		fseek(infile, curoff, 0);
200 #ifdef SENDMAIL
201 		if (!first)
202 			fprintf(outfile, "Return-Path: <%s>\n", namebuf);
203 #else SENDMAIL
204 		if (first)
205 			fprintf(outfile, "From: uucp\n");
206 		else
207 			fprintf(outfile, "From: %s\n", namebuf);
208 #endif SENDMAIL
209 		return;
210 	}
211 #endif UUCP
212 #ifdef SENDMAIL
213 	if (hl.l_from[0] == '<')
214 		fprintf(outfile, "Return-Path: %s\n", hl.l_from);
215 	else
216 		fprintf(outfile, "Return-Path: <%s>\n", hl.l_from);
217 #else SENDMAIL
218 	fprintf(outfile, "From: %s\n", hl.l_from);
219 #endif SENDMAIL
220 }
221 
222 #ifdef UUCP
223 
224 /*
225  * Return liberal word i from the given string.
226  * The words are numbered 1, 2, 3, . . .  from the left
227  * and -1, -2, . . . from the right.
228  */
229 
230 char *
231 word(index, str)
232 	char str[];
233 {
234 	register char *cp;
235 	char *secbuf;
236 	register int c;
237 	static char retbuf[100];
238 	char *gword();
239 
240 	cp = str;
241 	if ((c = index) > 0) {
242 		while (c-- > 0)
243 			cp = gword(cp, retbuf);
244 		return(retbuf);
245 	}
246 	if (c == 0)
247 		return("");
248 	secbuf = (char *) alloca(strlen(str) + 1);
249 	strcpy(secbuf, str);
250 	rev(secbuf);
251 	cp = word(-index, secbuf);
252 	rev(cp);
253 	return(cp);
254 }
255 
256 /*
257  * Skip leading blanks in the string, return
258  * first liberal word collected.
259  */
260 
261 char *
262 gword(cp, buf)
263 	register char *cp;
264 	char buf[];
265 {
266 	register char *cp2;
267 
268 	cp2 = buf;
269 	while (*cp && any(*cp, " \t\n"))
270 		cp++;
271 	while (*cp && !any(*cp, " \t\n"))
272 		*cp2++ = *cp++;
273 	*cp2 = 0;
274 	return(cp);
275 }
276 
277 /*
278  * Reverse the characters in the string in place
279  */
280 
281 rev(str)
282 	char str[];
283 {
284 	register char *cpl, *cpr;
285 	register int s;
286 
287 	s = strlen(str);
288 	cpl = str;
289 	cpr = &str[s-1];
290 	while (cpl < cpr) {
291 		s = *cpl;
292 		*cpl++ = *cpr;
293 		*cpr-- = s;
294 	}
295 }
296 #endif UUCP
297 
298 /*
299  * Save a string in dynamic space.
300  * This little goodie is needed for
301  * a headline detector in head.c
302  */
303 
304 char *
305 savestr(str)
306 	char str[];
307 {
308 	register char *top;
309 
310 	top = calloc(strlen(str) + 1, 1);
311 	if (top == NOSTR) {
312 		fprintf(stderr, "unixtomh:  Ran out of memory\n");
313 		exit(1);
314 	}
315 	copy(str, top);
316 	return(top);
317 }
318 
319 /*
320  * See if the passed line buffer is a mail header.
321  * Return true if yes.  Note the extreme pains to
322  * accomodate all funny formats.
323  */
324 
325 ishead(linebuf)
326 	char linebuf[];
327 {
328 	register char *cp;
329 	struct headline hl;
330 	char parbuf[BUFSIZ];
331 
332 	cp = linebuf;
333 	if (!isname("From ", cp, 5))
334 		return(0);
335 	parse(cp, &hl, parbuf);
336 	if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
337 		fail(linebuf, "No from or date field");
338 		return(0);
339 	}
340 	if (!isdate(hl.l_date)) {
341 		fail(linebuf, "Date field not legal date");
342 		return(0);
343 	}
344 
345 	/*
346 	 * I guess we got it!
347 	 */
348 
349 	return(1);
350 }
351 
352 fail(linebuf, reason)
353 	char linebuf[], reason[];
354 {
355 	return;
356 }
357 
358 /*
359  * Split a headline into its useful components.
360  * Copy the line into dynamic string space, then set
361  * pointers into the copied line in the passed headline
362  * structure.  Actually, it scans.
363  */
364 
365 parse(line, hl, pbuf)
366 	char line[], pbuf[];
367 	struct headline *hl;
368 {
369 	register char *cp, *dp;
370 	char *sp;
371 	char word[BUFSIZ];
372 
373 	hl->l_from = NOSTR;
374 	hl->l_tty = NOSTR;
375 	hl->l_date = NOSTR;
376 	cp = line;
377 	sp = pbuf;
378 
379 	/*
380 	 * Skip the first "word" of the line, which should be "From"
381 	 * anyway.
382 	 */
383 
384 	cp = nextword(cp, word);
385 	dp = nextword(cp, word);
386 	if (word[0] != 0)
387 		hl->l_from = copyin(word, &sp);
388 	if (isname(dp, "tty", 3)) {
389 		cp = nextword(dp, word);
390 		hl->l_tty = copyin(word, &sp);
391 		if (cp != NOSTR)
392 			hl->l_date = copyin(cp, &sp);
393 	}
394 	else
395 		if (dp != NOSTR)
396 			hl->l_date = copyin(dp, &sp);
397 }
398 
399 /*
400  * Copy the string on the left into the string on the right
401  * and bump the right (reference) string pointer by the length.
402  * Thus, dynamically allocate space in the right string, copying
403  * the left string into it.
404  */
405 
406 char *
407 copyin(src, space)
408 	char src[];
409 	char **space;
410 {
411 	register char *cp, *top;
412 	register int s;
413 
414 	s = strlen(src);
415 	cp = *space;
416 	top = cp;
417 	strcpy(cp, src);
418 	cp += s + 1;
419 	*space = cp;
420 	return(top);
421 }
422 
423 /*
424  * See if the two passed strings agree in the first n characters.
425  * Return true if they do, gnu.
426  */
427 
428 isname(as1, as2, acount)
429 	char *as1, *as2;
430 {
431 	register char *s1, *s2;
432 	register count;
433 
434 	s1 = as1;
435 	s2 = as2;
436 	count = acount;
437 	if (count > 0)
438 		do
439 			if (*s1++ != *s2++)
440 				return(0);
441 		while (--count);
442 	return(1);
443 }
444 
445 /*
446  * Test to see if the passed string is a ctime(3) generated
447  * date string as documented in the manual.  The template
448  * below is used as the criterion of correctness.
449  * Also, we check for a possible trailing time zone using
450  * the auxtype template.
451  */
452 
453 #define	L	1		/* A lower case char */
454 #define	S	2		/* A space */
455 #define	D	3		/* A digit */
456 #define	O	4		/* An optional digit or space */
457 #define	C	5		/* A colon */
458 #define	N	6		/* A new line */
459 #define U	7		/* An upper case char */
460 
461 char ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0};
462 char tmztypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0};
463 
464 isdate(date)
465 	char date[];
466 {
467 	register char *cp;
468 
469 	cp = date;
470 	if (cmatch(cp, ctypes))
471 		return(1);
472 	return(cmatch(cp, tmztypes));
473 }
474 
475 /*
476  * Match the given string against the given template.
477  * Return 1 if they match, 0 if they don't
478  */
479 
480 cmatch(str, temp)
481 	char str[], temp[];
482 {
483 	register char *cp, *tp;
484 	register int c;
485 
486 	cp = str;
487 	tp = temp;
488 	while (*cp != '\0' && *tp != 0) {
489 		c = *cp++;
490 		switch (*tp++) {
491 		case L:
492 			if (!islower(c))
493 				return(0);
494 			break;
495 
496 		case S:
497 			if (c != ' ')
498 				return(0);
499 			break;
500 
501 		case D:
502 			if (!isdigit(c))
503 				return(0);
504 			break;
505 
506 		case O:
507 			if (c != ' ' && !isdigit(c))
508 				return(0);
509 			break;
510 
511 		case C:
512 			if (c != ':')
513 				return(0);
514 			break;
515 
516 		case N:
517 			if (c != '\n')
518 				return(0);
519 			break;
520 
521 		case U:
522 			if (!isupper(c))
523 				return(0);
524 			break;
525 		}
526 	}
527 	if (*cp != '\0' || *tp != 0)
528 		return(0);
529 	return(1);
530 }
531 
532 /*
533  * Collect a liberal (space, tab delimited) word into the word buffer
534  * passed.  Also, return a pointer to the next word following that,
535  * or NOSTR if none follow.
536  */
537 
538 char *
539 nextword(wp, wbuf)
540 	char wp[], wbuf[];
541 {
542 	register char *cp, *cp2;
543 
544 	if ((cp = wp) == NOSTR) {
545 		copy("", wbuf);
546 		return(NOSTR);
547 	}
548 	cp2 = wbuf;
549 	while (!any(*cp, " \t") && *cp != '\0')
550 		if (*cp == '"') {
551  			*cp2++ = *cp++;
552  			while (*cp != '\0' && *cp != '"')
553  				*cp2++ = *cp++;
554  			if (*cp == '"')
555  				*cp2++ = *cp++;
556  		} else
557  			*cp2++ = *cp++;
558 	*cp2 = '\0';
559 	while (any(*cp, " \t"))
560 		cp++;
561 	if (*cp == '\0')
562 		return(NOSTR);
563 	return(cp);
564 }
565 
566 /*
567  * Copy str1 to str2, return pointer to null in str2.
568  */
569 
570 char *
571 copy(str1, str2)
572 	char *str1, *str2;
573 {
574 	register char *s1, *s2;
575 
576 	s1 = str1;
577 	s2 = str2;
578 	while (*s1)
579 		*s2++ = *s1++;
580 	*s2 = 0;
581 	return(s2);
582 }
583 
584 /*
585  * Is ch any of the characters in str?
586  */
587 
588 any(ch, str)
589 	char *str;
590 {
591 	register char *f;
592 	register c;
593 
594 	f = str;
595 	c = ch;
596 	while (*f)
597 		if (c == *f++)
598 			return(1);
599 	return(0);
600 }
601 
602 /*
603  * Convert lower case letters to upper case.
604  */
605 
606 raise(c)
607 	register int c;
608 {
609 	if (c >= 'a' && c <= 'z')
610 		c += 'A' - 'a';
611 	return(c);
612 }
613