1 /* $OpenBSD: rfc5322.c,v 1.4 2023/12/05 13:38:25 op Exp $ */
2
3 /*
4 * Copyright (c) 2018 Eric Faurot <eric@openbsd.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 <errno.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "rfc5322.h"
25
26 struct buf {
27 char *buf;
28 size_t bufsz;
29 size_t buflen;
30 size_t bufmax;
31 };
32
33 static int buf_alloc(struct buf *, size_t);
34 static int buf_grow(struct buf *, size_t);
35 static int buf_cat(struct buf *, const char *);
36
37 struct rfc5322_parser {
38 const char *line;
39 int state; /* last parser state */
40 int next; /* parser needs data */
41 int unfold;
42 const char *currhdr;
43 struct buf hdr;
44 struct buf val;
45 };
46
47 struct rfc5322_parser *
rfc5322_parser_new(void)48 rfc5322_parser_new(void)
49 {
50 struct rfc5322_parser *parser;
51
52 parser = calloc(1, sizeof(*parser));
53 if (parser == NULL)
54 return NULL;
55
56 rfc5322_clear(parser);
57 parser->hdr.bufmax = 1024;
58 parser->val.bufmax = 65536;
59
60 return parser;
61 }
62
63 void
rfc5322_free(struct rfc5322_parser * parser)64 rfc5322_free(struct rfc5322_parser *parser)
65 {
66 free(parser->hdr.buf);
67 free(parser->val.buf);
68 free(parser);
69 }
70
71 void
rfc5322_clear(struct rfc5322_parser * parser)72 rfc5322_clear(struct rfc5322_parser *parser)
73 {
74 parser->line = NULL;
75 parser->state = RFC5322_NONE;
76 parser->next = 0;
77 parser->hdr.buflen = 0;
78 parser->val.buflen = 0;
79 }
80
81 int
rfc5322_push(struct rfc5322_parser * parser,const char * line)82 rfc5322_push(struct rfc5322_parser *parser, const char *line)
83 {
84 if (parser->line) {
85 errno = EALREADY;
86 return -1;
87 }
88
89 parser->line = line;
90 parser->next = 0;
91
92 return 0;
93 }
94
95 int
rfc5322_unfold_header(struct rfc5322_parser * parser)96 rfc5322_unfold_header(struct rfc5322_parser *parser)
97 {
98 if (parser->unfold) {
99 errno = EALREADY;
100 return -1;
101 }
102
103 if (parser->currhdr == NULL) {
104 errno = EOPNOTSUPP;
105 return -1;
106 }
107
108 if (buf_cat(&parser->val, parser->currhdr) == -1)
109 return -1;
110
111 parser->currhdr = NULL;
112 parser->unfold = 1;
113
114 return 0;
115 }
116
117 static int
_rfc5322_next(struct rfc5322_parser * parser,struct rfc5322_result * res)118 _rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
119 {
120 size_t len;
121 const char *pos, *line;
122
123 line = parser->line;
124
125 switch(parser->state) {
126
127 case RFC5322_HEADER_START:
128 case RFC5322_HEADER_CONT:
129 res->hdr = parser->hdr.buf;
130
131 if (line && (line[0] == ' ' || line[0] == '\t')) {
132 parser->line = NULL;
133 parser->next = 1;
134 if (parser->unfold) {
135 if (buf_cat(&parser->val, "\n") == -1 ||
136 buf_cat(&parser->val, line) == -1)
137 return -1;
138 }
139 res->value = line;
140 return RFC5322_HEADER_CONT;
141 }
142
143 if (parser->unfold) {
144 parser->val.buflen = 0;
145 parser->unfold = 0;
146 res->value = parser->val.buf;
147 }
148 return RFC5322_HEADER_END;
149
150 case RFC5322_NONE:
151 case RFC5322_HEADER_END:
152 if (line && line[0] != ' ' && line[0] != '\t' &&
153 (pos = strchr(line, ':'))) {
154 len = pos - line;
155 if (buf_grow(&parser->hdr, len + 1) == -1)
156 return -1;
157 (void)memcpy(parser->hdr.buf, line, len);
158 parser->hdr.buf[len] = '\0';
159 parser->hdr.buflen = len + 1;
160 parser->line = NULL;
161 parser->next = 1;
162 parser->currhdr = pos + 1;
163 res->hdr = parser->hdr.buf;
164 res->value = pos + 1;
165 return RFC5322_HEADER_START;
166 }
167
168 return RFC5322_END_OF_HEADERS;
169
170 case RFC5322_END_OF_HEADERS:
171 if (line == NULL)
172 return RFC5322_END_OF_MESSAGE;
173
174 if (line[0] == '\0') {
175 parser->line = NULL;
176 parser->next = 1;
177 res->value = line;
178 return RFC5322_BODY_START;
179 }
180
181 errno = EINVAL;
182 return -1;
183
184 case RFC5322_BODY_START:
185 case RFC5322_BODY:
186 if (line == NULL)
187 return RFC5322_END_OF_MESSAGE;
188
189 parser->line = NULL;
190 parser->next = 1;
191 res->value = line;
192 return RFC5322_BODY;
193
194 case RFC5322_END_OF_MESSAGE:
195 errno = ENOMSG;
196 return -1;
197
198 default:
199 errno = EINVAL;
200 return -1;
201 }
202 }
203
204 int
rfc5322_next(struct rfc5322_parser * parser,struct rfc5322_result * res)205 rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
206 {
207 memset(res, 0, sizeof(*res));
208
209 if (parser->next)
210 return RFC5322_NONE;
211
212 return (parser->state = _rfc5322_next(parser, res));
213 }
214
215 static int
buf_alloc(struct buf * b,size_t need)216 buf_alloc(struct buf *b, size_t need)
217 {
218 char *buf;
219 size_t alloc;
220
221 if (b->buf && b->bufsz >= need)
222 return 0;
223
224 if (need >= b->bufmax) {
225 errno = ERANGE;
226 return -1;
227 }
228
229 #define N 256
230 alloc = N * (need / N) + ((need % N) ? N : 0);
231 #undef N
232 buf = reallocarray(b->buf, alloc, 1);
233 if (buf == NULL)
234 return -1;
235
236 b->buf = buf;
237 b->bufsz = alloc;
238
239 return 0;
240 }
241
242 static int
buf_grow(struct buf * b,size_t sz)243 buf_grow(struct buf *b, size_t sz)
244 {
245 if (SIZE_T_MAX - b->buflen <= sz) {
246 errno = ERANGE;
247 return -1;
248 }
249
250 return buf_alloc(b, b->buflen + sz);
251 }
252
253 static int
buf_cat(struct buf * b,const char * s)254 buf_cat(struct buf *b, const char *s)
255 {
256 size_t len = strlen(s);
257
258 if (buf_grow(b, len + 1) == -1)
259 return -1;
260
261 (void)memmove(b->buf + b->buflen, s, len + 1);
262 b->buflen += len;
263 return 0;
264 }
265