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