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 */