rfc822: Retrieve header fields by name
authorDavid Gibson <david@gibson.dropbear.id.au>
Mon, 8 Oct 2012 07:53:56 +0000 (18:53 +1100)
committerDavid Gibson <david@gibson.dropbear.id.au>
Mon, 8 Oct 2012 10:44:01 +0000 (21:44 +1100)
This patch adds functions to the rfc822 module to retrieve header fields
of a given name.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
ccan/rfc822/rfc822.c
ccan/rfc822/rfc822.h
ccan/rfc822/test/run-hdr-no-colon.c
ccan/rfc822/test/run-hdr-of-name.c [new file with mode: 0644]
ccan/rfc822/test/testdata.h

index 6a0f95fc5f31ed7d9246c21719328b9641823774..f5ffd958e767c10ee9602059fac13fa1e8c59bde 100644 (file)
@@ -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;
+}
index 1170c113e92a236f149e677d45024980574a8cdb..19fccea8f35afbb3d36b0b68c420f7e30ef3572c 100644 (file)
@@ -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_ */
index fcea7dc4b966bb8f9e081bc75b7ea8442a506e99..2ad767f3a7683e7329a8b09f6fbe5172da8229e2 100644 (file)
@@ -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 (file)
index 0000000..aa98ac5
--- /dev/null
@@ -0,0 +1,84 @@
+#include <ccan/foreach/foreach.h>
+#include <ccan/failtest/failtest_override.h>
+#include <ccan/failtest/failtest.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CCAN_RFC822_DEBUG
+
+#include <ccan/rfc822/rfc822.h>
+
+#include <ccan/rfc822/rfc822.c>
+
+#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());
+}
index 421b74875a1c37378c95a003dbf44e73a55826d8..f6dff03d61eb021bce50a470ffcce96dbd35c894 100644 (file)
@@ -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))                 \