[MEDIUM] enable/disable servers from the stats web interface

Based on a patch provided by Judd Montgomery, it is now possible to
enable/disable servers from the stats web interface. This allows to select
several servers in a backend and apply the action to them at the same time.

Currently, there are 2 known limitations :
- The POST data are limited to one packet
  (don't alter too many servers at a time).
- Expect: 100-continue is not supported.
(cherry picked from commit 7693948766cb5647ac03b48e782cfee2b1f14491)
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 59607f9..5195d8c 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -1135,6 +1135,44 @@
 }
 
 
+/* We don't want to land on the posted stats page because a refresh will
+ * repost the data.  We don't want this to happen on accident so we redirect
+ * the browse to the stats page with a GET.
+ */
+int stats_http_redir(struct session *s, struct buffer *rep, struct uri_auth *uri)
+{
+	struct chunk msg;
+
+	chunk_init(&msg, trash, sizeof(trash));
+
+	switch (s->data_state) {
+	case DATA_ST_INIT:
+		chunk_printf(&msg,
+			"HTTP/1.0 303 See Other\r\n"
+			"Cache-Control: no-cache\r\n"
+			"Content-Type: text/plain\r\n"
+			"Connection: close\r\n"
+			"Location: %s;st=%s",
+			uri->uri_prefix, s->data_ctx.stats.st_code);
+		chunk_printf(&msg, "\r\n\r\n");
+
+		if (buffer_feed_chunk(rep, &msg) >= 0)
+			return 0;
+
+		s->txn.status = 303;
+
+		if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
+			s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
+		if (!(s->flags & SN_FINST_MASK))
+			s->flags |= SN_FINST_R;
+
+		s->data_state = DATA_ST_FIN;
+		return 1;
+	}
+	return 1;
+}
+
+
 /* This I/O handler runs as an applet embedded in a stream interface. It is
  * used to send HTTP stats over a TCP socket. The mechanism is very simple.
  * si->st0 becomes non-zero once the transfer is finished. The handler
@@ -1154,9 +1192,16 @@
 		si->st0 = 1;
 
 	if (!si->st0) {
-		if (stats_dump_http(s, res, s->be->uri_auth)) {
-			si->st0 = 1;
-			si->shutw(si);
+		if (s->txn.meth == HTTP_METH_POST) {
+			if (stats_http_redir(s, res, s->be->uri_auth)) {
+				si->st0 = 1;
+				si->shutw(si);
+			}
+		} else {
+			if (stats_dump_http(s, res, s->be->uri_auth)) {
+				si->st0 = 1;
+				si->shutw(si);
+			}
 		}
 	}
 
@@ -1446,6 +1491,39 @@
 			     ""
 			     );
 
+			if (s->data_ctx.stats.st_code) {
+				if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_DONE) == 0) {
+					chunk_printf(&msg,
+						     "<p><div class=active3>"
+						     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+						     "Action processed successfully."
+						     "</div>\n", uri->uri_prefix);
+				}
+				else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_NONE) == 0) {
+					chunk_printf(&msg,
+						     "<p><div class=active2>"
+						     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+						     "Nothing has changed."
+						     "</div>\n", uri->uri_prefix);
+				}
+				else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_EXCD) == 0) {
+					chunk_printf(&msg,
+						     "<p><div class=active0>"
+						     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+						     "<b>Action not processed : the buffer couldn't store all the data.<br>"
+						     "You should retry with less servers at a time.</b>"
+						     "</div>\n", uri->uri_prefix);
+				}
+				else {
+					chunk_printf(&msg,
+						     "<p><div class=active6>"
+						     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+						     "Unexpected result."
+						     "</div>\n", uri->uri_prefix);
+				}
+				chunk_printf(&msg,"<p>\n");
+			}
+
 			if (buffer_feed_chunk(rep, &msg) >= 0)
 				return 0;
 		}
@@ -1546,6 +1624,13 @@
 
 	case DATA_ST_PX_TH:
 		if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
+			if (px->cap & PR_CAP_BE && px->srv) {
+				/* A form to enable/disable this proxy servers */
+				chunk_printf(&msg,
+					"<form action=\"%s\" method=\"post\">",
+					uri->uri_prefix);
+			}
+
 			/* print a new table */
 			chunk_printf(&msg,
 				     "<table class=\"tbl\" width=\"100%%\">\n"
@@ -1568,7 +1653,18 @@
 				     "</tr>\n"
 				     "</table>\n"
 				     "<table class=\"tbl\" width=\"100%%\">\n"
-				     "<tr class=\"titre\">"
+				     "<tr class=\"titre\">",
+				     (uri->flags & ST_SHLGNDS)?"<u>":"",
+				     px->id, px->id, px->id,
+				     (uri->flags & ST_SHLGNDS)?"</u>":"",
+				     px->desc ? "desc" : "empty", px->desc ? px->desc : "");
+
+			if (px->cap & PR_CAP_BE && px->srv) {
+				 /* Column heading for Enable or Disable server */
+				chunk_printf(&msg, "<th rowspan=2 width=1></th>");
+			}
+
+			chunk_printf(&msg,
 				     "<th rowspan=2></th>"
 				     "<th colspan=3>Queue</th>"
 				     "<th colspan=3>Session rate</th><th colspan=5>Sessions</th>"
@@ -1585,11 +1681,7 @@
 				     "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
 				     "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
 				     "<th>Thrtle</th>\n"
-				     "</tr>",
-				     (uri->flags & ST_SHLGNDS)?"<u>":"",
-				     px->id, px->id, px->id,
-				     (uri->flags & ST_SHLGNDS)?"</u>":"",
-				     px->desc ? "desc" : "empty", px->desc ? px->desc : "");
+				     "</tr>");
 
 			if (buffer_feed_chunk(rep, &msg) >= 0)
 				return 0;
@@ -1605,9 +1697,18 @@
 			if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
 				chunk_printf(&msg,
 				     /* name, queue */
-				     "<tr class=\"frontend\"><td class=ac>"
+				     "<tr class=\"frontend\">");
+
+				if (px->cap & PR_CAP_BE && px->srv) {
+					/* Column sub-heading for Enable or Disable server */
+					chunk_printf(&msg, "<td></td>");
+				}
+
+				chunk_printf(&msg,
+				     "<td class=ac>"
 				     "<a name=\"%s/Frontend\"></a>"
-				     "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td><td colspan=3></td>"
+				     "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
+				     "<td colspan=3></td>"
 				     "",
 				     px->id, px->id);
 
@@ -1765,7 +1866,12 @@
 			}
 
 			if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
-				chunk_printf(&msg, "<tr class=socket><td class=ac");
+				chunk_printf(&msg, "<tr class=socket>");
+				if (px->cap & PR_CAP_BE && px->srv) {
+					 /* Column sub-heading for Enable or Disable server */
+					chunk_printf(&msg, "<td></td>");
+				}
+				chunk_printf(&msg, "<td class=ac");
 
 					if (uri->flags&ST_SHLGNDS) {
 						char str[INET6_ADDRSTRLEN], *fmt = NULL;
@@ -1939,16 +2045,21 @@
 				if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) {
 					chunk_printf(&msg,
 					    /* name */
-					    "<tr class=\"maintain\"><td class=ac"
+					    "<tr class=\"maintain\">"
 					);
 				}
 				else {
 					chunk_printf(&msg,
 					    /* name */
-					    "<tr class=\"%s%d\"><td class=ac",
+					    "<tr class=\"%s%d\">",
 					    (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
 				}
 
+				chunk_printf(&msg,
+					     "<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>"
+					     "<td class=ac",
+					     sv->id);
+
 				if (uri->flags&ST_SHLGNDS) {
 					char str[INET6_ADDRSTRLEN];
 
@@ -2279,9 +2390,12 @@
 		if ((px->cap & PR_CAP_BE) &&
 		    (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
 			if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
-				chunk_printf(&msg,
-				     /* name */
-				     "<tr class=\"backend\"><td class=ac");
+				chunk_printf(&msg, "<tr class=\"backend\">");
+				if (px->cap & PR_CAP_BE && px->srv) {
+					/* Column sub-heading for Enable or Disable server */
+					chunk_printf(&msg, "<td></td>");
+				}
+				chunk_printf(&msg, "<td class=ac");
 
 				if (uri->flags&ST_SHLGNDS) {
 					/* balancing */
@@ -2305,6 +2419,7 @@
 				}
 
 				chunk_printf(&msg,
+				     /* name */
 				     ">%s<a name=\"%s/Backend\"></a>"
 				     "<a class=lfsb href=\"#%s/Backend\">Backend</a>%s</td>"
 				     /* queue : current, max */
@@ -2467,7 +2582,24 @@
 
 	case DATA_ST_PX_END:
 		if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
-			chunk_printf(&msg, "</table><p>\n");
+			chunk_printf(&msg, "</table>");
+
+			if (px->cap & PR_CAP_BE && px->srv) {
+				/* close the form used to enable/disable this proxy servers */
+				chunk_printf(&msg,
+					"Choose the action to perform on the checked servers : "
+					"<select name=action>"
+					"<option value=\"\"></option>"
+					"<option value=\"disable\">Disable</option>"
+					"<option value=\"enable\">Enable</option>"
+					"</select>"
+					"<input type=\"hidden\" name=\"b\" value=\"%s\">"
+					"&nbsp;<input type=\"submit\" value=\"Apply\">"
+					"</form>",
+					px->id);
+			}
+
+			chunk_printf(&msg, "<p>\n");
 
 			if (buffer_feed_chunk(rep, &msg) >= 0)
 				return 0;