Commit 344611f5 344611f57409fc78699198b165c8acf986aceb65 by Sergey Poznyakoff

Rewrite from scratch

1 parent 2039d455
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
20 #define _HEADER0_H 20 #define _HEADER0_H
21 21
22 #ifdef DMALLOC 22 #ifdef DMALLOC
23 # include <dmalloc.h> 23 # include <dmalloc.h>
24 #endif 24 #endif
25 25
26 #include <mailutils/header.h> 26 #include <mailutils/header.h>
...@@ -33,38 +33,42 @@ extern "C" { ...@@ -33,38 +33,42 @@ extern "C" {
33 33
34 /* The structure members are offset that point to the begin/end of header 34 /* The structure members are offset that point to the begin/end of header
35 fields. */ 35 fields. */
36 struct _hdr 36 struct mu_hdrent
37 { 37 {
38 char *fn; 38 struct mu_hdrent *prev;
39 char *fn_end; 39 struct mu_hdrent *next;
40 char *fv; 40 size_t fn;
41 char *fv_end; 41 size_t nlen;
42 size_t fv;
43 size_t vlen;
44 size_t nlines;
42 }; 45 };
43 46
44 /* The blurb member represents the headers, hdr_count the number of distinct
45 header field and the layout is done by struct_hdr *hdr. */
46 struct _mu_header 47 struct _mu_header
47 { 48 {
48 /* Owner. */ 49 /* Owner. */
49 void *owner; 50 void *owner;
50 51
51 /* Data. */ 52 /* Data. */
52 mu_stream_t mstream; 53 char *spool;
53 size_t stream_len; 54 size_t spool_size;
54 char *blurb; 55 size_t spool_used;
55 size_t blurb_len; 56 struct mu_hdrent *head, *tail;
56 size_t hdr_count;
57 struct _hdr *hdr;
58 int flags; 57 int flags;
59 58
60 mu_assoc_t cache; 59 size_t numhdr;
60 size_t numlines;
61 size_t size;
62
63 /* Temporary storage */
64 mu_stream_t mstream;
65 size_t mstream_size;
61 66
62 /* Stream. */ 67 /* Stream. */
63 mu_stream_t stream; 68 mu_stream_t stream;
64 int (*_get_value) (mu_header_t, const char *, char *, size_t , size_t *); 69 size_t strpos;
65 int (*_set_value) (mu_header_t, const char *, const char *, int); 70
66 int (*_lines) (mu_header_t, size_t *); 71 /* Methods */
67 int (*_size) (mu_header_t, size_t *);
68 int (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *); 72 int (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *);
69 }; 73 };
70 74
......
...@@ -17,6 +17,17 @@ ...@@ -17,6 +17,17 @@
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301 USA */ 18 Boston, MA 02110-1301 USA */
19 19
20 /* This all header business needs a good rewrite.
21 * -- Alain Magloire, 2000-07-03 (rev. 1.21)
22 *
23 * It's the job that's never started as takes longest to finish.
24 * -- Hamfast Gamgee, some time in the Third Age
25 *
26 * It took almost 7 years to gather the courage to start the job,
27 * and only one day to finish it.
28 * -- Sergey Poznyakoff, 2007-06-24
29 */
30
20 #ifdef HAVE_CONFIG_H 31 #ifdef HAVE_CONFIG_H
21 # include <config.h> 32 # include <config.h>
22 #endif 33 #endif
...@@ -37,120 +48,267 @@ ...@@ -37,120 +48,267 @@
37 48
38 #include <header0.h> 49 #include <header0.h>
39 50
40 #define HEADER_MODIFIED 1 51 #define HEADER_MODIFIED 0x01
52 #define HEADER_INVALIDATE 0x02
41 53
42 static int header_parse (mu_header_t, const char *, int); 54 #define HEADER_SET_MODIFIED(h) \
43 static int header_read (mu_stream_t, char *, size_t, mu_off_t, size_t *); 55 ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
44 static int header_readline (mu_stream_t, char *, size_t, mu_off_t, size_t *);
45 static int header_write (mu_stream_t, const char *, size_t, mu_off_t, size_t *);
46 static int fill_blurb (mu_header_t);
47 static void header_free_cache (mu_header_t);
48 56
49 int 57
50 mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) 58 /* mu_hdrent manipulation */
59
60 #define MU_HDRENT_NAME(hp,ep) ((hp)->spool + (ep)->fn)
61 #define MU_HDRENT_VALUE(hp,ep) ((hp)->spool + (ep)->fv)
62 #define MU_STR_SIZE(nlen,vlen) ((nlen) + 2 + (vlen) + 1)
63
64 static struct mu_hdrent *
65 mu_hdrent_nth (struct _mu_header *hdr, int n)
51 { 66 {
52 mu_header_t header; 67 struct mu_hdrent *p;
53 int status = 0; 68 for (p = hdr->head; p; p = p->next)
69 if (n-- == 1)
70 break;
71 return p;
72 }
54 73
55 header = calloc (1, sizeof (*header)); 74 static struct mu_hdrent *
56 if (header == NULL) 75 mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos)
57 return ENOMEM; 76 {
77 struct mu_hdrent *p;
78 for (p = hdr->head; p; p = p->next)
79 if (strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1)
80 break;
81 return p;
82 }
58 83
59 header->owner = owner; 84 static int
85 mu_hdrent_find_stream_pos (struct _mu_header *hdr, mu_off_t pos,
86 struct mu_hdrent **pent, size_t *poff)
87 {
88 mu_off_t x;
89 struct mu_hdrent *p;
60 90
61 status = header_parse (header, blurb, len); 91 for (p = hdr->head, x = 0; p; p = p->next)
92 {
93 size_t strsize = MU_STR_SIZE (p->nlen, p->vlen);
94 if (x <= pos && pos < x + strsize)
95 {
96 *poff = pos - x;
97 *pent = p;
98 return 0;
99 }
100 x += strsize;
101 }
102 if (x == pos)
103 {
104 /* To supply the trailing '\n' */
105 p = hdr->tail;
106 *pent = p;
107 *poff = MU_STR_SIZE (p->nlen, p->vlen) - 1;
108 return 0;
109 }
110 return 1;
111 }
112
113 static void
114 mu_hdrent_count (struct _mu_header *hdr, size_t *pcount, size_t *psize,
115 size_t *plines)
116 {
117 if (hdr->flags & HEADER_INVALIDATE)
118 {
119 size_t size = 0;
120 size_t count = 0;
121 size_t lines = 0;
122 struct mu_hdrent *p;
123 for (p = hdr->head; p; p = p->next)
124 {
125 count++;
126 size += MU_STR_SIZE (p->nlen, p->vlen);
127 lines += p->nlines;
128 }
62 129
63 *ph = header; 130 hdr->numhdr = count;
64 return status; 131 hdr->numlines = lines;
132 hdr->size = size;
133 hdr->flags &= ~HEADER_INVALIDATE;
134 }
135
136 *pcount = hdr->numhdr;
137 *psize = hdr->size;
138 *plines = hdr->numlines;
65 } 139 }
66 140
67 static void 141 static void
68 header_free_cache (mu_header_t header) 142 mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent)
69 { 143 {
70 mu_assoc_clear (header->cache); 144 struct mu_hdrent *p = ent->prev;
145 if (p)
146 p->next = ent->next;
147 else
148 hdr->head = ent->next;
149
150 p = ent->next;
151 if (p)
152 p->prev = ent->prev;
153 else
154 hdr->tail = ent->prev;
71 } 155 }
72 156
73 void 157 static void
74 mu_header_destroy (mu_header_t *ph, void *owner) 158 mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent)
75 { 159 {
76 if (ph && *ph) 160 struct mu_hdrent *p = hdr->head;
77 { 161 ent->prev = NULL;
78 mu_header_t header = *ph; 162 ent->next = p;
163 if (p)
164 p->prev = ent;
165 else
166 hdr->tail = ent;
167 hdr->head = ent;
168 }
79 169
80 /* Can we destroy ?. */ 170 static void
81 if (header->owner == owner) 171 mu_hdrent_append (struct _mu_header *hdr, struct mu_hdrent *ent)
172 {
173 struct mu_hdrent *p = hdr->tail;
174 ent->next = NULL;
175 ent->prev = p;
176 if (p)
177 p->next = ent;
178 else
179 hdr->head = ent;
180 hdr->tail = ent;
181 }
182
183 static int
184 mu_hdrent_insert (struct _mu_header *hdr, struct mu_hdrent *ent,
185 const char *name, int pos, int before)
186 {
187 struct mu_hdrent *p;
188 struct mu_hdrent *ref = mu_hdrent_find (hdr, name, pos);
189 if (!ref)
190 return MU_ERR_NOENT;
191
192 if (before)
193 {
194 ref = ref->prev;
195 if (!ref)
82 { 196 {
83 mu_stream_destroy (&(header->stream), header); 197 mu_hdrent_prepend (hdr, ent);
198 return 0;
199 }
200 }
84 201
85 if (header->hdr) 202 p = ref->next;
86 free (header->hdr); 203 if (!p)
204 {
205 mu_hdrent_append (hdr, ent);
206 return 0;
207 }
87 208
88 if (header->blurb) 209 ent->next = p;
89 free (header->blurb); 210 p->prev = ent;
211 ent->prev = ref;
212 ref->next = ent;
213
214 return 0;
215 }
90 216
91 header_free_cache (header); 217 #define SPOOLBLKSIZ 1024
92 218
93 mu_assoc_destroy (&header->cache); 219 static struct mu_hdrent *
94 220 mu_hdrent_create (struct _mu_header *ph,
95 if (header->mstream) 221 struct mu_hdrent *ent,
96 mu_stream_destroy (&(header->mstream), NULL); 222 const char *name, size_t nsize,
223 const char *value, size_t vsize)
224 {
225 size_t strsize;
226 size_t sizeleft;
227 char *p;
228
229 if (!ent)
230 {
231 ent = calloc (1, sizeof (*ent));
232 if (!ent)
233 return NULL;
234 }
235
236 strsize = MU_STR_SIZE (nsize, vsize);
237 sizeleft = ph->spool_size - ph->spool_used;
97 238
98 free (header); 239 /* Ensure there is enough space in spool */
99 } 240 if (sizeleft < strsize)
100 *ph = NULL; 241 {
242 char *newp;
243 size_t delta = (strsize - sizeleft + SPOOLBLKSIZ - 1) / SPOOLBLKSIZ;
244 delta *= SPOOLBLKSIZ;
245 newp = realloc (ph->spool, ph->spool_size + delta);
246 if (!newp)
247 return 0;
248 ph->spool = newp;
249 ph->spool_size += delta;
101 } 250 }
102 }
103 251
104 void * 252 /* Copy header name */
105 mu_header_get_owner (mu_header_t header) 253 ent->fn = ph->spool_used;
106 { 254 ent->nlen = nsize;
107 return (header) ? header->owner : NULL; 255 memcpy (ph->spool + ph->spool_used, name, nsize);
256 ph->spool_used += nsize;
257 ph->spool[ph->spool_used++] = 0;
258 ph->spool[ph->spool_used++] = ' ';
259
260 /* Copy header value */
261 ent->fv = ph->spool_used;
262 ent->vlen = vsize;
263 memcpy (ph->spool + ph->spool_used, value, vsize);
264 ph->spool_used += vsize;
265 ph->spool[ph->spool_used++] = 0;
266
267 ent->nlines = 1;
268 for (p = value; p < value + vsize; p++)
269 if (*p == '\n')
270 ent->nlines++;
271
272 return ent;
108 } 273 }
109 274
110 int 275 static void
111 mu_header_is_modified (mu_header_t header) 276 mu_hdrent_free_list (struct _mu_header *hdr)
112 { 277 {
113 return (header) ? (header->flags & HEADER_MODIFIED) : 0; 278 struct mu_hdrent *p;
279 for (p = hdr->head; p; )
280 {
281 struct mu_hdrent *next = p->next;
282 free (p);
283 p = next;
284 }
285 hdr->head = hdr->tail = NULL;
286 hdr->spool_used = 0;
114 } 287 }
288
115 289
116 int 290
117 mu_header_clear_modified (mu_header_t header) 291 #define ISLWSP(c) (((c) == ' ' || (c) == '\t'))
118 {
119 if (header)
120 header->flags &= ~HEADER_MODIFIED;
121 return 0;
122 }
123 292
124 /* Parsing is done in a rather simple fashion, meaning we just consider an 293 /* Parsing is done in a rather simple fashion, meaning we just consider an
125 entry to be a field-name an a field-value. So they maybe duplicate of 294 entry to be a field-name an a field-value. So they maybe duplicate of
126 field-name like "Received" they are just put in the array, see _get_value() 295 field-name like "Received" they are just put in the array, see _get_value()
127 on how to handle the case. in the case of error .i.e a bad header construct 296 on how to handle the case. in the case of error .i.e a bad header construct
128 we do a full stop and return what we have so far. */ 297 we do a full stop and return what we have so far. */
298
129 static int 299 static int
130 header_parse (mu_header_t header, const char *blurb, int len) 300 header_parse (mu_header_t header, const char *blurb, int len)
131 { 301 {
132 char *header_end; 302 const char *header_end;
133 char *header_start; 303 const char *header_start;
134 char *header_start2; 304 const char *header_start2;
135 struct _hdr *hdr; 305
136
137 header_free_cache (header);
138
139 /* Nothing to parse. */ 306 /* Nothing to parse. */
140 if (blurb == NULL) 307 if (blurb == NULL)
141 return 0; 308 return 0;
142
143 header->blurb_len = len;
144 /* Why "+ 1", if for a terminating NULL, where is written? */
145 header->blurb = calloc (1, header->blurb_len + 1);
146 if (header->blurb == NULL)
147 return ENOMEM;
148 memcpy (header->blurb, blurb, header->blurb_len);
149 309
150 if (header->hdr) 310 header->flags |= HEADER_INVALIDATE;
151 free (header->hdr); 311 mu_hdrent_free_list (header);
152 header->hdr = NULL;
153 header->hdr_count = 0;
154 312
155 /* Get a header, a header is: 313 /* Get a header, a header is:
156 field-name LWSP ':' 314 field-name LWSP ':'
...@@ -158,9 +316,10 @@ header_parse (mu_header_t header, const char *blurb, int len) ...@@ -158,9 +316,10 @@ header_parse (mu_header_t header, const char *blurb, int len)
158 *[ (' ' | '\t') field-value '\r' '\n' ] 316 *[ (' ' | '\t') field-value '\r' '\n' ]
159 */ 317 */
160 /* First loop goes through the blurb */ 318 /* First loop goes through the blurb */
161 for (header_start = header->blurb; ; header_start = ++header_end) 319 for (header_start = blurb; ; header_start = ++header_end)
162 { 320 {
163 char *fn, *fn_end, *fv, *fv_end; 321 const char *fn, *fn_end, *fv, *fv_end;
322 struct mu_hdrent *ent;
164 323
165 if (header_start[0] == ' ' 324 if (header_start[0] == ' '
166 || header_start[0] == '\t' 325 || header_start[0] == '\t'
...@@ -189,11 +348,11 @@ header_parse (mu_header_t header, const char *blurb, int len) ...@@ -189,11 +348,11 @@ header_parse (mu_header_t header, const char *blurb, int len)
189 } 348 }
190 349
191 if (header_end == NULL) 350 if (header_end == NULL)
192 break; /* Bail out. */ 351 break; /* FIXME: Bail out. */
193 352
194 /* Now save the header in the data structure. */ 353 /* Now save the header in the data structure. */
195 354
196 /* Treats unix "From " specially. */ 355 /* Treats unix "From " specially. FIXME: Should we? */
197 if ((header_end - header_start >= 5) 356 if ((header_end - header_start >= 5)
198 && strncmp (header_start, "From ", 5) == 0) 357 && strncmp (header_start, "From ", 5) == 0)
199 { 358 {
...@@ -206,366 +365,320 @@ header_parse (mu_header_t header, const char *blurb, int len) ...@@ -206,366 +365,320 @@ header_parse (mu_header_t header, const char *blurb, int len)
206 { 365 {
207 char *colon = memchr (header_start, ':', header_end - header_start); 366 char *colon = memchr (header_start, ':', header_end - header_start);
208 367
209 #define ISLWSP(c) (((c) == ' ' || (c) == '\t'))
210 /* Houston we have a problem. */ 368 /* Houston we have a problem. */
211 if (colon == NULL) 369 if (colon == NULL)
212 break; /* Disregard the rest and bailout. */ 370 break; /* FIXME: Disregard the rest and bailout. */
213 371
214 fn = header_start; 372 fn = header_start;
215 fn_end = colon; 373 fn_end = colon;
216 /* Shrink any LWSP after the field name -- CRITICAL for 374 /* Shrink any LWSP after the field name -- CRITICAL for
217 later name comparisons to work correctly! */ 375 later name comparisons to work correctly! */
218 while(ISLWSP(fn_end[-1])) 376 while (ISLWSP (fn_end[-1]))
219 fn_end--; 377 fn_end--;
220 378
221 fv = colon + 1; 379 fv = colon + 1;
222 fv_end = header_end; 380 fv_end = header_end;
223 381
224 /* Skip any LWSP before the field value -- unnecessary, but 382 /* Skip any LWSP before the field value -- unnecessary, but
225 might make some field values look a little tidier. */ 383 might make some field values look a little tidier. */
226 while(ISLWSP(fv[0])) 384 while (ISLWSP (fv[0]))
227 fv++; 385 fv++;
228 } 386 }
229 #undef ISLWSP 387
230 /* Allocate a new slot for the field:value. */ 388 /* Register this header */
231 hdr = realloc (header->hdr, (header->hdr_count + 1) * sizeof (*hdr)); 389 ent = mu_hdrent_create (header, NULL, fn, fn_end - fn, fv, fv_end - fv);
232 if (hdr == NULL) 390 if (!ent)
233 { 391 return ENOMEM;
234 free (header->blurb); 392 mu_hdrent_append (header, ent);
235 free (header->hdr);
236 header->blurb = NULL;
237 header->hdr = NULL;
238 return ENOMEM;
239 }
240 hdr[header->hdr_count].fn = fn;
241 hdr[header->hdr_count].fn_end = fn_end;
242 hdr[header->hdr_count].fv = fv;
243 hdr[header->hdr_count].fv_end = fv_end;
244 header->hdr = hdr;
245 header->hdr_count++;
246 } /* for (header_start ...) */ 393 } /* for (header_start ...) */
247 394
248 return 0; 395 return 0;
249 } 396 }
250 397
251 /* FIXME: grossly inneficient, too many copies and reallocating. 398
252 This all header business needs a good rewrite. */ 399 static int
253 int 400 mu_header_fill (mu_header_t header)
254 mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
255 int replace)
256 { 401 {
402 int status;
403 char buf[1024];
404 size_t nread;
405 mu_stream_t mstream;
406 size_t stream_len;
257 char *blurb; 407 char *blurb;
258 size_t len; 408
259 409 if (header->spool_used)
260 if (header == NULL || fn == NULL) 410 return 0;
261 return EINVAL; 411
262 412 if (header->_fill == NULL)
263 /* An fv of NULL means delete the field, but only do it if replace 413 return 0; /* FIXME: Really? */
264 was also set to true! */
265 if (fv == NULL && !replace)
266 return EINVAL;
267
268 /* Overload. */
269 if (header->_set_value)
270 return header->_set_value (header, fn, fv, replace);
271
272 /* Try to fill out the buffer, if we know how. */
273 if (header->blurb == NULL)
274 {
275 int err = fill_blurb (header);
276 if (err != 0)
277 return err;
278 }
279
280 /* Easy approach: if replace, overwrite the field-{name,value} and readjust
281 the pointers by calling header_parse () this is wastefull, we're just
282 fragmenting the memory it can be done better. But that may imply a
283 rewite of the headers ... for another day. */
284
285 /* If replace, remove all fields in the header blurb that have the
286 same name as the field we are writing.
287 414
288 Algorithm: 415 status = mu_memory_stream_create (&mstream, NULL, MU_STREAM_RDWR);
416 if (status != 0)
417 return status;
418 mu_stream_open (mstream);
419 stream_len = 0;
289 420
290 for i = 0, ... i < max_hdrs 421 /* Bring in the entire header. */
291 - if ith field has name 'fn' memmove() all following fields up over 422 do
292 this field
293 - reparse the headers
294 - restart the for loop at the ith field
295 */
296 if (replace)
297 { 423 {
298 size_t name_len; 424 nread = 0;
299 size_t i; 425 status = header->_fill (header, buf, sizeof buf,
300 size_t fn_len; /* Field Name len. */ 426 stream_len, &nread);
301 size_t fv_len; /* Field Value len. */ 427 if (status)
302 len = header->blurb_len; 428 {
303 /* Find FN in the header fields... */ 429 if (status != EAGAIN && status != EINTR)
304 for (name_len = strlen (fn), i = 0; i < header->hdr_count; i++) 430 mu_stream_destroy (&mstream, NULL);
431 return status;
432 }
433 if (nread > 0)
305 { 434 {
306 fn_len = header->hdr[i].fn_end - header->hdr[i].fn; 435 status = mu_stream_write (mstream, buf, nread, stream_len, NULL);
307 fv_len = header->hdr[i].fv_end - header->hdr[i].fv; 436 if (status)
308 if (fn_len == name_len &&
309 strncasecmp (header->hdr[i].fn, fn, fn_len) == 0)
310 { 437 {
311 blurb = header->blurb; 438 mu_stream_destroy (&mstream, NULL);
312 /* ... and if its NOT the last field, move the next field 439 return status;
313 through to last field into its place, */
314 if ((i + 1) < header->hdr_count)
315 {
316 memmove (header->hdr[i].fn, header->hdr[i + 1].fn,
317 header->hdr[header->hdr_count - 1].fv_end
318 - header->hdr[i + 1].fn + 3);
319 }
320 /* or if it is the last, just truncate the fields. */
321 else
322 {
323 header->hdr[i].fn[0] = '\n';
324 header->hdr[i].fn[1] = '\0';
325 }
326 /* Readjust the pointers. */
327 len -= header->hdr[i].fv_end - header->hdr[i].fn + 1;
328 i--;
329 blurb = header->blurb;
330 header_parse (header, blurb, len);
331 free (blurb);
332 header->flags |= HEADER_MODIFIED;
333 } 440 }
441 stream_len += nread;
334 } 442 }
335 } 443 }
444 while (nread > 0);
336 445
337 /* If FV is NULL, then we are done. */ 446 /* parse it. */
338 if (!fv) 447 blurb = calloc (1, stream_len + 1);
339 return 0; 448 if (blurb)
449 {
450 size_t len;
451
452 status = mu_stream_read (mstream, blurb, stream_len, 0, &len);
453 if (status == 0)
454 status = header_parse (header, blurb, len);
455 free (blurb);
456 }
457 else
458 status = ENOMEM;
459
460 mu_stream_destroy (&mstream, NULL);
461 return status;
462 }
340 463
341 /* Replacing was taken care of above, now write the new header. 464
342 header. Really not cute. 465
343 COLON SPACE NL = 3 ; */ 466 int
344 len = strlen (fn) + strlen (fv) + 3; 467 mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner)
345 /* Add one for the NULL and leak a bit by adding one more 468 {
346 it will be the separtor \n from the body if the first 469 mu_header_t header;
347 blurb did not have it. */ 470 int status = 0;
348 blurb = calloc (header->blurb_len + len + 2, 1); 471
349 if (blurb == NULL) 472 header = calloc (1, sizeof (*header));
473 if (header == NULL)
350 return ENOMEM; 474 return ENOMEM;
475
476 header->owner = owner;
351 477
352 sprintf (blurb, "%s: %s", fn, fv); 478 status = header_parse (header, blurb, len);
353 479
354 /* Strip off trailing newlines and LWSP. */ 480 *ph = header;
355 while (blurb[strlen (blurb) - 1] == '\n' || 481 return status;
356 blurb[strlen (blurb) - 1] == ' ' || 482 }
357 blurb[strlen (blurb) - 1] == '\t')
358 {
359 blurb[strlen (blurb) - 1] = '\0';
360 }
361 len = strlen (blurb);
362 blurb[len] = '\n';
363 len++;
364 483
365 /* Prepend the rest of the headers. */ 484 void
366 if (header->blurb) 485 mu_header_destroy (mu_header_t *ph, void *owner)
367 { 486 {
368 memcpy (blurb + len, header->blurb, header->blurb_len); 487 if (ph && *ph)
369 free (header->blurb); 488 {
370 header->blurb = NULL; 489 mu_header_t header = *ph;
371 }
372 else
373 blurb[len] = '\n';
374 490
375 /* before parsing the new blurb make sure it is properly terminated 491 if (header->owner == owner)
376 by \n\n. The trailing NL separator. */ 492 {
377 if (blurb[header->blurb_len + len - 1] != '\n' 493 mu_stream_destroy (&header->stream, header);
378 || blurb[header->blurb_len + len - 2] != '\n') 494 mu_hdrent_free_list (header);
379 { 495 free (header->spool);
380 blurb[header->blurb_len + len] = '\n'; 496
381 len++; 497 free (header);
498 *ph = NULL;
499 }
382 } 500 }
383 header_parse (header, blurb, len + header->blurb_len);
384 free (blurb);
385 header->flags |= HEADER_MODIFIED;
386 return 0;
387 } 501 }
388 502
389 struct _hdr_cache { 503
390 size_t len; 504 int
391 char *ptr; 505 mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
392 }; 506 int replace)
393
394 static void
395 _hdr_cache_destroy (void *data)
396 { 507 {
397 struct _hdr_cache *hc = data; 508 int status;
398 free (hc->ptr); 509 struct mu_hdrent *ent = NULL;
399 } 510
511 if (header == NULL || fn == NULL)
512 return EINVAL;
400 513
401 static int 514 status = mu_header_fill (header);
402 header_get_fvalue (mu_header_t header, const char *name, char *buffer, 515 if (status)
403 size_t buflen, size_t *pn) 516 return status;
404 { 517
405 struct _hdr_cache *hdr = mu_assoc_ref (header->cache, name); 518 /* An fv of NULL means delete the field, but only do it if replace
406 if (hdr) 519 was also set to true! */
520 if (fv == NULL && !replace)
521 return EINVAL;
522
523 if (replace)
407 { 524 {
408 if (hdr->len == 0) 525 ent = mu_hdrent_find (header, fn, 1);
409 return MU_ERR_NOENT; 526 if (ent)
410 else
411 { 527 {
412 size_t fv_len = hdr->len; 528 if (fv == NULL)
413
414 if (buffer && buflen > 0)
415 { 529 {
416 buflen--; 530 /* Delete the header */
417 fv_len = (fv_len < buflen) ? fv_len : buflen; 531 mu_hdrent_remove (header, ent);
418 memcpy (buffer, hdr->ptr, fv_len); 532 free (ent);
419 buffer[fv_len] = '\0'; 533 return 0;
420 } 534 }
421 if (pn) 535 mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
422 *pn = fv_len; 536 HEADER_SET_MODIFIED (header);
423 return 0; 537 return 0;
424 } 538 }
539 else if (fv == NULL)
540 return 0;
425 } 541 }
426 return MU_ERR_NOENT; 542
543 ent = mu_hdrent_create (header, NULL,
544 fn, strlen (fn), fv, strlen (fv));
545 if (!ent)
546 return ENOMEM;
547 mu_hdrent_prepend (header, ent);
548 HEADER_SET_MODIFIED (header);
549 return 0;
427 } 550 }
428 551
429 /* We try to cache the headers here to reduce networking access 552 int
430 especially for IMAP. When the buffer is NULL it means that 553 mu_header_remove (mu_header_t header, const char *fn, int n)
431 the field does not exist on the server and we should not
432 attempt to contact the server again for this field. */
433 static int
434 header_set_fvalue (mu_header_t header, const char *name, char *buffer,
435 size_t len)
436 { 554 {
437 int rc; 555 int status;
438 struct _hdr_cache hcache; 556 struct mu_hdrent *ent;
439 557
440 if (!header->cache) 558 if (header == NULL || fn == NULL)
441 { 559 return EINVAL;
442 rc = mu_assoc_create (&header->cache, sizeof(struct _hdr_cache), 560
443 MU_ASSOC_ICASE); 561 status = mu_header_fill (header);
444 if (rc) 562 if (status)
445 return rc; 563 return status;
446 mu_assoc_set_free (header->cache, _hdr_cache_destroy); 564
447 } 565 ent = mu_hdrent_find (header, fn, n);
448 if (buffer == NULL) 566 if (!ent)
567 return MU_ERR_NOENT;
568
569 mu_hdrent_remove (header, ent);
570 free (ent);
571 return 0;
572 }
573
574 int
575 mu_header_insert (mu_header_t header,
576 const char *fn, const char *fv,
577 const char *ref, int n, int flags)
578 {
579 int status;
580 struct mu_hdrent *ent;
581
582 if (header == NULL || fn == NULL || fv == NULL)
583 return EINVAL;
584
585 status = mu_header_fill (header);
586 if (status)
587 return status;
588
589 if (flags & MU_HEADER_REPLACE)
449 { 590 {
450 hcache.ptr = NULL; 591 if (!ref)
451 hcache.len = 0; 592 ref = fn;
593 ent = mu_hdrent_find (header, ref, n);
594 mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
452 } 595 }
453 else 596 else
454 { 597 {
455 if (!len) 598 ent = mu_hdrent_create (header, NULL,
456 len = strlen (buffer); 599 fn, strlen (fn), fv, strlen (fv));
457 hcache.ptr = malloc (len + 1); 600 if (!ent)
458 if (!hcache.ptr)
459 return ENOMEM; 601 return ENOMEM;
460 memcpy (hcache.ptr, buffer, len); 602 if (ref)
461 hcache.ptr[len] = 0; 603 return mu_hdrent_insert (header, ent, ref, n,
462 hcache.len = len; 604 flags & MU_HEADER_BEFORE);
605 else
606 mu_hdrent_prepend (header, ent);
463 } 607 }
464 rc = mu_assoc_install (header->cache, name, &hcache); 608 HEADER_SET_MODIFIED (header);
465 if (rc) 609 return 0;
466 free (hcache.ptr);
467 return rc;
468 } 610 }
469 611
612
470 int 613 int
471 mu_header_get_value (mu_header_t header, const char *name, char *buffer, 614 mu_header_sget_value_n (mu_header_t header,
472 size_t buflen, size_t *pn) 615 const char *name, int n,
616 const char **pval)
473 { 617 {
474 size_t i = 0; 618 int status;
475 size_t name_len; 619 struct mu_hdrent *ent;
476 size_t fn_len = 0, fv_len = 0;
477 int err = 0;
478 620
479 if (header == NULL || name == NULL) 621 if (header == NULL || name == NULL)
480 return EINVAL; 622 return EINVAL;
623 status = mu_header_fill (header);
624 if (status)
625 return status;
626
627 ent = mu_hdrent_find (header, name, n);
628 if (!ent)
629 return MU_ERR_NOENT;
630
631 *pval = MU_HDRENT_VALUE (header, ent);
632 return 0;
633 }
481 634
482 /* First scan our cache headers for hits. */ 635 int
483 err = header_get_fvalue (header, name, buffer, buflen, pn); 636 mu_header_aget_value_n (mu_header_t header,
484 switch (err) 637 const char *name, int n,
638 char **pval)
639 {
640 const char *s;
641 int status = mu_header_sget_value_n (header, name, n, &s);
642 if (status == 0)
485 { 643 {
486 case EINVAL: /* Permanent failure. */ 644 *pval = strdup (s);
487 err = MU_ERR_NOENT; 645 if (!*pval)
488 case ENOMEM: 646 status = ENOMEM;
489 if (pn)
490 *pn = 0;
491 case 0:
492 return err;
493 } 647 }
648 return status;
649 }
494 650
495 if (header->_get_value) 651 int
496 { 652 mu_header_get_value_n (mu_header_t header, const char *name, int n,
497 char buf[1024]; /* should suffice for field-value. */ 653 char *buffer, size_t buflen, size_t *pn)
498 size_t len = 0; 654 {
499 err = header->_get_value (header, name, buf, sizeof (buf), &len); 655 const char *s;
500 if (err == 0) 656 int status = mu_header_sget_value_n (header, name, n, &s);
501 { 657 if (status == 0)
502 /* Save in the fast header buffer. */
503 header_set_fvalue (header, name, buf, 0);
504 if (buffer && buflen > 0)
505 {
506 buflen--;
507 buflen = (len < buflen) ? len : buflen;
508 memcpy (buffer, buf, buflen);
509 buffer[buflen] = '\0';
510 }
511 else
512 buflen = len;
513 if (pn)
514 *pn = buflen;
515 }
516 else
517 {
518 /* Cache permanent failure also. */
519 header_set_fvalue (header, name, NULL, 0);
520 }
521 return err;
522 }
523
524 /* Try to fill out the buffer, if we know how. */
525 if (header->blurb == NULL)
526 { 658 {
527 err = fill_blurb (header); 659 size_t slen = strlen (s);
528 if (err != 0)
529 return err;
530 }
531
532 /* We set the threshold to be 1 less for the null. */
533 --buflen;
534 660
535 for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) 661 if (buffer)
536 {
537 fn_len = header->hdr[i].fn_end - header->hdr[i].fn;
538 if (fn_len == name_len &&
539 strncasecmp (header->hdr[i].fn, name, fn_len) == 0)
540 { 662 {
541 fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); 663 if (slen > buflen)
542 header_set_fvalue (header, name, header->hdr[i].fv, fv_len); 664 slen = buflen;
543 /* Can everything fit in the buffer. */ 665 memcpy (buffer, s, slen);
544 if (buffer && buflen > 0) 666 buffer[slen] = 0;
545 {
546 if (fv_len > buflen)
547 fv_len = buflen;
548 memcpy (buffer, header->hdr[i].fv, fv_len);
549 buffer[fv_len] = 0;
550 }
551
552 if (pn)
553 *pn = fv_len;
554 return 0;
555 } 667 }
668 if (pn)
669 *pn = slen;
556 } 670 }
557 671 return status;
558 if (buffer)
559 *buffer = 0;
560
561 return MU_ERR_NOENT;
562 } 672 }
563 673
674
675 /* Unfolding functions */
564 int 676 int
565 mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer, 677 mu_header_get_value_unfold_n (mu_header_t header,
566 size_t buflen, size_t *pn) 678 const char *name, int n, char *buffer,
679 size_t buflen, size_t *pn)
567 { 680 {
568 int rc = mu_header_get_value (header, name, buffer, buflen, pn); 681 int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn);
569 682
570 if (rc == 0) 683 if (rc == 0)
571 mu_string_unfold (buffer, pn); 684 mu_string_unfold (buffer, pn);
...@@ -573,413 +686,343 @@ mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer, ...@@ -573,413 +686,343 @@ mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer,
573 } 686 }
574 687
575 int 688 int
576 mu_header_aget_value (mu_header_t header, const char *name, char **pvalue) 689 mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n,
690 char **pvalue)
577 { 691 {
578 char *value; 692 int rc = mu_header_aget_value_n (header, name, n, pvalue);
579 size_t n = 0;
580 int status = mu_header_get_value (header, name, NULL, 0, &n);
581 if (status == 0)
582 {
583 value = calloc (n + 1, 1);
584 if (value == NULL)
585 return ENOMEM;
586 mu_header_get_value (header, name, value, n + 1, NULL);
587 *pvalue = value;
588 }
589
590 return status;
591 }
592
593 int
594 mu_header_aget_value_unfold (mu_header_t header, const char *name, char **pvalue)
595 {
596 int rc = mu_header_aget_value (header, name, pvalue);
597 if (rc == 0) 693 if (rc == 0)
598 mu_string_unfold (*pvalue, NULL); 694 mu_string_unfold (*pvalue, NULL);
599 return rc; 695 return rc;
600 } 696 }
601 697
698
602 int 699 int
603 mu_header_get_address (mu_header_t header, const char *name, mu_address_t *addr) 700 mu_header_get_address_n (mu_header_t header, const char *name, int n,
701 mu_address_t *addr)
604 { 702 {
605 char* value = NULL; 703 const char *value = NULL;
606 int status = mu_header_aget_value(header, name, &value); 704 int status = mu_header_sget_value_n (header, name, n, &value);
607 705
608 if(status) 706 if (status)
609 return status; 707 return status;
610 708
611 status = mu_address_create(addr, value); 709 status = mu_address_create(addr, value);
612 710
613 free(value);
614
615 return status; 711 return status;
616 } 712 }
617 713
714
618 int 715 int
619 mu_header_get_field_count (mu_header_t header, size_t *pcount) 716 mu_header_get_field_count (mu_header_t header, size_t *pcount)
620 { 717 {
718 size_t count;
719 size_t size;
720 size_t lines;
721 int status;
722
621 if (header == NULL) 723 if (header == NULL)
622 { 724 return EINVAL;
623 if (pcount)
624 *pcount = 0;
625 return EINVAL;
626 }
627 725
628 /* Try to fill out the buffer, if we know how. */ 726 status = mu_header_fill (header);
629 if (header->blurb == NULL) 727 if (status == 0)
630 { 728 {
631 int err = fill_blurb (header); 729 mu_hdrent_count (header, &count, &size, &lines);
632 if (err != 0) 730
633 return err; 731 if (pcount)
732 *pcount = count;
634 } 733 }
635 734
636 if (pcount) 735 return status;
637 *pcount = header->hdr_count;
638 return 0;
639 } 736 }
640 737
641 int 738 int
642 mu_header_get_field_name (mu_header_t header, size_t num, char *buf, 739 mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr)
643 size_t buflen, size_t *nwriten)
644 { 740 {
645 size_t len; 741 int status;
646 742
647 if (header == NULL) 743 if (header == NULL)
648 return EINVAL; 744 return EINVAL;
649 745
650 /* Try to fill out the buffer, if we know how. */ 746 status = mu_header_fill (header);
651 if (header->blurb == NULL) 747 if (status == 0)
652 { 748 {
653 int err = fill_blurb (header); 749 struct mu_hdrent *ent = mu_hdrent_nth (header, num);
654 if (err != 0) 750 if (ent)
655 return err; 751 *sptr = MU_HDRENT_NAME (header, ent);
752 else
753 status = MU_ERR_NOENT;
656 } 754 }
755 return status;
756 }
657 757
658 if (header->hdr_count == 0 || num > header->hdr_count || num == 0) 758 int
659 return MU_ERR_NOENT; 759 mu_header_get_field_name (mu_header_t header, size_t num, char *buffer,
660 760 size_t buflen, size_t *pn)
661 num--; 761 {
662 len = (header->hdr[num].fn_end - header->hdr[num].fn); 762 const char *s;
663 if (buf && buflen) 763 int status = mu_header_sget_field_name (header, num, &s);
764 if (status == 0)
664 { 765 {
665 /* save one for the null */ 766 size_t slen = strlen (s);
666 --buflen; 767
667 len = (len > buflen) ? buflen : len; 768 if (buffer)
668 memcpy (buf, header->hdr[num].fn, len); 769 {
669 buf[len] = '\0'; 770 if (slen > buflen)
771 slen = buflen;
772 memcpy (buffer, s, slen);
773 buffer[slen] = 0;
774 }
775 if (pn)
776 *pn = slen;
670 } 777 }
671 if (nwriten) 778 return status;
672 *nwriten = len;
673 return 0;
674 } 779 }
675 780
676 int 781 int
677 mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue) 782 mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue)
678 { 783 {
679 char *value; 784 const char *s;
680 size_t n = 0; 785 int status = mu_header_sget_field_name (header, num, &s);
681 int status = mu_header_get_field_name (header, num, NULL, 0, &n);
682 if (status == 0) 786 if (status == 0)
683 { 787 {
684 value = calloc (n + 1, 1); 788 if ((*pvalue = strdup (s)) == NULL)
685 if (value == NULL) 789 status = ENOMEM;
686 return ENOMEM;
687 mu_header_get_field_name (header, num, value, n + 1, NULL);
688 *pvalue = value;
689 } 790 }
690 else
691 *pvalue = strdup ("");
692 return status; 791 return status;
693 } 792 }
694 793
794
695 int 795 int
696 mu_header_get_field_value (mu_header_t header, size_t num, char *buf, 796 mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr)
697 size_t buflen, size_t *nwritten)
698 { 797 {
699 size_t len; 798 int status;
700 799
701 if (header == NULL || num < 1) 800 if (header == NULL)
702 return EINVAL; 801 return EINVAL;
703 802
704 /* Try to fill out the buffer, if we know how. */ 803 status = mu_header_fill (header);
705 if (header->blurb == NULL) 804 if (status == 0)
706 {
707 int err = fill_blurb (header);
708 if (err != 0)
709 return err;
710 }
711
712 if (header->hdr_count == 0 || num > header->hdr_count || num == 0)
713 return MU_ERR_NOENT;
714
715 num--;
716 len = header->hdr[num].fv_end - header->hdr[num].fv;
717 if (buf && buflen > 0)
718 { 805 {
719 /* save one for the null */ 806 struct mu_hdrent *ent = mu_hdrent_nth (header, num);
720 --buflen; 807 if (ent)
721 len = (len > buflen) ? buflen : len; 808 *sptr = MU_HDRENT_VALUE (header, ent);
722 memcpy (buf, header->hdr[num].fv, len); 809 else
723 buf[len] = '\0'; 810 status = MU_ERR_NOENT;
724 } 811 }
725 812 return status;
726 if (nwritten)
727 *nwritten = len;
728 return 0;
729 } 813 }
730 814
731 int 815 int
732 mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf, 816 mu_header_get_field_value (mu_header_t header, size_t num, char *buffer,
733 size_t buflen, size_t *nwritten) 817 size_t buflen, size_t *pn)
734 { 818 {
735 int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten); 819 const char *s;
736 if (rc == 0) 820 int status = mu_header_sget_field_value (header, num, &s);
737 mu_string_unfold (buf, nwritten); 821 if (status == 0)
738 return rc; 822 {
823 size_t slen = strlen (s);
824
825 if (buffer)
826 {
827 if (slen > buflen)
828 slen = buflen;
829 memcpy (buffer, s, slen);
830 buffer[slen] = 0;
831 }
832 if (pn)
833 *pn = slen;
834 }
835 return status;
739 } 836 }
740 837
741 int 838 int
742 mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue) 839 mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue)
743 { 840 {
744 char *value; 841 const char *s;
745 size_t n = 0; 842 int status = mu_header_sget_field_value (header, num, &s);
746 int status = mu_header_get_field_value (header, num, NULL, 0, &n);
747 if (status == 0) 843 if (status == 0)
748 { 844 {
749 value = calloc (n + 1, 1); 845 if ((*pvalue = strdup (s)) == NULL)
750 if (value == NULL) 846 status = ENOMEM;
751 return ENOMEM;
752 mu_header_get_field_value (header, num, value, n + 1, NULL);
753 *pvalue = value;
754 } 847 }
755 else
756 *pvalue = strdup ("");
757 return status; 848 return status;
758 } 849 }
759 850
760 int 851 int
761 mu_header_aget_field_value_unfold (mu_header_t header, size_t num, char **pvalue) 852 mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf,
853 size_t buflen, size_t *nwritten)
762 { 854 {
763 int rc = mu_header_aget_field_value (header, num, pvalue); 855 int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten);
764 if (rc == 0) 856 if (rc == 0)
765 mu_string_unfold (*pvalue, NULL); 857 mu_string_unfold (buf, nwritten);
766 return rc; 858 return rc;
767 } 859 }
768 860
769 int 861 int
770 mu_header_set_lines (mu_header_t header, int (*_lines) 862 mu_header_aget_field_value_unfold (mu_header_t header, size_t num,
771 (mu_header_t, size_t *), void *owner) 863 char **pvalue)
772 { 864 {
773 if (header == NULL) 865 int rc = mu_header_aget_field_value (header, num, pvalue);
774 return EINVAL; 866 if (rc == 0)
775 if (header->owner != owner) 867 mu_string_unfold (*pvalue, NULL);
776 return EACCES; 868 return rc;
777 header->_lines = _lines;
778 return 0;
779 } 869 }
780 870
871
781 int 872 int
782 mu_header_lines (mu_header_t header, size_t *plines) 873 mu_header_lines (mu_header_t header, size_t *plines)
783 { 874 {
784 int n; 875 int status;
785 size_t lines = 0;
786 876
787 if (header == NULL) 877 if (header == NULL)
788 return EINVAL; 878 return EINVAL;
789 if (plines == NULL) 879 if (plines == NULL)
790 return MU_ERR_OUT_PTR_NULL; 880 return MU_ERR_OUT_PTR_NULL;
791 881
792 /* Overload. */ 882 status = mu_header_fill (header);
793 if (header->_lines) 883 if (status == 0)
794 return header->_lines (header, plines);
795
796 /* Try to fill out the buffer, if we know how. */
797 if (header->blurb == NULL)
798 {
799 int err = fill_blurb (header);
800 if (err != 0)
801 return err;
802 }
803
804 for (n = header->blurb_len - 1; n >= 0; n--)
805 { 884 {
806 if (header->blurb[n] == '\n') 885 size_t count;
807 lines++; 886 size_t size;
887 size_t lines;
888 mu_hdrent_count (header, &count, &size, &lines);
889 *plines = lines + 1;
808 } 890 }
809 if (plines) 891 return status;
810 *plines = lines;
811 return 0;
812 }
813
814 int
815 mu_header_set_size (mu_header_t header, int (*_size)
816 (mu_header_t, size_t *), void *owner)
817 {
818 if (header == NULL)
819 return EINVAL;
820 if (header->owner != owner)
821 return EACCES;
822 header->_size = _size;
823 return 0;
824 } 892 }
825 893
826 int 894 int
827 mu_header_size (mu_header_t header, size_t *psize) 895 mu_header_size (mu_header_t header, size_t *psize)
828 { 896 {
829 if (header == NULL) 897 int status;
830 return EINVAL;
831 898
832 /* Overload. */ 899 if (header == NULL)
833 if (header->_size) 900 return EINVAL;
834 return header->_size (header, psize); 901 if (psize == NULL)
902 return MU_ERR_OUT_PTR_NULL;
835 903
836 /* Try to fill out the buffer, if we know how. */ 904 status = mu_header_fill (header);
837 if (header->blurb == NULL) 905 if (status == 0)
838 { 906 {
839 int err = fill_blurb (header); 907 size_t count;
840 if (err != 0) 908 size_t size;
841 return err; 909 size_t lines;
910 mu_hdrent_count (header, &count, &size, &lines);
911 *psize = size + 1;
842 } 912 }
843 913 return status;
844 if (psize)
845 *psize = header->blurb_len;
846 return 0;
847 } 914 }
848 915
849 int 916
850 mu_header_set_get_value (mu_header_t header, int (*_get_value) 917 static void
851 (mu_header_t, const char *, char *, size_t, size_t *), 918 mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent)
852 void *owner)
853 { 919 {
854 if (header == NULL) 920 char *s = MU_HDRENT_NAME (hdr, ent);
855 return EINVAL; 921 s[ent->nlen] = ':';
856 if (header->owner != owner) 922 s = MU_HDRENT_VALUE (hdr, ent);
857 return EACCES; 923 s[ent->vlen] = '\n';
858 header->_get_value = _get_value;
859 return 0;
860 } 924 }
861 925
862 int 926 static void
863 mu_header_set_set_value (mu_header_t header, int (*_set_value) 927 mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent)
864 (mu_header_t , const char *, const char *, int),
865 void *owner)
866 { 928 {
867 if (header == NULL) 929 char *s = MU_HDRENT_NAME (hdr, ent);
868 return EINVAL; 930 s[ent->nlen] = 0;
869 if (header->owner != owner) 931 s = MU_HDRENT_VALUE (hdr, ent);
870 return EACCES; 932 s[ent->vlen] = 0;
871 header->_set_value = _set_value;
872 return 0;
873 } 933 }
874 934
875 int 935 static int
876 mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner) 936 header_read (mu_stream_t is, char *buffer, size_t buflen,
937 mu_off_t off, size_t *pnread)
877 { 938 {
878 if (header == NULL) 939 mu_header_t header;
940 struct mu_hdrent *ent;
941 size_t ent_off;
942 int status;
943 size_t nread;
944
945 if (is == NULL)
879 return EINVAL; 946 return EINVAL;
880 if (header->owner != owner)
881 return EACCES;
882 header->stream = stream;
883 return 0;
884 }
885 947
886 int 948 header = mu_stream_get_owner (is);
887 mu_header_set_fill (mu_header_t header, int 949 status = mu_header_fill (header);
888 (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), 950 if (status)
889 void *owner) 951 return status;
890 { 952
891 if (header == NULL) 953 if (mu_hdrent_find_stream_pos (header, off, &ent, &ent_off))
892 return EINVAL; 954 {
893 if (header->owner != owner) 955 if (pnread)
894 return EACCES; 956 *pnread = 0;
895 header->_fill = _fill; 957 return 0;
958 }
959
960 for (nread = 0; nread < buflen && ent; ent = ent->next)
961 {
962 size_t rest = buflen - nread;
963 size_t strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
964 if (rest > strsize)
965 rest = strsize;
966 mu_hdrent_fixup (header, ent);
967 memcpy (buffer + nread, MU_HDRENT_NAME (header, ent) + ent_off, rest);
968 mu_hdrent_unroll_fixup (header, ent);
969 nread += rest;
970 off += rest;
971 ent_off = 0;
972 }
973 if (pnread)
974 *pnread = nread;
896 return 0; 975 return 0;
897 } 976 }
898 977
899 static int 978 static int
900 fill_blurb (mu_header_t header) 979 header_readline (mu_stream_t is, char *buffer, size_t buflen,
980 mu_off_t off, size_t *pnread)
901 { 981 {
982 mu_header_t header;
983 struct mu_hdrent *ent;
984 size_t ent_off;
902 int status; 985 int status;
903 char buf[1024]; 986 size_t strsize;
904 size_t nread; 987
905 988 if (is == NULL)
906 if (header->_fill == NULL) 989 return EINVAL;
907 return 0;
908
909 /* The entire header is now ours(part of mu_header_t), clear all the
910 overloading. */
911 header_free_cache (header);
912 header->_get_value = NULL;
913 header->_set_value = NULL;
914 header->_size = NULL;
915 header->_lines = NULL;
916
917 if (header->mstream == NULL)
918 {
919 status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR);
920 if (status != 0)
921 return status;
922 mu_stream_open (header->mstream);
923 header->stream_len = 0;
924 }
925 990
926 /* Bring in the entire header. */ 991 header = mu_stream_get_owner (is);
927 do 992 status = mu_header_fill (header);
993 if (status)
994 return status;
995
996 if (mu_hdrent_find_stream_pos (header, off, &ent, &ent_off))
928 { 997 {
929 nread = 0; 998 if (pnread)
930 status = header->_fill (header, buf, sizeof buf, 999 *pnread = 0;
931 header->stream_len, &nread) ; 1000 return 0;
932 if (status != 0)
933 {
934 if (status != EAGAIN && status != EINTR)
935 {
936 mu_stream_destroy (&(header->mstream), NULL);
937 header->stream_len = 0;
938 }
939 return status;
940 }
941 if (nread > 0)
942 {
943 status = mu_stream_write (header->mstream, buf, nread, header->stream_len, NULL);
944 if (status != 0)
945 {
946 mu_stream_destroy (&(header->mstream), NULL);
947 header->stream_len = 0;
948 return status;
949 }
950 header->stream_len += nread;
951 }
952 } 1001 }
953 while (nread > 0);
954 1002
955 /* parse it. */ 1003 strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
956 { 1004 if (buflen > strsize)
957 char *blurb; 1005 buflen = strsize;
958 size_t len = header->stream_len; 1006 mu_hdrent_fixup (header, ent);
959 blurb = calloc (1, len + 1); 1007 memcpy (buffer, MU_HDRENT_NAME (header, ent) + ent_off, buflen);
960 if (blurb) 1008 mu_hdrent_unroll_fixup (header, ent);
961 { 1009 if (pnread)
962 mu_stream_read (header->mstream, blurb, len, 0, &len); 1010 *pnread = buflen;
963 status = header_parse (header, blurb, len); 1011 return 0;
964 }
965 free (blurb);
966 }
967 mu_stream_destroy (&header->mstream, NULL);
968 header->stream_len = 0;
969 return status;
970 } 1012 }
971 1013
972 static int 1014 static int
973 header_write (mu_stream_t os, const char *buf, size_t buflen, 1015 header_write (mu_stream_t os, const char *buf, size_t buflen,
974 mu_off_t off, size_t *pnwrite) 1016 mu_off_t off, size_t *pnwrite)
975 { 1017 {
1018 size_t wrsize = 0;
976 mu_header_t header = mu_stream_get_owner (os); 1019 mu_header_t header = mu_stream_get_owner (os);
977 int status; 1020 int status;
978 1021
979 if (header == NULL) 1022 if (header == NULL)
980 return EINVAL; 1023 return EINVAL;
981 1024
982 if ((size_t)off != header->stream_len) 1025 if ((size_t)off != header->mstream_size)
983 return ESPIPE; 1026 return ESPIPE;
984 1027
985 /* Skip the obvious. */ 1028 /* Skip the obvious. */
...@@ -990,53 +1033,49 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, ...@@ -990,53 +1033,49 @@ header_write (mu_stream_t os, const char *buf, size_t buflen,
990 return 0; 1033 return 0;
991 } 1034 }
992 1035
993 if (header->mstream == NULL) 1036 if (!header->mstream)
994 { 1037 {
995 status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR); 1038 status = mu_memory_stream_create (&header->mstream, NULL,
996 if (status != 0) 1039 MU_STREAM_RDWR);
1040 if (status)
997 return status; 1041 return status;
998 status = mu_stream_open (header->mstream); 1042 status = mu_stream_open (header->mstream);
999 if (status != 0) 1043 if (status)
1000 { 1044 {
1001 mu_stream_destroy(&header->mstream, NULL); 1045 mu_stream_destroy (&header->mstream, NULL);
1002 return status; 1046 return status;
1003 } 1047 }
1004 header->stream_len = 0; 1048 header->mstream_size = 0;
1005 } 1049 }
1006 1050
1007 status = mu_stream_write (header->mstream, buf, buflen, header->stream_len, &buflen); 1051 do
1008 if (status != 0)
1009 { 1052 {
1010 mu_stream_destroy (&header->mstream, NULL); 1053 size_t nbytes;
1011 header->stream_len = 0; 1054 status = mu_stream_write (header->mstream, buf + wrsize, buflen - wrsize,
1012 return status; 1055 header->mstream_size, &nbytes);
1056 if (status)
1057 {
1058 mu_stream_destroy (&header->mstream, NULL);
1059 header->mstream_size = 0;
1060 return status;
1061 }
1062 if (nbytes == 0)
1063 break;
1064 wrsize += nbytes;
1065 header->mstream_size += nbytes;
1013 } 1066 }
1014 header->stream_len += buflen; 1067 while (buflen);
1015 1068
1016 /* We detect an empty line .i.e "^\n$" this signal the end of the 1069 if (header->mstream_size > 1)
1017 header. */
1018 if (header->stream_len)
1019 { 1070 {
1020 int finish = 0; 1071 char nlbuf[2];
1021 char nlnl[2]; 1072 size_t len;
1022 nlnl[1] = nlnl[0] = '\0'; 1073 status = mu_stream_read (header->mstream, nlbuf, 2,
1023 mu_stream_read (header->mstream, nlnl, 1, 0, NULL); 1074 header->mstream_size - 2, &len);
1024 if (nlnl[0] == '\n') 1075 if (status == 0 && len == 2 && memcmp (nlbuf, "\n\n", 2) == 0)
1025 {
1026 finish = 1;
1027 }
1028 else
1029 {
1030 mu_stream_read (header->mstream, nlnl, 2, header->stream_len - 2, NULL);
1031 if (nlnl[0] == '\n' && nlnl[1] == '\n')
1032 {
1033 finish = 1;
1034 }
1035 }
1036 if (finish)
1037 { 1076 {
1038 char *blurb; 1077 char *blurb;
1039 size_t len = header->stream_len; 1078 size_t len = header->mstream_size;
1040 blurb = calloc (1, len + 1); 1079 blurb = calloc (1, len + 1);
1041 if (blurb) 1080 if (blurb)
1042 { 1081 {
...@@ -1045,95 +1084,36 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, ...@@ -1045,95 +1084,36 @@ header_write (mu_stream_t os, const char *buf, size_t buflen,
1045 } 1084 }
1046 free (blurb); 1085 free (blurb);
1047 mu_stream_destroy (&header->mstream, NULL); 1086 mu_stream_destroy (&header->mstream, NULL);
1048 header->stream_len = 0; 1087 header->mstream_size = 0;
1049 } 1088 }
1050 }
1051
1052 if (pnwrite)
1053 *pnwrite = buflen;
1054 return 0;
1055
1056 }
1057
1058 static int
1059 header_read (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pnread)
1060 {
1061 int len;
1062 mu_header_t header = mu_stream_get_owner (is);
1063
1064 if (is == NULL || header == NULL)
1065 return EINVAL;
1066
1067 if (buf == NULL || buflen == 0)
1068 {
1069 if (pnread)
1070 *pnread = 0;
1071 return 0;
1072 } 1089 }
1073 1090
1074 /* Try to fill out the buffer, if we know how. */ 1091 if (pnwrite)
1075 if (header->blurb == NULL) 1092 *pnwrite = wrsize;
1076 { 1093
1077 int err = fill_blurb (header);
1078 if (err != 0)
1079 return err;
1080 }
1081
1082 len = header->blurb_len - off;
1083 if (len > 0)
1084 {
1085 len = (buflen < (size_t)len) ? buflen : (size_t)len;
1086 memcpy (buf, header->blurb + off, len);
1087 }
1088 else
1089 len = 0;
1090
1091 if (pnread)
1092 *pnread = len;
1093 return 0; 1094 return 0;
1094 } 1095 }
1095 1096
1096 static int 1097 static int
1097 header_readline (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pn) 1098 header_size (mu_stream_t str, mu_off_t *psize)
1098 { 1099 {
1099 int len; 1100 mu_header_t header;
1100 mu_header_t header = mu_stream_get_owner (is); 1101 int status;
1101 1102 size_t size;
1102 if (is == NULL || header == NULL) 1103
1104 if (str == NULL)
1103 return EINVAL; 1105 return EINVAL;
1104 1106 if (psize == NULL)
1105 if (buf == NULL || buflen == 0) 1107 return MU_ERR_OUT_PTR_NULL;
1106 { 1108
1107 if (pn) 1109 header = mu_stream_get_owner (str);
1108 *pn = 0; 1110 status = mu_header_fill (header);
1109 return 0; 1111 if (status)
1110 } 1112 return status;
1111 1113 status = mu_header_size (header, &size);
1112 /* Try to fill out the buffer, if we know how. */ 1114 if (status == 0)
1113 if (header->blurb == NULL) 1115 *psize = size;
1114 { 1116 return status;
1115 int err = fill_blurb (header);
1116 if (err != 0)
1117 return err;
1118 }
1119
1120 buflen--; /* Space for the null. */
1121
1122 len = header->blurb_len - off;
1123 if (len > 0)
1124 {
1125 char *nl = memchr (header->blurb + off, '\n', len);
1126 if (nl)
1127 len = nl - (header->blurb + off) + 1;
1128 len = (buflen < (size_t)len) ? buflen : (size_t)len;
1129 memcpy (buf, header->blurb + off, len);
1130 }
1131 else
1132 len = 0;
1133 if (pn)
1134 *pn = len;
1135 buf[len] = '\0';
1136 return 0;
1137 } 1117 }
1138 1118
1139 int 1119 int
...@@ -1141,18 +1121,57 @@ mu_header_get_stream (mu_header_t header, mu_stream_t *pstream) ...@@ -1141,18 +1121,57 @@ mu_header_get_stream (mu_header_t header, mu_stream_t *pstream)
1141 { 1121 {
1142 if (header == NULL) 1122 if (header == NULL)
1143 return EINVAL; 1123 return EINVAL;
1124
1144 if (pstream == NULL) 1125 if (pstream == NULL)
1145 return MU_ERR_OUT_PTR_NULL; 1126 return MU_ERR_OUT_PTR_NULL;
1127
1146 if (header->stream == NULL) 1128 if (header->stream == NULL)
1147 { 1129 {
1148 int status = mu_stream_create (&(header->stream), MU_STREAM_RDWR, header); 1130 int status = mu_stream_create (&header->stream, MU_STREAM_RDWR, header);
1149 if (status != 0) 1131 if (status != 0)
1150 return status; 1132 return status;
1151 mu_stream_set_read (header->stream, header_read, header); 1133 mu_stream_set_read (header->stream, header_read, header);
1152 mu_stream_set_readline (header->stream, header_readline, header); 1134 mu_stream_set_readline (header->stream, header_readline, header);
1153 mu_stream_set_write (header->stream, header_write, header); 1135 mu_stream_set_write (header->stream, header_write, header);
1136 mu_stream_set_size (header->stream, header_size, header);
1154 } 1137 }
1155 *pstream = header->stream; 1138 *pstream = header->stream;
1156 return 0; 1139 return 0;
1157 } 1140 }
1158 1141
1142
1143
1144 int
1145 mu_header_set_fill (mu_header_t header, int
1146 (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *),
1147 void *owner)
1148 {
1149 if (header == NULL)
1150 return EINVAL;
1151 if (header->owner != owner)
1152 return EACCES;
1153 header->_fill = _fill;
1154 return 0;
1155 }
1156
1157
1158 void *
1159 mu_header_get_owner (mu_header_t header)
1160 {
1161 return (header) ? header->owner : NULL;
1162 }
1163
1164 int
1165 mu_header_is_modified (mu_header_t header)
1166 {
1167 return (header) ? (header->flags & HEADER_MODIFIED) : 0;
1168 }
1169
1170 int
1171 mu_header_clear_modified (mu_header_t header)
1172 {
1173 if (header)
1174 header->flags &= ~HEADER_MODIFIED;
1175 return 0;
1176 }
1177
......