Commit 344611f5 344611f57409fc78699198b165c8acf986aceb65 by Sergey Poznyakoff

Rewrite from scratch

1 parent 2039d455
......@@ -20,7 +20,7 @@
#define _HEADER0_H
#ifdef DMALLOC
# include <dmalloc.h>
# include <dmalloc.h>
#endif
#include <mailutils/header.h>
......@@ -33,38 +33,42 @@ extern "C" {
/* The structure members are offset that point to the begin/end of header
fields. */
struct _hdr
struct mu_hdrent
{
char *fn;
char *fn_end;
char *fv;
char *fv_end;
struct mu_hdrent *prev;
struct mu_hdrent *next;
size_t fn;
size_t nlen;
size_t fv;
size_t vlen;
size_t nlines;
};
/* The blurb member represents the headers, hdr_count the number of distinct
header field and the layout is done by struct_hdr *hdr. */
struct _mu_header
{
/* Owner. */
void *owner;
/* Data. */
mu_stream_t mstream;
size_t stream_len;
char *blurb;
size_t blurb_len;
size_t hdr_count;
struct _hdr *hdr;
char *spool;
size_t spool_size;
size_t spool_used;
struct mu_hdrent *head, *tail;
int flags;
mu_assoc_t cache;
size_t numhdr;
size_t numlines;
size_t size;
/* Temporary storage */
mu_stream_t mstream;
size_t mstream_size;
/* Stream. */
mu_stream_t stream;
int (*_get_value) (mu_header_t, const char *, char *, size_t , size_t *);
int (*_set_value) (mu_header_t, const char *, const char *, int);
int (*_lines) (mu_header_t, size_t *);
int (*_size) (mu_header_t, size_t *);
size_t strpos;
/* Methods */
int (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *);
};
......
......@@ -17,6 +17,17 @@
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
......@@ -37,120 +48,267 @@
#include <header0.h>
#define HEADER_MODIFIED 1
#define HEADER_MODIFIED 0x01
#define HEADER_INVALIDATE 0x02
static int header_parse (mu_header_t, const char *, int);
static int header_read (mu_stream_t, char *, size_t, mu_off_t, size_t *);
static int header_readline (mu_stream_t, char *, size_t, mu_off_t, size_t *);
static int header_write (mu_stream_t, const char *, size_t, mu_off_t, size_t *);
static int fill_blurb (mu_header_t);
static void header_free_cache (mu_header_t);
#define HEADER_SET_MODIFIED(h) \
((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
int
mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner)
/* 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)
{
mu_header_t header;
int status = 0;
struct mu_hdrent *p;
for (p = hdr->head; p; p = p->next)
if (n-- == 1)
break;
return p;
}
header = calloc (1, sizeof (*header));
if (header == NULL)
return ENOMEM;
static struct mu_hdrent *
mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos)
{
struct mu_hdrent *p;
for (p = hdr->head; p; p = p->next)
if (strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1)
break;
return p;
}
header->owner = owner;
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;
status = header_parse (header, blurb, len);
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)
{
/* 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;
}
*ph = header;
return status;
hdr->numhdr = count;
hdr->numlines = lines;
hdr->size = size;
hdr->flags &= ~HEADER_INVALIDATE;
}
*pcount = hdr->numhdr;
*psize = hdr->size;
*plines = hdr->numlines;
}
static void
header_free_cache (mu_header_t header)
mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent)
{
mu_assoc_clear (header->cache);
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;
}
void
mu_header_destroy (mu_header_t *ph, void *owner)
static void
mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent)
{
if (ph && *ph)
{
mu_header_t header = *ph;
struct mu_hdrent *p = hdr->head;
ent->prev = NULL;
ent->next = p;
if (p)
p->prev = ent;
else
hdr->tail = ent;
hdr->head = ent;
}
/* Can we destroy ?. */
if (header->owner == owner)
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_stream_destroy (&(header->stream), header);
mu_hdrent_prepend (hdr, ent);
return 0;
}
}
if (header->hdr)
free (header->hdr);
p = ref->next;
if (!p)
{
mu_hdrent_append (hdr, ent);
return 0;
}
if (header->blurb)
free (header->blurb);
ent->next = p;
p->prev = ent;
ent->prev = ref;
ref->next = ent;
return 0;
}
header_free_cache (header);
#define SPOOLBLKSIZ 1024
mu_assoc_destroy (&header->cache);
if (header->mstream)
mu_stream_destroy (&(header->mstream), NULL);
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;
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;
free (header);
}
*ph = NULL;
/* 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;
}
}
void *
mu_header_get_owner (mu_header_t header)
{
return (header) ? header->owner : NULL;
/* 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;
}
int
mu_header_is_modified (mu_header_t header)
static void
mu_hdrent_free_list (struct _mu_header *hdr)
{
return (header) ? (header->flags & HEADER_MODIFIED) : 0;
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;
}
int
mu_header_clear_modified (mu_header_t header)
{
if (header)
header->flags &= ~HEADER_MODIFIED;
return 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)
{
char *header_end;
char *header_start;
char *header_start2;
struct _hdr *hdr;
header_free_cache (header);
const char *header_end;
const char *header_start;
const char *header_start2;
/* Nothing to parse. */
if (blurb == NULL)
return 0;
header->blurb_len = len;
/* Why "+ 1", if for a terminating NULL, where is written? */
header->blurb = calloc (1, header->blurb_len + 1);
if (header->blurb == NULL)
return ENOMEM;
memcpy (header->blurb, blurb, header->blurb_len);
if (header->hdr)
free (header->hdr);
header->hdr = NULL;
header->hdr_count = 0;
header->flags |= HEADER_INVALIDATE;
mu_hdrent_free_list (header);
/* Get a header, a header is:
field-name LWSP ':'
......@@ -158,9 +316,10 @@ header_parse (mu_header_t header, const char *blurb, int len)
*[ (' ' | '\t') field-value '\r' '\n' ]
*/
/* First loop goes through the blurb */
for (header_start = header->blurb; ; header_start = ++header_end)
for (header_start = blurb; ; header_start = ++header_end)
{
char *fn, *fn_end, *fv, *fv_end;
const char *fn, *fn_end, *fv, *fv_end;
struct mu_hdrent *ent;
if (header_start[0] == ' '
|| header_start[0] == '\t'
......@@ -189,11 +348,11 @@ header_parse (mu_header_t header, const char *blurb, int len)
}
if (header_end == NULL)
break; /* Bail out. */
break; /* FIXME: Bail out. */
/* Now save the header in the data structure. */
/* Treats unix "From " specially. */
/* Treats unix "From " specially. FIXME: Should we? */
if ((header_end - header_start >= 5)
&& strncmp (header_start, "From ", 5) == 0)
{
......@@ -206,366 +365,320 @@ header_parse (mu_header_t header, const char *blurb, int len)
{
char *colon = memchr (header_start, ':', header_end - header_start);
#define ISLWSP(c) (((c) == ' ' || (c) == '\t'))
/* Houston we have a problem. */
if (colon == NULL)
break; /* Disregard the rest and bailout. */
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]))
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]))
might make some field values look a little tidier. */
while (ISLWSP (fv[0]))
fv++;
}
#undef ISLWSP
/* Allocate a new slot for the field:value. */
hdr = realloc (header->hdr, (header->hdr_count + 1) * sizeof (*hdr));
if (hdr == NULL)
{
free (header->blurb);
free (header->hdr);
header->blurb = NULL;
header->hdr = NULL;
return ENOMEM;
}
hdr[header->hdr_count].fn = fn;
hdr[header->hdr_count].fn_end = fn_end;
hdr[header->hdr_count].fv = fv;
hdr[header->hdr_count].fv_end = fv_end;
header->hdr = hdr;
header->hdr_count++;
/* 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;
return 0;
}
/* FIXME: grossly inneficient, too many copies and reallocating.
This all header business needs a good rewrite. */
int
mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
int replace)
static int
mu_header_fill (mu_header_t header)
{
int status;
char buf[1024];
size_t nread;
mu_stream_t mstream;
size_t stream_len;
char *blurb;
size_t len;
if (header == NULL || fn == NULL)
return EINVAL;
/* 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;
/* Overload. */
if (header->_set_value)
return header->_set_value (header, fn, fv, replace);
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
/* Easy approach: if replace, overwrite the field-{name,value} and readjust
the pointers by calling header_parse () this is wastefull, we're just
fragmenting the memory it can be done better. But that may imply a
rewite of the headers ... for another day. */
/* If replace, remove all fields in the header blurb that have the
same name as the field we are writing.
if (header->spool_used)
return 0;
if (header->_fill == NULL)
return 0; /* FIXME: Really? */
Algorithm:
status = mu_memory_stream_create (&mstream, NULL, MU_STREAM_RDWR);
if (status != 0)
return status;
mu_stream_open (mstream);
stream_len = 0;
for i = 0, ... i < max_hdrs
- if ith field has name 'fn' memmove() all following fields up over
this field
- reparse the headers
- restart the for loop at the ith field
*/
if (replace)
/* Bring in the entire header. */
do
{
size_t name_len;
size_t i;
size_t fn_len; /* Field Name len. */
size_t fv_len; /* Field Value len. */
len = header->blurb_len;
/* Find FN in the header fields... */
for (name_len = strlen (fn), i = 0; i < header->hdr_count; i++)
nread = 0;
status = header->_fill (header, buf, sizeof buf,
stream_len, &nread);
if (status)
{
if (status != EAGAIN && status != EINTR)
mu_stream_destroy (&mstream, NULL);
return status;
}
if (nread > 0)
{
fn_len = header->hdr[i].fn_end - header->hdr[i].fn;
fv_len = header->hdr[i].fv_end - header->hdr[i].fv;
if (fn_len == name_len &&
strncasecmp (header->hdr[i].fn, fn, fn_len) == 0)
status = mu_stream_write (mstream, buf, nread, stream_len, NULL);
if (status)
{
blurb = header->blurb;
/* ... and if its NOT the last field, move the next field
through to last field into its place, */
if ((i + 1) < header->hdr_count)
{
memmove (header->hdr[i].fn, header->hdr[i + 1].fn,
header->hdr[header->hdr_count - 1].fv_end
- header->hdr[i + 1].fn + 3);
}
/* or if it is the last, just truncate the fields. */
else
{
header->hdr[i].fn[0] = '\n';
header->hdr[i].fn[1] = '\0';
}
/* Readjust the pointers. */
len -= header->hdr[i].fv_end - header->hdr[i].fn + 1;
i--;
blurb = header->blurb;
header_parse (header, blurb, len);
free (blurb);
header->flags |= HEADER_MODIFIED;
mu_stream_destroy (&mstream, NULL);
return status;
}
stream_len += nread;
}
}
while (nread > 0);
/* If FV is NULL, then we are done. */
if (!fv)
return 0;
/* parse it. */
blurb = calloc (1, stream_len + 1);
if (blurb)
{
size_t len;
status = mu_stream_read (mstream, blurb, stream_len, 0, &len);
if (status == 0)
status = header_parse (header, blurb, len);
free (blurb);
}
else
status = ENOMEM;
mu_stream_destroy (&mstream, NULL);
return status;
}
/* Replacing was taken care of above, now write the new header.
header. Really not cute.
COLON SPACE NL = 3 ; */
len = strlen (fn) + strlen (fv) + 3;
/* Add one for the NULL and leak a bit by adding one more
it will be the separtor \n from the body if the first
blurb did not have it. */
blurb = calloc (header->blurb_len + len + 2, 1);
if (blurb == NULL)
int
mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner)
{
mu_header_t header;
int status = 0;
header = calloc (1, sizeof (*header));
if (header == NULL)
return ENOMEM;
header->owner = owner;
sprintf (blurb, "%s: %s", fn, fv);
status = header_parse (header, blurb, len);
/* Strip off trailing newlines and LWSP. */
while (blurb[strlen (blurb) - 1] == '\n' ||
blurb[strlen (blurb) - 1] == ' ' ||
blurb[strlen (blurb) - 1] == '\t')
{
blurb[strlen (blurb) - 1] = '\0';
}
len = strlen (blurb);
blurb[len] = '\n';
len++;
*ph = header;
return status;
}
/* Prepend the rest of the headers. */
if (header->blurb)
{
memcpy (blurb + len, header->blurb, header->blurb_len);
free (header->blurb);
header->blurb = NULL;
}
else
blurb[len] = '\n';
void
mu_header_destroy (mu_header_t *ph, void *owner)
{
if (ph && *ph)
{
mu_header_t header = *ph;
/* before parsing the new blurb make sure it is properly terminated
by \n\n. The trailing NL separator. */
if (blurb[header->blurb_len + len - 1] != '\n'
|| blurb[header->blurb_len + len - 2] != '\n')
{
blurb[header->blurb_len + len] = '\n';
len++;
if (header->owner == owner)
{
mu_stream_destroy (&header->stream, header);
mu_hdrent_free_list (header);
free (header->spool);
free (header);
*ph = NULL;
}
}
header_parse (header, blurb, len + header->blurb_len);
free (blurb);
header->flags |= HEADER_MODIFIED;
return 0;
}
struct _hdr_cache {
size_t len;
char *ptr;
};
static void
_hdr_cache_destroy (void *data)
int
mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
int replace)
{
struct _hdr_cache *hc = data;
free (hc->ptr);
}
int status;
struct mu_hdrent *ent = NULL;
if (header == NULL || fn == NULL)
return EINVAL;
static int
header_get_fvalue (mu_header_t header, const char *name, char *buffer,
size_t buflen, size_t *pn)
{
struct _hdr_cache *hdr = mu_assoc_ref (header->cache, name);
if (hdr)
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)
{
if (hdr->len == 0)
return MU_ERR_NOENT;
else
ent = mu_hdrent_find (header, fn, 1);
if (ent)
{
size_t fv_len = hdr->len;
if (buffer && buflen > 0)
if (fv == NULL)
{
buflen--;
fv_len = (fv_len < buflen) ? fv_len : buflen;
memcpy (buffer, hdr->ptr, fv_len);
buffer[fv_len] = '\0';
/* Delete the header */
mu_hdrent_remove (header, ent);
free (ent);
return 0;
}
if (pn)
*pn = fv_len;
mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
HEADER_SET_MODIFIED (header);
return 0;
}
else if (fv == NULL)
return 0;
}
return MU_ERR_NOENT;
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;
}
/* We try to cache the headers here to reduce networking access
especially for IMAP. When the buffer is NULL it means that
the field does not exist on the server and we should not
attempt to contact the server again for this field. */
static int
header_set_fvalue (mu_header_t header, const char *name, char *buffer,
size_t len)
int
mu_header_remove (mu_header_t header, const char *fn, int n)
{
int rc;
struct _hdr_cache hcache;
int status;
struct mu_hdrent *ent;
if (!header->cache)
{
rc = mu_assoc_create (&header->cache, sizeof(struct _hdr_cache),
MU_ASSOC_ICASE);
if (rc)
return rc;
mu_assoc_set_free (header->cache, _hdr_cache_destroy);
}
if (buffer == NULL)
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);
free (ent);
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)
{
hcache.ptr = NULL;
hcache.len = 0;
if (!ref)
ref = fn;
ent = mu_hdrent_find (header, ref, n);
mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
}
else
{
if (!len)
len = strlen (buffer);
hcache.ptr = malloc (len + 1);
if (!hcache.ptr)
ent = mu_hdrent_create (header, NULL,
fn, strlen (fn), fv, strlen (fv));
if (!ent)
return ENOMEM;
memcpy (hcache.ptr, buffer, len);
hcache.ptr[len] = 0;
hcache.len = len;
if (ref)
return mu_hdrent_insert (header, ent, ref, n,
flags & MU_HEADER_BEFORE);
else
mu_hdrent_prepend (header, ent);
}
rc = mu_assoc_install (header->cache, name, &hcache);
if (rc)
free (hcache.ptr);
return rc;
HEADER_SET_MODIFIED (header);
return 0;
}
int
mu_header_get_value (mu_header_t header, const char *name, char *buffer,
size_t buflen, size_t *pn)
mu_header_sget_value_n (mu_header_t header,
const char *name, int n,
const char **pval)
{
size_t i = 0;
size_t name_len;
size_t fn_len = 0, fv_len = 0;
int err = 0;
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;
}
/* First scan our cache headers for hits. */
err = header_get_fvalue (header, name, buffer, buflen, pn);
switch (err)
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)
{
case EINVAL: /* Permanent failure. */
err = MU_ERR_NOENT;
case ENOMEM:
if (pn)
*pn = 0;
case 0:
return err;
*pval = strdup (s);
if (!*pval)
status = ENOMEM;
}
return status;
}
if (header->_get_value)
{
char buf[1024]; /* should suffice for field-value. */
size_t len = 0;
err = header->_get_value (header, name, buf, sizeof (buf), &len);
if (err == 0)
{
/* Save in the fast header buffer. */
header_set_fvalue (header, name, buf, 0);
if (buffer && buflen > 0)
{
buflen--;
buflen = (len < buflen) ? len : buflen;
memcpy (buffer, buf, buflen);
buffer[buflen] = '\0';
}
else
buflen = len;
if (pn)
*pn = buflen;
}
else
{
/* Cache permanent failure also. */
header_set_fvalue (header, name, NULL, 0);
}
return err;
}
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
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)
{
err = fill_blurb (header);
if (err != 0)
return err;
}
/* We set the threshold to be 1 less for the null. */
--buflen;
size_t slen = strlen (s);
for (name_len = strlen (name), i = 0; i < header->hdr_count; i++)
{
fn_len = header->hdr[i].fn_end - header->hdr[i].fn;
if (fn_len == name_len &&
strncasecmp (header->hdr[i].fn, name, fn_len) == 0)
if (buffer)
{
fv_len = (header->hdr[i].fv_end - header->hdr[i].fv);
header_set_fvalue (header, name, header->hdr[i].fv, fv_len);
/* Can everything fit in the buffer. */
if (buffer && buflen > 0)
{
if (fv_len > buflen)
fv_len = buflen;
memcpy (buffer, header->hdr[i].fv, fv_len);
buffer[fv_len] = 0;
}
if (pn)
*pn = fv_len;
return 0;
if (slen > buflen)
slen = buflen;
memcpy (buffer, s, slen);
buffer[slen] = 0;
}
if (pn)
*pn = slen;
}
if (buffer)
*buffer = 0;
return MU_ERR_NOENT;
return status;
}
/* Unfolding functions */
int
mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer,
size_t buflen, size_t *pn)
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 (header, name, buffer, buflen, pn);
int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn);
if (rc == 0)
mu_string_unfold (buffer, pn);
......@@ -573,413 +686,343 @@ mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer,
}
int
mu_header_aget_value (mu_header_t header, const char *name, char **pvalue)
mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n,
char **pvalue)
{
char *value;
size_t n = 0;
int status = mu_header_get_value (header, name, NULL, 0, &n);
if (status == 0)
{
value = calloc (n + 1, 1);
if (value == NULL)
return ENOMEM;
mu_header_get_value (header, name, value, n + 1, NULL);
*pvalue = value;
}
return status;
}
int
mu_header_aget_value_unfold (mu_header_t header, const char *name, char **pvalue)
{
int rc = mu_header_aget_value (header, name, 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 (mu_header_t header, const char *name, mu_address_t *addr)
mu_header_get_address_n (mu_header_t header, const char *name, int n,
mu_address_t *addr)
{
char* value = NULL;
int status = mu_header_aget_value(header, name, &value);
const char *value = NULL;
int status = mu_header_sget_value_n (header, name, n, &value);
if(status)
if (status)
return status;
status = mu_address_create(addr, value);
free(value);
return status;
}
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)
{
if (pcount)
*pcount = 0;
return EINVAL;
}
return EINVAL;
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
status = mu_header_fill (header);
if (status == 0)
{
int err = fill_blurb (header);
if (err != 0)
return err;
mu_hdrent_count (header, &count, &size, &lines);
if (pcount)
*pcount = count;
}
if (pcount)
*pcount = header->hdr_count;
return 0;
return status;
}
int
mu_header_get_field_name (mu_header_t header, size_t num, char *buf,
size_t buflen, size_t *nwriten)
mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr)
{
size_t len;
int status;
if (header == NULL)
return EINVAL;
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
status = mu_header_fill (header);
if (status == 0)
{
int err = fill_blurb (header);
if (err != 0)
return err;
struct mu_hdrent *ent = mu_hdrent_nth (header, num);
if (ent)
*sptr = MU_HDRENT_NAME (header, ent);
else
status = MU_ERR_NOENT;
}
return status;
}
if (header->hdr_count == 0 || num > header->hdr_count || num == 0)
return MU_ERR_NOENT;
num--;
len = (header->hdr[num].fn_end - header->hdr[num].fn);
if (buf && buflen)
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)
{
/* save one for the null */
--buflen;
len = (len > buflen) ? buflen : len;
memcpy (buf, header->hdr[num].fn, len);
buf[len] = '\0';
size_t slen = strlen (s);
if (buffer)
{
if (slen > buflen)
slen = buflen;
memcpy (buffer, s, slen);
buffer[slen] = 0;
}
if (pn)
*pn = slen;
}
if (nwriten)
*nwriten = len;
return 0;
return status;
}
int
mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue)
{
char *value;
size_t n = 0;
int status = mu_header_get_field_name (header, num, NULL, 0, &n);
const char *s;
int status = mu_header_sget_field_name (header, num, &s);
if (status == 0)
{
value = calloc (n + 1, 1);
if (value == NULL)
return ENOMEM;
mu_header_get_field_name (header, num, value, n + 1, NULL);
*pvalue = value;
if ((*pvalue = strdup (s)) == NULL)
status = ENOMEM;
}
else
*pvalue = strdup ("");
return status;
}
int
mu_header_get_field_value (mu_header_t header, size_t num, char *buf,
size_t buflen, size_t *nwritten)
mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr)
{
size_t len;
if (header == NULL || num < 1)
int status;
if (header == NULL)
return EINVAL;
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
if (header->hdr_count == 0 || num > header->hdr_count || num == 0)
return MU_ERR_NOENT;
num--;
len = header->hdr[num].fv_end - header->hdr[num].fv;
if (buf && buflen > 0)
status = mu_header_fill (header);
if (status == 0)
{
/* save one for the null */
--buflen;
len = (len > buflen) ? buflen : len;
memcpy (buf, header->hdr[num].fv, len);
buf[len] = '\0';
struct mu_hdrent *ent = mu_hdrent_nth (header, num);
if (ent)
*sptr = MU_HDRENT_VALUE (header, ent);
else
status = MU_ERR_NOENT;
}
if (nwritten)
*nwritten = len;
return 0;
return status;
}
int
mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf,
size_t buflen, size_t *nwritten)
mu_header_get_field_value (mu_header_t header, size_t num, char *buffer,
size_t buflen, size_t *pn)
{
int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten);
if (rc == 0)
mu_string_unfold (buf, nwritten);
return rc;
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)
{
char *value;
size_t n = 0;
int status = mu_header_get_field_value (header, num, NULL, 0, &n);
const char *s;
int status = mu_header_sget_field_value (header, num, &s);
if (status == 0)
{
value = calloc (n + 1, 1);
if (value == NULL)
return ENOMEM;
mu_header_get_field_value (header, num, value, n + 1, NULL);
*pvalue = value;
if ((*pvalue = strdup (s)) == NULL)
status = ENOMEM;
}
else
*pvalue = strdup ("");
return status;
}
int
mu_header_aget_field_value_unfold (mu_header_t header, size_t num, char **pvalue)
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_aget_field_value (header, num, pvalue);
int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten);
if (rc == 0)
mu_string_unfold (*pvalue, NULL);
mu_string_unfold (buf, nwritten);
return rc;
}
int
mu_header_set_lines (mu_header_t header, int (*_lines)
(mu_header_t, size_t *), void *owner)
mu_header_aget_field_value_unfold (mu_header_t header, size_t num,
char **pvalue)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_lines = _lines;
return 0;
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 n;
size_t lines = 0;
int status;
if (header == NULL)
return EINVAL;
if (plines == NULL)
return MU_ERR_OUT_PTR_NULL;
/* Overload. */
if (header->_lines)
return header->_lines (header, plines);
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
for (n = header->blurb_len - 1; n >= 0; n--)
status = mu_header_fill (header);
if (status == 0)
{
if (header->blurb[n] == '\n')
lines++;
size_t count;
size_t size;
size_t lines;
mu_hdrent_count (header, &count, &size, &lines);
*plines = lines + 1;
}
if (plines)
*plines = lines;
return 0;
}
int
mu_header_set_size (mu_header_t header, int (*_size)
(mu_header_t, size_t *), void *owner)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_size = _size;
return 0;
return status;
}
int
mu_header_size (mu_header_t header, size_t *psize)
{
if (header == NULL)
return EINVAL;
int status;
/* Overload. */
if (header->_size)
return header->_size (header, psize);
if (header == NULL)
return EINVAL;
if (psize == NULL)
return MU_ERR_OUT_PTR_NULL;
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
status = mu_header_fill (header);
if (status == 0)
{
int err = fill_blurb (header);
if (err != 0)
return err;
size_t count;
size_t size;
size_t lines;
mu_hdrent_count (header, &count, &size, &lines);
*psize = size + 1;
}
if (psize)
*psize = header->blurb_len;
return 0;
return status;
}
int
mu_header_set_get_value (mu_header_t header, int (*_get_value)
(mu_header_t, const char *, char *, size_t, size_t *),
void *owner)
static void
mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_get_value = _get_value;
return 0;
char *s = MU_HDRENT_NAME (hdr, ent);
s[ent->nlen] = ':';
s = MU_HDRENT_VALUE (hdr, ent);
s[ent->vlen] = '\n';
}
int
mu_header_set_set_value (mu_header_t header, int (*_set_value)
(mu_header_t , const char *, const char *, int),
void *owner)
static void
mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_set_value = _set_value;
return 0;
char *s = MU_HDRENT_NAME (hdr, ent);
s[ent->nlen] = 0;
s = MU_HDRENT_VALUE (hdr, ent);
s[ent->vlen] = 0;
}
int
mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner)
static int
header_read (mu_stream_t is, char *buffer, size_t buflen,
mu_off_t off, size_t *pnread)
{
if (header == NULL)
mu_header_t header;
struct mu_hdrent *ent;
size_t ent_off;
int status;
size_t nread;
if (is == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->stream = stream;
return 0;
}
int
mu_header_set_fill (mu_header_t header, int
(*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *),
void *owner)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_fill = _fill;
header = mu_stream_get_owner (is);
status = mu_header_fill (header);
if (status)
return status;
if (mu_hdrent_find_stream_pos (header, 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;
off += rest;
ent_off = 0;
}
if (pnread)
*pnread = nread;
return 0;
}
static int
fill_blurb (mu_header_t header)
header_readline (mu_stream_t is, char *buffer, size_t buflen,
mu_off_t off, size_t *pnread)
{
mu_header_t header;
struct mu_hdrent *ent;
size_t ent_off;
int status;
char buf[1024];
size_t nread;
if (header->_fill == NULL)
return 0;
/* The entire header is now ours(part of mu_header_t), clear all the
overloading. */
header_free_cache (header);
header->_get_value = NULL;
header->_set_value = NULL;
header->_size = NULL;
header->_lines = NULL;
if (header->mstream == NULL)
{
status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR);
if (status != 0)
return status;
mu_stream_open (header->mstream);
header->stream_len = 0;
}
size_t strsize;
if (is == NULL)
return EINVAL;
/* Bring in the entire header. */
do
header = mu_stream_get_owner (is);
status = mu_header_fill (header);
if (status)
return status;
if (mu_hdrent_find_stream_pos (header, off, &ent, &ent_off))
{
nread = 0;
status = header->_fill (header, buf, sizeof buf,
header->stream_len, &nread) ;
if (status != 0)
{
if (status != EAGAIN && status != EINTR)
{
mu_stream_destroy (&(header->mstream), NULL);
header->stream_len = 0;
}
return status;
}
if (nread > 0)
{
status = mu_stream_write (header->mstream, buf, nread, header->stream_len, NULL);
if (status != 0)
{
mu_stream_destroy (&(header->mstream), NULL);
header->stream_len = 0;
return status;
}
header->stream_len += nread;
}
if (pnread)
*pnread = 0;
return 0;
}
while (nread > 0);
/* parse it. */
{
char *blurb;
size_t len = header->stream_len;
blurb = calloc (1, len + 1);
if (blurb)
{
mu_stream_read (header->mstream, blurb, len, 0, &len);
status = header_parse (header, blurb, len);
}
free (blurb);
}
mu_stream_destroy (&header->mstream, NULL);
header->stream_len = 0;
return status;
strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
if (buflen > strsize)
buflen = strsize;
mu_hdrent_fixup (header, ent);
memcpy (buffer, MU_HDRENT_NAME (header, ent) + ent_off, buflen);
mu_hdrent_unroll_fixup (header, ent);
if (pnread)
*pnread = buflen;
return 0;
}
static int
header_write (mu_stream_t os, const char *buf, size_t buflen,
mu_off_t off, size_t *pnwrite)
{
size_t wrsize = 0;
mu_header_t header = mu_stream_get_owner (os);
int status;
if (header == NULL)
return EINVAL;
if ((size_t)off != header->stream_len)
if ((size_t)off != header->mstream_size)
return ESPIPE;
/* Skip the obvious. */
......@@ -990,53 +1033,49 @@ header_write (mu_stream_t os, const char *buf, size_t buflen,
return 0;
}
if (header->mstream == NULL)
if (!header->mstream)
{
status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR);
if (status != 0)
status = mu_memory_stream_create (&header->mstream, NULL,
MU_STREAM_RDWR);
if (status)
return status;
status = mu_stream_open (header->mstream);
if (status != 0)
{
mu_stream_destroy(&header->mstream, NULL);
return status;
}
header->stream_len = 0;
if (status)
{
mu_stream_destroy (&header->mstream, NULL);
return status;
}
header->mstream_size = 0;
}
status = mu_stream_write (header->mstream, buf, buflen, header->stream_len, &buflen);
if (status != 0)
do
{
mu_stream_destroy (&header->mstream, NULL);
header->stream_len = 0;
return status;
size_t nbytes;
status = mu_stream_write (header->mstream, buf + wrsize, buflen - wrsize,
header->mstream_size, &nbytes);
if (status)
{
mu_stream_destroy (&header->mstream, NULL);
header->mstream_size = 0;
return status;
}
if (nbytes == 0)
break;
wrsize += nbytes;
header->mstream_size += nbytes;
}
header->stream_len += buflen;
while (buflen);
/* We detect an empty line .i.e "^\n$" this signal the end of the
header. */
if (header->stream_len)
if (header->mstream_size > 1)
{
int finish = 0;
char nlnl[2];
nlnl[1] = nlnl[0] = '\0';
mu_stream_read (header->mstream, nlnl, 1, 0, NULL);
if (nlnl[0] == '\n')
{
finish = 1;
}
else
{
mu_stream_read (header->mstream, nlnl, 2, header->stream_len - 2, NULL);
if (nlnl[0] == '\n' && nlnl[1] == '\n')
{
finish = 1;
}
}
if (finish)
char nlbuf[2];
size_t len;
status = mu_stream_read (header->mstream, nlbuf, 2,
header->mstream_size - 2, &len);
if (status == 0 && len == 2 && memcmp (nlbuf, "\n\n", 2) == 0)
{
char *blurb;
size_t len = header->stream_len;
size_t len = header->mstream_size;
blurb = calloc (1, len + 1);
if (blurb)
{
......@@ -1045,95 +1084,36 @@ header_write (mu_stream_t os, const char *buf, size_t buflen,
}
free (blurb);
mu_stream_destroy (&header->mstream, NULL);
header->stream_len = 0;
header->mstream_size = 0;
}
}
if (pnwrite)
*pnwrite = buflen;
return 0;
}
static int
header_read (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pnread)
{
int len;
mu_header_t header = mu_stream_get_owner (is);
if (is == NULL || header == NULL)
return EINVAL;
if (buf == NULL || buflen == 0)
{
if (pnread)
*pnread = 0;
return 0;
}
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
len = header->blurb_len - off;
if (len > 0)
{
len = (buflen < (size_t)len) ? buflen : (size_t)len;
memcpy (buf, header->blurb + off, len);
}
else
len = 0;
if (pnread)
*pnread = len;
if (pnwrite)
*pnwrite = wrsize;
return 0;
}
static int
header_readline (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pn)
header_size (mu_stream_t str, mu_off_t *psize)
{
int len;
mu_header_t header = mu_stream_get_owner (is);
if (is == NULL || header == NULL)
mu_header_t header;
int status;
size_t size;
if (str == NULL)
return EINVAL;
if (buf == NULL || buflen == 0)
{
if (pn)
*pn = 0;
return 0;
}
/* Try to fill out the buffer, if we know how. */
if (header->blurb == NULL)
{
int err = fill_blurb (header);
if (err != 0)
return err;
}
buflen--; /* Space for the null. */
len = header->blurb_len - off;
if (len > 0)
{
char *nl = memchr (header->blurb + off, '\n', len);
if (nl)
len = nl - (header->blurb + off) + 1;
len = (buflen < (size_t)len) ? buflen : (size_t)len;
memcpy (buf, header->blurb + off, len);
}
else
len = 0;
if (pn)
*pn = len;
buf[len] = '\0';
return 0;
if (psize == NULL)
return MU_ERR_OUT_PTR_NULL;
header = mu_stream_get_owner (str);
status = mu_header_fill (header);
if (status)
return status;
status = mu_header_size (header, &size);
if (status == 0)
*psize = size;
return status;
}
int
......@@ -1141,18 +1121,57 @@ mu_header_get_stream (mu_header_t header, mu_stream_t *pstream)
{
if (header == NULL)
return EINVAL;
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
if (header->stream == NULL)
{
int status = mu_stream_create (&(header->stream), MU_STREAM_RDWR, header);
int status = mu_stream_create (&header->stream, MU_STREAM_RDWR, header);
if (status != 0)
return status;
mu_stream_set_read (header->stream, header_read, header);
mu_stream_set_readline (header->stream, header_readline, header);
mu_stream_set_write (header->stream, header_write, header);
mu_stream_set_size (header->stream, header_size, header);
}
*pstream = header->stream;
return 0;
}
int
mu_header_set_fill (mu_header_t header, int
(*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *),
void *owner)
{
if (header == NULL)
return EINVAL;
if (header->owner != owner)
return EACCES;
header->_fill = _fill;
return 0;
}
void *
mu_header_get_owner (mu_header_t header)
{
return (header) ? header->owner : NULL;
}
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;
}
......