2 * rfc822.c -- code for slicing and dicing RFC822 mail headers
4 * Copyright 1996 by Eric S. Raymond
6 * For license terms, see the file COPYING in this directory.
12 #if defined(STDC_HEADERS)
16 #include "fetchmail.h"
18 void reply_hack(buf, host)
19 /* hack message headers so replies will work properly */
20 char *buf; /* header to be hacked */
21 const char *host; /* server hostname */
24 int parendepth, oldstate, state = 0, tokencount = 0, nlterm = 0;
25 char mycopy[POPBUFSIZE+1];
27 if (strncmp("From: ", buf, 6)
28 && strncmp("To: ", buf, 4)
29 && strncmp("Reply-", buf, 6)
30 && strncmp("Cc: ", buf, 4)
31 && strncmp("Bcc: ", buf, 5)) {
36 if (mycopy[strlen(mycopy) - 1] == '\n')
38 mycopy[strlen(mycopy) - 1] = '\0';
41 if (mycopy[strlen(mycopy) - 1] != ',')
44 for (from = mycopy; *from; from++)
47 printf("state %d: %s", state, mycopy);
48 printf("%*s^\n", from - mycopy + 10, " ");
51 #define INSERT_HOSTNAME \
59 case 0: /* before header colon */
64 case 1: /* we've seen the colon, we're looking for addresses */
67 else if (*from == '(')
73 else if (*from == '<')
75 else if (isalnum(*from))
77 else if (isspace(*from))
81 case 2: /* found a token boundary -- reset without copying */
91 case 3: /* we're in a quoted human name, copy and ignore */
96 case 4: /* we're in a parenthesized human name, copy and ignore */
99 else if (*from == ')')
105 case 5: /* we're in a <>-enclosed address */
108 else if (*from == '>')
114 case 6: /* not string or comment, could be a bare address */
118 else if (*from == '<')
121 else if (*from == '(')
127 /* on proper termination with no @, insert hostname */
128 else if (*from == ',')
134 /* If the address token is not properly terminated, ignore it. */
135 else if (isspace(*from))
140 * The only lookahead case. If we're looking at space or tab,
141 * we might be looking at a local name immediately followed
144 for (cp = from; isspace(*cp); cp++)
148 char *bp = strchr(cp, '<');
149 char *ep = strchr(cp, ',');
158 /* everything else, including alphanumerics, just passes through */
161 case 7: /* we're done with this address, skip to end */
170 /* all characters from the old buffer get copied to the new one */
173 #undef INSERT_HOSTNAME
175 /* back up and nuke the appended comma sentinel */
183 /* parse addresses in succession out of a specified RFC822 header */
184 const char *hdr; /* header to be parsed, NUL to continue previous hdr */
186 static char *tp, address[POPBUFSIZE+1];
187 static const char *hp;
188 static int state, oldstate;
192 * Note: it is important that this routine not stop on \r, since
193 * we use \r as a marker for RFC822 continuations elsewhere.
195 #define START_HDR 0 /* before header colon */
196 #define SKIP_JUNK 1 /* skip whitespace, \n, and junk */
197 #define BARE_ADDRESS 2 /* collecting address without delimiters */
198 #define INSIDE_DQUOTE 3 /* inside double quotes */
199 #define INSIDE_PARENS 4 /* inside parentheses */
200 #define INSIDE_BRACKETS 5 /* inside bracketed address */
201 #define ENDIT_ALL 6 /* after last address */
213 case START_HDR: /* before header colon */
226 case SKIP_JUNK: /* looking for address start */
227 if (*hp == '\n') /* no more addresses */
232 else if (*hp == '\\') /* handle RFC822 escaping */
234 *tp++ = *hp++; /* take the escape */
235 *tp++ = *hp; /* take following char */
237 else if (*hp == '"') /* quoted string */
239 oldstate = SKIP_JUNK;
240 state = INSIDE_DQUOTE;
243 else if (*hp == '(') /* address comment -- ignore */
246 state = INSIDE_PARENS;
248 else if (*hp == '<') /* begin <address> */
250 state = INSIDE_BRACKETS;
253 else if (!isspace(*hp)) /* ignore space */
256 state = BARE_ADDRESS;
260 case BARE_ADDRESS: /* collecting address without delimiters */
261 if (*hp == '\n') /* end of bare address */
267 return(tp = address);
270 else if (*hp == '\\') /* handle RFC822 escaping */
272 *tp++ = *hp++; /* take the escape */
273 *tp++ = *hp; /* take following char */
275 else if (*hp == ',') /* end of address */
281 return(tp = address);
284 else if (*hp == '<') /* beginning of real address */
286 state = INSIDE_BRACKETS;
289 else /* just take it */
293 case INSIDE_DQUOTE: /* we're in a quoted string, copy verbatim */
294 if (*hp == '\n') /* premature end of string */
299 else if (*hp == '\\') /* handle RFC822 escaping */
301 *tp++ = *hp++; /* take the escape */
302 *tp++ = *hp; /* take following char */
313 case INSIDE_PARENS: /* we're in a parenthesized comment, ignore */
314 if (*hp == '\n') /* end of line, just bomb out */
316 else if (*hp == '\\') /* handle RFC822 escaping */
318 *tp++ = *hp++; /* take the escape */
319 *tp++ = *hp; /* take following char */
329 case INSIDE_BRACKETS: /* possible <>-enclosed address */
330 if (*hp == '\\') /* handle RFC822 escaping */
332 *tp++ = *hp++; /* take the escape */
333 *tp++ = *hp; /* take following char */
335 else 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 */
354 case ENDIT_ALL: /* after last address */
364 main(int argc, char *argv[])
366 char buf[POPBUFSIZE], *cp;
367 int reply = (argc > 1 && !strcmp(argv[1], "-r"));
369 while (fgets(buf, sizeof(buf)-1, stdin))
371 if (strncmp("From: ", buf, 6)
372 && strncmp("To: ", buf, 4)
373 && strncmp("Reply-", buf, 6)
374 && strncmp("Cc: ", buf, 4)
375 && strncmp("Bcc: ", buf, 5))
382 reply_hack(buf, "HOSTNAME.NET");
383 printf("Rewritten buffer: %s", buf);
386 if ((cp = nxtaddr(buf)) != (char *)NULL)
388 printf("\t%s\n", cp);
390 ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
395 #endif /* TESTMAIN */