1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library 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 GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 /* This all header business needs a good rewrite.
19 * -- Alain Magloire, 2000-07-03 (rev. 1.21)
20 *
21 * It's the job that's never started as takes longest to finish.
22 * -- Hamfast Gamgee, some time in the Third Age
23 *
24 * It took almost 7 years to gather the courage to start the job,
25 * and only one day to finish it.
26 * -- Sergey Poznyakoff, 2007-06-24
27 */
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37
38 #ifdef HAVE_STRINGS_H
39 # include <strings.h>
40 #endif
41
42 #include <mailutils/stream.h>
43 #include <mailutils/address.h>
44 #include <mailutils/util.h>
45 #include <mailutils/errno.h>
46 #include <mailutils/cstr.h>
47 #include <mailutils/sys/header_stream.h>
48 #include <mailutils/sys/header.h>
49
50 #define HEADER_MODIFIED 0x01
51 #define HEADER_INVALIDATE 0x02
52 #define HEADER_STREAMMOD 0x04
53
54 #define HEADER_SET_MODIFIED(h) \
55 ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
56
57
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 *
mu_hdrent_nth(struct _mu_header * hdr,int n)65 mu_hdrent_nth (struct _mu_header *hdr, int n)
66 {
67 struct mu_hdrent *p;
68 for (p = hdr->head; p; p = p->next)
69 if (n-- == 1)
70 break;
71 return p;
72 }
73
74 static struct mu_hdrent *
mu_hdrent_find(struct _mu_header * hdr,const char * name,int pos)75 mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos)
76 {
77 struct mu_hdrent *p;
78
79 if (pos > 0)
80 {
81 for (p = hdr->head; p; p = p->next)
82 if ((!name || mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0) &&
83 pos-- == 1)
84 break;
85 }
86 else if (pos < 0)
87 {
88 for (p = hdr->tail; p; p = p->prev)
89 if ((!name || mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0) &&
90 ++pos == 0)
91 break;
92 }
93 else
94 p = NULL;
95 return p;
96 }
97
98 static int
mu_hdrent_find_stream_pos(struct _mu_header * hdr,mu_off_t pos,struct mu_hdrent ** pent,size_t * poff)99 mu_hdrent_find_stream_pos (struct _mu_header *hdr, mu_off_t pos,
100 struct mu_hdrent **pent, size_t *poff)
101 {
102 mu_off_t x;
103 struct mu_hdrent *p;
104
105 for (p = hdr->head, x = 0; p; p = p->next)
106 {
107 size_t strsize = MU_STR_SIZE (p->nlen, p->vlen);
108 if (x <= pos && pos < x + strsize)
109 {
110 *poff = pos - x;
111 *pent = p;
112 return 0;
113 }
114 x += strsize;
115 }
116 if (x == pos && hdr->tail)
117 {
118 /* To supply the trailing '\n' */
119 p = hdr->tail;
120 *pent = p;
121 *poff = MU_STR_SIZE (p->nlen, p->vlen) - 1;
122 return 0;
123 }
124 return 1;
125 }
126
127 static void
mu_hdrent_count(struct _mu_header * hdr,size_t * pcount,size_t * psize,size_t * plines)128 mu_hdrent_count (struct _mu_header *hdr, size_t *pcount, size_t *psize,
129 size_t *plines)
130 {
131 if (hdr->flags & HEADER_INVALIDATE)
132 {
133 size_t size = 0;
134 size_t count = 0;
135 size_t lines = 0;
136 struct mu_hdrent *p;
137 for (p = hdr->head; p; p = p->next)
138 {
139 count++;
140 size += MU_STR_SIZE (p->nlen, p->vlen);
141 lines += p->nlines;
142 }
143
144 hdr->numhdr = count;
145 hdr->numlines = lines;
146 hdr->size = size;
147 hdr->flags &= ~HEADER_INVALIDATE;
148 }
149
150 *pcount = hdr->numhdr;
151 *psize = hdr->size;
152 *plines = hdr->numlines;
153 }
154
155 static void
mu_hdrent_remove(struct _mu_header * hdr,struct mu_hdrent * ent)156 mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent)
157 {
158 struct mu_hdrent *p = ent->prev;
159 if (p)
160 p->next = ent->next;
161 else
162 hdr->head = ent->next;
163
164 p = ent->next;
165 if (p)
166 p->prev = ent->prev;
167 else
168 hdr->tail = ent->prev;
169 }
170
171 static void
mu_hdrent_prepend(struct _mu_header * hdr,struct mu_hdrent * ent)172 mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent)
173 {
174 struct mu_hdrent *p = hdr->head;
175 ent->prev = NULL;
176 ent->next = p;
177 if (p)
178 p->prev = ent;
179 else
180 hdr->tail = ent;
181 hdr->head = ent;
182 }
183
184 static void
mu_hdrent_append(struct _mu_header * hdr,struct mu_hdrent * ent)185 mu_hdrent_append (struct _mu_header *hdr, struct mu_hdrent *ent)
186 {
187 struct mu_hdrent *p = hdr->tail;
188 ent->next = NULL;
189 ent->prev = p;
190 if (p)
191 p->next = ent;
192 else
193 hdr->head = ent;
194 hdr->tail = ent;
195 }
196
197 static int
mu_hdrent_insert(struct _mu_header * hdr,struct mu_hdrent * ent,const char * name,int pos,int before)198 mu_hdrent_insert (struct _mu_header *hdr, struct mu_hdrent *ent,
199 const char *name, int pos, int before)
200 {
201 struct mu_hdrent *p;
202 struct mu_hdrent *ref = mu_hdrent_find (hdr, name, pos);
203 if (!ref)
204 return MU_ERR_NOENT;
205
206 if (before)
207 {
208 ref = ref->prev;
209 if (!ref)
210 {
211 mu_hdrent_prepend (hdr, ent);
212 return 0;
213 }
214 }
215
216 p = ref->next;
217 if (!p)
218 {
219 mu_hdrent_append (hdr, ent);
220 return 0;
221 }
222
223 ent->next = p;
224 p->prev = ent;
225 ent->prev = ref;
226 ref->next = ent;
227
228 return 0;
229 }
230
231 #define SPOOLBLKSIZ 1024
232
233 static struct mu_hdrent *
mu_hdrent_create(struct _mu_header * ph,struct mu_hdrent * ent,const char * name,size_t nsize,const char * value,size_t vsize)234 mu_hdrent_create (struct _mu_header *ph,
235 struct mu_hdrent *ent,
236 const char *name, size_t nsize,
237 const char *value, size_t vsize)
238 {
239 size_t strsize;
240 size_t sizeleft;
241 const char *p;
242
243 if (!ent)
244 {
245 ent = calloc (1, sizeof (*ent));
246 if (!ent)
247 return NULL;
248 }
249
250 strsize = MU_STR_SIZE (nsize, vsize);
251 sizeleft = ph->spool_size - ph->spool_used;
252
253 /* Ensure there is enough space in spool */
254 if (sizeleft < strsize)
255 {
256 char *newp;
257 size_t delta = (strsize - sizeleft + SPOOLBLKSIZ - 1) / SPOOLBLKSIZ;
258 delta *= SPOOLBLKSIZ;
259 newp = realloc (ph->spool, ph->spool_size + delta);
260 if (!newp)
261 return 0;
262 ph->spool = newp;
263 ph->spool_size += delta;
264 }
265
266 /* Copy header name */
267 ent->fn = ph->spool_used;
268 ent->nlen = nsize;
269 memcpy (ph->spool + ph->spool_used, name, nsize);
270 ph->spool_used += nsize;
271 ph->spool[ph->spool_used++] = 0;
272 ph->spool[ph->spool_used++] = ' ';
273
274 /* Copy header value */
275 ent->fv = ph->spool_used;
276 ent->vlen = vsize;
277 memcpy (ph->spool + ph->spool_used, value, vsize);
278 ph->spool_used += vsize;
279 ph->spool[ph->spool_used++] = 0;
280
281 ent->nlines = 1;
282 for (p = value; p < value + vsize; p++)
283 if (*p == '\n')
284 ent->nlines++;
285
286 return ent;
287 }
288
289 static void
mu_hdrent_free_list(struct _mu_header * hdr)290 mu_hdrent_free_list (struct _mu_header *hdr)
291 {
292 struct mu_hdrent *p;
293 for (p = hdr->head; p; )
294 {
295 struct mu_hdrent *next = p->next;
296 free (p);
297 p = next;
298 }
299 hdr->head = hdr->tail = NULL;
300 hdr->spool_used = 0;
301 }
302
303
304
305 #define ISLWSP(c) (((c) == ' ' || (c) == '\t'))
306
307 /* Parsing is done in a rather simple fashion, meaning we just consider an
308 entry to be a field-name an a field-value. So they maybe duplicate of
309 field-name like "Received" they are just put in the array, see _get_value()
310 on how to handle the case. in the case of error .i.e a bad header construct
311 we do a full stop and return what we have so far. */
312
313 static int
header_parse(mu_header_t header,const char * blurb,int len)314 header_parse (mu_header_t header, const char *blurb, int len)
315 {
316 const char *header_end;
317 const char *header_start;
318 const char *header_start2;
319
320 /* Nothing to parse. */
321 if (blurb == NULL)
322 return 0;
323
324 header->flags |= HEADER_INVALIDATE;
325 mu_hdrent_free_list (header);
326
327 /* Get a header, a header is:
328 field-name LWSP ':'
329 LWSP field-value '\r' '\n'
330 *[ (' ' | '\t') field-value '\r' '\n' ]
331 */
332 /* First loop goes through the blurb */
333 for (header_start = blurb; len > 0; header_start = ++header_end)
334 {
335 const char *fn, *fn_end, *fv, *fv_end;
336 struct mu_hdrent *ent;
337
338 if (header_start[0] == ' '
339 || header_start[0] == '\t'
340 || header_start[0] == '\n')
341 break;
342
343 /* Second loop extract one header field. */
344 for (header_start2 = header_start; len; header_start2 = ++header_end)
345 {
346 header_end = memchr (header_start2, '\n', len);
347 if (header_end == NULL)
348 {
349 header_end = header_start2 + len;
350 len = 0;
351 break;
352 }
353 else
354 {
355 len -= (header_end - header_start2 + 1);
356 if (!len
357 || (header_end[1] != ' '
358 && header_end[1] != '\t'))
359 break; /* New header break the inner for. */
360 }
361 /* *header_end = ' '; smash LF ? NO */
362 }
363
364 /* Now save the header in the data structure. */
365
366 /* Treats unix "From " specially. FIXME: Should we? */
367 if ((header_end - header_start >= 5)
368 && strncmp (header_start, "From ", 5) == 0)
369 {
370 fn = header_start;
371 fn_end = header_start + 5;
372 fv = header_start + 5;
373 fv_end = header_end;
374 }
375 else /* Break the header in key: value */
376 {
377 char *colon = memchr (header_start, ':', header_end - header_start);
378
379 /* Houston we have a problem. */
380 if (colon == NULL)
381 break; /* FIXME: Disregard the rest and bailout. */
382
383 fn = header_start;
384 fn_end = colon;
385 /* Shrink any LWSP after the field name -- CRITICAL for
386 later name comparisons to work correctly! */
387 while (ISLWSP (fn_end[-1]))
388 fn_end--;
389
390 fv = colon + 1;
391 fv_end = header_end;
392
393 /* Skip any LWSP before the field value -- unnecessary, but
394 might make some field values look a little tidier. */
395 while (ISLWSP (fv[0]))
396 fv++;
397 }
398
399 /* Register this header */
400 ent = mu_hdrent_create (header, NULL, fn, fn_end - fn, fv, fv_end - fv);
401 if (!ent)
402 return ENOMEM;
403 mu_hdrent_append (header, ent);
404 } /* for (header_start ...) */
405
406 return 0;
407 }
408
409
410 static int
mu_header_fill(mu_header_t header)411 mu_header_fill (mu_header_t header)
412 {
413 int status;
414 size_t blurb_len = 0;
415 char *blurb = NULL;
416
417 if (header->mstream && header->flags & HEADER_STREAMMOD)
418 {
419 mu_off_t end;
420
421 mu_header_invalidate (header);
422 status = mu_stream_size (header->mstream, &end);
423 if (status)
424 return status;
425 status = mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL);
426 if (status)
427 return status;
428 blurb_len = end;
429 blurb = malloc (blurb_len + 1);
430 if (!blurb)
431 return ENOMEM;
432 status = mu_stream_read (header->mstream, blurb, blurb_len, NULL);
433 if (status)
434 {
435 free (blurb);
436 return status;
437 }
438 status = header_parse (header, blurb, blurb_len);
439 free (blurb);
440 if (status == 0)
441 header->flags &= ~HEADER_STREAMMOD;
442 return status;
443 }
444 if (header->spool_used)
445 return 0;
446
447 if (header->_fill == NULL)
448 return 0; /* FIXME: Really? */
449
450 /* Bring in the entire header. */
451 status = header->_fill (header->data, &blurb, &blurb_len);
452 if (status)
453 return status;
454 status = header_parse (header, blurb, blurb_len);
455 free (blurb);
456 return status;
457 }
458
459
460
461 int
mu_header_create(mu_header_t * ph,const char * blurb,size_t len)462 mu_header_create (mu_header_t *ph, const char *blurb, size_t len)
463 {
464 mu_header_t header;
465 int status = 0;
466
467 header = calloc (1, sizeof (*header));
468 if (header == NULL)
469 return ENOMEM;
470
471 status = header_parse (header, blurb, len);
472
473 *ph = header;
474 return status;
475 }
476
477 void
mu_header_destroy(mu_header_t * ph)478 mu_header_destroy (mu_header_t *ph)
479 {
480 if (ph && *ph)
481 {
482 mu_header_t header = *ph;
483
484 mu_stream_destroy (&header->mstream);
485 mu_stream_destroy (&header->stream);
486 mu_hdrent_free_list (header);
487 free (header->spool);
488 free (header);
489 *ph = NULL;
490 }
491 }
492
493 int
mu_header_set_value(mu_header_t header,const char * fn,const char * fv,int replace)494 mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
495 int replace)
496 {
497 int status;
498 struct mu_hdrent *ent = NULL;
499
500 if (header == NULL || fn == NULL)
501 return EINVAL;
502
503 status = mu_header_fill (header);
504 if (status)
505 return status;
506
507 /* An fv of NULL means delete the field, but only do it if replace
508 was also set to true! */
509 if (fv == NULL && !replace)
510 return EINVAL;
511
512 ent = mu_hdrent_find (header, fn, 1);
513
514 if (replace)
515 {
516 if (ent)
517 {
518 if (fv == NULL)
519 {
520 /* Delete the header */
521 mu_hdrent_remove (header, ent);
522 free (ent);
523 return 0;
524 }
525 mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
526 HEADER_SET_MODIFIED (header);
527 return 0;
528 }
529 else if (fv == NULL)
530 return 0;
531 }
532 else if (ent)
533 {
534 return MU_ERR_EXISTS;
535 }
536
537 ent = mu_hdrent_create (header, NULL,
538 fn, strlen (fn), fv, strlen (fv));
539 if (!ent)
540 return ENOMEM;
541 mu_hdrent_append (header, ent);
542 HEADER_SET_MODIFIED (header);
543 return 0;
544 }
545
546 int
mu_header_remove(mu_header_t header,const char * fn,int n)547 mu_header_remove (mu_header_t header, const char *fn, int n)
548 {
549 int status;
550 struct mu_hdrent *ent;
551
552 if (header == NULL)
553 return EINVAL;
554
555 status = mu_header_fill (header);
556 if (status)
557 return status;
558
559 ent = mu_hdrent_find (header, fn, n);
560 if (!ent)
561 return MU_ERR_NOENT;
562
563 mu_iterator_delitem (header->itr, ent);
564 mu_hdrent_remove (header, ent);
565 HEADER_SET_MODIFIED (header);
566 free (ent);
567 return 0;
568 }
569
570 int
mu_header_append(mu_header_t header,const char * fn,const char * fv)571 mu_header_append (mu_header_t header, const char *fn, const char *fv)
572 {
573 int status;
574 struct mu_hdrent *ent;
575
576 if (header == NULL || fn == NULL || fv == NULL)
577 return EINVAL;
578
579 status = mu_header_fill (header);
580 if (status)
581 return status;
582
583 ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv));
584 if (!ent)
585 return ENOMEM;
586 mu_hdrent_append (header, ent);
587 HEADER_SET_MODIFIED (header);
588 return 0;
589 }
590
591 int
mu_header_prepend(mu_header_t header,const char * fn,const char * fv)592 mu_header_prepend (mu_header_t header, const char *fn, const char *fv)
593 {
594 int status;
595 struct mu_hdrent *ent;
596
597 if (header == NULL || fn == NULL || fv == NULL)
598 return EINVAL;
599
600 status = mu_header_fill (header);
601 if (status)
602 return status;
603
604 ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv));
605 if (!ent)
606 return ENOMEM;
607 mu_hdrent_prepend (header, ent);
608 HEADER_SET_MODIFIED (header);
609 return 0;
610 }
611
612 int
mu_header_insert(mu_header_t header,const char * fn,const char * fv,const char * ref,int n,int flags)613 mu_header_insert (mu_header_t header,
614 const char *fn, const char *fv,
615 const char *ref, int n, int flags)
616 {
617 int status;
618 struct mu_hdrent *ent;
619
620 if (header == NULL || fn == NULL || fv == NULL)
621 return EINVAL;
622
623 status = mu_header_fill (header);
624 if (status)
625 return status;
626
627 if (flags & MU_HEADER_REPLACE)
628 {
629 if (!ref)
630 ref = fn;
631 ent = mu_hdrent_find (header, ref, n);
632 mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
633 }
634 else
635 {
636 ent = mu_hdrent_create (header, NULL,
637 fn, strlen (fn), fv, strlen (fv));
638 if (!ent)
639 return ENOMEM;
640 if (ref)
641 return mu_hdrent_insert (header, ent, ref, n,
642 flags & MU_HEADER_BEFORE);
643 else
644 mu_hdrent_prepend (header, ent);
645 }
646 HEADER_SET_MODIFIED (header);
647 return 0;
648 }
649
650 int
mu_header_clear(mu_header_t header)651 mu_header_clear (mu_header_t header)
652 {
653 int status;
654
655 if (header == NULL)
656 return EINVAL;
657
658 status = mu_header_fill (header);
659 if (status)
660 return status;
661 mu_header_invalidate (header);
662 HEADER_SET_MODIFIED (header);
663 return 0;
664 }
665
666
667 int
mu_header_sget_value_n(mu_header_t header,const char * name,int n,const char ** pval)668 mu_header_sget_value_n (mu_header_t header,
669 const char *name, int n,
670 const char **pval)
671 {
672 int status;
673 struct mu_hdrent *ent;
674
675 if (header == NULL || name == NULL)
676 return EINVAL;
677 status = mu_header_fill (header);
678 if (status)
679 return status;
680
681 ent = mu_hdrent_find (header, name, n);
682 if (!ent)
683 return MU_ERR_NOENT;
684 if (pval)
685 *pval = MU_HDRENT_VALUE (header, ent);
686 return 0;
687 }
688
689 int
mu_header_aget_value_n(mu_header_t header,const char * name,int n,char ** pval)690 mu_header_aget_value_n (mu_header_t header,
691 const char *name, int n,
692 char **pval)
693 {
694 const char *s;
695 int status = mu_header_sget_value_n (header, name, n, &s);
696 if (status == 0)
697 {
698 *pval = strdup (s);
699 if (!*pval)
700 status = ENOMEM;
701 }
702 return status;
703 }
704
705 int
mu_header_get_value_n(mu_header_t header,const char * name,int n,char * buffer,size_t buflen,size_t * pn)706 mu_header_get_value_n (mu_header_t header, const char *name, int n,
707 char *buffer, size_t buflen, size_t *pn)
708 {
709 const char *s;
710 int status = mu_header_sget_value_n (header, name, n, &s);
711 if (status == 0)
712 {
713 size_t slen = strlen (s);
714
715 if (buffer)
716 {
717 if (slen > buflen)
718 slen = buflen;
719 memcpy (buffer, s, slen);
720 buffer[slen] = 0;
721 }
722 if (pn)
723 *pn = slen;
724 }
725 return status;
726 }
727
728
729 /* Unfolding functions */
730 int
mu_header_get_value_unfold_n(mu_header_t header,const char * name,int n,char * buffer,size_t buflen,size_t * pn)731 mu_header_get_value_unfold_n (mu_header_t header,
732 const char *name, int n, char *buffer,
733 size_t buflen, size_t *pn)
734 {
735 int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn);
736
737 if (rc == 0)
738 mu_string_unfold (buffer, pn);
739 return rc;
740 }
741
742 int
mu_header_aget_value_unfold_n(mu_header_t header,const char * name,int n,char ** pvalue)743 mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n,
744 char **pvalue)
745 {
746 int rc = mu_header_aget_value_n (header, name, n, pvalue);
747 if (rc == 0)
748 mu_string_unfold (*pvalue, NULL);
749 return rc;
750 }
751
752
753 int
mu_header_get_address_n(mu_header_t header,const char * name,int n,mu_address_t * addr)754 mu_header_get_address_n (mu_header_t header, const char *name, int n,
755 mu_address_t *addr)
756 {
757 const char *value = NULL;
758 int status = mu_header_sget_value_n (header, name, n, &value);
759
760 if (status)
761 return status;
762
763 return mu_address_create (addr, value);
764 }
765
766
767 int
mu_header_get_field_count(mu_header_t header,size_t * pcount)768 mu_header_get_field_count (mu_header_t header, size_t *pcount)
769 {
770 size_t count;
771 size_t size;
772 size_t lines;
773 int status;
774
775 if (header == NULL)
776 return EINVAL;
777
778 status = mu_header_fill (header);
779 if (status == 0)
780 {
781 mu_hdrent_count (header, &count, &size, &lines);
782
783 if (pcount)
784 *pcount = count;
785 }
786
787 return status;
788 }
789
790 int
mu_header_get_itemptr(mu_header_t header,size_t num,const void ** sptr)791 mu_header_get_itemptr (mu_header_t header, size_t num, const void **sptr)
792 {
793 int status;
794
795 if (header == NULL)
796 return EINVAL;
797
798 status = mu_header_fill (header);
799 if (status == 0)
800 {
801 struct mu_hdrent *ent = mu_hdrent_nth (header, num);
802 if (ent)
803 *sptr = ent;
804 else
805 status = MU_ERR_NOENT;
806 }
807 return status;
808 }
809
810 int
mu_header_sget_field_name(mu_header_t header,size_t num,const char ** sptr)811 mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr)
812 {
813 int status;
814
815 if (header == NULL)
816 return EINVAL;
817
818 status = mu_header_fill (header);
819 if (status == 0)
820 {
821 struct mu_hdrent *ent = mu_hdrent_nth (header, num);
822 if (ent)
823 *sptr = MU_HDRENT_NAME (header, ent);
824 else
825 status = MU_ERR_NOENT;
826 }
827 return status;
828 }
829
830 int
mu_header_get_field_name(mu_header_t header,size_t num,char * buffer,size_t buflen,size_t * pn)831 mu_header_get_field_name (mu_header_t header, size_t num, char *buffer,
832 size_t buflen, size_t *pn)
833 {
834 const char *s;
835 int status = mu_header_sget_field_name (header, num, &s);
836 if (status == 0)
837 {
838 size_t slen = strlen (s);
839
840 if (buffer)
841 {
842 if (slen > buflen)
843 slen = buflen;
844 memcpy (buffer, s, slen);
845 buffer[slen] = 0;
846 }
847 if (pn)
848 *pn = slen;
849 }
850 return status;
851 }
852
853 int
mu_header_aget_field_name(mu_header_t header,size_t num,char ** pvalue)854 mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue)
855 {
856 const char *s;
857 int status = mu_header_sget_field_name (header, num, &s);
858 if (status == 0)
859 {
860 if ((*pvalue = strdup (s)) == NULL)
861 status = ENOMEM;
862 }
863 return status;
864 }
865
866
867 int
mu_header_sget_field_value(mu_header_t header,size_t num,const char ** sptr)868 mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr)
869 {
870 int status;
871
872 if (header == NULL)
873 return EINVAL;
874
875 status = mu_header_fill (header);
876 if (status == 0)
877 {
878 struct mu_hdrent *ent = mu_hdrent_nth (header, num);
879 if (ent)
880 *sptr = MU_HDRENT_VALUE (header, ent);
881 else
882 status = MU_ERR_NOENT;
883 }
884 return status;
885 }
886
887 int
mu_header_get_field_value(mu_header_t header,size_t num,char * buffer,size_t buflen,size_t * pn)888 mu_header_get_field_value (mu_header_t header, size_t num, char *buffer,
889 size_t buflen, size_t *pn)
890 {
891 const char *s;
892 int status = mu_header_sget_field_value (header, num, &s);
893 if (status == 0)
894 {
895 size_t slen = strlen (s);
896
897 if (buffer)
898 {
899 if (slen > buflen)
900 slen = buflen;
901 memcpy (buffer, s, slen);
902 buffer[slen] = 0;
903 }
904 if (pn)
905 *pn = slen;
906 }
907 return status;
908 }
909
910 int
mu_header_aget_field_value(mu_header_t header,size_t num,char ** pvalue)911 mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue)
912 {
913 const char *s;
914 int status = mu_header_sget_field_value (header, num, &s);
915 if (status == 0)
916 {
917 if ((*pvalue = strdup (s)) == NULL)
918 status = ENOMEM;
919 }
920 return status;
921 }
922
923 int
mu_header_get_field_value_unfold(mu_header_t header,size_t num,char * buf,size_t buflen,size_t * nwritten)924 mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf,
925 size_t buflen, size_t *nwritten)
926 {
927 int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten);
928 if (rc == 0)
929 mu_string_unfold (buf, nwritten);
930 return rc;
931 }
932
933 int
mu_header_aget_field_value_unfold(mu_header_t header,size_t num,char ** pvalue)934 mu_header_aget_field_value_unfold (mu_header_t header, size_t num,
935 char **pvalue)
936 {
937 int rc = mu_header_aget_field_value (header, num, pvalue);
938 if (rc == 0)
939 mu_string_unfold (*pvalue, NULL);
940 return rc;
941 }
942
943
944 int
mu_header_lines(mu_header_t header,size_t * plines)945 mu_header_lines (mu_header_t header, size_t *plines)
946 {
947 int status;
948
949 if (header == NULL)
950 return EINVAL;
951 if (plines == NULL)
952 return MU_ERR_OUT_PTR_NULL;
953
954 status = mu_header_fill (header);
955 if (status == 0)
956 {
957 size_t count;
958 size_t size;
959 size_t lines;
960 mu_hdrent_count (header, &count, &size, &lines);
961 *plines = lines + 1;
962 }
963 return status;
964 }
965
966 int
mu_header_size(mu_header_t header,size_t * psize)967 mu_header_size (mu_header_t header, size_t *psize)
968 {
969 int status;
970
971 if (header == NULL)
972 return EINVAL;
973 if (psize == NULL)
974 return MU_ERR_OUT_PTR_NULL;
975
976 status = mu_header_fill (header);
977 if (status == 0)
978 {
979 size_t count;
980 size_t size;
981 size_t lines;
982 mu_hdrent_count (header, &count, &size, &lines);
983 *psize = size + 1;
984 }
985 return status;
986 }
987
988 int
mu_header_invalidate(mu_header_t hdr)989 mu_header_invalidate (mu_header_t hdr)
990 {
991 if (hdr == NULL)
992 return EINVAL;
993 mu_hdrent_free_list (hdr);
994 return 0;
995 }
996
997
998 static void
mu_hdrent_fixup(mu_header_t hdr,struct mu_hdrent * ent)999 mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent)
1000 {
1001 char *s = MU_HDRENT_NAME (hdr, ent);
1002 s[ent->nlen] = ':';
1003 s = MU_HDRENT_VALUE (hdr, ent);
1004 s[ent->vlen] = '\n';
1005 }
1006
1007 static void
mu_hdrent_unroll_fixup(mu_header_t hdr,struct mu_hdrent * ent)1008 mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent)
1009 {
1010 char *s = MU_HDRENT_NAME (hdr, ent);
1011 s[ent->nlen] = 0;
1012 s = MU_HDRENT_VALUE (hdr, ent);
1013 s[ent->vlen] = 0;
1014 }
1015
1016 int
header_seek(mu_stream_t str,mu_off_t off,mu_off_t * presult)1017 header_seek (mu_stream_t str, mu_off_t off, mu_off_t *presult)
1018 {
1019 struct _mu_header_stream *hstr = (struct _mu_header_stream *) str;
1020 size_t size;
1021 int status;
1022
1023 status = mu_header_size (hstr->hdr, &size);
1024 if (status)
1025 return status;
1026
1027 if (off < 0 || off > size)
1028 return ESPIPE;
1029 hstr->off = off;
1030 *presult = off;
1031 return 0;
1032 }
1033
1034 static int
header_read(mu_stream_t is,char * buffer,size_t buflen,size_t * pnread)1035 header_read (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread)
1036 {
1037 struct _mu_header_stream *hstr = (struct _mu_header_stream *) is;
1038 mu_header_t header;
1039 struct mu_hdrent *ent;
1040 size_t ent_off;
1041 int status;
1042 size_t nread;
1043
1044 if (is == NULL)
1045 return EINVAL;
1046
1047 header = hstr->hdr;
1048 status = mu_header_fill (header);
1049 if (status)
1050 return status;
1051
1052 if (header->spool_size == 0)
1053 {
1054 size_t len = 0;
1055
1056 if (hstr->off == 0 && buflen > 0)
1057 {
1058 /* Provide the trailing \n */
1059 *buffer = '\n';
1060 hstr->off++;
1061 len = 1;
1062 }
1063 if (pnread)
1064 *pnread = len;
1065 return 0;
1066 }
1067
1068 if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off))
1069 {
1070 if (pnread)
1071 *pnread = 0;
1072 return 0;
1073 }
1074
1075 for (nread = 0; nread < buflen && ent; ent = ent->next)
1076 {
1077 size_t rest = buflen - nread;
1078 size_t strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
1079 if (rest > strsize)
1080 rest = strsize;
1081 mu_hdrent_fixup (header, ent);
1082 memcpy (buffer + nread, MU_HDRENT_NAME (header, ent) + ent_off, rest);
1083 mu_hdrent_unroll_fixup (header, ent);
1084 nread += rest;
1085 hstr->off += rest;
1086 ent_off = 0;
1087 }
1088 if (pnread)
1089 *pnread = nread;
1090 return 0;
1091 }
1092
1093 #if 0
1094 /* FIXME: Implement header_readdelim based on this: */
1095 static int
1096 _header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread)
1097 {
1098 struct _mu_header_stream *hstr = (struct _mu_header_stream *) is;
1099 mu_header_t header = hstr->hdr;
1100 struct mu_hdrent *ent;
1101 size_t ent_off;
1102 int status;
1103 size_t strsize;
1104 char *start, *end;
1105
1106 if (buflen == 0)
1107 return EINVAL;
1108
1109 header = mu_stream_get_owner (is);
1110 status = mu_header_fill (header);
1111 if (status)
1112 return status;
1113 if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off))
1114 {
1115 if (pnread)
1116 *pnread = 0;
1117 return 0;
1118 }
1119
1120 buflen--; /* Account for the terminating nul */
1121
1122 mu_hdrent_fixup (header, ent);
1123 strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
1124 start = MU_HDRENT_NAME (header, ent) + ent_off;
1125 end = strchr (start, '\n');
1126 if (end)
1127 {
1128 size_t len = end - start + 1;
1129 if (len < strsize)
1130 strsize = len;
1131 }
1132
1133 if (strsize < buflen)
1134 buflen = strsize;
1135
1136 memcpy (buffer, start, buflen);
1137 buffer[buflen] = 0;
1138 hstr->off += buflen;
1139 mu_hdrent_unroll_fixup (header, ent);
1140 if (pnread)
1141 *pnread = buflen;
1142 return 0;
1143 }
1144 #endif
1145
1146 static int
header_write(mu_stream_t os,const char * buf,size_t buflen,size_t * pnwrite)1147 header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite)
1148 {
1149 struct _mu_header_stream *hstr = (struct _mu_header_stream *) os;
1150 mu_header_t header;
1151 int rc;
1152
1153 if (os == NULL)
1154 return EINVAL;
1155 header = hstr->hdr;
1156 if (!(header->flags & HEADER_STREAMMOD))
1157 {
1158 struct mu_hdrent *ent;
1159
1160 rc = mu_header_fill (header);
1161 if (rc)
1162 return rc;
1163
1164 if (!header->mstream)
1165 {
1166 rc = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR);
1167 if (rc)
1168 return rc;
1169 }
1170 mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL);
1171 if (header->spool_used)
1172 {
1173 for (ent = header->head; ent; ent = ent->next)
1174 mu_hdrent_fixup (header, ent);
1175 rc = mu_stream_write (header->mstream, header->spool,
1176 header->spool_used, NULL);
1177 for (ent = header->head; ent; ent = ent->next)
1178 mu_hdrent_unroll_fixup (header, ent);
1179 if (rc)
1180 return rc;
1181 mu_stream_truncate (header->mstream, header->spool_used);
1182 if (hstr->off > header->spool_used)
1183 hstr->off = header->spool_used;
1184 }
1185 header->flags |= HEADER_STREAMMOD;
1186 }
1187
1188 rc = mu_stream_seek (header->mstream, hstr->off, MU_SEEK_SET, NULL);
1189 if (rc)
1190 return rc;
1191
1192 return mu_stream_write (header->mstream, buf, buflen, pnwrite);
1193 }
1194
1195 static int
header_size(mu_stream_t str,mu_off_t * psize)1196 header_size (mu_stream_t str, mu_off_t *psize)
1197 {
1198 mu_header_t header;
1199 int status;
1200 size_t size;
1201
1202 if (str == NULL)
1203 return EINVAL;
1204 if (psize == NULL)
1205 return MU_ERR_OUT_PTR_NULL;
1206
1207 header = ((struct _mu_header_stream *) str)->hdr;
1208 status = mu_header_fill (header);
1209 if (status)
1210 return status;
1211 status = mu_header_size (header, &size);
1212 if (status == 0)
1213 *psize = size;
1214 return status;
1215 }
1216
1217 int
mu_header_get_streamref(mu_header_t header,mu_stream_t * pstream)1218 mu_header_get_streamref (mu_header_t header, mu_stream_t *pstream)
1219 {
1220 if (header == NULL)
1221 return EINVAL;
1222
1223 if (pstream == NULL)
1224 return MU_ERR_OUT_PTR_NULL;
1225
1226 if (header->stream == NULL)
1227 {
1228 struct _mu_header_stream *str =
1229 (struct _mu_header_stream *) _mu_stream_create (sizeof (*str),
1230 MU_STREAM_RDWR|MU_STREAM_SEEK);
1231 if (!str)
1232 return ENOMEM;
1233 str->stream.read = header_read;
1234 /*str->stream.rdelim? */
1235 str->stream.write = header_write;
1236 str->stream.seek = header_seek;
1237 str->stream.size = header_size;
1238 str->hdr = header;
1239 header->stream = (mu_stream_t) str;
1240 }
1241 return mu_streamref_create (pstream, header->stream);
1242 }
1243
1244
1245 int
mu_header_set_fill(mu_header_t header,int (* _fill)(void * data,char **,size_t *),void * data)1246 mu_header_set_fill (mu_header_t header, int
1247 (*_fill) (void *data, char **, size_t *),
1248 void *data)
1249 {
1250 if (header == NULL)
1251 return EINVAL;
1252 header->_fill = _fill;
1253 header->data = data;
1254 return 0;
1255 }
1256
1257
1258 int
mu_header_is_modified(mu_header_t header)1259 mu_header_is_modified (mu_header_t header)
1260 {
1261 return header ? (header->flags & HEADER_MODIFIED) : 0;
1262 }
1263
1264 int
mu_header_clear_modified(mu_header_t header)1265 mu_header_clear_modified (mu_header_t header)
1266 {
1267 if (header)
1268 header->flags &= ~HEADER_MODIFIED;
1269 return 0;
1270 }
1271
1272
1273