[MEDIUM] http: add support for conditional request filter execution

All the req* rules except the reqadd rules can now be specified with
an if/unless condition. If a condition is specified and does not match,
the filter is ignored. This is particularly useful with reqidel, reqirep
and reqtarpit.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index b77156d..e3d38f1 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3597,8 +3597,8 @@
   See also: "rspadd" and section 6 about HTTP header manipulation
 
 
-reqallow  <search>
-reqiallow <search>  (ignore case)
+reqallow  <search> [{if | unless} <cond>]
+reqiallow <search> [{if | unless} <cond>] (ignore case)
   Definitely allow an HTTP request if a line matches a regular expression
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3611,6 +3611,9 @@
               "reqallow" keyword strictly matches case while "reqiallow"
               ignores case.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   A request containing any line which matches extended regular expression
   <search> will mark the request as allowed, even if any later test would
   result in a deny. The test applies both to the request line and to request
@@ -3625,12 +3628,12 @@
      reqiallow ^Host:\ www\.
      reqideny  ^Host:\ .*\.local
 
-  See also: "reqdeny", "acl", "block" and section 6 about HTTP header
-            manipulation
+  See also: "reqdeny", "block", section 6 about HTTP header manipulation, and
+            section 7 about ACLs.
 
 
-reqdel  <search>
-reqidel <search>  (ignore case)
+reqdel  <search> [{if | unless} <cond>]
+reqidel <search> [{if | unless} <cond>]  (ignore case)
   Delete all headers matching a regular expression in an HTTP request
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3642,6 +3645,9 @@
               ('\'). The pattern applies to a full line at a time. The "reqdel"
               keyword strictly matches case while "reqidel" ignores case.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   Any header line matching extended regular expression <search> in the request
   will be completely deleted. Most common use of this is to remove unwanted
   and/or dangerous headers or cookies from a request before passing it to the
@@ -3656,12 +3662,12 @@
      reqidel ^X-Forwarded-For:.*
      reqidel ^Cookie:.*SERVER=
 
-  See also: "reqadd", "reqrep", "rspdel" and section 6 about HTTP header
-            manipulation
+  See also: "reqadd", "reqrep", "rspdel", section 6 about HTTP header
+            manipulation, and section 7 about ACLs.
 
 
-reqdeny  <search>
-reqideny <search>  (ignore case)
+reqdeny  <search> [{if | unless} <cond>]
+reqideny <search> [{if | unless} <cond>]  (ignore case)
   Deny an HTTP request if a line matches a regular expression
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3674,6 +3680,9 @@
               "reqdeny" keyword strictly matches case while "reqideny" ignores
               case.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   A request containing any line which matches extended regular expression
   <search> will mark the request as denied, even if any later test would
   result in an allow. The test applies both to the request line and to request
@@ -3692,12 +3701,12 @@
      reqideny  ^Host:\ .*\.local
      reqiallow ^Host:\ www\.
 
-  See also: "reqallow", "rspdeny", "acl", "block" and section 6 about HTTP
-            header manipulation
+  See also: "reqallow", "rspdeny", "block", section 6 about HTTP header
+            manipulation, and section 7 about ACLs.
 
 
-reqpass  <search>
-reqipass <search>  (ignore case)
+reqpass  <search> [{if | unless} <cond>]
+reqipass <search> [{if | unless} <cond>]  (ignore case)
   Ignore any HTTP request line matching a regular expression in next rules
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3710,6 +3719,9 @@
               "reqpass" keyword strictly matches case while "reqipass" ignores
               case.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   A request containing any line which matches extended regular expression
   <search> will skip next rules, without assigning any deny or allow verdict.
   The test applies both to the request line and to request headers. Keep in
@@ -3724,12 +3736,12 @@
      reqideny  ^Host:\ .*\.local
      reqiallow ^Host:\ www\.
 
-  See also: "reqallow", "reqdeny", "acl", "block" and section 6 about HTTP
-            header manipulation
+  See also: "reqallow", "reqdeny", "block", section 6 about HTTP header
+            manipulation, and section 7 about ACLs.
 
 
-reqrep  <search> <string>
-reqirep <search> <string>   (ignore case)
+reqrep  <search> <string> [{if | unless} <cond>]
+reqirep <search> <string> [{if | unless} <cond>]   (ignore case)
   Replace a regular expression with a string in an HTTP request line
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3747,6 +3759,9 @@
               being a single digit between 0 and 9. Please refer to section
               6 about HTTP header manipulation for more information.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   Any line matching extended regular expression <search> in the request (both
   the request line and header lines) will be completely replaced with <string>.
   Most common use of this is to rewrite URLs or domain names in "Host" headers.
@@ -3763,12 +3778,12 @@
      # replace "www.mydomain.com" with "www" in the host name.
      reqirep ^Host:\ www.mydomain.com   Host:\ www
 
-  See also: "reqadd", "reqdel", "rsprep" and section 6 about HTTP header
-            manipulation
+  See also: "reqadd", "reqdel", "rsprep", section 6 about HTTP header
+            manipulation, and section 7 about ACLs.
 
 
-reqtarpit  <search>
-reqitarpit <search>  (ignore case)
+reqtarpit  <search> [{if | unless} <cond>]
+reqitarpit <search> [{if | unless} <cond>]  (ignore case)
   Tarpit an HTTP request containing a line matching a regular expression
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
@@ -3781,6 +3796,9 @@
               "reqtarpit" keyword strictly matches case while "reqitarpit"
               ignores case.
 
+    <cond>    is an optional matching condition built from ACLs. It makes it
+              possible to ignore this rule when other conditions are not met.
+
   A request containing any line which matches extended regular expression
   <search> will be tarpitted, which means that it will connect to nowhere, will
   be kept open for a pre-defined time, then will return an HTTP error 500 so
@@ -3795,14 +3813,18 @@
   come. Depending on the environment and attack, it may be particularly
   efficient at reducing the load on the network and firewalls.
 
-  Example :
+  Examples :
      # ignore user-agents reporting any flavour of "Mozilla" or "MSIE", but
      # block all others.
      reqipass   ^User-Agent:\.*(Mozilla|MSIE)
      reqitarpit ^User-Agent:
 
-  See also: "reqallow", "reqdeny", "reqpass", and section 6 about HTTP header
-            manipulation
+     # block bad guys
+     acl badguys src 10.1.0.3 172.16.13.20/28
+     reqitarpit . if badguys
+
+  See also: "reqallow", "reqdeny", "reqpass", section 6 about HTTP header
+            manipulation, and section 7 about ACLs.
 
 
 retries <value>
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 78fb95b..4e724b2 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -912,6 +912,7 @@
 	regex_t *preg = NULL;
 	const char *err;
 	int err_code = 0;
+	struct acl_cond *cond = NULL;
 
 	if (px == &defproxy) {
 		Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, line, cmd);
@@ -928,6 +929,25 @@
 	if (warnifnotcap(px, PR_CAP_RS, file, line, cmd, NULL))
 		err_code |= ERR_WARN;
 
+	if (cond_start &&
+	    (strcmp(*cond_start, "if") == 0 || strcmp(*cond_start, "unless") == 0)) {
+		if ((cond = build_acl_cond(file, line, px, cond_start)) == NULL) {
+			Alert("parsing [%s:%d] : error detected while parsing a '%s' condition.\n",
+			      file, line, cmd);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto err;
+		}
+	}
+	else if (cond_start && **cond_start) {
+		Alert("parsing [%s:%d] : '%s' : Expecting nothing, 'if', or 'unless', got '%s'.\n",
+		      file, line, cmd, *cond_start);
+		err_code |= ERR_ALERT | ERR_FATAL;
+		goto err;
+	}
+
+	if (dir == ACL_DIR_REQ)
+		err_code |= warnif_cond_requires_resp(cond, file, line);
+
 	preg = calloc(1, sizeof(regex_t));
 	if (!preg) {
 		Alert("parsing [%s:%d] : '%s' : not enough memory to build regex.\n", file, line, cmd);
@@ -942,7 +962,7 @@
 	}
 
 	err = chain_regex((dir == ACL_DIR_REQ) ? &px->req_exp : &px->rsp_exp,
-			  preg, action, repl ? strdup(repl) : NULL, NULL);
+			  preg, action, repl ? strdup(repl) : NULL, cond);
 	if (repl && err) {
 		Alert("parsing [%s:%d] : '%s' : invalid character or unterminated sequence in replacement string near '%c'.\n",
 		      file, line, cmd, *err);
@@ -3599,56 +3619,56 @@
 
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_REPLACE, 0,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_REMOVE, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqdeny")) {  /* deny a request if a header matches this regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_DENY, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqpass")) {  /* pass this header without allowing or denying the request */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_PASS, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqallow")) {  /* allow a request if a header matches this regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_ALLOW, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqtarpit")) {  /* tarpit a request if a header matches this regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_TARPIT, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqsetbe")) { /* switch the backend from a regex, respecting case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_SETBE, 0,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqisetbe")) { /* switch the backend from a regex, ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_SETBE, REG_ICASE,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
@@ -3662,42 +3682,42 @@
 
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_REPLACE, REG_ICASE,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqidel")) {  /* delete request header from a regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_REMOVE, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqideny")) {  /* deny a request if a header matches this regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_DENY, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqipass")) {  /* pass this header without allowing or denying the request */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_PASS, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqiallow")) {  /* allow a request if a header matches this regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_ALLOW, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "reqitarpit")) {  /* tarpit a request if a header matches this regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_REQ, ACT_TARPIT, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}