]> Pileus Git - ~andy/fetchmail/blob - rfc822.c
Have reply_hack alter its input buffer in place.
[~andy/fetchmail] / rfc822.c
1 /*
2  * rfc822.c -- code for slicing and dicing RFC822 mail headers
3  *
4  * Copyright 1996 by Eric S. Raymond
5  * All rights reserved.
6  * For license terms, see the file COPYING in this directory.
7  */
8
9 #include  <stdio.h>
10 #include  <ctype.h>
11 #include  <string.h>
12 #if defined(STDC_HEADERS)
13 #include  <stdlib.h>
14 #endif
15
16 #include "fetchmail.h"
17
18 #define HEADER_END(p)   ((p)[0] == '\n' && ((p)[1] != ' ' && (p)[1] != '\t'))
19
20 void reply_hack(buf, host)
21 /* hack message headers so replies will work properly */
22 char *buf;              /* header to be hacked */
23 const char *host;       /* server hostname */
24 {
25     char *from, *cp;
26     int parendepth, state, has_host_part;
27
28     if (strncmp("From: ", buf, 6)
29         && strncmp("To: ", buf, 4)
30         && strncmp("Reply-", buf, 6)
31         && strncmp("Cc: ", buf, 4)
32         && strncmp("Bcc: ", buf, 5)) {
33         return;
34     }
35
36     parendepth = state = 0;
37     has_host_part = FALSE;
38     for (from = buf; *from; from++)
39     {
40 #ifdef TESTMAIN
41         printf("state %d: %s", state, mycopy);
42         printf("%*s^\n", from - mycopy + 10, " ");
43 #endif /* TESTMAIN */
44         if (state != 2)
45             if (*from == '(')
46                 ++parendepth;
47             else if (*from == ')')
48                 --parendepth;
49
50         if (!parendepth && !has_host_part)
51             switch (state)
52             {
53             case 0:     /* before header colon */
54                 if (*from == ':')
55                     state = 1;
56                 break;
57
58             case 1:     /* we've seen the colon, we're looking for addresses */
59                 if (*from == '<')
60                     state = 3;
61                 else if (*from == '@')
62                     has_host_part = TRUE;
63                 else if (*from == '"')
64                     state = 2;
65                 else if ((*from == ',' || HEADER_END(from)) && !has_host_part)
66                 {
67                     int hostlen;
68
69                     while (isspace(*from))
70                         --from;
71                     from++;
72                     hostlen = strlen(host);
73                     for (cp = from + strlen(from); cp >= from; --cp)
74                         cp[hostlen+1] = *cp;
75                     *from++ = '@';
76                     memcpy(from, host, hostlen);
77                     from += strlen(from);
78                     has_host_part = TRUE;
79                 }
80                 break;
81
82             case 2:     /* we're in a string */
83                 if (*from == '"')
84                     state = 1;
85                 break;
86
87             case 3:     /* we're in a <>-enclosed address */
88                 if (*from == '@')
89                     has_host_part = TRUE;
90                 else if (*from == '>' && !has_host_part)
91                 {
92                     int hostlen;
93
94                     hostlen = strlen(host);
95                     for (cp = from + strlen(from); cp >= from; --cp)
96                         cp[hostlen+1] = *cp;
97                     *from++ = '@';
98                     memcpy(from, host, hostlen);
99                     from += strlen(from);
100                     has_host_part = TRUE;
101                 }
102                 break;
103             }
104     }
105 }
106
107 char *nxtaddr(hdr)
108 /* parse addresses in succession out of a specified RFC822 header */
109 const char *hdr;        /* header to be parsed, NUL to continue previous hdr */
110 {
111     static char *tp, address[POPBUFSIZE+1];
112     static const char *hp;
113     static int  state, oldstate;
114     int parendepth;
115
116 #define START_HDR       0       /* before header colon */
117 #define SKIP_JUNK       1       /* skip whitespace, \n, and junk */
118 #define BARE_ADDRESS    2       /* collecting address without delimiters */
119 #define INSIDE_DQUOTE   3       /* inside double quotes */
120 #define INSIDE_PARENS   4       /* inside parentheses */
121 #define INSIDE_BRACKETS 5       /* inside bracketed address */
122 #define ENDIT_ALL       6       /* after last address */
123
124     if (hdr)
125     {
126         hp = hdr;
127         state = START_HDR;
128     }
129
130     for (; *hp; hp++)
131     {
132 #ifdef TESTMAIN
133         printf("state %d: %s", state, hdr);
134         printf("%*s^\n", hp - hdr + 10, " ");
135 #endif /* TESTMAIN */
136
137         if (state == ENDIT_ALL)         /* after last address */
138             return(NULL);
139         else if (HEADER_END(hp))
140         {
141             state = ENDIT_ALL;
142             while (isspace(*--tp))
143                 continue;
144             *++tp = '\0';
145             return(tp > address ? (tp = address) : (char *)NULL);
146         }
147         else if (*hp == '\\')           /* handle RFC822 escaping */
148         {
149             *tp++ = *hp++;                      /* take the escape */
150             *tp++ = *hp;                        /* take following char */
151         }
152         else switch (state)
153         {
154         case START_HDR:   /* before header colon */
155             if (*hp == ':')
156             {
157                 state = SKIP_JUNK;
158                 tp = address;
159             }
160             break;
161
162         case SKIP_JUNK:         /* looking for address start */
163             if (*hp == '"')     /* quoted string */
164             {
165                 oldstate = SKIP_JUNK;
166                 state = INSIDE_DQUOTE;
167                 *tp++ = *hp;
168             }
169             else if (*hp == '(')        /* address comment -- ignore */
170             {
171                 parendepth = 1;
172                 state = INSIDE_PARENS;    
173             }
174             else if (*hp == '<')        /* begin <address> */
175             {
176                 state = INSIDE_BRACKETS;
177                 tp = address;
178             }
179             else if (!isspace(*hp))     /* ignore space */
180             {
181                 --hp;
182                 state = BARE_ADDRESS;
183             }
184             break;
185
186         case BARE_ADDRESS:      /* collecting address without delimiters */
187             if (*hp == ',')     /* end of address */
188             {
189                 if (tp > address)
190                 {
191                     *tp++ = '\0';
192                     state = SKIP_JUNK;
193                     return(tp = address);
194                 }
195             }
196             else if (*hp == '(')        /* beginning of comment */
197             {
198                 parendepth = 1;
199                 state = INSIDE_PARENS;    
200             }
201             else if (*hp == '<')        /* beginning of real address */
202             {
203                 state = INSIDE_BRACKETS;
204                 tp = address;
205             }
206             else if (!isspace(*hp))     /* just take it, ignoring whitespace */
207                 *tp++ = *hp;
208             break;
209
210         case INSIDE_DQUOTE:     /* we're in a quoted string, copy verbatim */
211             if (*hp != '"')
212                 *tp++ = *hp;
213             else
214             {
215                 *tp++ = *hp;
216                 state = oldstate;
217             }
218             break;
219
220         case INSIDE_PARENS:     /* we're in a parenthesized comment, ignore */
221             if (*hp == '(')
222                 ++parendepth;
223             else if (*hp == ')')
224                 --parendepth;
225             if (parendepth == 0)
226                 state = SKIP_JUNK;
227             break;
228
229         case INSIDE_BRACKETS:   /* possible <>-enclosed address */
230             if (*hp == '>')     /* end of address */
231             {
232                 *tp++ = '\0';
233                 state = SKIP_JUNK;
234                 ++hp;
235                 return(tp = address);
236             }
237             else if (*hp == '<')        /* nested <> */
238                 tp = address;
239             else if (*hp == '"')        /* quoted address */
240             {
241                 *tp++ = *hp;
242                 oldstate = INSIDE_BRACKETS;
243                 state = INSIDE_DQUOTE;
244             }
245             else                        /* just copy address */
246                 *tp++ = *hp;
247             break;
248         }
249     }
250
251     return(NULL);
252 }
253
254 #ifdef TESTMAIN
255 main(int argc, char *argv[])
256 {
257     char        buf[MSGBUFSIZE], *cp;
258     int         reply =  (argc > 1 && !strcmp(argv[1], "-r"));
259
260     while (fgets(buf, sizeof(buf)-1, stdin))
261     {
262         if (strncmp("From: ", buf, 6)
263                     && strncmp("To: ", buf, 4)
264                     && strncmp("Reply-", buf, 6)
265                     && strncmp("Cc: ", buf, 4)
266                     && strncmp("Bcc: ", buf, 5))
267             continue;
268         else
269         {
270             fputs(buf, stdout);
271             if (reply)
272             {
273                 reply_hack(buf, "HOSTNAME.NET");
274                 printf("Rewritten buffer: %s", buf);
275             }
276             else
277                 if ((cp = nxtaddr(buf)) != (char *)NULL)
278                     do {
279                         printf("\t\"%s\"\n", cp);
280                     } while
281                         ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
282         }
283
284     }
285 }
286 #endif /* TESTMAIN */
287
288 /* rfc822.c end */