[MEDIUM] introduce the "timeout" keyword

A new "timeout" keyword replaces old "{con|cli|srv}timeout", and
provides the ability to independantly set the following timeouts :

  - client
  - tarpit
  - queue
  - connect
  - server
  - appsession

Additionally, the "clitimeout", "contimeout" and "srvtimeout" values
are supported but deprecated. No warning is emitted yet when they are
used since the option is very new.

Other timeouts should follow soon now.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 91b9f3c..8bb787e 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -976,78 +976,26 @@
 			return -1;
 		}
 	}
-	else if (!strcmp(args[0], "contimeout")) {  /* connect timeout */
-		if (!__tv_iseq(&curproxy->contimeout, &defproxy.contimeout)) {
-			Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
-			return 0;
-		}
-		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
-			return 0;
+	else if (!strcmp(args[0], "contimeout") || !strcmp(args[0], "clitimeout") ||
+		 !strcmp(args[0], "srvtimeout") || !strcmp(args[0], "timeout")) {
 
-		if (*(args[1]) == 0) {
-			Alert("parsing [%s:%d] : '%s' expects an integer <time_in_ms> as argument.\n",
-			      file, linenum, args[0]);
-			return -1;
-		}
-		err = parse_time_err(args[1], &val, TIME_UNIT_MS);
-		if (err) {
-			Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n",
-			      file, linenum, *err, args[0]);
-			return -1;
-		}
-		if (val > 0)
-			__tv_from_ms(&curproxy->contimeout, val);
-		else
-			tv_eternity(&curproxy->contimeout);
-	}
-	else if (!strcmp(args[0], "clitimeout")) {  /*  client timeout */
-		if (!__tv_iseq(&curproxy->clitimeout, &defproxy.clitimeout)) {
-			Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
-			      file, linenum, args[0]);
-			return 0;
-		}
-		else if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
-			return 0;
+		/* either we have {con|srv|cli}timeout <value> or we have the
+		 * new form: timeout <type> <value>. The parser needs the word
+		 * preceeding the value.
+		 */
+		const char **start_arg = (const char **)args;
 
-		if (*(args[1]) == 0) {
-			Alert("parsing [%s:%d] : '%s' expects an integer <time_in_ms> as argument.\n",
-			      file, linenum, args[0]);
-			return -1;
-		}
-		err = parse_time_err(args[1], &val, TIME_UNIT_MS);
-		if (err) {
-			Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n",
-			      file, linenum, *err, args[0]);
-			return -1;
-		}
-		if (val > 0)
-			__tv_from_ms(&curproxy->clitimeout, val);
-		else
-			tv_eternity(&curproxy->clitimeout);
-	}
-	else if (!strcmp(args[0], "srvtimeout")) {  /*  server timeout */
-		if (!__tv_iseq(&curproxy->srvtimeout, &defproxy.srvtimeout)) {
-			Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
-			return 0;
-		}
-		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
-			return 0;
+		if (strcmp(args[0], "timeout") == 0)
+			start_arg++;
 
-		if (*(args[1]) == 0) {
-			Alert("parsing [%s:%d] : '%s' expects an integer <time_in_ms> as argument.\n",
-			      file, linenum, args[0]);
-			return -1;
-		}
-		err = parse_time_err(args[1], &val, TIME_UNIT_MS);
-		if (err) {
-			Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n",
-			      file, linenum, *err, args[0]);
+		snprintf(trash, sizeof(trash), "error near '%s'", args[0]);
+		rc = proxy_parse_timeout(start_arg, curproxy, &defproxy, trash, sizeof(trash));
+		if (rc < 0) {
+			Alert("parsing [%s:%d] : %s\n", file, linenum, trash);
 			return -1;
 		}
-		if (val > 0)
-			__tv_from_ms(&curproxy->srvtimeout, val);
-		else
-			tv_eternity(&curproxy->srvtimeout);
+		if (rc > 0)
+			Warning("parsing [%s:%d] : %s\n", file, linenum, trash);
 	}
 	else if (!strcmp(args[0], "retries")) {  /* connection retries */
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
diff --git a/src/proxy.c b/src/proxy.c
index 06424c6..8263964 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -75,6 +75,89 @@
 		return "unknown";
 }
 
+/* This function parses a "timeout" statement in a proxy section. It returns
+ * -1 if there is any error, 1 for a warning, otherwise zero. If it does not
+ * return zero, it may write an error message into the <err> buffer, for at
+ * most <errlen> bytes, trailing zero included. The trailing '\n' must not
+ * be written. The function must be called with <args> pointing to the first
+ * word after "timeout", with <proxy> pointing to the proxy being parsed, and
+ * <defpx> to the default proxy or NULL. As a special case for compatibility
+ * with older configs, it also accepts "{cli|srv|con}timeout" in args[0].
+ */
+int proxy_parse_timeout(const char **args, struct proxy *proxy,
+			struct proxy *defpx, char *err, int errlen)
+{
+	unsigned timeout;
+	int retval, cap;
+	const char *res, *name;
+	struct timeval *tv = NULL;
+	struct timeval *td = NULL;
+
+	retval = 0;
+	name = args[0];
+	if (!strcmp(args[0], "client") || !strcmp(args[0], "clitimeout")) {
+		name = "client";
+		tv = &proxy->clitimeout;
+		td = &defpx->clitimeout;
+		cap = PR_CAP_FE;
+	} else if (!strcmp(args[0], "tarpit")) {
+		tv = &proxy->timeout.tarpit;
+		td = &defpx->timeout.tarpit;
+		cap = PR_CAP_FE;
+	} else if (!strcmp(args[0], "server") || !strcmp(args[0], "srvtimeout")) {
+		name = "server";
+		tv = &proxy->srvtimeout;
+		td = &defpx->srvtimeout;
+		cap = PR_CAP_BE;
+	} else if (!strcmp(args[0], "connect") || !strcmp(args[0], "contimeout")) {
+		name = "connect";
+		tv = &proxy->contimeout;
+		td = &defpx->contimeout;
+		cap = PR_CAP_BE;
+	} else if (!strcmp(args[0], "appsession")) {
+		tv = &proxy->appsession_timeout;
+		td = &defpx->appsession_timeout;
+		cap = PR_CAP_BE;
+	} else if (!strcmp(args[0], "queue")) {
+		tv = &proxy->timeout.queue;
+		td = &defpx->timeout.queue;
+		cap = PR_CAP_BE;
+	} else {
+		snprintf(err, errlen, "timeout '%s': must be 'client', 'server', 'connect', 'appsession', 'queue', or 'tarpit'",
+			 args[0]);
+		return -1;
+	}
+
+	if (*args[1] == 0) {
+		snprintf(err, errlen, "%s timeout expects an integer value (in milliseconds)", name);
+		return -1;
+	}
+
+	res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
+	if (res) {
+		snprintf(err, errlen, "unexpected character '%c' in %s timeout", *err, name);
+		return -1;
+	}
+
+	if (!(proxy->cap & cap)) {
+		snprintf(err, errlen, "%s timeout will be ignored because %s '%s' has no %s capability",
+			 name, proxy_type_str(proxy), proxy->id,
+			 (cap & PR_CAP_BE) ? "backend" : "frontend");
+		retval = 1;
+	}
+	else if (defpx && !__tv_iseq(tv, td)) {
+		snprintf(err, errlen, "overwriting %s timeout which was already specified", name);
+		retval = 1;
+	}
+
+	if (timeout)
+		__tv_from_ms(tv, timeout);
+	else
+		tv_eternity(tv);
+
+	return retval;
+}
+
 /*
  * This function finds a proxy with matching name, mode and with satisfying
  * capabilities. It also checks if there are more matching proxies with