2 * rfc822.c -- code for slicing and dicing RFC822 mail headers
4 * Copyright 1997 by Eric S. Raymond
5 * For license terms, see the file COPYING in this directory.
7 * Modified by Hiroyuki Yamamoto <hiro-y@kcn.ne.jp>
17 /* output noise level */
18 #define O_SILENT 0 /* mute, max squelch, etc. */
19 #define O_NORMAL 1 /* user-friendly */
20 #define O_VERBOSE 2 /* chatty */
21 #define O_DEBUG 3 /* prolix */
22 #define O_MONITOR O_VERBOSE
24 static int outlevel = O_SILENT;
26 #define POPBUFSIZE 512 /* max length of response (RFC1939) */
28 #define HEADER_END(p) ((p)[0] == '\n' && ((p)[1] != ' ' && (p)[1] != '\t'))
32 char *program_name = "rfc822";
35 char *reply_hack(buf, host)
36 /* hack message headers so replies will work properly */
37 char *buf; /* header to be hacked */
38 const char *host; /* server hostname */
40 char *from, *cp, last_nws = '\0', *parens_from = NULL;
41 int parendepth, state, has_bare_name_part, has_host_part;
46 if (strncasecmp("From: ", buf, 6)
47 && strncasecmp("To: ", buf, 4)
48 && strncasecmp("Reply-To: ", buf, 10)
49 && strncasecmp("Return-Path: ", buf, 13)
50 && strncasecmp("Cc: ", buf, 4)
51 && strncasecmp("Bcc: ", buf, 5)
52 && strncasecmp("Resent-From: ", buf, 13)
53 && strncasecmp("Resent-To: ", buf, 11)
54 && strncasecmp("Resent-Cc: ", buf, 11)
55 && strncasecmp("Resent-Bcc: ", buf, 12)
56 && strncasecmp("Apparently-From:", buf, 16)
57 && strncasecmp("Apparently-To:", buf, 14)
58 && strncasecmp("Sender:", buf, 7)
59 && strncasecmp("Resent-Sender:", buf, 14)
65 if (outlevel >= O_DEBUG)
66 fprintf(stdout, "About to rewrite %s", buf);
68 /* make room to hack the address; buf must be malloced */
69 for (cp = buf; *cp; cp++)
70 if (*cp == ',' || isspace(*cp))
72 buf = (char *)g_realloc(buf, strlen(buf) + addresscount * strlen(host) + 1);
76 * This is going to foo up on some ill-formed addresses.
77 * Note that we don't rewrite the fake address <> in order to
78 * avoid screwing up bounce suppression with a null Return-Path.
81 parendepth = state = 0;
82 has_host_part = has_bare_name_part = FALSE;
83 for (from = buf; *from; from++)
88 printf("state %d: %s", state, buf);
89 printf("%*s^\n", from - buf + 10, " ");
95 else if (*from == ')')
99 if (!parendepth && !has_host_part)
102 case 0: /* before header colon */
107 case 1: /* we've seen the colon, we're looking for addresses */
112 else if (*from == '@')
113 has_host_part = TRUE;
114 else if (*from == '"')
117 * Not expanding on last non-WS == ';' deals with groupnames,
118 * an obscure misfeature described in sections
119 * 6.1, 6.2.6, and A.1.5 of the RFC822 standard.
121 else if ((*from == ',' || HEADER_END(from))
122 && has_bare_name_part
132 while (isspace(*from) || (*from == ','))
135 hostlen = strlen(host);
136 for (cp = from + strlen(from); cp >= from; --cp)
139 memcpy(from, host, hostlen);
140 from = p + hostlen + 1;
141 has_host_part = TRUE;
143 else if (from[1] == '('
144 && has_bare_name_part
146 && last_nws != ';' && last_nws != ')')
150 else if (!isspace(*from))
151 has_bare_name_part = TRUE;
154 case 2: /* we're in a string */
159 case 3: /* we're in a <>-enclosed address */
161 has_host_part = TRUE;
162 else if (*from == '>' && from[-1] != '<')
169 hostlen = strlen(host);
170 for (cp = from + strlen(from); cp >= from; --cp)
173 memcpy(from, host, hostlen);
175 has_host_part = TRUE;
182 * If we passed a comma, reset everything.
184 if (from[-1] == ',' && !parendepth) {
185 has_host_part = has_bare_name_part = FALSE;
191 if (outlevel >= O_DEBUG)
192 fprintf(stdout, "Rewritten version is %s\n", buf);
193 #endif /* TESTMAIN */
198 /* parse addresses in succession out of a specified RFC822 header */
199 const char *hdr; /* header to be parsed, NUL to continue previous hdr */
201 static char *tp, address[POPBUFSIZE+1];
202 static const char *hp;
203 static int state, oldstate;
205 static const char *orighdr;
206 #endif /* TESTMAIN */
209 #define START_HDR 0 /* before header colon */
210 #define SKIP_JUNK 1 /* skip whitespace, \n, and junk */
211 #define BARE_ADDRESS 2 /* collecting address without delimiters */
212 #define INSIDE_DQUOTE 3 /* inside double quotes */
213 #define INSIDE_PARENS 4 /* inside parentheses */
214 #define INSIDE_BRACKETS 5 /* inside bracketed address */
215 #define ENDIT_ALL 6 /* after last address */
223 #endif /* TESTMAIN */
232 printf("state %d: %s", state, orighdr);
233 printf("%*s^\n", hp - orighdr + 10, " ");
235 #endif /* TESTMAIN */
237 if (state == ENDIT_ALL) /* after last address */
239 else if (HEADER_END(hp))
244 while (isspace(*--tp))
248 return(tp > address ? (tp = address) : (char *)NULL);
250 else if (*hp == '\\') /* handle RFC822 escaping */
252 if (state != INSIDE_PARENS)
254 *tp++ = *hp++; /* take the escape */
255 *tp++ = *hp; /* take following char */
260 case START_HDR: /* before header colon */
265 case SKIP_JUNK: /* looking for address start */
266 if (*hp == '"') /* quoted string */
268 oldstate = SKIP_JUNK;
269 state = INSIDE_DQUOTE;
272 else if (*hp == '(') /* address comment -- ignore */
275 oldstate = SKIP_JUNK;
276 state = INSIDE_PARENS;
278 else if (*hp == '<') /* begin <address> */
280 state = INSIDE_BRACKETS;
283 else if (*hp != ',' && !isspace(*hp))
286 state = BARE_ADDRESS;
290 case BARE_ADDRESS: /* collecting address without delimiters */
291 if (*hp == ',') /* end of address */
297 return(tp = address);
300 else if (*hp == '(') /* beginning of comment */
303 oldstate = BARE_ADDRESS;
304 state = INSIDE_PARENS;
306 else if (*hp == '<') /* beginning of real address */
308 state = INSIDE_BRACKETS;
311 else if (!isspace(*hp)) /* just take it, ignoring whitespace */
315 case INSIDE_DQUOTE: /* we're in a quoted string, copy verbatim */
325 case INSIDE_PARENS: /* we're in a parenthesized comment, ignore */
334 case INSIDE_BRACKETS: /* possible <>-enclosed address */
335 if (*hp == '>') /* end of address */
340 return(tp = address);
342 else if (*hp == '<') /* nested <> */
344 else if (*hp == '"') /* quoted address */
347 oldstate = INSIDE_BRACKETS;
348 state = INSIDE_DQUOTE;
350 else /* just copy address */
360 static void parsebuf(char *longbuf, int reply)
366 reply_hack(longbuf, "HOSTNAME.NET");
367 printf("Rewritten buffer: %s", longbuf);
370 if ((cp = nxtaddr(longbuf)) != (char *)NULL)
372 printf("\t-> \"%s\"\n", cp);
374 ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
379 main(int argc, char *argv[])
381 char buf[MSGBUFSIZE], longbuf[BUFSIZ];
384 verbose = reply = FALSE;
385 while ((ch = getopt(argc, argv, "rv")) != EOF)
397 while (fgets(buf, sizeof(buf)-1, stdin))
399 if (buf[0] == ' ' || buf[0] == '\t')
400 strcat(longbuf, buf);
401 else if (!strncasecmp("From: ", buf, 6)
402 || !strncasecmp("To: ", buf, 4)
403 || !strncasecmp("Reply-", buf, 6)
404 || !strncasecmp("Cc: ", buf, 4)
405 || !strncasecmp("Bcc: ", buf, 5))
406 strcpy(longbuf, buf);
410 fputs(longbuf, stdout);
411 parsebuf(longbuf, reply);
418 fputs(longbuf, stdout);
419 parsebuf(longbuf, reply);
422 #endif /* TESTMAIN */