REORG: acl/pattern: extract pattern matching from the acl file and create pattern.c

This patch just moves code without any change.

The ACL are just the association between sample and pattern. The pattern
contains the match method and the parse method. These two things are
different. This patch cleans the code by splitting it.
diff --git a/src/acl.c b/src/acl.c
index ca24693..1bb3e9f 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -26,6 +26,7 @@
 #include <proto/auth.h>
 #include <proto/channel.h>
 #include <proto/log.h>
+#include <proto/pattern.h>
 #include <proto/proxy.h>
 #include <proto/sample.h>
 #include <proto/stick_table.h>
@@ -37,54 +38,6 @@
 	.list = LIST_HEAD_INIT(acl_keywords.list)
 };
 
-char *acl_match_names[ACL_MATCH_NUM] = {
-	[ACL_MATCH_FOUND] = "found",
-	[ACL_MATCH_BOOL]  = "bool",
-	[ACL_MATCH_INT]   = "int",
-	[ACL_MATCH_IP]    = "ip",
-	[ACL_MATCH_BIN]   = "bin",
-	[ACL_MATCH_LEN]   = "len",
-	[ACL_MATCH_STR]   = "str",
-	[ACL_MATCH_BEG]   = "beg",
-	[ACL_MATCH_SUB]   = "sub",
-	[ACL_MATCH_DIR]   = "dir",
-	[ACL_MATCH_DOM]   = "dom",
-	[ACL_MATCH_END]   = "end",
-	[ACL_MATCH_REG]   = "reg",
-};
-
-int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **) = {
-	[ACL_MATCH_FOUND] = acl_parse_nothing,
-	[ACL_MATCH_BOOL]  = acl_parse_nothing,
-	[ACL_MATCH_INT]   = acl_parse_int,
-	[ACL_MATCH_IP]    = acl_parse_ip,
-	[ACL_MATCH_BIN]   = acl_parse_bin,
-	[ACL_MATCH_LEN]   = acl_parse_int,
-	[ACL_MATCH_STR]   = acl_parse_str,
-	[ACL_MATCH_BEG]   = acl_parse_str,
-	[ACL_MATCH_SUB]   = acl_parse_str,
-	[ACL_MATCH_DIR]   = acl_parse_str,
-	[ACL_MATCH_DOM]   = acl_parse_str,
-	[ACL_MATCH_END]   = acl_parse_str,
-	[ACL_MATCH_REG]   = acl_parse_reg,
-};
-
-int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *) = {
-	[ACL_MATCH_FOUND] = NULL,
-	[ACL_MATCH_BOOL]  = acl_match_nothing,
-	[ACL_MATCH_INT]   = acl_match_int,
-	[ACL_MATCH_IP]    = acl_match_ip,
-	[ACL_MATCH_BIN]   = acl_match_bin,
-	[ACL_MATCH_LEN]   = acl_match_len,
-	[ACL_MATCH_STR]   = acl_match_str,
-	[ACL_MATCH_BEG]   = acl_match_beg,
-	[ACL_MATCH_SUB]   = acl_match_sub,
-	[ACL_MATCH_DIR]   = acl_match_dir,
-	[ACL_MATCH_DOM]   = acl_match_dom,
-	[ACL_MATCH_END]   = acl_match_end,
-	[ACL_MATCH_REG]   = acl_match_reg,
-};
-
 /* return the ACL_MATCH_* index for match name "name", or < 0 if not found */
 static int acl_find_match_name(const char *name)
 {
@@ -97,707 +50,6 @@
 }
 
 /*
- * These functions are exported and may be used by any other component.
- */
-
-/* ignore the current line */
-int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	return 1;
-}
-
-/* always return false */
-int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern)
-{
-	return ACL_PAT_FAIL;
-}
-
-
-/* NB: For two strings to be identical, it is required that their lengths match */
-int acl_match_str(struct sample *smp, struct acl_pattern *pattern)
-{
-	int icase;
-
-	if (pattern->len != smp->data.str.len)
-		return ACL_PAT_FAIL;
-
-	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
-	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) ||
-	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0))
-		return ACL_PAT_PASS;
-	return ACL_PAT_FAIL;
-}
-
-/* NB: For two binaries buf to be identical, it is required that their lengths match */
-int acl_match_bin(struct sample *smp, struct acl_pattern *pattern)
-{
-	if (pattern->len != smp->data.str.len)
-		return ACL_PAT_FAIL;
-
-	if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)
-		return ACL_PAT_PASS;
-	return ACL_PAT_FAIL;
-}
-
-/* Lookup a string in the expression's pattern tree. The node is returned if it
- * exists, otherwise NULL.
- */
-static void *acl_lookup_str(struct sample *smp, struct acl_expr *expr)
-{
-	/* data are stored in a tree */
-	struct ebmb_node *node;
-	char prev;
-
-	/* we may have to force a trailing zero on the test pattern */
-	prev = smp->data.str.str[smp->data.str.len];
-	if (prev)
-		smp->data.str.str[smp->data.str.len] = '\0';
-	node = ebst_lookup(&expr->pattern_tree, smp->data.str.str);
-	if (prev)
-		smp->data.str.str[smp->data.str.len] = prev;
-	return node;
-}
-
-/* Executes a regex. It temporarily changes the data to add a trailing zero,
- * and restores the previous character when leaving.
- */
-int acl_match_reg(struct sample *smp, struct acl_pattern *pattern)
-{
-	if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0)
-		return ACL_PAT_PASS;
-	return ACL_PAT_FAIL;
-}
-
-/* Checks that the pattern matches the beginning of the tested string. */
-int acl_match_beg(struct sample *smp, struct acl_pattern *pattern)
-{
-	int icase;
-
-	if (pattern->len > smp->data.str.len)
-		return ACL_PAT_FAIL;
-
-	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
-	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) ||
-	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0))
-		return ACL_PAT_FAIL;
-	return ACL_PAT_PASS;
-}
-
-/* Checks that the pattern matches the end of the tested string. */
-int acl_match_end(struct sample *smp, struct acl_pattern *pattern)
-{
-	int icase;
-
-	if (pattern->len > smp->data.str.len)
-		return ACL_PAT_FAIL;
-	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
-	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) ||
-	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0))
-		return ACL_PAT_FAIL;
-	return ACL_PAT_PASS;
-}
-
-/* Checks that the pattern is included inside the tested string.
- * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
- */
-int acl_match_sub(struct sample *smp, struct acl_pattern *pattern)
-{
-	int icase;
-	char *end;
-	char *c;
-
-	if (pattern->len > smp->data.str.len)
-		return ACL_PAT_FAIL;
-
-	end = smp->data.str.str + smp->data.str.len - pattern->len;
-	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
-	if (icase) {
-		for (c = smp->data.str.str; c <= end; c++) {
-			if (tolower(*c) != tolower(*pattern->ptr.str))
-				continue;
-			if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0)
-				return ACL_PAT_PASS;
-		}
-	} else {
-		for (c = smp->data.str.str; c <= end; c++) {
-			if (*c != *pattern->ptr.str)
-				continue;
-			if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
-				return ACL_PAT_PASS;
-		}
-	}
-	return ACL_PAT_FAIL;
-}
-
-/* Background: Fast way to find a zero byte in a word
- * http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
- * hasZeroByte = (v - 0x01010101UL) & ~v & 0x80808080UL;
- *
- * To look for 4 different byte values, xor the word with those bytes and
- * then check for zero bytes:
- *
- * v = (((unsigned char)c * 0x1010101U) ^ delimiter)
- * where <delimiter> is the 4 byte values to look for (as an uint)
- * and <c> is the character that is being tested
- */
-static inline unsigned int is_delimiter(unsigned char c, unsigned int mask)
-{
-	mask ^= (c * 0x01010101); /* propagate the char to all 4 bytes */
-	return (mask - 0x01010101) & ~mask & 0x80808080U;
-}
-
-static inline unsigned int make_4delim(unsigned char d1, unsigned char d2, unsigned char d3, unsigned char d4)
-{
-	return d1 << 24 | d2 << 16 | d3 << 8 | d4;
-}
-
-/* This one is used by other real functions. It checks that the pattern is
- * included inside the tested string, but enclosed between the specified
- * delimiters or at the beginning or end of the string. The delimiters are
- * provided as an unsigned int made by make_4delim() and match up to 4 different
- * delimiters. Delimiters are stripped at the beginning and end of the pattern.
- */
-static int match_word(struct sample *smp, struct acl_pattern *pattern, unsigned int delimiters)
-{
-	int may_match, icase;
-	char *c, *end;
-	char *ps;
-	int pl;
-
-	pl = pattern->len;
-	ps = pattern->ptr.str;
-
-	while (pl > 0 && is_delimiter(*ps, delimiters)) {
-		pl--;
-		ps++;
-	}
-
-	while (pl > 0 && is_delimiter(ps[pl - 1], delimiters))
-		pl--;
-
-	if (pl > smp->data.str.len)
-		return ACL_PAT_FAIL;
-
-	may_match = 1;
-	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
-	end = smp->data.str.str + smp->data.str.len - pl;
-	for (c = smp->data.str.str; c <= end; c++) {
-		if (is_delimiter(*c, delimiters)) {
-			may_match = 1;
-			continue;
-		}
-
-		if (!may_match)
-			continue;
-
-		if (icase) {
-			if ((tolower(*c) == tolower(*ps)) &&
-			    (strncasecmp(ps, c, pl) == 0) &&
-			    (c == end || is_delimiter(c[pl], delimiters)))
-				return ACL_PAT_PASS;
-		} else {
-			if ((*c == *ps) &&
-			    (strncmp(ps, c, pl) == 0) &&
-			    (c == end || is_delimiter(c[pl], delimiters)))
-				return ACL_PAT_PASS;
-		}
-		may_match = 0;
-	}
-	return ACL_PAT_FAIL;
-}
-
-/* Checks that the pattern is included inside the tested string, but enclosed
- * between the delimiters '?' or '/' or at the beginning or end of the string.
- * Delimiters at the beginning or end of the pattern are ignored.
- */
-int acl_match_dir(struct sample *smp, struct acl_pattern *pattern)
-{
-	return match_word(smp, pattern, make_4delim('/', '?', '?', '?'));
-}
-
-/* Checks that the pattern is included inside the tested string, but enclosed
- * between the delmiters '/', '?', '.' or ":" or at the beginning or end of
- * the string. Delimiters at the beginning or end of the pattern are ignored.
- */
-int acl_match_dom(struct sample *smp, struct acl_pattern *pattern)
-{
-	return match_word(smp, pattern, make_4delim('/', '?', '.', ':'));
-}
-
-/* Checks that the integer in <test> is included between min and max */
-int acl_match_int(struct sample *smp, struct acl_pattern *pattern)
-{
-	if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) &&
-	    (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max))
-		return ACL_PAT_PASS;
-	return ACL_PAT_FAIL;
-}
-
-/* Checks that the length of the pattern in <test> is included between min and max */
-int acl_match_len(struct sample *smp, struct acl_pattern *pattern)
-{
-	if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) &&
-	    (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max))
-		return ACL_PAT_PASS;
-	return ACL_PAT_FAIL;
-}
-
-int acl_match_ip(struct sample *smp, struct acl_pattern *pattern)
-{
-	unsigned int v4; /* in network byte order */
-	struct in6_addr *v6;
-	int bits, pos;
-	struct in6_addr tmp6;
-
-	if (pattern->type == SMP_T_IPV4) {
-		if (smp->type == SMP_T_IPV4) {
-			v4 = smp->data.ipv4.s_addr;
-		}
-		else if (smp->type == SMP_T_IPV6) {
-			/* v4 match on a V6 sample. We want to check at least for
-			 * the following forms :
-			 *   - ::ffff:ip:v4 (ipv4 mapped)
-			 *   - ::0000:ip:v4 (old ipv4 mapped)
-			 *   - 2002:ip:v4:: (6to4)
-			 */
-			if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 &&
-			    *(uint32_t*)&smp->data.ipv6.s6_addr[4]  == 0 &&
-			    (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 ||
-			     *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) {
-				v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12];
-			}
-			else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) {
-				v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) +
-				            ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4]));
-			}
-			else
-				return ACL_PAT_FAIL;
-		}
-		else
-			return ACL_PAT_FAIL;
-
-		if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
-			return ACL_PAT_PASS;
-		else
-			return ACL_PAT_FAIL;
-	}
-	else if (pattern->type == SMP_T_IPV6) {
-		if (smp->type == SMP_T_IPV4) {
-			/* Convert the IPv4 sample address to IPv4 with the
-			 * mapping method using the ::ffff: prefix.
-			 */
-			memset(&tmp6, 0, 10);
-			*(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff);
-			*(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr;
-			v6 = &tmp6;
-		}
-		else if (smp->type == SMP_T_IPV6) {
-			v6 = &smp->data.ipv6;
-		}
-		else {
-			return ACL_PAT_FAIL;
-		}
-
-		bits = pattern->val.ipv6.mask;
-		for (pos = 0; bits > 0; pos += 4, bits -= 32) {
-			v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos];
-			if (bits < 32)
-				v4 &= htonl((~0U) << (32-bits));
-			if (v4)
-				return ACL_PAT_FAIL;
-		}
-		return ACL_PAT_PASS;
-	}
-	return ACL_PAT_FAIL;
-}
-
-/* Lookup an IPv4 address in the expression's pattern tree using the longest
- * match method. The node is returned if it exists, otherwise NULL.
- */
-static void *acl_lookup_ip(struct sample *smp, struct acl_expr *expr)
-{
-	struct in_addr *s;
-
-	if (smp->type != SMP_T_IPV4)
-		return ACL_PAT_FAIL;
-
-	s = &smp->data.ipv4;
-	return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr);
-}
-
-/* Parse a string. It is allocated and duplicated. */
-int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	int len;
-
-	len  = strlen(*text);
-	pattern->type = SMP_T_CSTR;
-
-	if (pattern->flags & ACL_PAT_F_TREE_OK) {
-		/* we're allowed to put the data in a tree whose root is pointed
-		 * to by val.tree.
-		 */
-		struct acl_idx_elt *node;
-
-		node = calloc(1, sizeof(*node) + len + 1);
-		if (!node) {
-			memprintf(err, "out of memory while loading string pattern");
-			return 0;
-		}
-		node->smp = smp;
-		memcpy(node->node.key, *text, len + 1);
-		if (ebst_insert(pattern->val.tree, &node->node) != &node->node)
-			free(node); /* was a duplicate */
-		pattern->flags |= ACL_PAT_F_TREE; /* this pattern now contains a tree */
-		return 1;
-	}
-
-	pattern->ptr.str = strdup(*text);
-	pattern->smp = smp;
-	if (!pattern->ptr.str) {
-		memprintf(err, "out of memory while loading string pattern");
-		return 0;
-	}
-	pattern->len = len;
-	return 1;
-}
-
-/* Parse a binary written in hexa. It is allocated. */
-int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	int len;
-	const char *p = *text;
-	int i,j;
-
-	len  = strlen(p);
-	if (len%2) {
-		memprintf(err, "an even number of hex digit is expected");
-		return 0;
-	}
-
-	pattern->type = SMP_T_CBIN;
-	pattern->len = len >> 1;
-	pattern->ptr.str = malloc(pattern->len);
-	pattern->smp = smp;
-	if (!pattern->ptr.str) {
-		memprintf(err, "out of memory while loading string pattern");
-		return 0;
-	}
-
-	i = j = 0;
-	while (j < pattern->len) {
-		if (!ishex(p[i++]))
-			goto bad_input;
-		if (!ishex(p[i++]))
-			goto bad_input;
-		pattern->ptr.str[j++] =  (hex2i(p[i-2]) << 4) + hex2i(p[i-1]);
-	}
-	return 1;
-
-bad_input:
-	memprintf(err, "an hex digit is expected (found '%c')", p[i-1]);
-	free(pattern->ptr.str);
-	return 0;
-}
-
-/* Parse and concatenate all further strings into one. */
-int
-acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-
-	int len = 0, i;
-	char *s;
-
-	for (i = 0; *text[i]; i++)
-		len += strlen(text[i])+1;
-
-	pattern->type = SMP_T_CSTR;
-	pattern->ptr.str = s = calloc(1, len);
-	pattern->smp = smp;
-	if (!pattern->ptr.str) {
-		memprintf(err, "out of memory while loading pattern");
-		return 0;
-	}
-
-	for (i = 0; *text[i]; i++)
-		s += sprintf(s, i?" %s":"%s", text[i]);
-
-	pattern->len = len;
-
-	return i;
-}
-
-/* Free data allocated by acl_parse_reg */
-static void acl_free_reg(void *ptr)
-{
-	regex_free(ptr);
-}
-
-/* Parse a regex. It is allocated. */
-int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	regex *preg;
-
-	preg = calloc(1, sizeof(*preg));
-
-	if (!preg) {
-		memprintf(err, "out of memory while loading pattern");
-		return 0;
-	}
-
-	if (!regex_comp(*text, preg, !(pattern->flags & ACL_PAT_F_IGNORE_CASE), 0, err)) {
-		free(preg);
-		return 0;
-	}
-
-	pattern->ptr.reg = preg;
-	pattern->freeptrbuf = &acl_free_reg;
-	pattern->smp = smp;
-	return 1;
-}
-
-/* Parse a range of positive integers delimited by either ':' or '-'. If only
- * one integer is read, it is set as both min and max. An operator may be
- * specified as the prefix, among this list of 5 :
- *
- *    0:eq, 1:gt, 2:ge, 3:lt, 4:le
- *
- * The default operator is "eq". It supports range matching. Ranges are
- * rejected for other operators. The operator may be changed at any time.
- * The operator is stored in the 'opaque' argument.
- *
- * If err is non-NULL, an error message will be returned there on errors and
- * the caller will have to free it.
- *
- */
-int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	signed long long i;
-	unsigned int j, last, skip = 0;
-	const char *ptr = *text;
-
-	pattern->type = SMP_T_UINT;
-	pattern->smp = smp;
-	while (!isdigit((unsigned char)*ptr)) {
-		switch (get_std_op(ptr)) {
-		case STD_OP_EQ: *opaque = 0; break;
-		case STD_OP_GT: *opaque = 1; break;
-		case STD_OP_GE: *opaque = 2; break;
-		case STD_OP_LT: *opaque = 3; break;
-		case STD_OP_LE: *opaque = 4; break;
-		default:
-			memprintf(err, "'%s' is neither a number nor a supported operator", ptr);
-			return 0;
-		}
-
-		skip++;
-		ptr = text[skip];
-	}
-
-	last = i = 0;
-	while (1) {
-                j = *ptr++;
-		if ((j == '-' || j == ':') && !last) {
-			last++;
-			pattern->val.range.min = i;
-			i = 0;
-			continue;
-		}
-		j -= '0';
-                if (j > 9)
-			// also catches the terminating zero
-                        break;
-                i *= 10;
-                i += j;
-        }
-
-	if (last && *opaque >= 1 && *opaque <= 4) {
-		/* having a range with a min or a max is absurd */
-		memprintf(err, "integer range '%s' specified with a comparison operator", text[skip]);
-		return 0;
-	}
-
-	if (!last)
-		pattern->val.range.min = i;
-	pattern->val.range.max = i;
-
-	switch (*opaque) {
-	case 0: /* eq */
-		pattern->val.range.min_set = 1;
-		pattern->val.range.max_set = 1;
-		break;
-	case 1: /* gt */
-		pattern->val.range.min++; /* gt = ge + 1 */
-	case 2: /* ge */
-		pattern->val.range.min_set = 1;
-		pattern->val.range.max_set = 0;
-		break;
-	case 3: /* lt */
-		pattern->val.range.max--; /* lt = le - 1 */
-	case 4: /* le */
-		pattern->val.range.min_set = 0;
-		pattern->val.range.max_set = 1;
-		break;
-	}
-	return skip + 1;
-}
-
-/* Parse a range of positive 2-component versions delimited by either ':' or
- * '-'. The version consists in a major and a minor, both of which must be
- * smaller than 65536, because internally they will be represented as a 32-bit
- * integer.
- * If only one version is read, it is set as both min and max. Just like for
- * pure integers, an operator may be specified as the prefix, among this list
- * of 5 :
- *
- *    0:eq, 1:gt, 2:ge, 3:lt, 4:le
- *
- * The default operator is "eq". It supports range matching. Ranges are
- * rejected for other operators. The operator may be changed at any time.
- * The operator is stored in the 'opaque' argument. This allows constructs
- * such as the following one :
- *
- *    acl obsolete_ssl    ssl_req_proto lt 3
- *    acl unsupported_ssl ssl_req_proto gt 3.1
- *    acl valid_ssl       ssl_req_proto 3.0-3.1
- *
- */
-int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	signed long long i;
-	unsigned int j, last, skip = 0;
-	const char *ptr = *text;
-
-
-	while (!isdigit((unsigned char)*ptr)) {
-		switch (get_std_op(ptr)) {
-		case STD_OP_EQ: *opaque = 0; break;
-		case STD_OP_GT: *opaque = 1; break;
-		case STD_OP_GE: *opaque = 2; break;
-		case STD_OP_LT: *opaque = 3; break;
-		case STD_OP_LE: *opaque = 4; break;
-		default:
-			memprintf(err, "'%s' is neither a number nor a supported operator", ptr);
-			return 0;
-		}
-
-		skip++;
-		ptr = text[skip];
-	}
-
-	last = i = 0;
-	while (1) {
-                j = *ptr++;
-		if (j == '.') {
-			/* minor part */
-			if (i >= 65536)
-				return 0;
-			i <<= 16;
-			continue;
-		}
-		if ((j == '-' || j == ':') && !last) {
-			last++;
-			if (i < 65536)
-				i <<= 16;
-			pattern->val.range.min = i;
-			i = 0;
-			continue;
-		}
-		j -= '0';
-                if (j > 9)
-			// also catches the terminating zero
-                        break;
-                i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10;
-                i += j;
-        }
-
-	/* if we only got a major version, let's shift it now */
-	if (i < 65536)
-		i <<= 16;
-
-	if (last && *opaque >= 1 && *opaque <= 4) {
-		/* having a range with a min or a max is absurd */
-		memprintf(err, "version range '%s' specified with a comparison operator", text[skip]);
-		return 0;
-	}
-
-	pattern->smp = smp;
-
-	if (!last)
-		pattern->val.range.min = i;
-	pattern->val.range.max = i;
-
-	switch (*opaque) {
-	case 0: /* eq */
-		pattern->val.range.min_set = 1;
-		pattern->val.range.max_set = 1;
-		break;
-	case 1: /* gt */
-		pattern->val.range.min++; /* gt = ge + 1 */
-	case 2: /* ge */
-		pattern->val.range.min_set = 1;
-		pattern->val.range.max_set = 0;
-		break;
-	case 3: /* lt */
-		pattern->val.range.max--; /* lt = le - 1 */
-	case 4: /* le */
-		pattern->val.range.min_set = 0;
-		pattern->val.range.max_set = 1;
-		break;
-	}
-	return skip + 1;
-}
-
-/* Parse an IP address and an optional mask in the form addr[/mask].
- * The addr may either be an IPv4 address or a hostname. The mask
- * may either be a dotted mask or a number of bits. Returns 1 if OK,
- * otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6).
- */
-int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
-{
-	struct eb_root *tree = NULL;
-	if (pattern->flags & ACL_PAT_F_TREE_OK)
-		tree = pattern->val.tree;
-
-	if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
-		unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr);
-		struct acl_idx_elt *node;
-		/* check if the mask is contiguous so that we can insert the
-		 * network into the tree. A continuous mask has only ones on
-		 * the left. This means that this mask + its lower bit added
-		 * once again is null.
-		 */
-		pattern->type = SMP_T_IPV4;
-		if (mask + (mask & -mask) == 0 && tree) {
-			mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */
-			/* FIXME: insert <addr>/<mask> into the tree here */
-			node = calloc(1, sizeof(*node) + 4); /* reserve 4 bytes for IPv4 address */
-			if (!node) {
-				memprintf(err, "out of memory while loading IPv4 pattern");
-				return 0;
-			}
-			node->smp = smp;
-			memcpy(node->node.key, &pattern->val.ipv4.addr, 4); /* network byte order */
-			node->node.node.pfx = mask;
-			if (ebmb_insert_prefix(tree, &node->node, 4) != &node->node)
-				free(node); /* was a duplicate */
-			pattern->flags |= ACL_PAT_F_TREE;
-			return 1;
-		}
-		return 1;
-	}
-	else if (str62net(*text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) {
-		/* no tree support right now */
-		pattern->type = SMP_T_IPV6;
-		return 1;
-	}
-	else {
-		memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", *text);
-		return 0;
-	}
-}
-
-/*
  * Registers the ACL keyword list <kwl> as a list of valid keywords for next
  * parsing sessions.
  */
@@ -851,41 +103,6 @@
 	return NULL;
 }
 
-/* NB: does nothing if <pat> is NULL */
-static void free_pattern(struct acl_pattern *pat)
-{
-	if (!pat)
-		return;
-
-	if (pat->ptr.ptr) {
-		if (pat->freeptrbuf)
-			pat->freeptrbuf(pat->ptr.ptr);
-
-		free(pat->ptr.ptr);
-	}
-
-	free(pat);
-}
-
-static void free_pattern_list(struct list *head)
-{
-	struct acl_pattern *pat, *tmp;
-	list_for_each_entry_safe(pat, tmp, head, list)
-		free_pattern(pat);
-}
-
-static void free_pattern_tree(struct eb_root *root)
-{
-	struct eb_node *node, *next;
-	node = eb_first(root);
-	while (node) {
-		next = eb_next(node);
-		eb_delete(node);
-		free(node);
-		node = next;
-	}
-}
-
 static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
 {
 	struct arg *arg;
@@ -909,120 +126,6 @@
 	return expr;
 }
 
-/* return 1 if the process is ok
- * return -1 if the parser fail. The err message is filled.
- * return -2 if out of memory
- */
-int acl_register_pattern(struct acl_expr *expr, char *text,
-                         struct sample_storage *smp,
-                         struct acl_pattern **pattern,
-                         int patflags, char **err)
-{
-	const char *args[2];
-	int opaque = 0;
-
-	args[0] = text;
-	args[1] = "";
-
-	/* we keep the previous pattern along iterations as long as it's not used */
-	if (!*pattern)
-		*pattern = (struct acl_pattern *)malloc(sizeof(**pattern));
-	if (!*pattern)
-		return -1;
-
-	memset(*pattern, 0, sizeof(**pattern));
-	(*pattern)->flags = patflags;
-
-	if (!((*pattern)->flags & ACL_PAT_F_IGNORE_CASE) &&
-	    (expr->match == acl_match_str || expr->match == acl_match_ip)) {
-		/* we pre-set the data pointer to the tree's head so that functions
-		 * which are able to insert in a tree know where to do that.
-		 */
-		(*pattern)->flags |= ACL_PAT_F_TREE_OK;
-		(*pattern)->val.tree = &expr->pattern_tree;
-	}
-
-	(*pattern)->type = SMP_TYPES; /* unspecified type by default */
-	if (!expr->parse(args, *pattern, smp, &opaque, err))
-		return -1;
-
-	/* if the parser did not feed the tree, let's chain the pattern to the list */
-	if (!((*pattern)->flags & ACL_PAT_F_TREE)) {
-		LIST_ADDQ(&expr->patterns, &(*pattern)->list);
-		*pattern = NULL; /* get a new one */
-	}
-
-	return 1;
-}
-
-/* Reads patterns from a file. If <err_msg> is non-NULL, an error message will
- * be returned there on errors and the caller will have to free it.
- */
-static int acl_read_patterns_from_file(struct acl_expr *expr,
-                                       const char *filename, int patflags,
-                                       char **err)
-{
-	FILE *file;
-	char *c;
-	char *arg;
-	struct acl_pattern *pattern;
-	int ret = 0;
-	int line = 0;
-	int code;
-
-	file = fopen(filename, "r");
-	if (!file) {
-		memprintf(err, "failed to open pattern file <%s>", filename);
-		return 0;
-	}
-
-	/* now parse all patterns. The file may contain only one pattern per
-	 * line. If the line contains spaces, they will be part of the pattern.
-	 * The pattern stops at the first CR, LF or EOF encountered.
-	 */
-	pattern = NULL;
-	while (fgets(trash.str, trash.size, file) != NULL) {
-		line++;
-		c = trash.str;
-
-		/* ignore lines beginning with a dash */
-		if (*c == '#')
-			continue;
-
-		/* strip leading spaces and tabs */
-		while (*c == ' ' || *c == '\t')
-			c++;
-
-
-		arg = c;
-		while (*c && *c != '\n' && *c != '\r')
-			c++;
-		*c = 0;
-
-		/* empty lines are ignored too */
-		if (c == arg)
-			continue;
-
-		code = acl_register_pattern(expr, arg, NULL, &pattern, patflags, err);
-		if (code == -2) {
-			memprintf(err, "out of memory when loading patterns from file <%s>", filename);
-			goto out_close;
-		}
-		else if (code < 0) {
-			memprintf(err, "%s when loading patterns from file <%s>", *err, filename);
-			goto out_free_pattern;
-		}
-	}
-
-	ret = 1; /* success */
-
- out_free_pattern:
-	free_pattern(pattern);
- out_close:
-	fclose(file);
-	return ret;
-}
-
 /* Parse an ACL expression starting at <args>[0], and return it. If <err> is
  * not NULL, it will be filled with a pointer to an error message in case of
  * error. This pointer must be freeable or NULL. <al> is an arg_list serving
@@ -1844,57 +947,6 @@
 	return cond;
 }
 
-/* This function execute the match part of the acl. It's applying
- * acl <expr> on sample <smp>. <sample> is filled only if the pointer
- * is not NULL. The function return ACL_PAT_FAIL, ACL_PAT_MISS or
- * ACL_PAT_PASS
- */
-inline int acl_exec_match(struct acl_expr *expr, struct sample *smp,
-                          struct sample_storage **sample)
-{
-	int acl_res = ACL_PAT_FAIL;
-	struct acl_pattern *pattern;
-	struct ebmb_node *node = NULL;
-	struct acl_idx_elt *elt;
-
-	if (expr->match == acl_match_nothing) {
-		if (smp->data.uint)
-			acl_res |= ACL_PAT_PASS;
-		else
-			acl_res |= ACL_PAT_FAIL;
-	}
-	else if (!expr->match) {
-		/* just check for existence */
-		acl_res |= ACL_PAT_PASS;
-	}
-	else {
-		if (!eb_is_empty(&expr->pattern_tree)) {
-			/* a tree is present, let's check what type it is */
-			if (expr->match == acl_match_str)
-				node = acl_lookup_str(smp, expr);
-			else if (expr->match == acl_match_ip)
-				node = acl_lookup_ip(smp, expr);
-			if (node) {
-				acl_res |= ACL_PAT_PASS;
-				elt = ebmb_entry(node, struct acl_idx_elt, node);
-				if (sample)
-					*sample = elt->smp;
-			}
-		}
-
-		/* call the match() function for all tests on this value */
-		list_for_each_entry(pattern, &expr->patterns, list) {
-			if (acl_res == ACL_PAT_PASS)
-				break;
-			acl_res |= expr->match(smp, pattern);
-			if (sample)
-				*sample = pattern->smp;
-		}
-	}
-
-	return acl_res;
-}
-
 /* Execute condition <cond> and return either ACL_PAT_FAIL, ACL_PAT_MISS or
  * ACL_PAT_PASS depending on the test results. ACL_PAT_MISS may only be
  * returned if <opt> does not contain SMP_OPT_FINAL, indicating that incomplete
diff --git a/src/pattern.c b/src/pattern.c
new file mode 100644
index 0000000..6bf7d1d
--- /dev/null
+++ b/src/pattern.c
@@ -0,0 +1,974 @@
+/*
+ * Pattern management functions.
+ *
+ * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <common/config.h>
+#include <common/standard.h>
+
+#include <types/global.h>
+#include <types/pattern.h>
+
+#include <proto/pattern.h>
+
+#include <ebsttree.h>
+
+char *acl_match_names[ACL_MATCH_NUM] = {
+	[ACL_MATCH_FOUND] = "found",
+	[ACL_MATCH_BOOL]  = "bool",
+	[ACL_MATCH_INT]   = "int",
+	[ACL_MATCH_IP]    = "ip",
+	[ACL_MATCH_BIN]   = "bin",
+	[ACL_MATCH_LEN]   = "len",
+	[ACL_MATCH_STR]   = "str",
+	[ACL_MATCH_BEG]   = "beg",
+	[ACL_MATCH_SUB]   = "sub",
+	[ACL_MATCH_DIR]   = "dir",
+	[ACL_MATCH_DOM]   = "dom",
+	[ACL_MATCH_END]   = "end",
+	[ACL_MATCH_REG]   = "reg",
+};
+
+int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, struct sample_storage *, int *, char **) = {
+	[ACL_MATCH_FOUND] = acl_parse_nothing,
+	[ACL_MATCH_BOOL]  = acl_parse_nothing,
+	[ACL_MATCH_INT]   = acl_parse_int,
+	[ACL_MATCH_IP]    = acl_parse_ip,
+	[ACL_MATCH_BIN]   = acl_parse_bin,
+	[ACL_MATCH_LEN]   = acl_parse_int,
+	[ACL_MATCH_STR]   = acl_parse_str,
+	[ACL_MATCH_BEG]   = acl_parse_str,
+	[ACL_MATCH_SUB]   = acl_parse_str,
+	[ACL_MATCH_DIR]   = acl_parse_str,
+	[ACL_MATCH_DOM]   = acl_parse_str,
+	[ACL_MATCH_END]   = acl_parse_str,
+	[ACL_MATCH_REG]   = acl_parse_reg,
+};
+
+int (*acl_match_fcts[ACL_MATCH_NUM])(struct sample *, struct acl_pattern *) = {
+	[ACL_MATCH_FOUND] = NULL,
+	[ACL_MATCH_BOOL]  = acl_match_nothing,
+	[ACL_MATCH_INT]   = acl_match_int,
+	[ACL_MATCH_IP]    = acl_match_ip,
+	[ACL_MATCH_BIN]   = acl_match_bin,
+	[ACL_MATCH_LEN]   = acl_match_len,
+	[ACL_MATCH_STR]   = acl_match_str,
+	[ACL_MATCH_BEG]   = acl_match_beg,
+	[ACL_MATCH_SUB]   = acl_match_sub,
+	[ACL_MATCH_DIR]   = acl_match_dir,
+	[ACL_MATCH_DOM]   = acl_match_dom,
+	[ACL_MATCH_END]   = acl_match_end,
+	[ACL_MATCH_REG]   = acl_match_reg,
+};
+
+/*
+ * These functions are exported and may be used by any other component.
+ */
+
+/* ignore the current line */
+int acl_parse_nothing(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	return 1;
+}
+
+/* always return false */
+int acl_match_nothing(struct sample *smp, struct acl_pattern *pattern)
+{
+	return ACL_PAT_FAIL;
+}
+
+
+/* NB: For two strings to be identical, it is required that their lengths match */
+int acl_match_str(struct sample *smp, struct acl_pattern *pattern)
+{
+	int icase;
+
+	if (pattern->len != smp->data.str.len)
+		return ACL_PAT_FAIL;
+
+	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
+	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) ||
+	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0))
+		return ACL_PAT_PASS;
+	return ACL_PAT_FAIL;
+}
+
+/* NB: For two binaries buf to be identical, it is required that their lengths match */
+int acl_match_bin(struct sample *smp, struct acl_pattern *pattern)
+{
+	if (pattern->len != smp->data.str.len)
+		return ACL_PAT_FAIL;
+
+	if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)
+		return ACL_PAT_PASS;
+	return ACL_PAT_FAIL;
+}
+
+/* Lookup a string in the expression's pattern tree. The node is returned if it
+ * exists, otherwise NULL.
+ */
+static void *acl_lookup_str(struct sample *smp, struct acl_expr *expr)
+{
+	/* data are stored in a tree */
+	struct ebmb_node *node;
+	char prev;
+
+	/* we may have to force a trailing zero on the test pattern */
+	prev = smp->data.str.str[smp->data.str.len];
+	if (prev)
+		smp->data.str.str[smp->data.str.len] = '\0';
+	node = ebst_lookup(&expr->pattern_tree, smp->data.str.str);
+	if (prev)
+		smp->data.str.str[smp->data.str.len] = prev;
+	return node;
+}
+
+/* Executes a regex. It temporarily changes the data to add a trailing zero,
+ * and restores the previous character when leaving.
+ */
+int acl_match_reg(struct sample *smp, struct acl_pattern *pattern)
+{
+	if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0)
+		return ACL_PAT_PASS;
+	return ACL_PAT_FAIL;
+}
+
+/* Checks that the pattern matches the beginning of the tested string. */
+int acl_match_beg(struct sample *smp, struct acl_pattern *pattern)
+{
+	int icase;
+
+	if (pattern->len > smp->data.str.len)
+		return ACL_PAT_FAIL;
+
+	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
+	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) ||
+	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0))
+		return ACL_PAT_FAIL;
+	return ACL_PAT_PASS;
+}
+
+/* Checks that the pattern matches the end of the tested string. */
+int acl_match_end(struct sample *smp, struct acl_pattern *pattern)
+{
+	int icase;
+
+	if (pattern->len > smp->data.str.len)
+		return ACL_PAT_FAIL;
+	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
+	if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) ||
+	    (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0))
+		return ACL_PAT_FAIL;
+	return ACL_PAT_PASS;
+}
+
+/* Checks that the pattern is included inside the tested string.
+ * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
+ */
+int acl_match_sub(struct sample *smp, struct acl_pattern *pattern)
+{
+	int icase;
+	char *end;
+	char *c;
+
+	if (pattern->len > smp->data.str.len)
+		return ACL_PAT_FAIL;
+
+	end = smp->data.str.str + smp->data.str.len - pattern->len;
+	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
+	if (icase) {
+		for (c = smp->data.str.str; c <= end; c++) {
+			if (tolower(*c) != tolower(*pattern->ptr.str))
+				continue;
+			if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0)
+				return ACL_PAT_PASS;
+		}
+	} else {
+		for (c = smp->data.str.str; c <= end; c++) {
+			if (*c != *pattern->ptr.str)
+				continue;
+			if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
+				return ACL_PAT_PASS;
+		}
+	}
+	return ACL_PAT_FAIL;
+}
+
+/* Background: Fast way to find a zero byte in a word
+ * http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
+ * hasZeroByte = (v - 0x01010101UL) & ~v & 0x80808080UL;
+ *
+ * To look for 4 different byte values, xor the word with those bytes and
+ * then check for zero bytes:
+ *
+ * v = (((unsigned char)c * 0x1010101U) ^ delimiter)
+ * where <delimiter> is the 4 byte values to look for (as an uint)
+ * and <c> is the character that is being tested
+ */
+static inline unsigned int is_delimiter(unsigned char c, unsigned int mask)
+{
+	mask ^= (c * 0x01010101); /* propagate the char to all 4 bytes */
+	return (mask - 0x01010101) & ~mask & 0x80808080U;
+}
+
+static inline unsigned int make_4delim(unsigned char d1, unsigned char d2, unsigned char d3, unsigned char d4)
+{
+	return d1 << 24 | d2 << 16 | d3 << 8 | d4;
+}
+
+/* This one is used by other real functions. It checks that the pattern is
+ * included inside the tested string, but enclosed between the specified
+ * delimiters or at the beginning or end of the string. The delimiters are
+ * provided as an unsigned int made by make_4delim() and match up to 4 different
+ * delimiters. Delimiters are stripped at the beginning and end of the pattern.
+ */
+static int match_word(struct sample *smp, struct acl_pattern *pattern, unsigned int delimiters)
+{
+	int may_match, icase;
+	char *c, *end;
+	char *ps;
+	int pl;
+
+	pl = pattern->len;
+	ps = pattern->ptr.str;
+
+	while (pl > 0 && is_delimiter(*ps, delimiters)) {
+		pl--;
+		ps++;
+	}
+
+	while (pl > 0 && is_delimiter(ps[pl - 1], delimiters))
+		pl--;
+
+	if (pl > smp->data.str.len)
+		return ACL_PAT_FAIL;
+
+	may_match = 1;
+	icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
+	end = smp->data.str.str + smp->data.str.len - pl;
+	for (c = smp->data.str.str; c <= end; c++) {
+		if (is_delimiter(*c, delimiters)) {
+			may_match = 1;
+			continue;
+		}
+
+		if (!may_match)
+			continue;
+
+		if (icase) {
+			if ((tolower(*c) == tolower(*ps)) &&
+			    (strncasecmp(ps, c, pl) == 0) &&
+			    (c == end || is_delimiter(c[pl], delimiters)))
+				return ACL_PAT_PASS;
+		} else {
+			if ((*c == *ps) &&
+			    (strncmp(ps, c, pl) == 0) &&
+			    (c == end || is_delimiter(c[pl], delimiters)))
+				return ACL_PAT_PASS;
+		}
+		may_match = 0;
+	}
+	return ACL_PAT_FAIL;
+}
+
+/* Checks that the pattern is included inside the tested string, but enclosed
+ * between the delimiters '?' or '/' or at the beginning or end of the string.
+ * Delimiters at the beginning or end of the pattern are ignored.
+ */
+int acl_match_dir(struct sample *smp, struct acl_pattern *pattern)
+{
+	return match_word(smp, pattern, make_4delim('/', '?', '?', '?'));
+}
+
+/* Checks that the pattern is included inside the tested string, but enclosed
+ * between the delmiters '/', '?', '.' or ":" or at the beginning or end of
+ * the string. Delimiters at the beginning or end of the pattern are ignored.
+ */
+int acl_match_dom(struct sample *smp, struct acl_pattern *pattern)
+{
+	return match_word(smp, pattern, make_4delim('/', '?', '.', ':'));
+}
+
+/* Checks that the integer in <test> is included between min and max */
+int acl_match_int(struct sample *smp, struct acl_pattern *pattern)
+{
+	if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) &&
+	    (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max))
+		return ACL_PAT_PASS;
+	return ACL_PAT_FAIL;
+}
+
+/* Checks that the length of the pattern in <test> is included between min and max */
+int acl_match_len(struct sample *smp, struct acl_pattern *pattern)
+{
+	if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) &&
+	    (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max))
+		return ACL_PAT_PASS;
+	return ACL_PAT_FAIL;
+}
+
+int acl_match_ip(struct sample *smp, struct acl_pattern *pattern)
+{
+	unsigned int v4; /* in network byte order */
+	struct in6_addr *v6;
+	int bits, pos;
+	struct in6_addr tmp6;
+
+	if (pattern->type == SMP_T_IPV4) {
+		if (smp->type == SMP_T_IPV4) {
+			v4 = smp->data.ipv4.s_addr;
+		}
+		else if (smp->type == SMP_T_IPV6) {
+			/* v4 match on a V6 sample. We want to check at least for
+			 * the following forms :
+			 *   - ::ffff:ip:v4 (ipv4 mapped)
+			 *   - ::0000:ip:v4 (old ipv4 mapped)
+			 *   - 2002:ip:v4:: (6to4)
+			 */
+			if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 &&
+			    *(uint32_t*)&smp->data.ipv6.s6_addr[4]  == 0 &&
+			    (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 ||
+			     *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) {
+				v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12];
+			}
+			else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) {
+				v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) +
+				            ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4]));
+			}
+			else
+				return ACL_PAT_FAIL;
+		}
+		else
+			return ACL_PAT_FAIL;
+
+		if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
+			return ACL_PAT_PASS;
+		else
+			return ACL_PAT_FAIL;
+	}
+	else if (pattern->type == SMP_T_IPV6) {
+		if (smp->type == SMP_T_IPV4) {
+			/* Convert the IPv4 sample address to IPv4 with the
+			 * mapping method using the ::ffff: prefix.
+			 */
+			memset(&tmp6, 0, 10);
+			*(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff);
+			*(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr;
+			v6 = &tmp6;
+		}
+		else if (smp->type == SMP_T_IPV6) {
+			v6 = &smp->data.ipv6;
+		}
+		else {
+			return ACL_PAT_FAIL;
+		}
+
+		bits = pattern->val.ipv6.mask;
+		for (pos = 0; bits > 0; pos += 4, bits -= 32) {
+			v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos];
+			if (bits < 32)
+				v4 &= htonl((~0U) << (32-bits));
+			if (v4)
+				return ACL_PAT_FAIL;
+		}
+		return ACL_PAT_PASS;
+	}
+	return ACL_PAT_FAIL;
+}
+
+/* Lookup an IPv4 address in the expression's pattern tree using the longest
+ * match method. The node is returned if it exists, otherwise NULL.
+ */
+static void *acl_lookup_ip(struct sample *smp, struct acl_expr *expr)
+{
+	struct in_addr *s;
+
+	if (smp->type != SMP_T_IPV4)
+		return ACL_PAT_FAIL;
+
+	s = &smp->data.ipv4;
+	return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr);
+}
+
+/* Parse a string. It is allocated and duplicated. */
+int acl_parse_str(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	int len;
+
+	len  = strlen(*text);
+	pattern->type = SMP_T_CSTR;
+
+	if (pattern->flags & ACL_PAT_F_TREE_OK) {
+		/* we're allowed to put the data in a tree whose root is pointed
+		 * to by val.tree.
+		 */
+		struct acl_idx_elt *node;
+
+		node = calloc(1, sizeof(*node) + len + 1);
+		if (!node) {
+			memprintf(err, "out of memory while loading string pattern");
+			return 0;
+		}
+		node->smp = smp;
+		memcpy(node->node.key, *text, len + 1);
+		if (ebst_insert(pattern->val.tree, &node->node) != &node->node)
+			free(node); /* was a duplicate */
+		pattern->flags |= ACL_PAT_F_TREE; /* this pattern now contains a tree */
+		return 1;
+	}
+
+	pattern->ptr.str = strdup(*text);
+	pattern->smp = smp;
+	if (!pattern->ptr.str) {
+		memprintf(err, "out of memory while loading string pattern");
+		return 0;
+	}
+	pattern->len = len;
+	return 1;
+}
+
+/* Parse a binary written in hexa. It is allocated. */
+int acl_parse_bin(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	int len;
+	const char *p = *text;
+	int i,j;
+
+	len  = strlen(p);
+	if (len%2) {
+		memprintf(err, "an even number of hex digit is expected");
+		return 0;
+	}
+
+	pattern->type = SMP_T_CBIN;
+	pattern->len = len >> 1;
+	pattern->ptr.str = malloc(pattern->len);
+	pattern->smp = smp;
+	if (!pattern->ptr.str) {
+		memprintf(err, "out of memory while loading string pattern");
+		return 0;
+	}
+
+	i = j = 0;
+	while (j < pattern->len) {
+		if (!ishex(p[i++]))
+			goto bad_input;
+		if (!ishex(p[i++]))
+			goto bad_input;
+		pattern->ptr.str[j++] =  (hex2i(p[i-2]) << 4) + hex2i(p[i-1]);
+	}
+	return 1;
+
+bad_input:
+	memprintf(err, "an hex digit is expected (found '%c')", p[i-1]);
+	free(pattern->ptr.str);
+	return 0;
+}
+
+/* Parse and concatenate all further strings into one. */
+int
+acl_parse_strcat(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+
+	int len = 0, i;
+	char *s;
+
+	for (i = 0; *text[i]; i++)
+		len += strlen(text[i])+1;
+
+	pattern->type = SMP_T_CSTR;
+	pattern->ptr.str = s = calloc(1, len);
+	pattern->smp = smp;
+	if (!pattern->ptr.str) {
+		memprintf(err, "out of memory while loading pattern");
+		return 0;
+	}
+
+	for (i = 0; *text[i]; i++)
+		s += sprintf(s, i?" %s":"%s", text[i]);
+
+	pattern->len = len;
+
+	return i;
+}
+
+/* Free data allocated by acl_parse_reg */
+static void acl_free_reg(void *ptr)
+{
+	regex_free(ptr);
+}
+
+/* Parse a regex. It is allocated. */
+int acl_parse_reg(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	regex *preg;
+
+	preg = calloc(1, sizeof(*preg));
+
+	if (!preg) {
+		memprintf(err, "out of memory while loading pattern");
+		return 0;
+	}
+
+	if (!regex_comp(*text, preg, !(pattern->flags & ACL_PAT_F_IGNORE_CASE), 0, err)) {
+		free(preg);
+		return 0;
+	}
+
+	pattern->ptr.reg = preg;
+	pattern->freeptrbuf = &acl_free_reg;
+	pattern->smp = smp;
+	return 1;
+}
+
+/* Parse a range of positive integers delimited by either ':' or '-'. If only
+ * one integer is read, it is set as both min and max. An operator may be
+ * specified as the prefix, among this list of 5 :
+ *
+ *    0:eq, 1:gt, 2:ge, 3:lt, 4:le
+ *
+ * The default operator is "eq". It supports range matching. Ranges are
+ * rejected for other operators. The operator may be changed at any time.
+ * The operator is stored in the 'opaque' argument.
+ *
+ * If err is non-NULL, an error message will be returned there on errors and
+ * the caller will have to free it.
+ *
+ */
+int acl_parse_int(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	signed long long i;
+	unsigned int j, last, skip = 0;
+	const char *ptr = *text;
+
+	pattern->type = SMP_T_UINT;
+	pattern->smp = smp;
+	while (!isdigit((unsigned char)*ptr)) {
+		switch (get_std_op(ptr)) {
+		case STD_OP_EQ: *opaque = 0; break;
+		case STD_OP_GT: *opaque = 1; break;
+		case STD_OP_GE: *opaque = 2; break;
+		case STD_OP_LT: *opaque = 3; break;
+		case STD_OP_LE: *opaque = 4; break;
+		default:
+			memprintf(err, "'%s' is neither a number nor a supported operator", ptr);
+			return 0;
+		}
+
+		skip++;
+		ptr = text[skip];
+	}
+
+	last = i = 0;
+	while (1) {
+                j = *ptr++;
+		if ((j == '-' || j == ':') && !last) {
+			last++;
+			pattern->val.range.min = i;
+			i = 0;
+			continue;
+		}
+		j -= '0';
+                if (j > 9)
+			// also catches the terminating zero
+                        break;
+                i *= 10;
+                i += j;
+        }
+
+	if (last && *opaque >= 1 && *opaque <= 4) {
+		/* having a range with a min or a max is absurd */
+		memprintf(err, "integer range '%s' specified with a comparison operator", text[skip]);
+		return 0;
+	}
+
+	if (!last)
+		pattern->val.range.min = i;
+	pattern->val.range.max = i;
+
+	switch (*opaque) {
+	case 0: /* eq */
+		pattern->val.range.min_set = 1;
+		pattern->val.range.max_set = 1;
+		break;
+	case 1: /* gt */
+		pattern->val.range.min++; /* gt = ge + 1 */
+	case 2: /* ge */
+		pattern->val.range.min_set = 1;
+		pattern->val.range.max_set = 0;
+		break;
+	case 3: /* lt */
+		pattern->val.range.max--; /* lt = le - 1 */
+	case 4: /* le */
+		pattern->val.range.min_set = 0;
+		pattern->val.range.max_set = 1;
+		break;
+	}
+	return skip + 1;
+}
+
+/* Parse a range of positive 2-component versions delimited by either ':' or
+ * '-'. The version consists in a major and a minor, both of which must be
+ * smaller than 65536, because internally they will be represented as a 32-bit
+ * integer.
+ * If only one version is read, it is set as both min and max. Just like for
+ * pure integers, an operator may be specified as the prefix, among this list
+ * of 5 :
+ *
+ *    0:eq, 1:gt, 2:ge, 3:lt, 4:le
+ *
+ * The default operator is "eq". It supports range matching. Ranges are
+ * rejected for other operators. The operator may be changed at any time.
+ * The operator is stored in the 'opaque' argument. This allows constructs
+ * such as the following one :
+ *
+ *    acl obsolete_ssl    ssl_req_proto lt 3
+ *    acl unsupported_ssl ssl_req_proto gt 3.1
+ *    acl valid_ssl       ssl_req_proto 3.0-3.1
+ *
+ */
+int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	signed long long i;
+	unsigned int j, last, skip = 0;
+	const char *ptr = *text;
+
+
+	while (!isdigit((unsigned char)*ptr)) {
+		switch (get_std_op(ptr)) {
+		case STD_OP_EQ: *opaque = 0; break;
+		case STD_OP_GT: *opaque = 1; break;
+		case STD_OP_GE: *opaque = 2; break;
+		case STD_OP_LT: *opaque = 3; break;
+		case STD_OP_LE: *opaque = 4; break;
+		default:
+			memprintf(err, "'%s' is neither a number nor a supported operator", ptr);
+			return 0;
+		}
+
+		skip++;
+		ptr = text[skip];
+	}
+
+	last = i = 0;
+	while (1) {
+                j = *ptr++;
+		if (j == '.') {
+			/* minor part */
+			if (i >= 65536)
+				return 0;
+			i <<= 16;
+			continue;
+		}
+		if ((j == '-' || j == ':') && !last) {
+			last++;
+			if (i < 65536)
+				i <<= 16;
+			pattern->val.range.min = i;
+			i = 0;
+			continue;
+		}
+		j -= '0';
+                if (j > 9)
+			// also catches the terminating zero
+                        break;
+                i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10;
+                i += j;
+        }
+
+	/* if we only got a major version, let's shift it now */
+	if (i < 65536)
+		i <<= 16;
+
+	if (last && *opaque >= 1 && *opaque <= 4) {
+		/* having a range with a min or a max is absurd */
+		memprintf(err, "version range '%s' specified with a comparison operator", text[skip]);
+		return 0;
+	}
+
+	pattern->smp = smp;
+
+	if (!last)
+		pattern->val.range.min = i;
+	pattern->val.range.max = i;
+
+	switch (*opaque) {
+	case 0: /* eq */
+		pattern->val.range.min_set = 1;
+		pattern->val.range.max_set = 1;
+		break;
+	case 1: /* gt */
+		pattern->val.range.min++; /* gt = ge + 1 */
+	case 2: /* ge */
+		pattern->val.range.min_set = 1;
+		pattern->val.range.max_set = 0;
+		break;
+	case 3: /* lt */
+		pattern->val.range.max--; /* lt = le - 1 */
+	case 4: /* le */
+		pattern->val.range.min_set = 0;
+		pattern->val.range.max_set = 1;
+		break;
+	}
+	return skip + 1;
+}
+
+/* Parse an IP address and an optional mask in the form addr[/mask].
+ * The addr may either be an IPv4 address or a hostname. The mask
+ * may either be a dotted mask or a number of bits. Returns 1 if OK,
+ * otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6).
+ */
+int acl_parse_ip(const char **text, struct acl_pattern *pattern, struct sample_storage *smp, int *opaque, char **err)
+{
+	struct eb_root *tree = NULL;
+	if (pattern->flags & ACL_PAT_F_TREE_OK)
+		tree = pattern->val.tree;
+
+	if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
+		unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr);
+		struct acl_idx_elt *node;
+		/* check if the mask is contiguous so that we can insert the
+		 * network into the tree. A continuous mask has only ones on
+		 * the left. This means that this mask + its lower bit added
+		 * once again is null.
+		 */
+		pattern->type = SMP_T_IPV4;
+		if (mask + (mask & -mask) == 0 && tree) {
+			mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */
+			/* FIXME: insert <addr>/<mask> into the tree here */
+			node = calloc(1, sizeof(*node) + 4); /* reserve 4 bytes for IPv4 address */
+			if (!node) {
+				memprintf(err, "out of memory while loading IPv4 pattern");
+				return 0;
+			}
+			node->smp = smp;
+			memcpy(node->node.key, &pattern->val.ipv4.addr, 4); /* network byte order */
+			node->node.node.pfx = mask;
+			if (ebmb_insert_prefix(tree, &node->node, 4) != &node->node)
+				free(node); /* was a duplicate */
+			pattern->flags |= ACL_PAT_F_TREE;
+			return 1;
+		}
+		return 1;
+	}
+	else if (str62net(*text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) {
+		/* no tree support right now */
+		pattern->type = SMP_T_IPV6;
+		return 1;
+	}
+	else {
+		memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", *text);
+		return 0;
+	}
+}
+
+/* NB: does nothing if <pat> is NULL */
+void free_pattern(struct acl_pattern *pat)
+{
+	if (!pat)
+		return;
+
+	if (pat->ptr.ptr) {
+		if (pat->freeptrbuf)
+			pat->freeptrbuf(pat->ptr.ptr);
+
+		free(pat->ptr.ptr);
+	}
+
+	free(pat);
+}
+
+void free_pattern_list(struct list *head)
+{
+	struct acl_pattern *pat, *tmp;
+	list_for_each_entry_safe(pat, tmp, head, list)
+		free_pattern(pat);
+}
+
+void free_pattern_tree(struct eb_root *root)
+{
+	struct eb_node *node, *next;
+	node = eb_first(root);
+	while (node) {
+		next = eb_next(node);
+		eb_delete(node);
+		free(node);
+		node = next;
+	}
+}
+
+/* return 1 if the process is ok
+ * return -1 if the parser fail. The err message is filled.
+ * return -2 if out of memory
+ */
+int acl_register_pattern(struct acl_expr *expr, char *text,
+                         struct sample_storage *smp,
+                         struct acl_pattern **pattern,
+                         int patflags, char **err)
+{
+	const char *args[2];
+	int opaque = 0;
+
+	args[0] = text;
+	args[1] = "";
+
+	/* we keep the previous pattern along iterations as long as it's not used */
+	if (!*pattern)
+		*pattern = (struct acl_pattern *)malloc(sizeof(**pattern));
+	if (!*pattern)
+		return -1;
+
+	memset(*pattern, 0, sizeof(**pattern));
+	(*pattern)->flags = patflags;
+
+	if (!((*pattern)->flags & ACL_PAT_F_IGNORE_CASE) &&
+	    (expr->match == acl_match_str || expr->match == acl_match_ip)) {
+		/* we pre-set the data pointer to the tree's head so that functions
+		 * which are able to insert in a tree know where to do that.
+		 */
+		(*pattern)->flags |= ACL_PAT_F_TREE_OK;
+		(*pattern)->val.tree = &expr->pattern_tree;
+	}
+
+	(*pattern)->type = SMP_TYPES; /* unspecified type by default */
+	if (!expr->parse(args, *pattern, smp, &opaque, err))
+		return -1;
+
+	/* if the parser did not feed the tree, let's chain the pattern to the list */
+	if (!((*pattern)->flags & ACL_PAT_F_TREE)) {
+		LIST_ADDQ(&expr->patterns, &(*pattern)->list);
+		*pattern = NULL; /* get a new one */
+	}
+
+	return 1;
+}
+
+/* Reads patterns from a file. If <err_msg> is non-NULL, an error message will
+ * be returned there on errors and the caller will have to free it.
+ */
+int acl_read_patterns_from_file(struct acl_expr *expr,
+                                const char *filename, int patflags,
+                                char **err)
+{
+	FILE *file;
+	char *c;
+	char *arg;
+	struct acl_pattern *pattern;
+	int ret = 0;
+	int line = 0;
+	int code;
+
+	file = fopen(filename, "r");
+	if (!file) {
+		memprintf(err, "failed to open pattern file <%s>", filename);
+		return 0;
+	}
+
+	/* now parse all patterns. The file may contain only one pattern per
+	 * line. If the line contains spaces, they will be part of the pattern.
+	 * The pattern stops at the first CR, LF or EOF encountered.
+	 */
+	pattern = NULL;
+	while (fgets(trash.str, trash.size, file) != NULL) {
+		line++;
+		c = trash.str;
+
+		/* ignore lines beginning with a dash */
+		if (*c == '#')
+			continue;
+
+		/* strip leading spaces and tabs */
+		while (*c == ' ' || *c == '\t')
+			c++;
+
+
+		arg = c;
+		while (*c && *c != '\n' && *c != '\r')
+			c++;
+		*c = 0;
+
+		/* empty lines are ignored too */
+		if (c == arg)
+			continue;
+
+		code = acl_register_pattern(expr, arg, NULL, &pattern, patflags, err);
+		if (code == -2) {
+			memprintf(err, "out of memory when loading patterns from file <%s>", filename);
+			goto out_close;
+		}
+		else if (code < 0) {
+			memprintf(err, "%s when loading patterns from file <%s>", *err, filename);
+			goto out_free_pattern;
+		}
+	}
+
+	ret = 1; /* success */
+
+ out_free_pattern:
+	free_pattern(pattern);
+ out_close:
+	fclose(file);
+	return ret;
+}
+
+/* This function execute the match part of the acl. It's applying
+ * acl <expr> on sample <smp>. <sample> is filled only if the pointer
+ * is not NULL. The function return ACL_PAT_FAIL, ACL_PAT_MISS or
+ * ACL_PAT_PASS
+ */
+inline int acl_exec_match(struct acl_expr *expr, struct sample *smp,
+                          struct sample_storage **sample)
+{
+	int acl_res = ACL_PAT_FAIL;
+	struct acl_pattern *pattern;
+	struct ebmb_node *node = NULL;
+	struct acl_idx_elt *elt;
+
+	if (expr->match == acl_match_nothing) {
+		if (smp->data.uint)
+			acl_res |= ACL_PAT_PASS;
+		else
+			acl_res |= ACL_PAT_FAIL;
+	}
+	else if (!expr->match) {
+		/* just check for existence */
+		acl_res |= ACL_PAT_PASS;
+	}
+	else {
+		if (!eb_is_empty(&expr->pattern_tree)) {
+			/* a tree is present, let's check what type it is */
+			if (expr->match == acl_match_str)
+				node = acl_lookup_str(smp, expr);
+			else if (expr->match == acl_match_ip)
+				node = acl_lookup_ip(smp, expr);
+			if (node) {
+				acl_res |= ACL_PAT_PASS;
+				elt = ebmb_entry(node, struct acl_idx_elt, node);
+				if (sample)
+					*sample = elt->smp;
+			}
+		}
+
+		/* call the match() function for all tests on this value */
+		list_for_each_entry(pattern, &expr->patterns, list) {
+			if (acl_res == ACL_PAT_PASS)
+				break;
+			acl_res |= expr->match(smp, pattern);
+			if (sample)
+				*sample = pattern->smp;
+		}
+	}
+
+	return acl_res;
+}
+
diff --git a/src/payload.c b/src/payload.c
index de6395f..4356025 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -16,6 +16,7 @@
 #include <proto/acl.h>
 #include <proto/arg.h>
 #include <proto/channel.h>
+#include <proto/pattern.h>
 #include <proto/payload.h>
 #include <proto/sample.h>
 
diff --git a/src/proto_http.c b/src/proto_http.c
index e6d5fc5..6a3d315 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -54,6 +54,7 @@
 #include <proto/frontend.h>
 #include <proto/log.h>
 #include <proto/hdr_idx.h>
+#include <proto/pattern.h>
 #include <proto/proto_tcp.h>
 #include <proto/proto_http.h>
 #include <proto/proxy.h>
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index e1e18c3..fc92af1 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -66,6 +66,7 @@
 #include <proto/freq_ctr.h>
 #include <proto/frontend.h>
 #include <proto/listener.h>
+#include <proto/pattern.h>
 #include <proto/server.h>
 #include <proto/log.h>
 #include <proto/proxy.h>