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
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
757 additions
and
734 deletions
libproto/include/header0.h
mailbox/header.c
libproto/include/header0.h
View file @
344611f
...
...
@@ -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
);
int
mu_header_create
(
mu_header_t
*
ph
,
const
char
*
blurb
,
size_t
len
,
void
*
owner
)
{
mu_header_t
header
;
int
status
=
0
;
#define HEADER_SET_MODIFIED(h) \
((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
header
=
calloc
(
1
,
sizeof
(
*
header
));
if
(
header
==
NULL
)
return
ENOMEM
;
header
->
owner
=
owner
;
/* mu_hdrent manipulation */
status
=
header_parse
(
header
,
blurb
,
len
);
#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)
*
ph
=
header
;
return
status
;
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
void
header_free_cache
(
mu_header_t
header
)
static
struct
mu_hdrent
*
mu_hdrent_find
(
struct
_mu_header
*
hdr
,
const
char
*
name
,
int
pos
)
{
mu_assoc_clear
(
header
->
cache
);
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
;
}
void
mu_header_destroy
(
mu_header_t
*
ph
,
void
*
owner
)
static
int
mu_hdrent_find_stream_pos
(
struct
_mu_header
*
hdr
,
mu_off_t
pos
,
struct
mu_hdrent
**
pent
,
size_t
*
poff
)
{
if
(
ph
&&
*
ph
)
{
mu_header_t
header
=
*
ph
;
mu_off_t
x
;
struct
mu_hdrent
*
p
;
/* Can we destroy ?. */
if
(
header
->
owner
==
owner
)
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
)
{
mu_stream_destroy
(
&
(
header
->
stream
),
header
);
/* To supply the trailing '\n' */
p
=
hdr
->
tail
;
*
pent
=
p
;
*
poff
=
MU_STR_SIZE
(
p
->
nlen
,
p
->
vlen
)
-
1
;
return
0
;
}
return
1
;
}
if
(
header
->
hdr
)
free
(
header
->
hdr
);
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
;
}
if
(
header
->
blurb
)
free
(
header
->
blurb
);
hdr
->
numhdr
=
count
;
hdr
->
numlines
=
lines
;
hdr
->
size
=
size
;
hdr
->
flags
&=
~
HEADER_INVALIDATE
;
}
header_free_cache
(
header
);
*
pcount
=
hdr
->
numhdr
;
*
psize
=
hdr
->
size
;
*
plines
=
hdr
->
numlines
;
}
mu_assoc_destroy
(
&
header
->
cache
);
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
;
if
(
header
->
mstream
)
mu_stream_destroy
(
&
(
header
->
mstream
),
NULL
);
p
=
ent
->
next
;
if
(
p
)
p
->
prev
=
ent
->
prev
;
else
hdr
->
tail
=
ent
->
prev
;
}
free
(
header
);
}
*
ph
=
NULL
;
}
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
;
}
void
*
mu_h
eader_get_owner
(
mu_header_t
header
)
static
void
mu_h
drent_append
(
struct
_mu_header
*
hdr
,
struct
mu_hdrent
*
ent
)
{
return
(
header
)
?
header
->
owner
:
NULL
;
struct
mu_hdrent
*
p
=
hdr
->
tail
;
ent
->
next
=
NULL
;
ent
->
prev
=
p
;
if
(
p
)
p
->
next
=
ent
;
else
hdr
->
head
=
ent
;
hdr
->
tail
=
ent
;
}
int
mu_header_is_modified
(
mu_header_t
header
)
static
int
mu_hdrent_insert
(
struct
_mu_header
*
hdr
,
struct
mu_hdrent
*
ent
,
const
char
*
name
,
int
pos
,
int
before
)
{
return
(
header
)
?
(
header
->
flags
&
HEADER_MODIFIED
)
:
0
;
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
;
}
int
mu_header_clear_modified
(
mu_header_t
header
)
#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
)
{
if
(
header
)
header
->
flags
&=
~
HEADER_MODIFIED
;
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
;
/* 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
)
{
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,16 +365,15 @@ 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
]))
while
(
ISLWSP
(
fn_end
[
-
1
]))
fn_end
--
;
fv
=
colon
+
1
;
...
...
@@ -223,349 +381,304 @@ header_parse (mu_header_t header, const char *blurb, int len)
/* Skip any LWSP before the field value -- unnecessary, but
might make some field values look a little tidier. */
while
(
ISLWSP
(
fv
[
0
]))
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
;
/* Register this header */
ent
=
mu_hdrent_create
(
header
,
NULL
,
fn
,
fn_end
-
fn
,
fv
,
fv_end
-
fv
);
if
(
!
ent
)
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
++
;
mu_hdrent_append
(
header
,
ent
);
}
/* for (header_start ...) */
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
(
header
->
spool_used
)
return
0
;
/* If replace, remove all fields in the header blurb that have the
same name as the field we are writing.
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
)
{
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
++
)
{
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
)
/* Bring in the entire header. */
do
{
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
)
nread
=
0
;
status
=
header
->
_fill
(
header
,
buf
,
sizeof
buf
,
stream_len
,
&
nread
);
if
(
status
)
{
memmove
(
header
->
hdr
[
i
].
fn
,
header
->
hdr
[
i
+
1
].
fn
,
header
->
hdr
[
header
->
hdr_count
-
1
].
fv_end
-
header
->
hdr
[
i
+
1
].
fn
+
3
)
;
if
(
status
!=
EAGAIN
&&
status
!=
EINTR
)
mu_stream_destroy
(
&
mstream
,
NULL
);
return
status
;
}
/* or if it is the last, just truncate the fields. */
else
if
(
nread
>
0
)
{
status
=
mu_stream_write
(
mstream
,
buf
,
nread
,
stream_len
,
NULL
);
if
(
status
)
{
header
->
hdr
[
i
].
fn
[
0
]
=
'\n'
;
header
->
hdr
[
i
].
fn
[
1
]
=
'\0'
;
mu_stream_destroy
(
&
mstream
,
NULL
)
;
return
status
;
}
/* 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
;
stream_len
+=
nread
;
}
}
while
(
nread
>
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
;
/* If FV is NULL, then we are done. */
if
(
!
fv
)
return
0
;
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
;
sprintf
(
blurb
,
"%s: %s"
,
fn
,
fv
);
header
->
owner
=
owner
;
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
)
void
mu_header_destroy
(
mu_header_t
*
ph
,
void
*
owner
)
{
if
(
ph
&&
*
ph
)
{
memcpy
(
blurb
+
len
,
header
->
blurb
,
header
->
blurb_len
);
free
(
header
->
blurb
);
header
->
blurb
=
NULL
;
}
else
blurb
[
len
]
=
'\n'
;
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'
)
if
(
header
->
owner
==
owner
)
{
blurb
[
header
->
blurb_len
+
len
]
=
'\n'
;
len
++
;
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
;
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
)
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
)
{
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
;
}
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
);
free
(
ent
);
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_insert
(
mu_header_t
header
,
const
char
*
fn
,
const
char
*
fv
,
const
char
*
ref
,
int
n
,
int
flags
)
{
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
||
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
;
/* First scan our cache headers for hits. */
err
=
header_get_fvalue
(
header
,
name
,
buffer
,
buflen
,
pn
);
switch
(
err
)
{
case
EINVAL
:
/* Permanent failure. */
err
=
MU_ERR_NOENT
;
case
ENOMEM
:
if
(
pn
)
*
pn
=
0
;
case
0
:
return
err
;
}
ent
=
mu_hdrent_find
(
header
,
name
,
n
);
if
(
!
ent
)
return
MU_ERR_NOENT
;
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
;
}
*
pval
=
MU_HDRENT_VALUE
(
header
,
ent
);
return
0
;
}
/* Try to fill out the buffer, if we know how. */
if
(
header
->
blurb
==
NULL
)
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
)
{
err
=
fill_blurb
(
header
);
if
(
err
!=
0
)
return
err
;
*
pval
=
strdup
(
s
);
if
(
!*
pval
)
status
=
ENOMEM
;
}
return
status
;
}
/* We set the threshold to be 1 less for the null. */
--
buflen
;
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
)
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
)
{
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
)
size_t
slen
=
strlen
(
s
);
if
(
buffer
)
{
if
(
fv_
len
>
buflen
)
fv_
len
=
buflen
;
memcpy
(
buffer
,
header
->
hdr
[
i
].
fv
,
fv_
len
);
buffer
[
fv_
len
]
=
0
;
if
(
s
len
>
buflen
)
s
len
=
buflen
;
memcpy
(
buffer
,
s
,
s
len
);
buffer
[
s
len
]
=
0
;
}
if
(
pn
)
*
pn
=
fv_len
;
return
0
;
*
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
,
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,159 +686,166 @@ 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
)
{
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
)
mu_header_aget_value_unfold_n
(
mu_header_t
header
,
const
char
*
name
,
int
n
,
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
;
}
/* 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
=
header
->
hdr_count
;
return
0
;
*
pcount
=
count
;
}
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
;
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
);
num
--
;
len
=
(
header
->
hdr
[
num
].
fn_end
-
header
->
hdr
[
num
].
fn
);
if
(
buf
&&
buflen
)
if
(
buffer
)
{
/* save one for the null */
--
buflen
;
len
=
(
len
>
buflen
)
?
buflen
:
len
;
memcpy
(
buf
,
header
->
hdr
[
num
].
fn
,
len
);
buf
[
len
]
=
'\0'
;
if
(
slen
>
buflen
)
slen
=
buflen
;
memcpy
(
buffer
,
s
,
slen
);
buffer
[
slen
]
=
0
;
}
if
(
nwriten
)
*
nwriten
=
len
;
return
0
;
if
(
pn
)
*
pn
=
slen
;
}
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
;
int
status
;
if
(
header
==
NULL
||
num
<
1
)
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_VALUE
(
header
,
ent
);
else
status
=
MU_ERR_NOENT
;
}
return
status
;
}
if
(
header
->
hdr_count
==
0
||
num
>
header
->
hdr_count
||
num
==
0
)
return
MU_ERR_NOENT
;
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
);
num
--
;
len
=
header
->
hdr
[
num
].
fv_end
-
header
->
hdr
[
num
].
fv
;
if
(
buf
&&
buflen
>
0
)
if
(
buffer
)
{
/* save one for the null */
--
buflen
;
len
=
(
len
>
buflen
)
?
buflen
:
len
;
memcpy
(
buf
,
header
->
hdr
[
num
].
fv
,
len
);
buf
[
len
]
=
'\0'
;
if
(
slen
>
buflen
)
slen
=
buflen
;
memcpy
(
buffer
,
s
,
slen
);
buffer
[
slen
]
=
0
;
}
if
(
pn
)
*
pn
=
slen
;
}
return
status
;
}
if
(
nwritten
)
*
nwritten
=
len
;
return
0
;
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
...
...
@@ -739,26 +859,8 @@ mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf,
}
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
);
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
;
}
else
*
pvalue
=
strdup
(
""
);
return
status
;
}
int
mu_header_aget_field_value_unfold
(
mu_header_t
header
,
size_t
num
,
char
**
pvalue
)
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
)
...
...
@@ -766,220 +868,161 @@ mu_header_aget_field_value_unfold (mu_header_t header, size_t num, char **pvalue
return
rc
;
}
int
mu_header_set_lines
(
mu_header_t
header
,
int
(
*
_lines
)
(
mu_header_t
,
size_t
*
),
void
*
owner
)
{
if
(
header
==
NULL
)
return
EINVAL
;
if
(
header
->
owner
!=
owner
)
return
EACCES
;
header
->
_lines
=
_lines
;
return
0
;
}
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
)
{
int
status
;
if
(
header
==
NULL
)
return
EINVAL
;
if
(
psize
==
NULL
)
return
MU_ERR_OUT_PTR_NULL
;
/* Overload. */
if
(
header
->
_size
)
return
header
->
_size
(
header
,
psize
);
/* 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
)
{
if
(
header
==
NULL
)
return
EINVAL
;
if
(
header
->
owner
!=
owner
)
return
EACCES
;
header
->
_get_value
=
_get_value
;
return
0
;
}
int
mu_header_set_set_value
(
mu_header_t
header
,
int
(
*
_set_value
)
(
mu_header_t
,
const
char
*
,
const
char
*
,
int
),
void
*
owner
)
{
if
(
header
==
NULL
)
return
EINVAL
;
if
(
header
->
owner
!=
owner
)
return
EACCES
;
header
->
_set_value
=
_set_value
;
return
0
;
}
int
mu_header_set_stream
(
mu_header_t
header
,
mu_stream_t
stream
,
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
->
stream
=
stream
;
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_fill
(
mu_header_t
header
,
int
(
*
_fill
)
(
mu_header_t
,
char
*
,
size_t
,
mu_off_t
,
size_t
*
),
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
->
_fill
=
_fill
;
return
0
;
char
*
s
=
MU_HDRENT_NAME
(
hdr
,
ent
);
s
[
ent
->
nlen
]
=
0
;
s
=
MU_HDRENT_VALUE
(
hdr
,
ent
);
s
[
ent
->
vlen
]
=
0
;
}
static
int
fill_blurb
(
mu_header_t
header
)
header_read
(
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
;
if
(
is
==
NULL
)
return
EINVAL
;
/* 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
;
header
=
mu_stream_get_owner
(
is
);
status
=
mu_header_fill
(
header
);
if
(
status
)
return
status
;
if
(
header
->
mstream
==
NULL
)
if
(
mu_hdrent_find_stream_pos
(
header
,
off
,
&
ent
,
&
ent_off
)
)
{
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
;
if
(
pnread
)
*
pnread
=
0
;
return
0
;
}
/* Bring in the entire header. */
do
for
(
nread
=
0
;
nread
<
buflen
&&
ent
;
ent
=
ent
->
next
)
{
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
;
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
(
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
;
if
(
pnread
)
*
pnread
=
nread
;
return
0
;
}
static
int
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
;
size_t
strsize
;
if
(
is
==
NULL
)
return
EINVAL
;
header
=
mu_stream_get_owner
(
is
);
status
=
mu_header_fill
(
header
);
if
(
status
)
return
status
;
}
header
->
stream_len
+=
nread
;
}
}
while
(
nread
>
0
);
/* parse it. */
{
char
*
blurb
;
size_t
len
=
header
->
stream_len
;
blurb
=
calloc
(
1
,
len
+
1
);
if
(
blurb
)
if
(
mu_hdrent_find_stream_pos
(
header
,
off
,
&
ent
,
&
ent_off
))
{
mu_stream_read
(
header
->
mstream
,
blurb
,
len
,
0
,
&
len
);
status
=
header_parse
(
header
,
blurb
,
len
);
}
free
(
blurb
);
if
(
pnread
)
*
pnread
=
0
;
return
0
;
}
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
)
if
(
status
)
{
mu_stream_destroy
(
&
header
->
mstream
,
NULL
);
mu_stream_destroy
(
&
header
->
mstream
,
NULL
);
return
status
;
}
header
->
stream_len
=
0
;
header
->
mstream_size
=
0
;
}
status
=
mu_stream_write
(
header
->
mstream
,
buf
,
buflen
,
header
->
stream_len
,
&
buflen
);
if
(
status
!=
0
)
do
{
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
->
stream_len
=
0
;
header
->
mstream_size
=
0
;
return
status
;
}
header
->
stream_len
+=
buflen
;
/* We detect an empty line .i.e "^\n$" this signal the end of the
header. */
if
(
header
->
stream_len
)
{
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
;
if
(
nbytes
==
0
)
break
;
wrsize
+=
nbytes
;
header
->
mstream_size
+=
nbytes
;
}
else
{
mu_stream_read
(
header
->
mstream
,
nlnl
,
2
,
header
->
stream_len
-
2
,
NULL
);
if
(
nlnl
[
0
]
==
'\n'
&&
nlnl
[
1
]
==
'\n'
)
while
(
buflen
);
if
(
header
->
mstream_size
>
1
)
{
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
);
*
pnwrite
=
wrsize
;
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
;
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
);
mu_header_t
header
;
int
status
;
size_t
size
;
if
(
is
==
NULL
||
heade
r
==
NULL
)
if
(
st
r
==
NULL
)
return
EINVAL
;
if
(
psize
==
NULL
)
return
MU_ERR_OUT_PTR_NULL
;
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
;
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