]> Pileus Git - ~andy/fetchmail/blob - rfc822.c
8497c54ba3464d9e69f2bb2edf54d7fefc6a5940
[~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 = 0, tokencount = 0;
25     char mycopy[POPBUFSIZE+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     for (from = mycopy; *from; from++)
37     {
38         switch (state)
39         {
40         case 0:   /* before header colon */
41             if (*from == ':')
42                 state = 1;
43             break;
44
45         case 1:   /* we've seen the colon, we're looking for addresses */
46             if (*from == '"')
47                 state = 3;
48             else if (*from == '(')
49             {
50                 parendepth = 1;
51                 state = 4;    
52             }
53             else if (*from == '<' || isalnum(*from))
54                 state = 5;
55             else if (isspace(*from))
56                 state = 2;
57             break;
58
59         case 2:     /* found a token boundary -- reset without copying */
60             if (*from != ' ' && *from != '\t')
61             {
62                 tokencount++;
63                 state = 1;
64                 --from;
65                 continue;
66             }
67
68         case 3:   /* we're in a quoted human name, copy and ignore */
69             if (*from == '"')
70                 state = 1;
71             break;
72
73         case 4:   /* we're in a parenthesized human name, copy and ignore */
74             if (*from == '(')
75                 ++parendepth;
76             else if (*from == ')')
77                 --parendepth;
78             if (parendepth == 0)
79                 state = 1;
80             break;
81
82         case 5:   /* the real work gets done here */
83             /*
84              * We're in something that might be an address part,
85              * either a bare unquoted/unparenthesized text or text
86              * enclosed in <> as per RFC822.
87              */
88             /* if the address part contains an @, don't mess with it */
89             if (*from == '@')
90                 state = 6;
91
92             /* If the address token is not properly terminated, ignore it. */
93             else if (*from == ' ' || *from == '\t')
94             {
95                 const char *cp;
96
97                 /*
98                  * The only lookahead case.  If we're looking at space or tab,
99                  * we might be looking at a local name immediately followed
100                  * by a human name.
101                  */
102                 for (cp = from; isspace(*cp); cp++)
103                     continue;
104                 if (*cp == '(')
105                 {
106                     strcpy(buf, "@");
107                     strcat(buf, host);
108                     buf += strlen(buf);
109                     state = 1;
110                 }
111             }
112
113             /*
114              * On proper termination with no @, insert hostname.
115              * Case '>' catches <>-enclosed mail IDs.  Case ',' catches
116              * comma-separated bare IDs.
117              */
118             else if (strchr(">,", *from))
119             {
120                 strcpy(buf, "@");
121                 strcat(buf, host);
122                 buf += strlen(buf);
123                 tokencount = 0;
124                 state = 1;
125             }
126
127             /* a single local name alone on the line */
128             else if (*from == '\n' && tokencount == 1)
129             {
130                 strcpy(buf, "@");
131                 strcat(buf, host);
132                 buf += strlen(buf);
133                 state = 2;
134             }
135
136             /* everything else, including alphanumerics, just passes through */
137             break;
138
139         case 6:   /* we're in a remote mail ID, no need to append hostname */
140             if (*from == '>' || *from == ',' || isspace(*from))
141                 state = 1;
142             break;
143         }
144
145         /* all characters from the old buffer get copied to the new one */
146         *buf++ = *from;
147     }
148     *buf++ = '\0';
149 }
150
151 char *nxtaddr(hdr)
152 /* parse addresses in succession out of a specified RFC822 header */
153 const char *hdr;        /* header to be parsed, NUL to continue previous hdr */
154 {
155     static char *tp, address[POPBUFSIZE+1];
156     static const char *hp;
157     static int  state, oldstate;
158     int parendepth;
159
160     /*
161      * Note 1: RFC822 escaping with \ is *not* handled.  Note 2: it is
162      * important that this routine not stop on \r, since we use \r as
163      * a marker for RFC822 continuations elsewhere.
164      */
165 #define START_HDR       0       /* before header colon */
166 #define SKIP_JUNK       1       /* skip whitespace, \n, and junk */
167 #define BARE_ADDRESS    2       /* collecting address without delimiters */
168 #define INSIDE_DQUOTE   3       /* inside double quotes */
169 #define INSIDE_PARENS   4       /* inside parentheses */
170 #define INSIDE_BRACKETS 5       /* inside bracketed address */
171 #define ENDIT_ALL       6       /* after last address */
172
173     if (hdr)
174     {
175         hp = hdr;
176         state = START_HDR;
177     }
178
179     for (; *hp; hp++)
180     {
181         switch (state)
182         {
183         case START_HDR:   /* before header colon */
184             if (*hp == '\n')
185             {
186                 state = ENDIT_ALL;
187                 return(NULL);
188             }
189             else if (*hp == ':')
190             {
191                 state = SKIP_JUNK;
192                 tp = address;
193             }
194             break;
195
196         case SKIP_JUNK:         /* looking for address start */
197             if (*hp == '\n')            /* no more addresses */
198             {
199                 state = ENDIT_ALL;
200                 return(NULL);
201             }
202             else if (*hp == '"')        /* quoted string */
203             {
204                 oldstate = SKIP_JUNK;
205                 state = INSIDE_DQUOTE;
206                 *tp++ = *hp;
207             }
208             else if (*hp == '(')        /* address comment -- ignore */
209             {
210                 parendepth = 1;
211                 state = INSIDE_PARENS;    
212             }
213             else if (*hp == '<')        /* begin <address> */
214             {
215                 state = INSIDE_BRACKETS;
216                 tp = address;
217             }
218             else if (!isspace(*hp))     /* ignore space */
219             {
220                 --hp;
221                 state = BARE_ADDRESS;
222             }
223             break;
224
225         case BARE_ADDRESS:   /* collecting address without delimiters */
226             if (*hp == '\n')    /* end of bare address */
227             {
228                 *tp++ = '\0';
229                 state = ENDIT_ALL;
230                 return(tp = address);
231             }
232             else if (*hp == ',' || isspace(*hp))  /* end of address */
233             {
234                 if (tp > address)
235                 {
236                     *tp++ = '\0';
237                     ++hp;
238                     state = SKIP_JUNK;
239                     return(tp = address);
240                 }
241             }
242             else                /* just take it */
243             {
244                 state = BARE_ADDRESS;
245                 *tp++ = *hp;
246             }
247             break;
248
249         case INSIDE_DQUOTE:   /* we're in a quoted string, copy verbatim */
250             if (*hp == '\n')
251             {
252                 state = ENDIT_ALL;
253                 return(NULL);
254             }
255             if (*hp != '"')
256                 *tp++ = *hp;
257             else
258             {
259                 *tp++ = *hp;
260                 state = oldstate;
261             }
262             break;
263
264         case INSIDE_PARENS:   /* we're in a parenthesized comment, ignore */
265             if (*hp == '\n')
266                 return(NULL);
267             else if (*hp == '(')
268                 ++parendepth;
269             else if (*hp == ')')
270                 --parendepth;
271             if (parendepth == 0)
272                 state = SKIP_JUNK;
273             break;
274
275         case INSIDE_BRACKETS:   /* possible <>-enclosed address */
276             if (*hp == '>') /* end of address */
277             {
278                 *tp++ = '\0';
279                 state = SKIP_JUNK;
280                 ++hp;
281                 return(tp = address);
282             }
283             else if (*hp == '<')  /* nested <> */
284                 tp = address;
285             else if (*hp == '"') /* quoted address */
286             {
287                 *tp++ = *hp;
288                 oldstate = INSIDE_BRACKETS;
289                 state = INSIDE_DQUOTE;
290             }
291             else  /* just copy address */
292                 *tp++ = *hp;
293             break;
294
295         case ENDIT_ALL: /* after last address */
296             return(NULL);
297             break;
298         }
299     }
300
301     return(NULL);
302 }
303
304 #ifdef TESTMAIN
305 main(int argc, char *argv[])
306 {
307     char        buf[POPBUFSIZE], *cp;
308
309     while (fgets(buf, sizeof(buf)-1, stdin))
310     {
311         if (strncmp("From: ", buf, 6)
312                     && strncmp("To: ", buf, 4)
313                     && strncmp("Reply-", buf, 6)
314                     && strncmp("Cc: ", buf, 4)
315                     && strncmp("Bcc: ", buf, 5))
316             continue;
317         else
318         {
319             fputs(buf, stdout);
320             if ((cp = nxtaddr(buf)) != (char *)NULL)
321                 do {
322                     printf("%s\n", cp);
323                 } while
324                     ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
325         }
326
327     }
328 }
329 #endif /* TESTMAIN */
330
331 /* rfc822.c end */