[BUG] stats: support url-encoded forms

Bashkim Kasa reported that the stats admin page did not work when colons
were used in server or backend names. This was caused by url-encoding
resulting in ':' being sent as '%3A'. Now we systematically decode the
field names and values to fix this issue.
diff --git a/include/common/standard.h b/include/common/standard.h
index 0082087..769aec6 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -227,6 +227,13 @@
 		    const char escape, const fd_set *map,
 		    const char *string);
 
+/* Decode an URL-encoded string in-place. The resulting string might
+ * be shorter. If some forbidden characters are found, the conversion is
+ * aborted, the string is truncated before the issue and non-zero is returned,
+ * otherwise the operation returns non-zero indicating success.
+ */
+int url_decode(char *string);
+
 /* This one is 6 times faster than strtoul() on athlon, but does
  * no check at all.
  */
diff --git a/src/proto_http.c b/src/proto_http.c
index 1fc7c94..69232f9 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2932,6 +2932,9 @@
 				*value++ = '\0';
 			}
 
+			if (!url_decode(key) || !url_decode(value))
+				break;
+
 			/* Now we can check the key to see what to do */
 			if (!backend && strcmp(key, "b") == 0) {
 				backend = value;
diff --git a/src/standard.c b/src/standard.c
index d3b9ef7..5c27bdb 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -601,6 +601,40 @@
 	return start;
 }
 
+/* Decode an URL-encoded string in-place. The resulting string might
+ * be shorter. If some forbidden characters are found, the conversion is
+ * aborted, the string is truncated before the issue and non-zero is returned,
+ * otherwise the operation returns non-zero indicating success.
+ */
+int url_decode(char *string)
+{
+	char *in, *out;
+	int ret = 0;
+
+	in = string;
+	out = string;
+	while (*in) {
+		switch (*in) {
+		case '+' :
+			*out++ = ' ';
+			break;
+		case '%' :
+			if (!ishex(in[1]) || !ishex(in[2]))
+				goto end;
+			*out++ = (hex2i(in[1]) << 4) + hex2i(in[2]);
+			in += 2;
+			break;
+		default:
+			*out++ = *in;
+			break;
+		}
+		in++;
+	}
+	ret = 1; /* success */
+ end:
+	*out = 0;
+	return ret;
+}
 
 unsigned int str2ui(const char *s)
 {