MINOR: stats admin: allow unordered parameters in POST requests

Previously, the stats admin page required POST parameters to be provided
exactly in the same order as the HTML form.
This patch allows to handle those parameters in any orders.

Also, note that haproxy won't alter server states anymore if backend or server
names are ambiguous (duplicated names in the configuration) to prevent
unexpected results (the same should probably be applied to the stats socket).
diff --git a/src/proto_http.c b/src/proto_http.c
index ed8e018..bf91328 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2545,13 +2545,18 @@
  */
 int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn, struct buffer *req)
 {
-	struct proxy *px;
-	struct server *sv;
+	struct proxy *px = NULL;
+	struct server *sv = NULL;
 
-	char *backend = NULL;
-	int action = 0;
+	char key[LINESIZE];
+	int action = ST_ADM_ACTION_NONE;
+	int reprocess = 0;
+
+	int total_servers = 0;
+	int altered_servers = 0;
 
 	char *first_param, *cur_param, *next_param, *end_params;
+	char *st_cur_param, *st_next_param;
 
 	first_param = req->data + txn->req.eoh + 2;
 	end_params  = first_param + txn->req.body_len;
@@ -2578,15 +2583,21 @@
 	 * From the html form, the backend and the action are at the end.
 	 */
 	while (cur_param > first_param) {
-		char *key, *value;
+		char *value;
+		int poffset, plen;
 
 		cur_param--;
 		if ((*cur_param == '&') || (cur_param == first_param)) {
+ reprocess_servers:
 			/* Parse the key */
-			key = cur_param;
-			if (cur_param != first_param) {
-				/* delimit the string for the next loop */
-				*key++ = '\0';
+			poffset = (cur_param != first_param ? 1 : 0);
+			plen = next_param - cur_param + (cur_param == first_param ? 1 : 0);
+			if ((plen > 0) && (plen <= sizeof(key))) {
+				strncpy(key, cur_param + poffset, plen);
+				key[plen - 1] = '\0';
+			} else {
+				si->applet.ctx.stats.st_code = STAT_STATUS_EXCD;
+				goto out;
 			}
 
 			/* Parse the value */
@@ -2603,45 +2614,91 @@
 				break;
 
 			/* Now we can check the key to see what to do */
-			if (!backend && strcmp(key, "b") == 0) {
-				backend = value;
+			if (!px && (strcmp(key, "b") == 0)) {
+				if ((px = findproxy(value, PR_CAP_BE)) == NULL) {
+					/* the backend name is unknown or ambiguous (duplicate names) */
+					si->applet.ctx.stats.st_code = STAT_STATUS_ERRP;
+					goto out;
+				}
 			}
-			else if (!action && strcmp(key, "action") == 0) {
+			else if (!action && (strcmp(key, "action") == 0)) {
 				if (strcmp(value, "disable") == 0) {
-					action = 1;
+					action = ST_ADM_ACTION_DISABLE;
 				}
 				else if (strcmp(value, "enable") == 0) {
-					action = 2;
-				} else {
-					/* unknown action, no need to continue */
-					break;
+					action = ST_ADM_ACTION_ENABLE;
+				}
+				else {
+					si->applet.ctx.stats.st_code = STAT_STATUS_ERRP;
+					goto out;
 				}
 			}
 			else if (strcmp(key, "s") == 0) {
-				if (backend && action && get_backend_server(backend, value, &px, &sv)) {
+				if (!(px && action)) {
+					/*
+					 * Indicates that we'll need to reprocess the parameters
+					 * as soon as backend and action are known
+					 */
+					if (!reprocess) {
+						st_cur_param  = cur_param;
+						st_next_param = next_param;
+					}
+					reprocess = 1;
+				}
+				else if ((sv = findserver(px, value)) != NULL) {
 					switch (action) {
-					case 1:
+					case ST_ADM_ACTION_DISABLE:
 						if ((px->state != PR_STSTOPPED) && !(sv->state & SRV_MAINTAIN)) {
 							/* Not already in maintenance, we can change the server state */
 							sv->state |= SRV_MAINTAIN;
 							set_server_down(sv);
-							si->applet.ctx.stats.st_code = STAT_STATUS_DONE;
+							altered_servers++;
+							total_servers++;
 						}
 						break;
-					case 2:
+					case ST_ADM_ACTION_ENABLE:
 						if ((px->state != PR_STSTOPPED) && (sv->state & SRV_MAINTAIN)) {
 							/* Already in maintenance, we can change the server state */
 							set_server_up(sv);
 							sv->health = sv->rise;	/* up, but will fall down at first failure */
-							si->applet.ctx.stats.st_code = STAT_STATUS_DONE;
+							altered_servers++;
+							total_servers++;
 						}
 						break;
 					}
+				} else {
+					/* the server name is unknown or ambiguous (duplicate names) */
+					total_servers++;
 				}
 			}
+			if (reprocess && px && action) {
+				/* Now, we know the backend and the action chosen by the user.
+				 * We can safely restart from the first server parameter
+				 * to reprocess them
+				 */
+				cur_param  = st_cur_param;
+				next_param = st_next_param;
+				reprocess = 0;
+				goto reprocess_servers;
+			}
+
 			next_param = cur_param;
 		}
 	}
+
+	if (total_servers == 0) {
+		si->applet.ctx.stats.st_code = STAT_STATUS_NONE;
+	}
+	else if (altered_servers == 0) {
+		si->applet.ctx.stats.st_code = STAT_STATUS_ERRP;
+	}
+	else if (altered_servers == total_servers) {
+		si->applet.ctx.stats.st_code = STAT_STATUS_DONE;
+	}
+	else {
+		si->applet.ctx.stats.st_code = STAT_STATUS_PART;
+	}
+ out:
 	return 1;
 }
 
@@ -7124,6 +7181,10 @@
 				si->applet.ctx.stats.st_code = STAT_STATUS_DONE;
 			else if (memcmp(h, STAT_STATUS_NONE, 4) == 0)
 				si->applet.ctx.stats.st_code = STAT_STATUS_NONE;
+			else if (memcmp(h, STAT_STATUS_PART, 4) == 0)
+				si->applet.ctx.stats.st_code = STAT_STATUS_PART;
+			else if (memcmp(h, STAT_STATUS_ERRP, 4) == 0)
+				si->applet.ctx.stats.st_code = STAT_STATUS_ERRP;
 			else if (memcmp(h, STAT_STATUS_EXCD, 4) == 0)
 				si->applet.ctx.stats.st_code = STAT_STATUS_EXCD;
 			else if (memcmp(h, STAT_STATUS_DENY, 4) == 0)