]> Pileus Git - ~andy/fetchmail/blob - rfc822.c
Initial revision
[~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;
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
166     if (hdr)
167     {
168         hp = hdr;
169         state = 0;
170     }
171
172     for (; *hp; hp++)
173     {
174         switch (state)
175         {
176         case 0:   /* before header colon */
177             if (*hp == '\n')
178             {
179                 state = 6;
180                 return(NULL);
181             }
182             else if (*hp == ':')
183             {
184                 state = 1;
185                 tp = address;
186             }
187             break;
188
189         case 1:   /* we've seen the colon, now grab the address */
190             if (*hp == '\n')    /* end of address list */
191             {
192                 *tp++ = '\0';
193                 state = 6;
194                 return(tp = address);
195             }
196             else if (*hp == ',')  /* end of address */
197             {
198                 if (tp > address)
199                 {
200                     *tp++ = '\0';
201                     ++hp;
202                     return(tp = address);
203                 }
204             }
205             else if (*hp == '"') /* quoted string */
206             {
207                 state = 2;
208                 *tp++ = *hp;
209             }
210             else if (*hp == '(') /* address comment -- ignore */
211             {
212                 parendepth = 1;
213                 state = 3;    
214             }
215             else if (*hp == '<') /* begin <address> */
216             {
217                 state = 4;
218                 tp = address;
219             }
220             else if (isspace(*hp)) /* ignore space */
221                 state = 1;
222             else   /* just take it */
223             {
224                 state = 1;
225                 *tp++ = *hp;
226             }
227             break;
228
229         case 2:   /* we're in a quoted string, copy verbatim */
230             if (*hp == '\n')
231             {
232                 state = 6;
233                 return(NULL);
234             }
235             if (*hp != '"')
236                 *tp++ = *hp;
237             else if (*hp == '"')
238             {
239                 *tp++ = *hp;
240                 state = 1;
241             }
242             break;
243
244         case 3:   /* we're in a parenthesized comment, ignore */
245             if (*hp == '\n')
246                 return(NULL);
247             else if (*hp == '(')
248                 ++parendepth;
249             else if (*hp == ')')
250                 --parendepth;
251             if (parendepth == 0)
252                 state = 1;
253             break;
254
255         case 4:   /* possible <>-enclosed address */
256             if (*hp == '>') /* end of address */
257             {
258                 *tp++ = '\0';
259                 state = 1;
260                 ++hp;
261                 return(tp = address);
262             }
263             else if (*hp == '<')  /* nested <> */
264                 tp = address;
265             else if (*hp == '"') /* quoted address */
266             {
267                 *tp++ = *hp;
268                 state = 5;
269             }
270             else  /* just copy address */
271                 *tp++ = *hp;
272             break;
273
274         case 5:   /* we're in a quoted address, copy verbatim */
275             if (*hp == '\n')  /* mismatched quotes */
276             {
277                 state = 6;
278                 return(NULL);
279             }
280             if (*hp != '"')  /* just copy it if it isn't a quote */
281                 *tp++ = *hp;
282             else if (*hp == '"')  /* end of quoted string */
283             {
284                 *tp++ = *hp;
285                 state = 4;
286             }
287             break;
288
289         case 6: /* after last address */
290             return(NULL);
291             break;
292         }
293     }
294
295     return(NULL);
296 }
297
298 #ifdef TESTMAIN
299 main(int argc, char *argv[])
300 {
301     char        buf[POPBUFSIZE], *cp;
302
303     while (fgets(buf, sizeof(buf)-1, stdin))
304     {
305         if (strncmp("From: ", buf, 6)
306                     && strncmp("To: ", buf, 4)
307                     && strncmp("Reply-", buf, 6)
308                     && strncmp("Cc: ", buf, 4)
309                     && strncmp("Bcc: ", buf, 5))
310             continue;
311         else
312             if ((cp = nxtaddr(buf)) != (char *)NULL)
313                 do {
314                     printf("Address: %s\n", cp);
315                 } while
316                     ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
317
318     }
319 }
320 #endif /* TESTMAIN */
321
322 /* rfc822.c end */