Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
John McEleney
/
mailutils
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit
344611f5
...
344611f57409fc78699198b165c8acf986aceb65
authored
2007-06-26 13:54:54 +0000
by
Sergey Poznyakoff
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Rewrite from scratch
1 parent
2039d455
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
799 additions
and
776 deletions
libproto/include/header0.h
mailbox/header.c
libproto/include/header0.h
View file @
344611f
...
...
@@ -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
*
);
};
...
...
mailbox/header.c
View file @
344611f
...
...
@@ -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_h
eader_destroy
(
mu_header_t
*
ph
,
void
*
owner
)
static
void
mu_h
drent_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_h
eader_is_modified
(
mu_header_t
heade
r
)
static
void
mu_h
drent_free_list
(
struct
_mu_header
*
hd
r
)
{
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
;
s
printf
(
blurb
,
"%s: %s"
,
fn
,
fv
);
s
tatus
=
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
)
{
c
har
*
value
=
NULL
;
int
status
=
mu_header_
aget_value
(
header
,
name
,
&
value
);
c
onst
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
*
nwritte
n
)
mu_header_get_field_value
(
mu_header_t
header
,
size_t
num
,
char
*
buffer
,
size_t
buflen
,
size_t
*
p
n
)
{
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
;
}
...
...
Please
register
or
sign in
to post a comment