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