MAJOR: acl: add option -m to change the pattern matching method

ACL expressions now support "-m" in addition to "-i" and "-f". This new
option is followed by the name of the pattern matching method to be used
on the extracted pattern. This makes it possible to reuse existing sample
fetch methods with other matching methods (eg: regex). A "found" matching
method ignores any pattern and only verifies that the required sample was
found (useful for cookies).
diff --git a/doc/configuration.txt b/doc/configuration.txt
index dfc616c..da287f3 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -8092,8 +8092,74 @@
 
    -i : ignore case during matching of all subsequent patterns.
    -f : load patterns from a file.
+   -m : changes the pattern matching method
    -- : force end of flags. Useful when a string looks like one of the flags.
 
+The "-m" flag is special. It allows the default pattern matching method to be
+changed for the fetched sample. The default method depends on the keyword and
+is described later in this document. When "-m" is specified and followed by a
+pattern matching method name, this method is used instead. This makes it
+possible to match contents in ways that were not initially planned. There are
+some restrictions however. Not all methods can be used with all sample fetch
+methods. Also, if "-m" is used in conjunction with "-f", it must be placed
+first. The pattern matching method must be one of the following :
+
+  - "found" : only check if the requested sample could be found in the stream,
+              but do not compare it against any pattern. It is recommended not
+              to pass any pattern to avoid any confusion. This matching method
+              is particularly useful to detect presence of certain contents
+              such as headers, cookies, etc... even if they are empty and
+              without comparing them to anything nor counting them.
+
+  - "bool"  : check the value as a boolean. It can only be applied to fetches
+              which return a boolean or integer value, and takes no pattern.
+              Value zero does not match, all other values do match.
+
+  - "int"   : match the value as an integer. It can be used with integer and
+              boolean samples.
+
+  - "ip"    : match the value as an IPv4 or IPv6 address. It is compatible
+              with IP addresse only.
+
+  - "bin"   : match the contents against an hexadecimal string representing a
+              binary sequence. This may be used with binary or string samples.
+
+  - "len"   : match the sample's length as an integer. This may be used with
+              binary or string samples.
+
+  - "str"   : match the contents against a string. This may be used with
+              binary or string samples.
+
+  - "beg"   : check that the contents begin like the provided string patterns.
+              This may be used with binary or string samples.
+
+  - "sub"   : check that the contents contain at least one of the provided
+              string patterns. This may be used with binary or string samples.
+
+  - "dir"   : check that a slash-delimited portion of the contents exactly
+              match one of the provided string patterns. This may be used with
+              binary or string samples.
+
+  - "dom"   : check that a dot-delimited portion of the contents exactly
+              match one of the provided string patterns. This may be used with
+              binary or string samples.
+
+  - "end"   : check that the contents end like the provided string patterns.
+              This may be used with binary or string samples.
+
+  - "reg"   : match the contents against a list of regular expressions. This
+              may be used with binary or string samples.
+
+For example, to quickly detect the presence of cookie "JSESSIONID" in an HTTP
+request, it is possible to do :
+
+    acl jsess_present cook(JSESSIONID) -m found
+
+In order to apply a regular expression on the 500 first bytes of data in the
+buffer, one would use the following acl :
+
+    acl script_tag payload(0,500) -m reg -i <script>
+
 The "-f" flag is special as it loads all of the lines it finds in the file
 specified in argument and loads all of them before continuing. It is even
 possible to pass multiple "-f" arguments if the patterns are to be loaded from
diff --git a/include/types/acl.h b/include/types/acl.h
index 65644cc..e1d0f12 100644
--- a/include/types/acl.h
+++ b/include/types/acl.h
@@ -79,6 +79,25 @@
 	ACL_PAT_F_TREE        = 1 << 3,       /* some patterns are arranged in a tree */
 };
 
+/* ACL match methods */
+enum {
+	ACL_MATCH_FOUND, /* just ensure that fetch found the sample */
+	ACL_MATCH_BOOL,  /* match fetch's integer value as boolean */
+	ACL_MATCH_INT,   /* unsigned integer (int) */
+	ACL_MATCH_IP,    /* IPv4/IPv6 address (IP) */
+	ACL_MATCH_BIN,   /* hex string (bin) */
+	ACL_MATCH_LEN,   /* string length (str -> int) */
+	ACL_MATCH_STR,   /* exact string match (str) */
+	ACL_MATCH_BEG,   /* beginning of string (str) */
+	ACL_MATCH_SUB,   /* substring (str) */
+	ACL_MATCH_DIR,   /* directory-like sub-string (str) */
+	ACL_MATCH_DOM,   /* domain-like sub-string (str) */
+	ACL_MATCH_END,   /* end of string (str) */
+	ACL_MATCH_REG,   /* regex (str -> reg) */
+	/* keep this one last */
+	ACL_MATCH_NUM
+};
+
 /* How to store a time range and the valid days in 29 bits */
 struct acl_time {
 	int dow:7;              /* 1 bit per day of week: 0-6 */
diff --git a/src/acl.c b/src/acl.c
index 3c0d78f..9c75b97 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -37,6 +37,64 @@
 	.list = LIST_HEAD_INIT(acl_keywords.list)
 };
 
+static 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",
+};
+
+static int (*acl_parse_fcts[ACL_MATCH_NUM])(const char **, struct acl_pattern *, 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,
+};
+
+static 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)
+{
+	int i;
+
+	for (i = 0; i < ACL_MATCH_NUM; i++)
+		if (strcmp(name, acl_match_names[i]) == 0)
+			return i;
+	return -1;
+}
 
 /*
  * These functions are exported and may be used by any other component.
@@ -1082,6 +1140,7 @@
 	/* check for options before patterns. Supported options are :
 	 *   -i : ignore case for all patterns by default
 	 *   -f : read patterns from those files
+	 *   -m : force matching method (must be used before -f)
 	 *   -- : everything after this is not an option
 	 */
 	patflags = 0;
@@ -1091,6 +1150,45 @@
 		else if ((*args)[1] == 'f') {
 			if (!acl_read_patterns_from_file(expr, args[1], patflags | ACL_PAT_F_FROM_FILE, err))
 				goto out_free_expr;
+			args++;
+		}
+		else if ((*args)[1] == 'm') {
+			int idx;
+
+			if (!LIST_ISEMPTY(&expr->patterns) || !eb_is_empty(&expr->pattern_tree)) {
+				memprintf(err, "'-m' must only be specified before patterns and files in parsing ACL expression");
+				goto out_free_expr;
+			}
+
+			idx = acl_find_match_name(args[1]);
+			if (idx < 0) {
+				memprintf(err, "unknown matching method '%s' when parsing ACL expression", args[1]);
+				goto out_free_expr;
+			}
+
+			/* Note: -m found is always valid, bool/int are compatible, str/bin/reg/len are compatible */
+			if (idx == ACL_MATCH_FOUND ||                           /* -m found */
+			    ((idx == ACL_MATCH_BOOL || idx == ACL_MATCH_INT) && /* -m bool/int */
+			     (expr->smp->out_type == SMP_T_BOOL ||
+			      expr->smp->out_type == SMP_T_UINT ||
+			      expr->smp->out_type == SMP_T_SINT)) ||
+			    (idx == ACL_MATCH_IP &&                             /* -m ip */
+			     (expr->smp->out_type == SMP_T_IPV4 ||
+			      expr->smp->out_type == SMP_T_IPV6)) ||
+			    ((idx == ACL_MATCH_BIN || idx == ACL_MATCH_LEN || idx == ACL_MATCH_STR ||
+			      idx == ACL_MATCH_BEG || idx == ACL_MATCH_SUB || idx == ACL_MATCH_DIR ||
+			      idx == ACL_MATCH_DOM || idx == ACL_MATCH_END || idx == ACL_MATCH_REG) &&  /* strings */
+			     (expr->smp->out_type == SMP_T_STR ||
+			      expr->smp->out_type == SMP_T_BIN ||
+			      expr->smp->out_type == SMP_T_CSTR ||
+			      expr->smp->out_type == SMP_T_CBIN))) {
+				expr->parse = acl_parse_fcts[idx];
+				expr->match = acl_match_fcts[idx];
+			}
+			else {
+				memprintf(err, "matching method '%s' cannot be used with fetch keyword '%s'", args[1], expr->kw->kw);
+				goto out_free_expr;
+			}
 			args++;
 		}
 		else if ((*args)[1] == '-') {
@@ -1600,6 +1698,10 @@
 					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 */