diff options
Diffstat (limited to 'libmailutils/header.c')
-rw-r--r-- | libmailutils/header.c | 1228 |
1 files changed, 1228 insertions, 0 deletions
diff --git a/libmailutils/header.c b/libmailutils/header.c new file mode 100644 index 000000000..2861c676b --- /dev/null +++ b/libmailutils/header.c @@ -0,0 +1,1228 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA */ + +/* This all header business needs a good rewrite. + * -- Alain Magloire, 2000-07-03 (rev. 1.21) + * + * It's the job that's never started as takes longest to finish. + * -- Hamfast Gamgee, some time in the Third Age + * + * It took almost 7 years to gather the courage to start the job, + * and only one day to finish it. + * -- Sergey Poznyakoff, 2007-06-24 + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif + +#include <mailutils/stream.h> +#include <mailutils/address.h> +#include <mailutils/mutil.h> +#include <mailutils/errno.h> +#include <mailutils/cstr.h> +#include <mailutils/sys/header_stream.h> +#include <mailutils/sys/header.h> + +#define HEADER_MODIFIED 0x01 +#define HEADER_INVALIDATE 0x02 + +#define HEADER_SET_MODIFIED(h) \ + ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE)) + + +/* mu_hdrent manipulation */ + +#define MU_HDRENT_NAME(hp,ep) ((hp)->spool + (ep)->fn) +#define MU_HDRENT_VALUE(hp,ep) ((hp)->spool + (ep)->fv) +#define MU_STR_SIZE(nlen,vlen) ((nlen) + 2 + (vlen) + 1) + +static struct mu_hdrent * +mu_hdrent_nth (struct _mu_header *hdr, int n) +{ + struct mu_hdrent *p; + for (p = hdr->head; p; p = p->next) + if (n-- == 1) + break; + return p; +} + +static struct mu_hdrent * +mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos) +{ + struct mu_hdrent *p; + + if (pos > 0) + { + for (p = hdr->head; p; p = p->next) + if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1) + break; + } + else if (pos < 0) + { + for (p = hdr->tail; p; p = p->prev) + if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && ++pos == 0) + break; + } + else + p = NULL; + return p; +} + +static int +mu_hdrent_find_stream_pos (struct _mu_header *hdr, mu_off_t pos, + struct mu_hdrent **pent, size_t *poff) +{ + mu_off_t x; + struct mu_hdrent *p; + + for (p = hdr->head, x = 0; p; p = p->next) + { + size_t strsize = MU_STR_SIZE (p->nlen, p->vlen); + if (x <= pos && pos < x + strsize) + { + *poff = pos - x; + *pent = p; + return 0; + } + x += strsize; + } + if (x == pos && hdr->tail) + { + /* To supply the trailing '\n' */ + p = hdr->tail; + *pent = p; + *poff = MU_STR_SIZE (p->nlen, p->vlen) - 1; + return 0; + } + return 1; +} + +static void +mu_hdrent_count (struct _mu_header *hdr, size_t *pcount, size_t *psize, + size_t *plines) +{ + if (hdr->flags & HEADER_INVALIDATE) + { + size_t size = 0; + size_t count = 0; + size_t lines = 0; + struct mu_hdrent *p; + for (p = hdr->head; p; p = p->next) + { + count++; + size += MU_STR_SIZE (p->nlen, p->vlen); + lines += p->nlines; + } + + hdr->numhdr = count; + hdr->numlines = lines; + hdr->size = size; + hdr->flags &= ~HEADER_INVALIDATE; + } + + *pcount = hdr->numhdr; + *psize = hdr->size; + *plines = hdr->numlines; +} + +static void +mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent) +{ + struct mu_hdrent *p = ent->prev; + if (p) + p->next = ent->next; + else + hdr->head = ent->next; + + p = ent->next; + if (p) + p->prev = ent->prev; + else + hdr->tail = ent->prev; +} + +static void +mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent) +{ + struct mu_hdrent *p = hdr->head; + ent->prev = NULL; + ent->next = p; + if (p) + p->prev = ent; + else + hdr->tail = ent; + hdr->head = ent; +} + +static void +mu_hdrent_append (struct _mu_header *hdr, struct mu_hdrent *ent) +{ + struct mu_hdrent *p = hdr->tail; + ent->next = NULL; + ent->prev = p; + if (p) + p->next = ent; + else + hdr->head = ent; + hdr->tail = ent; +} + +static int +mu_hdrent_insert (struct _mu_header *hdr, struct mu_hdrent *ent, + const char *name, int pos, int before) +{ + struct mu_hdrent *p; + struct mu_hdrent *ref = mu_hdrent_find (hdr, name, pos); + if (!ref) + return MU_ERR_NOENT; + + if (before) + { + ref = ref->prev; + if (!ref) + { + mu_hdrent_prepend (hdr, ent); + return 0; + } + } + + p = ref->next; + if (!p) + { + mu_hdrent_append (hdr, ent); + return 0; + } + + ent->next = p; + p->prev = ent; + ent->prev = ref; + ref->next = ent; + + return 0; +} + +#define SPOOLBLKSIZ 1024 + +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) +{ + size_t strsize; + size_t sizeleft; + const char *p; + + if (!ent) + { + ent = calloc (1, sizeof (*ent)); + if (!ent) + return NULL; + } + + strsize = MU_STR_SIZE (nsize, vsize); + sizeleft = ph->spool_size - ph->spool_used; + + /* Ensure there is enough space in spool */ + if (sizeleft < strsize) + { + char *newp; + size_t delta = (strsize - sizeleft + SPOOLBLKSIZ - 1) / SPOOLBLKSIZ; + delta *= SPOOLBLKSIZ; + newp = realloc (ph->spool, ph->spool_size + delta); + if (!newp) + return 0; + ph->spool = newp; + ph->spool_size += delta; + } + + /* Copy header name */ + ent->fn = ph->spool_used; + ent->nlen = nsize; + memcpy (ph->spool + ph->spool_used, name, nsize); + ph->spool_used += nsize; + ph->spool[ph->spool_used++] = 0; + ph->spool[ph->spool_used++] = ' '; + + /* Copy header value */ + ent->fv = ph->spool_used; + ent->vlen = vsize; + memcpy (ph->spool + ph->spool_used, value, vsize); + ph->spool_used += vsize; + ph->spool[ph->spool_used++] = 0; + + ent->nlines = 1; + for (p = value; p < value + vsize; p++) + if (*p == '\n') + ent->nlines++; + + return ent; +} + +static void +mu_hdrent_free_list (struct _mu_header *hdr) +{ + struct mu_hdrent *p; + for (p = hdr->head; p; ) + { + struct mu_hdrent *next = p->next; + free (p); + p = next; + } + hdr->head = hdr->tail = NULL; + hdr->spool_used = 0; +} + + + +#define ISLWSP(c) (((c) == ' ' || (c) == '\t')) + +/* Parsing is done in a rather simple fashion, meaning we just consider an + entry to be a field-name an a field-value. So they maybe duplicate of + field-name like "Received" they are just put in the array, see _get_value() + on how to handle the case. in the case of error .i.e a bad header construct + we do a full stop and return what we have so far. */ + +static int +header_parse (mu_header_t header, const char *blurb, int len) +{ + const char *header_end; + const char *header_start; + const char *header_start2; + + /* Nothing to parse. */ + if (blurb == NULL) + return 0; + + header->flags |= HEADER_INVALIDATE; + mu_hdrent_free_list (header); + + /* Get a header, a header is: + field-name LWSP ':' + LWSP field-value '\r' '\n' + *[ (' ' | '\t') field-value '\r' '\n' ] + */ + /* First loop goes through the blurb */ + for (header_start = blurb; len > 0; header_start = ++header_end) + { + const char *fn, *fn_end, *fv, *fv_end; + struct mu_hdrent *ent; + + if (header_start[0] == ' ' + || header_start[0] == '\t' + || header_start[0] == '\n') + break; + + /* Second loop extract one header field. */ + for (header_start2 = header_start; len; header_start2 = ++header_end) + { + header_end = memchr (header_start2, '\n', len); + if (header_end == NULL) + { + header_end = header_start2 + len; + len = 0; + break; + } + else + { + len -= (header_end - header_start2 + 1); + if (!len + || (header_end[1] != ' ' + && header_end[1] != '\t')) + break; /* New header break the inner for. */ + } + /* *header_end = ' '; smash LF ? NO */ + } + + /* Now save the header in the data structure. */ + + /* Treats unix "From " specially. FIXME: Should we? */ + if ((header_end - header_start >= 5) + && strncmp (header_start, "From ", 5) == 0) + { + fn = header_start; + fn_end = header_start + 5; + fv = header_start + 5; + fv_end = header_end; + } + else /* Break the header in key: value */ + { + char *colon = memchr (header_start, ':', header_end - header_start); + + /* Houston we have a problem. */ + if (colon == NULL) + break; /* FIXME: Disregard the rest and bailout. */ + + fn = header_start; + fn_end = colon; + /* Shrink any LWSP after the field name -- CRITICAL for + later name comparisons to work correctly! */ + while (ISLWSP (fn_end[-1])) + fn_end--; + + fv = colon + 1; + fv_end = header_end; + + /* Skip any LWSP before the field value -- unnecessary, but + might make some field values look a little tidier. */ + while (ISLWSP (fv[0])) + fv++; + } + + /* Register this header */ + ent = mu_hdrent_create (header, NULL, fn, fn_end - fn, fv, fv_end - fv); + if (!ent) + return ENOMEM; + mu_hdrent_append (header, ent); + } /* for (header_start ...) */ + + return 0; +} + + +static int +mu_header_fill (mu_header_t header) +{ + int status; + size_t blurb_len = 0; + char *blurb = NULL; + + if (header->spool_used) + return 0; + + if (header->_fill == NULL) + return 0; /* FIXME: Really? */ + + /* Bring in the entire header. */ + status = header->_fill (header->data, &blurb, &blurb_len); + if (status) + return status; + status = header_parse (header, blurb, blurb_len); + free (blurb); + return status; +} + + + +int +mu_header_create (mu_header_t *ph, const char *blurb, size_t len) +{ + mu_header_t header; + int status = 0; + + header = calloc (1, sizeof (*header)); + if (header == NULL) + return ENOMEM; + + status = header_parse (header, blurb, len); + + *ph = header; + return status; +} + +void +mu_header_destroy (mu_header_t *ph) +{ + if (ph && *ph) + { + mu_header_t header = *ph; + + mu_stream_destroy (&header->stream); + mu_hdrent_free_list (header); + free (header->spool); + free (header); + *ph = NULL; + } +} + + +int +mu_header_set_value (mu_header_t header, const char *fn, const char *fv, + int replace) +{ + int status; + struct mu_hdrent *ent = NULL; + + if (header == NULL || fn == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status) + return status; + + /* An fv of NULL means delete the field, but only do it if replace + was also set to true! */ + if (fv == NULL && !replace) + return EINVAL; + + if (replace) + { + ent = mu_hdrent_find (header, fn, 1); + if (ent) + { + if (fv == NULL) + { + /* Delete the header */ + mu_hdrent_remove (header, ent); + free (ent); + return 0; + } + mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv)); + HEADER_SET_MODIFIED (header); + return 0; + } + else if (fv == NULL) + return 0; + } + + ent = mu_hdrent_create (header, NULL, + fn, strlen (fn), fv, strlen (fv)); + if (!ent) + return ENOMEM; + mu_hdrent_prepend (header, ent); + HEADER_SET_MODIFIED (header); + return 0; +} + +int +mu_header_remove (mu_header_t header, const char *fn, int n) +{ + int status; + struct mu_hdrent *ent; + + if (header == NULL || fn == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status) + return status; + + ent = mu_hdrent_find (header, fn, n); + if (!ent) + return MU_ERR_NOENT; + + mu_hdrent_remove (header, ent); + HEADER_SET_MODIFIED (header); + free (ent); + return 0; +} + +int +mu_header_append (mu_header_t header, const char *fn, const char *fv) +{ + int status; + struct mu_hdrent *ent; + + if (header == NULL || fn == NULL || fv == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status) + return status; + + ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv)); + if (!ent) + return ENOMEM; + mu_hdrent_append (header, ent); + HEADER_SET_MODIFIED (header); + return 0; +} + +int +mu_header_prepend (mu_header_t header, const char *fn, const char *fv) +{ + int status; + struct mu_hdrent *ent; + + if (header == NULL || fn == NULL || fv == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status) + return status; + + ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv)); + if (!ent) + return ENOMEM; + mu_hdrent_prepend (header, ent); + HEADER_SET_MODIFIED (header); + return 0; +} + +int +mu_header_insert (mu_header_t header, + const char *fn, const char *fv, + const char *ref, int n, int flags) +{ + int status; + struct mu_hdrent *ent; + + if (header == NULL || fn == NULL || fv == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status) + return status; + + if (flags & MU_HEADER_REPLACE) + { + if (!ref) + ref = fn; + ent = mu_hdrent_find (header, ref, n); + mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv)); + } + else + { + ent = mu_hdrent_create (header, NULL, + fn, strlen (fn), fv, strlen (fv)); + if (!ent) + return ENOMEM; + if (ref) + return mu_hdrent_insert (header, ent, ref, n, + flags & MU_HEADER_BEFORE); + else + mu_hdrent_prepend (header, ent); + } + HEADER_SET_MODIFIED (header); + return 0; +} + + +int +mu_header_sget_value_n (mu_header_t header, + const char *name, int n, + const char **pval) +{ + int status; + struct mu_hdrent *ent; + + if (header == NULL || name == NULL) + return EINVAL; + status = mu_header_fill (header); + if (status) + return status; + + ent = mu_hdrent_find (header, name, n); + if (!ent) + return MU_ERR_NOENT; + + *pval = MU_HDRENT_VALUE (header, ent); + return 0; +} + +int +mu_header_aget_value_n (mu_header_t header, + const char *name, int n, + char **pval) +{ + const char *s; + int status = mu_header_sget_value_n (header, name, n, &s); + if (status == 0) + { + *pval = strdup (s); + if (!*pval) + status = ENOMEM; + } + return status; +} + +int +mu_header_get_value_n (mu_header_t header, const char *name, int n, + char *buffer, size_t buflen, size_t *pn) +{ + const char *s; + int status = mu_header_sget_value_n (header, name, n, &s); + if (status == 0) + { + size_t slen = strlen (s); + + if (buffer) + { + if (slen > buflen) + slen = buflen; + memcpy (buffer, s, slen); + buffer[slen] = 0; + } + if (pn) + *pn = slen; + } + return status; +} + + +/* Unfolding functions */ +int +mu_header_get_value_unfold_n (mu_header_t header, + const char *name, int n, char *buffer, + size_t buflen, size_t *pn) +{ + int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn); + + if (rc == 0) + mu_string_unfold (buffer, pn); + return rc; +} + +int +mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n, + char **pvalue) +{ + int rc = mu_header_aget_value_n (header, name, n, pvalue); + if (rc == 0) + mu_string_unfold (*pvalue, NULL); + return rc; +} + + +int +mu_header_get_address_n (mu_header_t header, const char *name, int n, + mu_address_t *addr) +{ + const char *value = NULL; + int status = mu_header_sget_value_n (header, name, n, &value); + + if (status) + return status; + + return mu_address_create (addr, value); +} + + +int +mu_header_get_field_count (mu_header_t header, size_t *pcount) +{ + size_t count; + size_t size; + size_t lines; + int status; + + if (header == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status == 0) + { + mu_hdrent_count (header, &count, &size, &lines); + + if (pcount) + *pcount = count; + } + + return status; +} + +int +mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr) +{ + int status; + + if (header == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status == 0) + { + struct mu_hdrent *ent = mu_hdrent_nth (header, num); + if (ent) + *sptr = MU_HDRENT_NAME (header, ent); + else + status = MU_ERR_NOENT; + } + return status; +} + +int +mu_header_get_field_name (mu_header_t header, size_t num, char *buffer, + size_t buflen, size_t *pn) +{ + const char *s; + int status = mu_header_sget_field_name (header, num, &s); + if (status == 0) + { + size_t slen = strlen (s); + + if (buffer) + { + if (slen > buflen) + slen = buflen; + memcpy (buffer, s, slen); + buffer[slen] = 0; + } + if (pn) + *pn = slen; + } + return status; +} + +int +mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue) +{ + const char *s; + int status = mu_header_sget_field_name (header, num, &s); + if (status == 0) + { + if ((*pvalue = strdup (s)) == NULL) + status = ENOMEM; + } + return status; +} + + +int +mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr) +{ + int status; + + if (header == NULL) + return EINVAL; + + status = mu_header_fill (header); + if (status == 0) + { + struct mu_hdrent *ent = mu_hdrent_nth (header, num); + if (ent) + *sptr = MU_HDRENT_VALUE (header, ent); + else + status = MU_ERR_NOENT; + } + return status; +} + +int +mu_header_get_field_value (mu_header_t header, size_t num, char *buffer, + size_t buflen, size_t *pn) +{ + const char *s; + int status = mu_header_sget_field_value (header, num, &s); + if (status == 0) + { + size_t slen = strlen (s); + + if (buffer) + { + if (slen > buflen) + slen = buflen; + memcpy (buffer, s, slen); + buffer[slen] = 0; + } + if (pn) + *pn = slen; + } + return status; +} + +int +mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue) +{ + const char *s; + int status = mu_header_sget_field_value (header, num, &s); + if (status == 0) + { + if ((*pvalue = strdup (s)) == NULL) + status = ENOMEM; + } + return status; +} + +int +mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf, + size_t buflen, size_t *nwritten) +{ + int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten); + if (rc == 0) + mu_string_unfold (buf, nwritten); + return rc; +} + +int +mu_header_aget_field_value_unfold (mu_header_t header, size_t num, + char **pvalue) +{ + int rc = mu_header_aget_field_value (header, num, pvalue); + if (rc == 0) + mu_string_unfold (*pvalue, NULL); + return rc; +} + + +int +mu_header_lines (mu_header_t header, size_t *plines) +{ + int status; + + if (header == NULL) + return EINVAL; + if (plines == NULL) + return MU_ERR_OUT_PTR_NULL; + + status = mu_header_fill (header); + if (status == 0) + { + size_t count; + size_t size; + size_t lines; + mu_hdrent_count (header, &count, &size, &lines); + *plines = lines + 1; + } + return status; +} + +int +mu_header_size (mu_header_t header, size_t *psize) +{ + int status; + + if (header == NULL) + return EINVAL; + if (psize == NULL) + return MU_ERR_OUT_PTR_NULL; + + status = mu_header_fill (header); + if (status == 0) + { + size_t count; + size_t size; + size_t lines; + mu_hdrent_count (header, &count, &size, &lines); + *psize = size + 1; + } + return status; +} + +int +mu_header_invalidate (mu_header_t hdr) +{ + if (hdr == NULL) + return EINVAL; + mu_hdrent_free_list (hdr); + return 0; +} + + +static void +mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent) +{ + char *s = MU_HDRENT_NAME (hdr, ent); + s[ent->nlen] = ':'; + s = MU_HDRENT_VALUE (hdr, ent); + s[ent->vlen] = '\n'; +} + +static void +mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent) +{ + char *s = MU_HDRENT_NAME (hdr, ent); + s[ent->nlen] = 0; + s = MU_HDRENT_VALUE (hdr, ent); + s[ent->vlen] = 0; +} + +int +header_seek (mu_stream_t str, mu_off_t off, mu_off_t *presult) +{ + struct _mu_header_stream *hstr = (struct _mu_header_stream *) str; + size_t size; + int status; + + status = mu_header_size (hstr->hdr, &size); + if (status) + return status; + + if (off < 0 || off > size) + return ESPIPE; + hstr->off = off; + *presult = off; + return 0; +} + +static int +header_read (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread) +{ + struct _mu_header_stream *hstr = (struct _mu_header_stream *) is; + mu_header_t header; + struct mu_hdrent *ent; + size_t ent_off; + int status; + size_t nread; + + if (is == NULL) + return EINVAL; + + header = hstr->hdr; + status = mu_header_fill (header); + if (status) + return status; + + if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off)) + { + if (pnread) + *pnread = 0; + return 0; + } + + for (nread = 0; nread < buflen && ent; ent = ent->next) + { + size_t rest = buflen - nread; + size_t strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off; + if (rest > strsize) + rest = strsize; + mu_hdrent_fixup (header, ent); + memcpy (buffer + nread, MU_HDRENT_NAME (header, ent) + ent_off, rest); + mu_hdrent_unroll_fixup (header, ent); + nread += rest; + hstr->off += rest; + ent_off = 0; + } + if (pnread) + *pnread = nread; + return 0; +} + +#if 0 +/* FIXME: Implement header_readdelim based on this: */ +static int +_header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread) +{ + struct _mu_header_stream *hstr = (struct _mu_header_stream *) is; + mu_header_t header = hstr->hdr; + struct mu_hdrent *ent; + size_t ent_off; + int status; + size_t strsize; + char *start, *end; + + if (buflen == 0) + return EINVAL; + + header = mu_stream_get_owner (is); + status = mu_header_fill (header); + if (status) + return status; + if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off)) + { + if (pnread) + *pnread = 0; + return 0; + } + + buflen--; /* Account for the terminating nul */ + + mu_hdrent_fixup (header, ent); + strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off; + start = MU_HDRENT_NAME (header, ent) + ent_off; + end = strchr (start, '\n'); + if (end) + { + size_t len = end - start + 1; + if (len < strsize) + strsize = len; + } + + if (strsize < buflen) + buflen = strsize; + + memcpy (buffer, start, buflen); + buffer[buflen] = 0; + hstr->off += buflen; + mu_hdrent_unroll_fixup (header, ent); + if (pnread) + *pnread = buflen; + return 0; +} +#endif + +static int +header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite) +{ + struct _mu_header_stream *hstr; + mu_header_t header; + int status; + mu_off_t mstream_size; + + if (!os || !buf) + return EINVAL; + + hstr = (struct _mu_header_stream *) os; + header = hstr->hdr; + if (header == NULL) + return EINVAL; + + /* Skip the obvious. */ + if (*buf == '\0' || buflen == 0) + { + if (pnwrite) + *pnwrite = 0; + return 0; + } + + if (!header->mstream) + { + status = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR); + if (status) + return status; + status = mu_stream_open (header->mstream); + if (status) + { + mu_stream_destroy (&header->mstream); + return status; + } + } + + status = mu_stream_write (header->mstream, buf, buflen, NULL); + if (status) + { + mu_stream_destroy (&header->mstream); + return status; + } + + status = mu_stream_size (header->mstream, &mstream_size); + if (status == 0 && mstream_size > 1) + { + char nlbuf[2]; + + status = mu_stream_seek (header->mstream, -2, MU_SEEK_END, NULL); + if (status == 0) + status = mu_stream_read (header->mstream, nlbuf, 2, NULL); + if (status == 0 && memcmp (nlbuf, "\n\n", 2) == 0) + { + char *blurb; + + blurb = calloc (1, mstream_size + 1); + if (blurb) + { + mu_stream_read (header->mstream, blurb, mstream_size, NULL); + status = header_parse (header, blurb, mstream_size); + } + free (blurb); + mu_stream_destroy (&header->mstream); + } + } + + if (pnwrite) + *pnwrite = buflen; + + return status; +} + +static int +header_size (mu_stream_t str, mu_off_t *psize) +{ + mu_header_t header; + int status; + size_t size; + + if (str == NULL) + return EINVAL; + if (psize == NULL) + return MU_ERR_OUT_PTR_NULL; + + header = ((struct _mu_header_stream *) str)->hdr; + status = mu_header_fill (header); + if (status) + return status; + status = mu_header_size (header, &size); + if (status == 0) + *psize = size; + return status; +} + +static int +_header_get_stream (mu_header_t header, mu_stream_t *pstream, int ref) +{ + if (header == NULL) + return EINVAL; + + if (pstream == NULL) + return MU_ERR_OUT_PTR_NULL; + + if (header->stream == NULL) + { + struct _mu_header_stream *str = + (struct _mu_header_stream *) _mu_stream_create (sizeof (*str), + MU_STREAM_RDWR|MU_STREAM_SEEK); + if (!str) + return ENOMEM; + str->stream.read = header_read; + /*str->stream.rdelim? */ + str->stream.write = header_write; + str->stream.seek = header_seek; + str->stream.size = header_size; + str->hdr = header; + header->stream = (mu_stream_t) str; + } + if (!ref) + { + *pstream = header->stream; + return 0; + } + return mu_streamref_create (pstream, header->stream); +} + +int +mu_header_get_stream (mu_header_t header, mu_stream_t *pstream) +{ + /* FIXME: Deprecation warning */ + return _header_get_stream (header, pstream, 0); +} + +int +mu_header_get_streamref (mu_header_t header, mu_stream_t *pstream) +{ + return _header_get_stream (header, pstream, 1); +} + + +int +mu_header_set_fill (mu_header_t header, int + (*_fill) (void *data, char **, size_t *), + void *data) +{ + if (header == NULL) + return EINVAL; + header->_fill = _fill; + header->data = data; + return 0; +} + + +int +mu_header_is_modified (mu_header_t header) +{ + return header ? (header->flags & HEADER_MODIFIED) : 0; +} + +int +mu_header_clear_modified (mu_header_t header) +{ + if (header) + header->flags &= ~HEADER_MODIFIED; + return 0; +} + + |