Browse Source

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 <david@gibson.dropbear.id.au>
David Gibson 13 years ago
parent
commit
f71d6c6492

+ 46 - 0
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;
+}

+ 19 - 0
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_ */

+ 5 - 1
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();
 

+ 84 - 0
ccan/rfc822/test/run-hdr-of-name.c

@@ -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());
+}

+ 21 - 1
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)) 		\