Commit | Line | Data |
---|---|---|
ea7815ad KB |
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 | } |