From: David Gibson Date: Mon, 8 Oct 2012 07:53:56 +0000 (+1100) Subject: rfc822: Retrieve header fields by name X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=f71d6c64922b68fe3a785ec3a9faf093ef9299e1 rfc822: Retrieve header fields by name This patch adds functions to the rfc822 module to retrieve header fields of a given name. Signed-off-by: David Gibson --- diff --git a/ccan/rfc822/rfc822.c b/ccan/rfc822/rfc822.c index 6a0f95fc..f5ffd958 100644 --- a/ccan/rfc822/rfc822.c +++ b/ccan/rfc822/rfc822.c @@ -350,3 +350,49 @@ struct bytestring rfc822_header_unfolded_value(struct rfc822_msg *msg, return hdr->unfolded; } + +/* Specifically locale *un*aware tolower() - headers should be ascii + * only, and if they're not best to leave them as is */ +static char xtolower(char c) +{ + if ((c >= 'A') && (c <= 'Z')) + return 'a' + (c - 'A'); + else + return c; +} + +static bool hdr_name_eq(struct bytestring a, struct bytestring b) +{ + int i; + + if (a.len != b.len) + return false; + + for (i = 0; i < a.len; i++) + if (xtolower(a.ptr[i]) != xtolower(b.ptr[i])) + return false; + + return true; +} + +bool rfc822_header_is(struct rfc822_msg *msg, struct rfc822_header *hdr, + const char *name) +{ + struct bytestring hname = rfc822_header_raw_name(msg, hdr); + + if (!hname.ptr || !name) + return false; + + return hdr_name_eq(hname, bytestring_from_string(name)); +} + +struct rfc822_header *rfc822_next_header_of_name(struct rfc822_msg *msg, + struct rfc822_header *hdr, + const char *name) +{ + do { + hdr = rfc822_next_header(msg, hdr); + } while (hdr && !rfc822_header_is(msg, hdr, name)); + + return hdr; +} diff --git a/ccan/rfc822/rfc822.h b/ccan/rfc822/rfc822.h index 1170c113..19fccea8 100644 --- a/ccan/rfc822/rfc822.h +++ b/ccan/rfc822/rfc822.h @@ -168,4 +168,23 @@ struct bytestring rfc822_header_raw_value(struct rfc822_msg *msg, struct bytestring rfc822_header_unfolded_value(struct rfc822_msg *msg, struct rfc822_header *hdr); +/** + * rfc822_header_is - determine if a header is of a given name + * @msg: message + * @hdr: a header handle + * @name: header name + * + * This returns true if the header field @hdr has name @name (case + * insensitive), otherwise false. + */ +bool rfc822_header_is(struct rfc822_msg *msg, struct rfc822_header *hdr, + const char *name); + +struct rfc822_header *rfc822_next_header_of_name(struct rfc822_msg *msg, + struct rfc822_header *hdr, + const char *name); + +#define rfc822_first_header_of_name(_msg, _name) \ + (rfc822_next_header_of_name((_msg), NULL, (_name))) + #endif /* CCAN_RFC822_H_ */ diff --git a/ccan/rfc822/test/run-hdr-no-colon.c b/ccan/rfc822/test/run-hdr-no-colon.c index fcea7dc4..2ad767f3 100644 --- a/ccan/rfc822/test/run-hdr-no-colon.c +++ b/ccan/rfc822/test/run-hdr-no-colon.c @@ -29,7 +29,7 @@ static void test_no_colon(const char *buf, size_t len) struct rfc822_header *hdr; struct bytestring hfull; - plan_tests(3); + plan_tests(6); msg = rfc822_start(NULL, buf, len); @@ -47,6 +47,10 @@ static void test_no_colon(const char *buf, size_t len) ok(hdr && (rfc822_header_errors(msg, hdr) == RFC822_HDR_NO_COLON), "Second header invalid"); + ok1(hdr && !rfc822_header_is(msg, hdr, NULL)); + ok1(hdr && !rfc822_header_is(msg, hdr, "")); + ok1(hdr && !rfc822_header_is(msg, hdr, NO_COLON_STR)); + hfull = rfc822_header_raw_content(msg, hdr); allocation_failure_check(); diff --git a/ccan/rfc822/test/run-hdr-of-name.c b/ccan/rfc822/test/run-hdr-of-name.c new file mode 100644 index 00000000..aa98ac5b --- /dev/null +++ b/ccan/rfc822/test/run-hdr-of-name.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include + +#define CCAN_RFC822_DEBUG + +#include + +#include + +#include "testdata.h" +#include "helper.h" + +static void test_hdrbyname(const struct aexample *e, const char *buf, size_t len, + const char *exname, int crlf) +{ + struct rfc822_msg *msg; + struct rfc822_header *h, *hx; + int i, j; + + msg = rfc822_start(NULL, buf, len); + allocation_failure_check(); + ok(msg, "opened %s", exname); + + for (i = 0; i < e->nhdrs; i++) { + struct testhdr *eh = &e->hdrs[i]; + + h = rfc822_first_header_of_name(msg, eh->name); + hx = rfc822_next_header_of_name(msg, NULL, eh->name); + ok1(h == hx); + + for (j = 0; h && j < eh->index; j++) + h = rfc822_next_header_of_name(msg, h, eh->name); + ok(h, "header \"%s\" (#%d) exists", eh->name, eh->index); + if (!h) + break; + check_header(msg, h, eh->name, eh->val, eh->errors, crlf); + + h = rfc822_next_header_of_name(msg, h, eh->name); + ok1((eh->index != eh->last) ^ !h); + } + + h = rfc822_first_header_of_name(msg, NULL); + ok(!h, "Never match NULL name"); + + rfc822_free(msg); + allocation_failure_check(); +} + +int main(int argc, char *argv[]) +{ + struct aexample *e; + + /* This is how many tests you plan to run */ + plan_tests(6*num_aexamples() + 14*num_aexample_hdrs()); + + failtest_setup(argc, argv); + + for_each_aexample(e) { + int crlf; + + foreach_int(crlf, 0, 1) { + const char *buf; + size_t len; + char exname[256]; + + sprintf(exname, "%s[%s]", e->name, NLT(crlf)); + + buf = assemble_msg(e, &len, crlf); + ok((buf), "assembled %s", exname); + if (!buf) + continue; + + test_hdrbyname(e, buf, len, exname, crlf); + + talloc_free(buf); + } + } + + /* This exits depending on whether all tests passed */ + failtest_exit(exit_status()); +} diff --git a/ccan/rfc822/test/testdata.h b/ccan/rfc822/test/testdata.h index 421b7487..f6dff03d 100644 --- a/ccan/rfc822/test/testdata.h +++ b/ccan/rfc822/test/testdata.h @@ -7,6 +7,7 @@ struct testhdr { const char *name, *val; + int index, last; enum rfc822_header_errors errors; }; @@ -69,12 +70,31 @@ struct testhdr bad_hdrs_hdrs[] = { #define bad_hdrs_body test_msg_1_body AEXAMPLE(bad_hdrs) +struct testhdr repeated_hdrs_1_hdrs[] = { + {"X-Repeated-Header", "#1", 0, 4}, + {"x-repeated-header", "#2", 1, 4}, + {"X-REPEATED-HEADER", "#3", 2, 4}, + {"x-rEpEaTeD-hEaDeR", "#4", 3, 4}, + {"X-Repeated-Header", "#5", 4, 4}, +}; +#define repeated_hdrs_1_body test_msg_1_body +AEXAMPLE(repeated_hdrs_1); + +struct testhdr prefix_hdr_hdrs[] = { + {"X-Prefix", "Prefix", 0}, + {"X-Prefix-and-Suffix", "Suffix", 0}, +}; +#define prefix_hdr_body test_msg_1_body +AEXAMPLE(prefix_hdr); + #define for_each_aexample(_e) \ foreach_ptr((_e), &test_msg_1, &test_msg_empty_body, \ &test_msg_nlnl_lf, &test_msg_nlnl_crlf, \ &test_msg_nlnl_mixed, \ &test_msg_space_body, \ - &bad_hdrs) + &bad_hdrs, \ + &repeated_hdrs_1, \ + &prefix_hdr) #define for_each_aexample_buf(_e, _buf, _len) \ for_each_aexample((_e)) \