1 /* $OpenBSD: rfc2822.c,v 1.7 2016/02/04 22:35:17 eric Exp $ */
2
3 /*
4 * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "rfc2822.h"
31
32 /* default no-op callbacks */
hdr_dflt_cb(const struct rfc2822_header * hdr,void * arg)33 static void hdr_dflt_cb(const struct rfc2822_header *hdr, void *arg) {}
body_dflt_cb(const char * line,void * arg)34 static void body_dflt_cb(const char *line, void *arg) {}
35
36 static void
header_reset(struct rfc2822_header * hdr)37 header_reset(struct rfc2822_header *hdr)
38 {
39 struct rfc2822_line *line;
40
41 while ((line = TAILQ_FIRST(&hdr->lines))) {
42 TAILQ_REMOVE(&hdr->lines, line, next);
43 free(line);
44 }
45 }
46
47 static void
header_callback(struct rfc2822_parser * rp)48 header_callback(struct rfc2822_parser *rp)
49 {
50 struct rfc2822_hdr_cb *hdr_cb;
51 struct rfc2822_hdr_miss_cb *hdr_miss_cb;
52
53 TAILQ_FOREACH(hdr_cb, &rp->hdr_cb, next)
54 if (strcasecmp(hdr_cb->name, rp->header.name) == 0) {
55 hdr_cb->func(&rp->header, hdr_cb->arg);
56 goto end;
57 }
58 rp->hdr_dflt_cb.func(&rp->header, rp->hdr_dflt_cb.arg);
59
60 end:
61 TAILQ_FOREACH(hdr_miss_cb, &rp->hdr_miss_cb, next)
62 if (strcasecmp(hdr_miss_cb->name, rp->header.name) == 0)
63 break;
64 if (hdr_miss_cb)
65 TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next);
66 free(hdr_miss_cb);
67 header_reset(&rp->header);
68 rp->in_hdr = 0;
69 return;
70 }
71
72 static void
missing_headers_callback(struct rfc2822_parser * rp)73 missing_headers_callback(struct rfc2822_parser *rp)
74 {
75 struct rfc2822_hdr_miss_cb *hdr_miss_cb;
76
77 while ((hdr_miss_cb = TAILQ_FIRST(&rp->hdr_miss_cb))) {
78 hdr_miss_cb->func(hdr_miss_cb->name, hdr_miss_cb->arg);
79 TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next);
80 free(hdr_miss_cb);
81 }
82 }
83
84 static void
eoh_callback(struct rfc2822_parser * rp)85 eoh_callback(struct rfc2822_parser *rp)
86 {
87 if (rp->eoh_cb.func)
88 rp->eoh_cb.func(rp->eoh_cb.arg);
89 }
90
91 static void
body_callback(struct rfc2822_parser * rp,const char * line)92 body_callback(struct rfc2822_parser *rp, const char *line)
93 {
94 rp->body_line_cb.func(line, rp->body_line_cb.arg);
95 }
96
97 static int
parser_feed_header(struct rfc2822_parser * rp,char * line)98 parser_feed_header(struct rfc2822_parser *rp, char *line)
99 {
100 struct rfc2822_line *hdrline;
101 char *pos;
102
103 /* new header */
104 if (!isspace(*line) && *line != '\0') {
105 rp->in_hdr = 1;
106 if ((pos = strchr(line, ':')) == NULL)
107 return 0;
108 memset(rp->header.name, 0, sizeof rp->header.name);
109 (void)memcpy(rp->header.name, line, pos - line);
110 if (isspace(*(pos + 1)))
111 return parser_feed_header(rp, pos + 1);
112 else {
113 *pos = ' ';
114 return parser_feed_header(rp, pos);
115 }
116 }
117
118 /* continuation */
119 if (!rp->in_hdr)
120 return 0;
121
122 /* append line to header */
123 if ((hdrline = calloc(1, sizeof *hdrline)) == NULL)
124 return -1;
125 (void)strlcpy(hdrline->buffer, line, sizeof hdrline->buffer);
126 TAILQ_INSERT_TAIL(&rp->header.lines, hdrline, next);
127 return 1;
128 }
129
130 static int
parser_feed_body(struct rfc2822_parser * rp,const char * line)131 parser_feed_body(struct rfc2822_parser *rp, const char *line)
132 {
133 /* for now, we only support per-line callbacks */
134 body_callback(rp, line);
135 return 1;
136 }
137
138
139 void
rfc2822_parser_init(struct rfc2822_parser * rp)140 rfc2822_parser_init(struct rfc2822_parser *rp)
141 {
142 memset(rp, 0, sizeof *rp);
143 TAILQ_INIT(&rp->hdr_cb);
144 TAILQ_INIT(&rp->hdr_miss_cb);
145 TAILQ_INIT(&rp->header.lines);
146 rfc2822_header_default_callback(rp, hdr_dflt_cb, NULL);
147 rfc2822_body_callback(rp, body_dflt_cb, NULL);
148 }
149
150 void
rfc2822_parser_flush(struct rfc2822_parser * rp)151 rfc2822_parser_flush(struct rfc2822_parser *rp)
152 {
153 if (!rp->in_hdrs)
154 return;
155
156 header_callback(rp);
157 }
158
159 void
rfc2822_parser_reset(struct rfc2822_parser * rp)160 rfc2822_parser_reset(struct rfc2822_parser *rp)
161 {
162 header_reset(&rp->header);
163 rp->in_hdrs = 1;
164 }
165
166 void
rfc2822_parser_release(struct rfc2822_parser * rp)167 rfc2822_parser_release(struct rfc2822_parser *rp)
168 {
169 struct rfc2822_hdr_cb *cb;
170 struct rfc2822_hdr_miss_cb *mcb;
171
172 rfc2822_parser_reset(rp);
173 while ((cb = TAILQ_FIRST(&rp->hdr_cb))) {
174 TAILQ_REMOVE(&rp->hdr_cb, cb, next);
175 free(cb);
176 }
177 while ((mcb = TAILQ_FIRST(&rp->hdr_miss_cb))) {
178 TAILQ_REMOVE(&rp->hdr_miss_cb, mcb, next);
179 free(mcb);
180 }
181 }
182
183 int
rfc2822_parser_feed(struct rfc2822_parser * rp,const char * line)184 rfc2822_parser_feed(struct rfc2822_parser *rp, const char *line)
185 {
186 char buffer[RFC2822_MAX_LINE_SIZE+1];
187
188 /* in header and line is not a continuation, execute callback */
189 if (rp->in_hdr && (*line == '\0' || !isspace(*line)))
190 header_callback(rp);
191
192 /* no longer in headers */
193 if (*line == '\0') {
194 if (rp->in_hdrs) {
195 missing_headers_callback(rp);
196 eoh_callback(rp);
197 }
198 rp->in_hdrs = 0;
199 }
200
201 if (rp->in_hdrs) {
202 /* line exceeds RFC maximum size requirement */
203 if (strlcpy(buffer, line, sizeof buffer) >= sizeof buffer)
204 return 0;
205 return parser_feed_header(rp, buffer);
206 }
207
208 /* don't enforce line max length on content, too many MUA break */
209 return parser_feed_body(rp, line);
210 }
211
212 int
rfc2822_header_callback(struct rfc2822_parser * rp,const char * header,void (* func)(const struct rfc2822_header *,void *),void * arg)213 rfc2822_header_callback(struct rfc2822_parser *rp, const char *header,
214 void (*func)(const struct rfc2822_header *, void *), void *arg)
215 {
216 struct rfc2822_hdr_cb *cb;
217 struct rfc2822_hdr_cb *cb_tmp;
218 char buffer[RFC2822_MAX_LINE_SIZE+1];
219
220 /* line exceeds RFC maximum size requirement */
221 if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer)
222 return 0;
223
224 TAILQ_FOREACH_SAFE(cb, &rp->hdr_cb, next, cb_tmp) {
225 if (strcasecmp(cb->name, buffer) == 0) {
226 TAILQ_REMOVE(&rp->hdr_cb, cb, next);
227 free(cb);
228 }
229 }
230
231 if ((cb = calloc(1, sizeof *cb)) == NULL)
232 return -1;
233 (void)strlcpy(cb->name, buffer, sizeof cb->name);
234 cb->func = func;
235 cb->arg = arg;
236 TAILQ_INSERT_TAIL(&rp->hdr_cb, cb, next);
237 return 1;
238 }
239
240 int
rfc2822_missing_header_callback(struct rfc2822_parser * rp,const char * header,void (* func)(const char *,void *),void * arg)241 rfc2822_missing_header_callback(struct rfc2822_parser *rp, const char *header,
242 void (*func)(const char *, void *), void *arg)
243 {
244 struct rfc2822_hdr_miss_cb *cb;
245 struct rfc2822_hdr_miss_cb *cb_tmp;
246 char buffer[RFC2822_MAX_LINE_SIZE+1];
247
248 /* line exceeds RFC maximum size requirement */
249 if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer)
250 return 0;
251
252 TAILQ_FOREACH_SAFE(cb, &rp->hdr_miss_cb, next, cb_tmp) {
253 if (strcasecmp(cb->name, buffer) == 0) {
254 TAILQ_REMOVE(&rp->hdr_miss_cb, cb, next);
255 free(cb);
256 }
257 }
258
259 if ((cb = calloc(1, sizeof *cb)) == NULL)
260 return -1;
261 (void)strlcpy(cb->name, buffer, sizeof cb->name);
262 cb->func = func;
263 cb->arg = arg;
264 TAILQ_INSERT_TAIL(&rp->hdr_miss_cb, cb, next);
265 return 1;
266 }
267
268 void
rfc2822_header_default_callback(struct rfc2822_parser * rp,void (* func)(const struct rfc2822_header *,void *),void * arg)269 rfc2822_header_default_callback(struct rfc2822_parser *rp,
270 void (*func)(const struct rfc2822_header *, void *), void *arg)
271 {
272 struct rfc2822_hdr_cb *cb;
273
274 cb = &rp->hdr_dflt_cb;
275 cb->func = func;
276 cb->arg = arg;
277 }
278
279 void
rfc2822_body_callback(struct rfc2822_parser * rp,void (* func)(const char *,void *),void * arg)280 rfc2822_body_callback(struct rfc2822_parser *rp,
281 void (*func)(const char *, void *), void *arg)
282 {
283 struct rfc2822_line_cb *cb;
284
285 cb = &rp->body_line_cb;
286 cb->func = func;
287 cb->arg = arg;
288 }
289
290 void
rfc2822_eoh_callback(struct rfc2822_parser * rp,void (* func)(void *),void * arg)291 rfc2822_eoh_callback(struct rfc2822_parser *rp,
292 void (*func)(void *), void *arg)
293 {
294 struct rfc2822_eoh_cb *cb;
295
296 cb = &rp->eoh_cb;
297 cb->func = func;
298 cb->arg = arg;
299 }
300
301