]> git.ozlabs.org Git - ccan/blob - ccan/rfc822/rfc822.c
rfc822: Rename RFC822_HDR_BAD_NAME constant
[ccan] / ccan / rfc822 / rfc822.c
1 /* Licensed under LGPLv2.1+ - see LICENSE file for details */
2
3 #include "config.h"
4
5 #include <string.h>
6
7 #include <ccan/str/str.h>
8 #include <ccan/talloc/talloc.h>
9 #include <ccan/list/list.h>
10
11 #include <ccan/rfc822/rfc822.h>
12
13 #if !HAVE_MEMMEM
14 void *memmem(const void *haystack, size_t haystacklen,
15              const void *needle, size_t needlelen)
16 {
17         const char *p, *last;
18
19         p = haystack;
20         last = p + haystacklen - needlelen;
21
22         do {
23                 if (memcmp(p, needle, needlelen) == 0)
24                         return (void *)p;
25         } while (p++ <= last);
26
27         return NULL;
28 }
29 #endif
30
31 static void (*allocation_failure_hook)(const char *);
32
33 static void NORETURN default_allocation_failure(const char *s)
34 {
35         fprintf(stderr, "ccan/rfc822: Allocation failure: %s", s);
36         abort();
37 }
38
39 static void allocation_failure(const char *s)
40 {
41         if (allocation_failure_hook)
42                 (*allocation_failure_hook)(s);
43         else
44                 default_allocation_failure(s);
45 }
46
47 void rfc822_set_allocation_failure_handler(void (*h)(const char *))
48 {
49         allocation_failure_hook = h;
50 }
51
52 #define ALLOC_CHECK(p, r) \
53         do { \
54                 if (!(p)) { \
55                         allocation_failure(__FILE__ ":" stringify(__LINE__)); \
56                         return (r); \
57                 } \
58         } while (0)
59
60 struct rfc822_msg {
61         const char *data, *end;
62         const char *remainder;
63         struct list_head headers;
64         const char *body;
65 };
66
67 struct rfc822_header {
68         struct bytestring all, rawname, rawvalue;
69         struct bytestring unfolded;
70         struct list_node list;
71 };
72
73 struct rfc822_msg *rfc822_check(const struct rfc822_msg *msg,
74                                 const char *abortstr)
75 {
76         assert(msg);
77         if (!list_check(&msg->headers, abortstr))
78                 return NULL;
79         return (struct rfc822_msg *)msg;
80 }
81
82 #ifdef CCAN_RFC822_DEBUG
83 #define CHECK(msg, str) do { rfc822_check((msg), (str)); } while (0)
84 #else
85 #define CHECK(msg, str) do { } while (0)
86 #endif
87
88 struct rfc822_msg *rfc822_start(const void *ctx, const char *p, size_t len)
89 {
90         struct rfc822_msg *msg;
91
92         msg = talloc(ctx, struct rfc822_msg);
93         ALLOC_CHECK(msg, NULL);
94
95         msg->data = p;
96         msg->end = p + len;
97
98         msg->remainder = msg->data;
99         msg->body = NULL;
100
101         list_head_init(&msg->headers);
102
103         CHECK(msg, "<rfc22_start");
104
105         return msg;
106 }
107
108 void rfc822_free(struct rfc822_msg *msg)
109 {
110         CHECK(msg, ">rfc822_free");
111         talloc_free(msg);
112 }
113
114 static struct rfc822_header *next_header_cached(struct rfc822_msg *msg,
115                                                 struct rfc822_header *hdr)
116 {
117         struct list_node *h = &msg->headers.n;
118         const struct list_node *n = h;
119
120         CHECK(msg, ">next_header_cached");
121
122         if (hdr)
123                 n = &hdr->list;
124
125         if (n->next == h)
126                 return NULL;
127
128         CHECK(msg, "<next_header_cached");
129
130         return list_entry(n->next, struct rfc822_header, list);
131 }
132
133 static const char *next_line(const char *start, const char *end)
134 {
135         const char *p = memchr(start, '\n', end - start);
136
137         return p ? (p + 1) : end;
138 }
139
140 static struct rfc822_header *next_header_parse(struct rfc822_msg *msg)
141 {
142         const char *h, *eh, *ev, *colon;
143         struct rfc822_header *hi;
144
145         CHECK(msg, ">next_header_parse");
146
147         if (!msg->remainder)
148                 return NULL;
149
150         if (msg->body && (msg->remainder >= msg->body))
151                 return NULL;
152
153         h = msg->remainder;
154         eh = next_line(h, msg->end);
155
156         ev = eh;
157         if ((ev > h) && (ev[-1] == '\n'))
158                 ev--;
159         if ((ev > h) && (ev[-1] == '\r'))
160                 ev--;
161         if (ev == h) {
162                 /* Found the end of the headers */
163
164                 assert(!msg->body || (msg->body == eh));
165
166                 if (eh < msg->end)
167                         msg->body = eh;
168                 return NULL;
169         }
170
171         while ((eh < msg->end) && rfc822_iswsp(*eh))
172                 eh = next_line(eh, msg->end);
173
174         if (eh >= msg->end)
175                 msg->remainder = NULL;
176         else
177                 msg->remainder = eh;
178
179
180         hi = talloc_zero(msg, struct rfc822_header);
181         ALLOC_CHECK(hi, NULL);
182
183         hi->all = bytestring(h, eh - h);
184         list_add_tail(&msg->headers, &hi->list);
185
186         colon = memchr(h, ':', hi->all.len);
187         if (colon) {
188                 hi->rawname = bytestring(h, colon - h);
189                 hi->rawvalue = bytestring(colon + 1, eh - colon - 1);
190         } else {
191                 hi->rawname = bytestring_NULL;
192                 hi->rawvalue = bytestring_NULL;
193         }
194
195         CHECK(msg, "<next_header_parse");
196
197         return hi;
198 }
199
200 struct rfc822_header *rfc822_next_header(struct rfc822_msg *msg,
201                                          struct rfc822_header *hdr)
202 {
203         struct rfc822_header *h;
204
205         CHECK(msg, ">rfc822_next_header");
206
207         h = next_header_cached(msg, hdr);
208         if (h)
209                 return h;
210
211         return next_header_parse(msg);
212 }
213
214 struct bytestring rfc822_body(struct rfc822_msg *msg)
215 {
216         CHECK(msg, ">rfc822_body");
217
218         if (!msg->body && msg->remainder) {
219                 const char *p, *q;
220
221                 p = memmem(msg->remainder, msg->end - msg->remainder,
222                            "\n\r\n", 3);
223                 q = memmem(msg->remainder, msg->end - msg->remainder,
224                            "\n\n", 2);
225
226                 if (p && (!q || (p < q)))
227                         msg->body = p + 3;
228                 else if (q && (!p || (q < p)))
229                         msg->body = q + 2;
230
231                 if (msg->body >= msg->end) {
232                         assert(msg->body == msg->end);
233                         msg->body = NULL;
234                 }
235         }
236
237         CHECK(msg, "<rfc822_body");
238
239         if (msg->body)
240                 return bytestring(msg->body, msg->end - msg->body);
241         else
242                 return bytestring_NULL;
243 }
244
245 enum rfc822_header_errors rfc822_header_errors(struct rfc822_msg *msg,
246                                                struct rfc822_header *hdr)
247 {
248         enum rfc822_header_errors err = 0;
249         int i;
250
251         if (!hdr->rawname.ptr) {
252                 err |= RFC822_HDR_NO_COLON;
253         } else {
254                 for (i = 0; i < hdr->rawname.len; i++) {
255                         char c = hdr->rawname.ptr[i];
256
257                         assert(c != ':');
258
259                         if ((c < 33) || (c > 126)) {
260                                 err |= RFC822_HDR_BAD_NAME_CHARS;
261                                 break;
262                         }
263                 }
264         }
265         return err;
266 }
267
268 struct bytestring rfc822_header_raw_content(struct rfc822_msg *msg,
269                                             struct rfc822_header *hdr)
270 {
271         return hdr->all;
272 }
273
274 struct bytestring rfc822_header_raw_name(struct rfc822_msg *msg,
275                                          struct rfc822_header *hdr)
276 {
277         return hdr->rawname;
278 }
279
280 struct bytestring rfc822_header_raw_value(struct rfc822_msg *msg,
281                                           struct rfc822_header *hdr)
282 {
283         return hdr->rawvalue;
284 }
285
286 static void get_line(struct bytestring in, struct bytestring *first,
287                      struct bytestring *rest)
288 {
289         size_t rawlen, trimlen;
290         const char *inp = in.ptr;
291         const char *nl;
292
293         nl = memchr(inp, '\n', in.len);
294         if (!nl)
295                 rawlen = in.len;
296         else
297                 rawlen = nl - inp + 1;
298
299         trimlen = rawlen;
300         if ((trimlen > 0) && (inp[trimlen-1] == '\n')) {
301                 trimlen--;
302                 if ((trimlen > 0) && (inp[trimlen-1] == '\r'))
303                         trimlen--;
304         }
305
306         *first = bytestring(in.ptr, trimlen);
307
308         if (rawlen < in.len)
309                 *rest = bytestring(in.ptr + rawlen, in.len - rawlen);
310         else
311                 *rest = bytestring_NULL;
312 }
313
314
315 struct bytestring rfc822_header_unfolded_value(struct rfc822_msg *msg,
316                                                struct rfc822_header *hdr)
317 {
318         struct bytestring raw = rfc822_header_raw_value(msg, hdr);
319         struct bytestring next, rest;
320         int lines = 0;
321         size_t len = 0;
322
323         if (!hdr->unfolded.ptr) {
324                 rest = raw;
325                 while (rest.ptr) {
326                         get_line(rest, &next, &rest);
327                         lines++;
328                         len += next.len;
329                 }
330
331                 if (lines <= 1) {
332                         hdr->unfolded = bytestring(raw.ptr, len);
333                 } else {
334                         char *unfold = talloc_array(msg, char, len);
335                         char *p = unfold;
336
337                         ALLOC_CHECK(unfold, bytestring_NULL);
338
339                         rest = raw;
340                         while (rest.ptr) {
341                                 get_line(rest, &next, &rest);
342                                 memcpy(p, next.ptr, next.len);
343                                 p += next.len;
344                         }
345
346                         assert(p == (unfold + len));
347                         hdr->unfolded = bytestring(unfold, len);
348                 }
349         }
350
351         return hdr->unfolded;
352 }