]> Pileus Git - ~andy/fetchmail/blob - rfc822.c
This should handle RFC822 continuations now.
[~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     const char *from;
26     int parendepth, state, has_host_part;
27     char mycopy[MSGBUFSIZE+1];
28
29     if (strncmp("From: ", buf, 6)
30         && strncmp("To: ", buf, 4)
31         && strncmp("Reply-", buf, 6)
32         && strncmp("Cc: ", buf, 4)
33         && strncmp("Bcc: ", buf, 5)) {
34         return;
35     }
36
37     strcpy(mycopy, buf);
38     parendepth = state = 0;
39     has_host_part = FALSE;
40     for (from = mycopy; *from; from++)
41     {
42 #ifdef TESTMAIN
43         printf("state %d: %s", state, mycopy);
44         printf("%*s^\n", from - mycopy + 10, " ");
45 #endif /* TESTMAIN */
46         if (*from == '(')
47             ++parendepth;
48         else if (*from == ')')
49             --parendepth;
50
51         if (!parendepth)
52             switch (state)
53             {
54             case 0:   /* before header colon */
55                 if (*from == ':')
56                     state = 1;
57                 break;
58
59             case 1:   /* we've seen the colon, we're looking for addresses */
60                 if (*from == '<')
61                     state = 2;
62                 else if (*from == '@')
63                     has_host_part = TRUE;
64                 else if ((*from == ',' || HEADER_END(from)) && !has_host_part)
65                 {
66                     while (isspace(*from))
67                         --from;
68                     while (isspace(*buf))
69                         --buf;
70                     strcpy(buf, "@");
71                     strcat(buf, host);
72                     buf += strlen(buf);
73                 }
74                 break;
75
76             case 2:   /* we're in a <>-enclosed address */
77                 if (*from == '@')
78                     has_host_part = TRUE;
79                 else if (*from == '>' && !has_host_part)
80                 {
81                     strcpy(buf, "@");
82                     strcat(buf, host);
83                     buf += strlen(buf);
84                     has_host_part = TRUE;
85                 }
86                 break;
87             }
88
89         /* all characters from the old buffer get copied to the new one */
90         *buf++ = *from;
91     }
92
93     *buf = '\0';
94 }
95
96 char *nxtaddr(hdr)
97 /* parse addresses in succession out of a specified RFC822 header */
98 const char *hdr;        /* header to be parsed, NUL to continue previous hdr */
99 {
100     static char *tp, address[POPBUFSIZE+1];
101     static const char *hp;
102     static int  state, oldstate;
103     int parendepth;
104
105 #define START_HDR       0       /* before header colon */
106 #define SKIP_JUNK       1       /* skip whitespace, \n, and junk */
107 #define BARE_ADDRESS    2       /* collecting address without delimiters */
108 #define INSIDE_DQUOTE   3       /* inside double quotes */
109 #define INSIDE_PARENS   4       /* inside parentheses */
110 #define INSIDE_BRACKETS 5       /* inside bracketed address */
111 #define ENDIT_ALL       6       /* after last address */
112
113     if (hdr)
114     {
115         hp = hdr;
116         state = START_HDR;
117     }
118
119     for (; *hp; hp++)
120     {
121         switch (state)
122         {
123         case START_HDR:   /* before header colon */
124             if (HEADER_END(hp))
125             {
126                 state = ENDIT_ALL;
127                 return(NULL);
128             }
129             else if (*hp == ':')
130             {
131                 state = SKIP_JUNK;
132                 tp = address;
133             }
134             break;
135
136         case SKIP_JUNK:         /* looking for address start */
137             if (HEADER_END(hp))         /* no more addresses */
138             {
139                 state = ENDIT_ALL;
140                 return(NULL);
141             }
142             else if (*hp == '\\')       /* handle RFC822 escaping */
143             {
144                 *tp++ = *hp++;                  /* take the escape */
145                 *tp++ = *hp;                    /* take following char */
146             }
147             else if (*hp == '"')        /* quoted string */
148             {
149                 oldstate = SKIP_JUNK;
150                 state = INSIDE_DQUOTE;
151                 *tp++ = *hp;
152             }
153             else if (*hp == '(')        /* address comment -- ignore */
154             {
155                 parendepth = 1;
156                 state = INSIDE_PARENS;    
157             }
158             else if (*hp == '<')        /* begin <address> */
159             {
160                 state = INSIDE_BRACKETS;
161                 tp = address;
162             }
163             else if (!isspace(*hp))     /* ignore space */
164             {
165                 --hp;
166                 state = BARE_ADDRESS;
167             }
168             break;
169
170         case BARE_ADDRESS:      /* collecting address without delimiters */
171             if (HEADER_END(hp))         /* end of bare address */
172             {
173                 if (tp > address)
174                 {
175                     while (isspace(*--tp))
176                         continue;
177                     *++tp = '\0';
178                     state = ENDIT_ALL;
179                     return(tp = address);
180                 }
181             }
182             else if (*hp == '\\')       /* handle RFC822 escaping */
183             {
184                 *tp++ = *hp++;                  /* take the escape */
185                 *tp++ = *hp;                    /* take following char */
186             }
187             else 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 real address */
197             {
198                 state = INSIDE_BRACKETS;
199                 tp = address;
200             }
201             else                /* just take it */
202                 *tp++ = *hp;
203             break;
204
205         case INSIDE_DQUOTE:     /* we're in a quoted string, copy verbatim */
206             if (HEADER_END(hp))         /* premature end of string */
207             {
208                 state = ENDIT_ALL;
209                 return(NULL);
210             }
211             else if (*hp == '\\')       /* handle RFC822 escaping */
212             {
213                 *tp++ = *hp++;                  /* take the escape */
214                 *tp++ = *hp;                    /* take following char */
215             }
216             else if (*hp != '"')
217                 *tp++ = *hp;
218             else
219             {
220                 *tp++ = *hp;
221                 state = oldstate;
222             }
223             break;
224
225         case INSIDE_PARENS:     /* we're in a parenthesized comment, ignore */
226             if (HEADER_END(hp))         /* end of line, just bomb out */
227             {
228                 state = ENDIT_ALL;
229                 return(NULL);
230             }
231             else if (*hp == '\\')       /* handle RFC822 escaping */
232             {
233                 *tp++ = *hp++;                  /* take the escape */
234                 *tp++ = *hp;                    /* take following char */
235             }
236             else if (*hp == '(')
237                 ++parendepth;
238             else if (*hp == ')')
239                 --parendepth;
240             if (parendepth == 0)
241                 state = SKIP_JUNK;
242             break;
243
244         case INSIDE_BRACKETS:   /* possible <>-enclosed address */
245             if (HEADER_END(hp))         /* end of line, just bomb out */
246             {
247                 state = ENDIT_ALL;
248                 return(NULL);
249             }
250             else if (*hp == '\\')       /* handle RFC822 escaping */
251             {
252                 *tp++ = *hp++;                  /* take the escape */
253                 *tp++ = *hp;                    /* take following char */
254             }
255             else if (*hp == '>')        /* end of address */
256             {
257                 *tp++ = '\0';
258                 state = SKIP_JUNK;
259                 ++hp;
260                 return(tp = address);
261             }
262             else if (*hp == '<')        /* nested <> */
263                 tp = address;
264             else if (*hp == '"')        /* quoted address */
265             {
266                 *tp++ = *hp;
267                 oldstate = INSIDE_BRACKETS;
268                 state = INSIDE_DQUOTE;
269             }
270             else                        /* just copy address */
271                 *tp++ = *hp;
272             break;
273
274         case ENDIT_ALL:         /* after last address */
275             return(NULL);
276             break;
277         }
278     }
279
280     return(NULL);
281 }
282
283 #ifdef TESTMAIN
284 main(int argc, char *argv[])
285 {
286     char        buf[MSGBUFSIZE], *cp;
287     int         reply =  (argc > 1 && !strcmp(argv[1], "-r"));
288
289     while (fgets(buf, sizeof(buf)-1, stdin))
290     {
291         if (strncmp("From: ", buf, 6)
292                     && strncmp("To: ", buf, 4)
293                     && strncmp("Reply-", buf, 6)
294                     && strncmp("Cc: ", buf, 4)
295                     && strncmp("Bcc: ", buf, 5))
296             continue;
297         else
298         {
299             fputs(buf, stdout);
300             if (reply)
301             {
302                 reply_hack(buf, "HOSTNAME.NET");
303                 printf("Rewritten buffer: %s", buf);
304             }
305             else
306                 if ((cp = nxtaddr(buf)) != (char *)NULL)
307                     do {
308                         printf("\t%s\n", cp);
309                     } while
310                         ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
311         }
312
313     }
314 }
315 #endif /* TESTMAIN */
316
317 /* rfc822.c end */