Commit af3dcca9 af3dcca9d5fa24d7086a2239ddbcec583b5e9c45 by Alain Magloire

header.c

rewrite to collapse rfc822.c
1 parent 985d3a58
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 }