header.c
rewrite to collapse rfc822.c
Showing
1 changed file
with
403 additions
and
0 deletions
mailbox/header.c
0 → 100644
1 | /* GNU mailutils - a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000 Free Software Foundation, Inc. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Library Public License as published by | ||
6 | the Free Software Foundation; either version 2, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU Library General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this program; if not, write to the Free Software | ||
16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | ||
17 | |||
18 | |||
19 | #ifdef HAVE_CONFIG_H | ||
20 | # include "config.h" | ||
21 | #endif | ||
22 | |||
23 | #include <header.h> | ||
24 | #include <io0.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <errno.h> | ||
28 | |||
29 | static int header_parse (header_t *h, const char *blurb, size_t len); | ||
30 | /* | ||
31 | static int rfc822_set_value (header_t h, const char *fn, const char *fb, | ||
32 | size_t n, int replace); | ||
33 | static int rfc822_get_value (header_t h, const char *fn, char *fb, | ||
34 | size_t len, size_t *n); | ||
35 | static int rfc822_entry_count (header_t, size_t *num); | ||
36 | static int rfc822_entry_name (header_t h, size_t num, char *buf, | ||
37 | size_t buflen, size_t *total); | ||
38 | static int rfc822_entry_value (header_t h, size_t num, char *buf, | ||
39 | size_t buflen, size_t *total); | ||
40 | static int rfc822_get_istream (header_t h, istream_t *pis); | ||
41 | static int rfc822_get_ostream (header_t h, ostream_t *pos); | ||
42 | */ | ||
43 | static int header_read (istream_t is, char *buf, size_t buflen, | ||
44 | off_t off, ssize_t *pnread); | ||
45 | |||
46 | struct _hdr | ||
47 | { | ||
48 | char *fn; | ||
49 | char *fn_end; | ||
50 | char *fv; | ||
51 | char *fv_end; | ||
52 | }; | ||
53 | |||
54 | struct _header | ||
55 | { | ||
56 | /* Data */ | ||
57 | char *blurb; | ||
58 | size_t blurb_len; | ||
59 | size_t hdr_count; | ||
60 | struct _hdr *hdr; | ||
61 | |||
62 | /* streams */ | ||
63 | istream_t is; | ||
64 | ostream_t os; | ||
65 | |||
66 | size_t num; | ||
67 | |||
68 | /* owner ? */ | ||
69 | void *owner; | ||
70 | int ref_count; | ||
71 | }; | ||
72 | |||
73 | |||
74 | |||
75 | int | ||
76 | header_init (header_t *ph, const char *blurb, size_t len, void *owner) | ||
77 | { | ||
78 | header_t h; | ||
79 | int status; | ||
80 | h = calloc (1, sizeof (*h)); | ||
81 | if (h == NULL) | ||
82 | return ENOMEM; | ||
83 | h->owner = owner; | ||
84 | |||
85 | status = header_parse (&h, blurb, len); | ||
86 | if (status != 0) | ||
87 | free (h); | ||
88 | *ph = h; | ||
89 | return status; | ||
90 | } | ||
91 | |||
92 | void | ||
93 | header_destroy (header_t *ph, void *owner) | ||
94 | { | ||
95 | if (ph && *ph) | ||
96 | { | ||
97 | header_t h = *ph; | ||
98 | |||
99 | /* if destroy is call always decremente */ | ||
100 | h->ref_count--; | ||
101 | |||
102 | /* can we destroy ? */ | ||
103 | if ((h->owner && h->owner == owner) || | ||
104 | (h->owner == NULL && h->ref_count <= 0)) | ||
105 | { | ||
106 | /* io */ | ||
107 | istream_destroy (&(h->is), owner); | ||
108 | ostream_destroy (&(h->os), owner); | ||
109 | |||
110 | free (h->hdr); | ||
111 | free (h->blurb); | ||
112 | free (h); | ||
113 | } | ||
114 | *ph = NULL; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Parsing is done in a rather simple fashion. | ||
120 | * meaning we just consider an entry to be | ||
121 | * a field-name an a field-value. So they | ||
122 | * maybe duplicate of field-name like "Received" | ||
123 | * they are just put in the array, see _get_value() | ||
124 | * on how to handle the case. | ||
125 | */ | ||
126 | static int | ||
127 | header_parse (header_t *h, const char *blurb, size_t len) | ||
128 | { | ||
129 | header_t header; | ||
130 | char *header_end; | ||
131 | char *header_start; | ||
132 | char *header_start2; | ||
133 | struct _hdr *hdr; | ||
134 | |||
135 | if (h == NULL || blurb == NULL || len == 0) | ||
136 | return EINVAL; | ||
137 | |||
138 | header = calloc (1, sizeof (*header)); | ||
139 | if (header == NULL) | ||
140 | return ENOMEM; | ||
141 | |||
142 | header->blurb = calloc (1, len); | ||
143 | if (header->blurb == NULL) | ||
144 | { | ||
145 | free (header); | ||
146 | return ENOMEM; | ||
147 | } | ||
148 | header->blurb_len = len; | ||
149 | memcpy (header->blurb, blurb, len); | ||
150 | |||
151 | for (header_start = header->blurb;; header_start = ++header_end) | ||
152 | { | ||
153 | /* get a header, a header is : | ||
154 | * field-name ':' ' ' field-value '\r' '\n' | ||
155 | * [ (' ' | '\t') field-value '\r' '\n' ] | ||
156 | */ | ||
157 | for (header_start2 = header_start;;header_start2 = ++header_end) | ||
158 | { | ||
159 | header_end = memchr (header_start2, '\n', len); | ||
160 | if (header_end == NULL) | ||
161 | break; | ||
162 | else | ||
163 | { | ||
164 | len -= (header_end - header_start2 + 1); | ||
165 | if (len == 0) | ||
166 | { | ||
167 | header_end = NULL; | ||
168 | break; | ||
169 | } | ||
170 | if (header_end[1] != ' ' | ||
171 | && header_end[1] != '\t') | ||
172 | break; /* new header break the inner for */ | ||
173 | } | ||
174 | /* *header_end = ' '; smash LF */ | ||
175 | } | ||
176 | |||
177 | if (header_end == NULL) | ||
178 | break; /* bail out */ | ||
179 | |||
180 | hdr = realloc (header->hdr, (header->hdr_count + 1) * sizeof (*hdr)); | ||
181 | if (hdr == NULL) | ||
182 | { | ||
183 | free (header->blurb); | ||
184 | free (header->hdr); | ||
185 | free (header); | ||
186 | return ENOMEM; | ||
187 | } | ||
188 | header->hdr = hdr; | ||
189 | header->hdr_count++; | ||
190 | /* Treats unix "From " specially */ | ||
191 | if ((header_end - header_start >= 5) | ||
192 | && strncmp (header_start, "From ", 5) == 0) | ||
193 | { | ||
194 | header->hdr[header->hdr_count - 1].fn = header_start; | ||
195 | header->hdr[header->hdr_count - 1].fn_end = header_start + 6; | ||
196 | header->hdr[header->hdr_count - 1].fv = header_start + 6; | ||
197 | header->hdr[header->hdr_count - 1].fv_end = header_end; | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | char *colon = memchr (header_start, ':', header_end - header_start); | ||
202 | if (colon == NULL) | ||
203 | { | ||
204 | /* Houston we have a problem */ | ||
205 | free (header->blurb); | ||
206 | free (header->hdr); | ||
207 | free (header); | ||
208 | return EINVAL; | ||
209 | } | ||
210 | header->hdr[header->hdr_count - 1].fn = header_start; | ||
211 | header->hdr[header->hdr_count - 1].fn_end = colon; | ||
212 | /* skip leading spaces */ | ||
213 | while (*(++colon) == ' '); | ||
214 | header->hdr[header->hdr_count - 1].fv = colon; | ||
215 | header->hdr[header->hdr_count - 1].fv_end = header_end; | ||
216 | } | ||
217 | } | ||
218 | *h = header; | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | int | ||
223 | header_set_value (header_t h, const char *fn, const char *fv, | ||
224 | size_t n, int replace) | ||
225 | { | ||
226 | (void)h; (void)fn; (void)fv; (void)n; (void)replace; | ||
227 | return ENOSYS; | ||
228 | } | ||
229 | |||
230 | int | ||
231 | header_get_value (header_t header, const char *name, char *buffer, | ||
232 | size_t buflen, size_t *n) | ||
233 | { | ||
234 | size_t i = 0; | ||
235 | size_t name_len; | ||
236 | size_t total = 0, fn_len = 0, fv_len = 0; | ||
237 | int threshold; | ||
238 | |||
239 | if (header == NULL || name == NULL) | ||
240 | return EINVAL; | ||
241 | |||
242 | /* we set the threshold to be 1 less for the null */ | ||
243 | threshold = --buflen; | ||
244 | |||
245 | /* | ||
246 | * Caution: We may have more then one value for a field | ||
247 | * name, for example a "Received" field-name is added by | ||
248 | * each passing MTA. The way that the parsing (_parse()) | ||
249 | * is done it's not take to account. So we just stuff in | ||
250 | * the buffer all the field-values to a corresponding field-name. | ||
251 | * FIXME: Should we kosher the output ? meaning replace | ||
252 | * occurences of " \t\r\n" for spaces ? for now we don't. | ||
253 | */ | ||
254 | for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) | ||
255 | { | ||
256 | fn_len = header->hdr[i].fn_end - header->hdr[i].fn; | ||
257 | if (fn_len == name_len && memcmp (header->hdr[i].fn, name, fn_len) == 0) | ||
258 | { | ||
259 | fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); | ||
260 | total += fv_len; | ||
261 | /* can everything fit in the buffer */ | ||
262 | if (buffer && threshold > 0) | ||
263 | { | ||
264 | threshold -= fv_len; | ||
265 | if (threshold > 0) | ||
266 | { | ||
267 | memcpy (buffer, header->hdr[i].fv, fv_len); | ||
268 | buffer += fv_len; | ||
269 | } | ||
270 | else if (threshold < 0) | ||
271 | { | ||
272 | threshold += fv_len; | ||
273 | memcpy (buffer, header->hdr[i].fv, threshold); | ||
274 | buffer += threshold; | ||
275 | threshold = 0; | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | if (buffer) | ||
281 | *buffer = '\0'; /* null terminated */ | ||
282 | if (n) | ||
283 | *n = total; | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | int | ||
288 | header_entry_count (header_t header, size_t *pnum) | ||
289 | { | ||
290 | if (header == NULL) | ||
291 | return EINVAL; | ||
292 | if (pnum) | ||
293 | *pnum = header->hdr_count; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | int | ||
298 | header_size (header_t header, size_t *pnum) | ||
299 | { | ||
300 | if (header == NULL) | ||
301 | return EINVAL; | ||
302 | if (pnum) | ||
303 | *pnum = header->blurb_len; | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | int | ||
308 | header_entry_name (header_t header, size_t num, char *buf, | ||
309 | size_t buflen, size_t *nwritten) | ||
310 | { | ||
311 | size_t len; | ||
312 | if (header == NULL) | ||
313 | return EINVAL; | ||
314 | if (header->hdr_count == 0 || num > header->hdr_count) | ||
315 | return ENOENT; | ||
316 | len = header->hdr[num].fn_end - header->hdr[num].fn; | ||
317 | /* save one for the null */ | ||
318 | --buflen; | ||
319 | if (buf && buflen > 0) | ||
320 | { | ||
321 | buflen = (len > buflen) ? buflen : len; | ||
322 | memcpy (buf, header->hdr[num].fn, buflen); | ||
323 | buf[buflen] = '\0'; | ||
324 | } | ||
325 | if (nwritten) | ||
326 | *nwritten = len; | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | int | ||
331 | header_entry_value (header_t header, size_t num, char *buf, | ||
332 | size_t buflen, size_t *nwritten) | ||
333 | { | ||
334 | size_t len; | ||
335 | if (header == NULL) | ||
336 | return EINVAL; | ||
337 | if (header->hdr_count == 0 || num > header->hdr_count) | ||
338 | return ENOENT; | ||
339 | len = header->hdr[num].fv_end - header->hdr[num].fv; | ||
340 | /* save one for the null */ | ||
341 | --buflen; | ||
342 | if (buf && buflen > 0) | ||
343 | { | ||
344 | buflen = (len > buflen) ? buflen : len; | ||
345 | memcpy (buf, header->hdr[num].fv, buflen); | ||
346 | buf[buflen] = '\0'; | ||
347 | } | ||
348 | if (nwritten) | ||
349 | *nwritten = len; | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int | ||
354 | header_read (istream_t is, char *buf, size_t buflen, | ||
355 | off_t off, ssize_t *pnread) | ||
356 | { | ||
357 | header_t header; | ||
358 | ssize_t len; | ||
359 | |||
360 | if (is == NULL || (header = (header_t)is->owner) == NULL) | ||
361 | return EINVAL; | ||
362 | |||
363 | len = header->blurb_len - off; | ||
364 | if ((header->blurb_len - off) > 0) | ||
365 | { | ||
366 | if (buf) | ||
367 | { | ||
368 | len = (buflen < (size_t)len) ? buflen : len; | ||
369 | memcpy (buf, header->blurb + off, len); | ||
370 | } | ||
371 | } | ||
372 | else | ||
373 | len = 0; | ||
374 | |||
375 | if (pnread) | ||
376 | *pnread = len; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | int | ||
381 | header_get_istream (header_t header, istream_t *pis) | ||
382 | { | ||
383 | int err; | ||
384 | if (header == NULL || pis == NULL) | ||
385 | return EINVAL; | ||
386 | /* already done */ | ||
387 | if (header->is) | ||
388 | *pis = header->is; | ||
389 | |||
390 | err = istream_init (&(header->is), header_read, header->owner); | ||
391 | if (err != 0) | ||
392 | return err; | ||
393 | *pis = header->is; | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | int | ||
398 | rfc822_get_ostream (header_t header, ostream_t *pos) | ||
399 | { | ||
400 | if (header == NULL || pos == NULL) | ||
401 | return EINVAL; | ||
402 | return ENOSYS; | ||
403 | } |
-
Please register or sign in to post a comment