#include <string.h>
#include <ccan/str/str.h>
-#include <ccan/talloc/talloc.h>
#include <ccan/list/list.h>
+#include <stdio.h>
+#include <ccan/mem/mem.h>
#include <ccan/rfc822/rfc822.h>
-#if !HAVE_MEMMEM
-void *memmem(const void *haystack, size_t haystacklen,
- const void *needle, size_t needlelen)
-{
- const char *p, *last;
-
- p = haystack;
- last = p + haystacklen - needlelen;
-
- do {
- if (memcmp(p, needle, needlelen) == 0)
- return (void *)p;
- } while (p++ <= last);
-
- return NULL;
-}
+#ifdef TAL_USE_TALLOC
+#include <ccan/tal/talloc/talloc.h>
+#else
+#include <ccan/tal/tal.h>
#endif
static void (*allocation_failure_hook)(const char *);
} \
} while (0)
+/*
+ * No real point doing fancy resizing hashes, when any given mail
+ * message is unlikely to have more than a fairly small number of
+ * distinct header types. This should be ample.
+ */
+#define INDEX_HASH_SIZE 63
+
struct rfc822_msg {
const char *data, *end;
const char *remainder;
struct list_head headers;
+ struct list_head header_index[INDEX_HASH_SIZE];
const char *body;
};
struct bytestring all, rawname, rawvalue;
struct bytestring unfolded;
struct list_node list;
+ struct rfc822_header *name_next;
+};
+
+struct rfc822_headers_of_name {
+ struct bytestring name;
+ struct rfc822_header *first;
+ struct rfc822_header **lastptr;
+ struct list_node bucket;
};
struct rfc822_msg *rfc822_check(const struct rfc822_msg *msg,
assert(msg);
if (!list_check(&msg->headers, abortstr))
return NULL;
+ if (!tal_check(msg, abortstr))
+ return NULL;
return (struct rfc822_msg *)msg;
}
struct rfc822_msg *rfc822_start(const void *ctx, const char *p, size_t len)
{
struct rfc822_msg *msg;
+ int i;
- msg = talloc(ctx, struct rfc822_msg);
+ msg = tal(ctx, struct rfc822_msg);
ALLOC_CHECK(msg, NULL);
msg->data = p;
list_head_init(&msg->headers);
+ for (i = 0; i < INDEX_HASH_SIZE; i++)
+ list_head_init(&msg->header_index[i]);
+
CHECK(msg, "<rfc22_start");
return msg;
void rfc822_free(struct rfc822_msg *msg)
{
CHECK(msg, ">rfc822_free");
- talloc_free(msg);
+ tal_free(msg);
}
static struct rfc822_header *next_header_cached(struct rfc822_msg *msg,
return p ? (p + 1) : end;
}
+static struct rfc822_header *index_header(struct rfc822_msg *msg,
+ struct rfc822_header *hdr);
+
static struct rfc822_header *next_header_parse(struct rfc822_msg *msg)
{
const char *h, *eh, *ev, *colon;
msg->remainder = eh;
- hi = talloc_zero(msg, struct rfc822_header);
+ hi = talz(msg, struct rfc822_header);
ALLOC_CHECK(hi, NULL);
hi->all = bytestring(h, eh - h);
CHECK(msg, "<next_header_parse");
- return hi;
+ return index_header(msg, hi);
}
struct rfc822_header *rfc822_next_header(struct rfc822_msg *msg,
if (lines <= 1) {
hdr->unfolded = bytestring(raw.ptr, len);
} else {
- char *unfold = talloc_array(msg, char, len);
+ char *unfold = tal_arr(msg, char, len);
char *p = unfold;
ALLOC_CHECK(unfold, bytestring_NULL);
return hdr_name_eq(hname, bytestring_from_string(name));
}
+static unsigned headerhash(struct bytestring name)
+{
+ /*
+ * This is stolen from hash_string() in ccan/hash, but adapted
+ * to add the xtolower() call and use a bytestring
+ */
+ unsigned ret = 0;
+ size_t i;
+
+ for (i = 0; i < name.len; i++)
+ ret = (ret << 5) - ret + xtolower(name.ptr[i]);
+
+ return ret % INDEX_HASH_SIZE;
+}
+
+static struct rfc822_headers_of_name *headers_of_name(struct rfc822_msg *msg,
+ struct bytestring name)
+{
+ unsigned hash = headerhash(name);
+ struct rfc822_headers_of_name *hn;
+
+ list_for_each(&msg->header_index[hash], hn, bucket) {
+ if (hdr_name_eq(hn->name, name))
+ return hn;
+ }
+
+ return NULL;
+}
+
+static struct rfc822_header *index_header(struct rfc822_msg *msg,
+ struct rfc822_header *hdr)
+{
+ struct bytestring hname = rfc822_header_raw_name(msg, hdr);
+ struct rfc822_headers_of_name *hn = headers_of_name(msg, hname);
+
+ if (!hn) {
+ unsigned hash = headerhash(hname);
+
+ hn = talz(msg, struct rfc822_headers_of_name);
+ ALLOC_CHECK(hn, NULL);
+
+ hn->name = hname;
+ hn->first = NULL;
+ hn->lastptr = &hn->first;
+ list_add_tail(&msg->header_index[hash], &hn->bucket);
+ }
+
+ hdr->name_next = NULL;
+ *(hn->lastptr) = hdr;
+ hn->lastptr = &hdr->name_next;
+ return hdr;
+}
+
+struct rfc822_header *rfc822_first_header_of_name(struct rfc822_msg *msg,
+ const char *name)
+{
+ struct bytestring namebs = bytestring_from_string(name);
+ struct rfc822_headers_of_name *hn = headers_of_name(msg, namebs);
+ struct rfc822_header *hdr;
+
+ if (hn)
+ return hn->first;
+
+ do {
+ hdr = next_header_parse(msg);
+ if (hdr && rfc822_header_is(msg, hdr, name))
+ return hdr;
+ } while (hdr);
+
+ return NULL;
+}
+
struct rfc822_header *rfc822_next_header_of_name(struct rfc822_msg *msg,
struct rfc822_header *hdr,
const char *name)
{
+ if (!hdr)
+ return rfc822_first_header_of_name(msg, name);
+
+ if (hdr->name_next) {
+ assert(rfc822_header_is(msg, hdr->name_next, name));
+ return hdr->name_next;
+ }
+
do {
- hdr = rfc822_next_header(msg, hdr);
- } while (hdr && !rfc822_header_is(msg, hdr, name));
+ hdr = next_header_parse(msg);
+ if (hdr && rfc822_header_is(msg, hdr, name))
+ return hdr;
+ } while (hdr);
- return hdr;
+ return NULL;
}