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.
11 #if defined(STDC_HEADERS)
15 #include "fetchmail.h"
17 #define HEADER_END(p) ((p)[0] == '\n' && ((p)[1] != ' ' && (p)[1] != '\t'))
23 void reply_hack(buf, host)
24 /* hack message headers so replies will work properly */
25 char *buf; /* header to be hacked */
26 const char *host; /* server hostname */
29 int parendepth, state, has_bare_name_part, has_host_part;
31 if (strncasecmp("From: ", buf, 6)
32 && strncasecmp("To: ", buf, 4)
33 && strncasecmp("Reply-To: ", buf, 10)
34 && strncasecmp("Return-Path: ", buf, 13)
35 && strncasecmp("Cc: ", buf, 4)
36 && strncasecmp("Bcc: ", buf, 5)) {
40 parendepth = state = 0;
41 has_host_part = has_bare_name_part = FALSE;
42 for (from = buf; *from; from++)
47 printf("state %d: %s", state, buf);
48 printf("%*s^\n", from - buf + 10, " ");
54 else if (*from == ')')
57 if (!parendepth && !has_host_part)
60 case 0: /* before header colon */
65 case 1: /* we've seen the colon, we're looking for addresses */
68 else if (*from == '@')
70 else if (*from == '"')
73 * Not expanding on from[-1] == ';' deals with groupnames,
74 * an obscure misfeature described in sections
75 * 6.1, 6.2.6, and A.1.5 of the RFC822 standard.
77 else if ((*from == ',' || HEADER_END(from)) && has_bare_name_part && !has_host_part && from[-1] != ';')
81 while (isspace(*from))
84 hostlen = strlen(host);
85 for (cp = from + strlen(from); cp >= from; --cp)
88 memcpy(from, host, hostlen);
92 else if (!isspace(*from))
93 has_bare_name_part = TRUE;
96 case 2: /* we're in a string */
101 case 3: /* we're in a <>-enclosed address */
103 has_host_part = TRUE;
104 else if (*from == '>' && !has_host_part)
108 hostlen = strlen(host);
109 for (cp = from + strlen(from); cp >= from; --cp)
112 memcpy(from, host, hostlen);
113 from += strlen(from);
114 has_host_part = TRUE;
122 /* parse addresses in succession out of a specified RFC822 header */
123 const char *hdr; /* header to be parsed, NUL to continue previous hdr */
125 static char *tp, address[POPBUFSIZE+1];
126 static const char *hp;
127 static int state, oldstate;
129 static const char *orighdr;
130 #endif /* TESTMAIN */
133 #define START_HDR 0 /* before header colon */
134 #define SKIP_JUNK 1 /* skip whitespace, \n, and junk */
135 #define BARE_ADDRESS 2 /* collecting address without delimiters */
136 #define INSIDE_DQUOTE 3 /* inside double quotes */
137 #define INSIDE_PARENS 4 /* inside parentheses */
138 #define INSIDE_BRACKETS 5 /* inside bracketed address */
139 #define ENDIT_ALL 6 /* after last address */
147 #endif /* TESTMAIN */
156 printf("state %d: %s", state, orighdr);
157 printf("%*s^\n", hp - orighdr + 10, " ");
159 #endif /* TESTMAIN */
161 if (state == ENDIT_ALL) /* after last address */
163 else if (HEADER_END(hp))
166 while (isspace(*--tp))
169 return(tp > address ? (tp = address) : (char *)NULL);
171 else if (*hp == '\\') /* handle RFC822 escaping */
173 if (state != INSIDE_PARENS)
175 *tp++ = *hp++; /* take the escape */
176 *tp++ = *hp; /* take following char */
181 case START_HDR: /* before header colon */
186 case SKIP_JUNK: /* looking for address start */
187 if (*hp == '"') /* quoted string */
189 oldstate = SKIP_JUNK;
190 state = INSIDE_DQUOTE;
193 else if (*hp == '(') /* address comment -- ignore */
196 oldstate = SKIP_JUNK;
197 state = INSIDE_PARENS;
199 else if (*hp == '<') /* begin <address> */
201 state = INSIDE_BRACKETS;
204 else if (*hp != ',' && !isspace(*hp))
207 state = BARE_ADDRESS;
211 case BARE_ADDRESS: /* collecting address without delimiters */
212 if (*hp == ',') /* end of address */
218 return(tp = address);
221 else if (*hp == '(') /* beginning of comment */
224 oldstate = BARE_ADDRESS;
225 state = INSIDE_PARENS;
227 else if (*hp == '<') /* beginning of real address */
229 state = INSIDE_BRACKETS;
232 else if (!isspace(*hp)) /* just take it, ignoring whitespace */
236 case INSIDE_DQUOTE: /* we're in a quoted string, copy verbatim */
246 case INSIDE_PARENS: /* we're in a parenthesized comment, ignore */
255 case INSIDE_BRACKETS: /* possible <>-enclosed address */
256 if (*hp == '>') /* end of address */
261 return(tp = address);
263 else if (*hp == '<') /* nested <> */
265 else if (*hp == '"') /* quoted address */
268 oldstate = INSIDE_BRACKETS;
269 state = INSIDE_DQUOTE;
271 else /* just copy address */
281 static void parsebuf(char *longbuf, int reply)
287 reply_hack(longbuf, "HOSTNAME.NET");
288 printf("Rewritten buffer: %s", longbuf);
291 if ((cp = nxtaddr(longbuf)) != (char *)NULL)
293 printf("\t-> \"%s\"\n", cp);
295 ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
300 main(int argc, char *argv[])
302 char buf[MSGBUFSIZE], longbuf[BUFSIZ];
305 verbose = reply = FALSE;
306 while ((ch = getopt(argc, argv, "rv")) != EOF)
318 while (fgets(buf, sizeof(buf)-1, stdin))
320 if (buf[0] == ' ' || buf[0] == '\t')
321 strcat(longbuf, buf);
322 else if (!strncasecmp("From: ", buf, 6)
323 || !strncasecmp("To: ", buf, 4)
324 || !strncasecmp("Reply-", buf, 6)
325 || !strncasecmp("Cc: ", buf, 4)
326 || !strncasecmp("Bcc: ", buf, 5))
327 strcpy(longbuf, buf);
331 fputs(longbuf, stdout);
332 parsebuf(longbuf, reply);
339 fputs(longbuf, stdout);
340 parsebuf(longbuf, reply);
343 #endif /* TESTMAIN */