* released 1.2.5-pre2
* implemented the HTTP 303 code for error redirection. This forces the
  browser to fetch the given URI with a GET request. The new keyword for
  this is 'errorloc303', and a new 'errorloc302' keyword has been created
  to make them easily distinguishable.
* added more controls in the parser for valid use of '\x' sequence.
* few fixes from Alex & Klaus
* fixed a few errors in the documentation
* do not pre-initialize unused file-descriptors before select() anymore.
diff --git a/CHANGELOG b/CHANGELOG
index df96dd6..f0af36f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,20 @@
 ChangeLog :
 ===========
 
+2005/04/24
+  - implemented the HTTP 303 code for error redirection. This forces the
+    browser to fetch the given URI with a GET request. The new keyword for
+    this is 'errorloc303', and a new 'errorloc302' keyword has been created
+    to make them easily distinguishable.
+  - added more controls in the parser for valid use of '\x' sequence.
+  - few fixes from Alex & Klaus
+
+2005/02/17
+  - fixed a few errors in the documentation
+
+2005/02/13
+  - do not pre-initialize unused file-descriptors before select() anymore.
+
 2005/01/22 : 1.2.4
   - merged Alexander Lazic's and Klaus Wagner's work on application
     cookie-based persistence. Since this is the first merge, this version is
diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt
index ab14dcf..65ebb96 100644
--- a/doc/haproxy-en.txt
+++ b/doc/haproxy-en.txt
@@ -2,9 +2,9 @@
                              H A - P r o x y
                             Reference  Manual
                            -------------------
-         		      version 1.2.3
+         		      version 1.2.5
 			      willy tarreau
-			        2005/01/22
+			        2005/04/24
 
 ============
 | Abstract |
@@ -1555,6 +1555,18 @@
         errorloc 503 http://192.168.114.58/error50x.html
         errorloc 504 http://192.168.114.58/error50x.html
 
+Note: RFC2616 says that a client must reuse the same method to fetch the
+Location returned by a 302, which causes problems with the POST method.
+The return code 303 was designed explicitly to force the client to fetch the
+Location URL with the GET method, but there are some browsers pre-dating
+HTTP/1.1 which don't support it. Anyway, most browsers still behave with 302 as
+if it was a 303. In order to allow the user to chose, version 1.2.5 brings two
+new keywords to replace 'errorloc' : 'errorloc302' and 'errorloc303'.
+
+They are preffered over errorloc (which still does 302). Consider using
+errorloc303 everytime you know that your clients support HTTP 303 responses..
+
+
 4.7) Modifying default values
 -----------------------------
 Version 1.1.22 introduced the notion of default values, which eliminates the
diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt
index 1db2bc4..109c45c 100644
--- a/doc/haproxy-fr.txt
+++ b/doc/haproxy-fr.txt
@@ -2,9 +2,9 @@
                              H A - P r o x y
                            Manuel de référence
                            -------------------
-         		      version 1.2.3
+         		      version 1.2.5
 			      willy tarreau
-			        2005/01/22
+			        2005/04/24
 
 ================
 | Introduction |
@@ -1578,6 +1578,21 @@
         errorloc 503 http://192.168.114.58/error50x.html
         errorloc 504 http://192.168.114.58/error50x.html
 
+Note: la RFC2616 stipule qu'un client doit réutiliser la même méthode pour
+accéder à l'URL de redirection que celle qui l'a retournée, ce qui pose des
+problèmes avec les requêtes POST. Le code de retour 303 a été créé exprès pour
+régler ce problème, indiquant au client qu'il doit accéder à l'URL retournée
+dans le champ Location avec la méthode GET uniquement. Seulement, certains
+navigateurs antérieurs à HTTP/1.1 ne connaissent pas ce code de retour. De
+plus, la plupart des navigateurs se comportent déjà avec le code 302 comme ils
+devraient le faire avec le 303. Donc, dans le but de laisser le choix à
+l'utilisateur, la version 1.2.5 apporte deux nouvelles commandes visant à
+remplacer 'errorloc' : 'errorloc302' et 'errorloc303'.
+
+Leur usage non ambigü est recommandé à la place de la commande 'errorloc' (qui
+utilise toujours 302). Dans le doute, préférez l'utilisation de 'errorloc303'
+dès que vous savez que vos clients supportent le code de retour HTTP 303.
+
 4.7) Changement des valeurs par défaut
 --------------------------------------
 Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui évite
diff --git a/haproxy.c b/haproxy.c
index 1445d1b..c5c2917 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -30,6 +30,7 @@
  *     and client suddenly disconnects. The server *should* switch to SHUT_WR, but
  *     still handle HTTP headers.
  *   - remove MAX_NEWHDR
+ *   - cut this huge file into several ones
  *
  */
 
@@ -63,8 +64,8 @@
 
 #include "include/appsession.h"
 
-#define HAPROXY_VERSION "1.2.4"
-#define HAPROXY_DATE	"2005/01/22"
+#define HAPROXY_VERSION "1.2.5"
+#define HAPROXY_DATE	"2005/04/24"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -680,6 +681,13 @@
 	"Connection: close\r\n"
 	"Location: "; /* not terminated since it will be concatenated with the URL */
 
+/* same as 302 except that the browser MUST retry with the GET method */
+const char *HTTP_303 =
+	"HTTP/1.0 303 See Other\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Location: "; /* not terminated since it will be concatenated with the URL */
+
 const char *HTTP_400 =
 	"HTTP/1.0 400 Bad request\r\n"
 	"Cache-Control: no-cache\r\n"
@@ -2695,7 +2703,8 @@
 		unsigned char hex1, hex2;
 		str++;
 
-		hex1=toupper(*str++) - '0'; hex2=toupper(*str++) - '0';
+		hex1 = toupper(*str++) - '0';
+		hex2 = toupper(*str++) - '0';
 
 		if (hex1 > 9) hex1 -= 'A' - '9' - 1;
 		if (hex2 > 9) hex2 -= 'A' - '9' - 1;
@@ -2711,6 +2720,43 @@
     return dst - old_dst;
 }
 
+static int ishex(char s)
+{
+    return (s >= '0' && s <= '9') || (s >= 'A' && s <= 'F') || (s >= 'a' && s <= 'f');
+}
+
+/* returns NULL if the replacement string <str> is valid, or the pointer to the first error */
+char *check_replace_string(char *str)
+{
+    char *err = NULL;
+    while (*str) {
+	if (*str == '\\') {
+	    err = str; /* in case of a backslash, we return the pointer to it */
+	    str++;
+	    if (!*str)
+		return err;
+	    else if (isdigit((int)*str))
+		err = NULL;
+	    else if (*str == 'x') {
+		str++;
+		if (!ishex(*str))
+		    return err;
+		str++;
+		if (!ishex(*str))
+		    return err;
+		err = NULL;
+	    }
+	    else {
+		Warning("'\\%c' : deprecated use of a backslash before something not '\\','x' or a digit.\n", *str);
+		err = NULL;
+	    }
+	}
+	str++;
+    }
+    return err;
+}
+
+
 
 /*
  * manages the client FSM and its socket. BTW, it also tries to handle the
@@ -4801,7 +4847,7 @@
       /* let's restore fdset state */
 
       readnotnull = 0; writenotnull = 0;
-      for (i = 0; i < (global.maxsock + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
+      for (i = 0; i < (maxfd + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
 	  readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
 	  writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
       }
@@ -5088,9 +5134,17 @@
     signal(sig, SIG_DFL);
 }
 
-void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
+/* returns the pointer to an error in the replacement string, or NULL if OK */
+char *chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
     struct hdr_exp *exp;
 
+    if (replace != NULL) {
+	char *err;
+	err = check_replace_string(replace);
+	if (err)
+	    return err;
+    }
+
     while (*head != NULL)
 	head = &(*head)->next;
 
@@ -5100,6 +5154,8 @@
     exp->replace = replace;
     exp->action = action;
     *head = exp;
+
+    return NULL;
 }
 
 
@@ -5261,6 +5317,7 @@
 int cfg_parse_listen(char *file, int linenum, char **args) {
     static struct proxy *curproxy = NULL;
     struct server *newsrv = NULL;
+    char *err;
     int rc;
 
     if (!strcmp(args[0], "listen")) {  /* new proxy */
@@ -5962,7 +6019,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+	err = chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
 	regex_t *preg;
@@ -6063,7 +6125,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+	err = chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "reqidel")) {  /* delete request header from a regex ignoring case */
 	regex_t *preg;
@@ -6178,7 +6245,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
 	regex_t *preg;
@@ -6198,7 +6270,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspdeny")) {  /* block response header from a regex */
 	regex_t *preg;
@@ -6218,7 +6295,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspirep")) {  /* replace response header from a regex ignoring case */
 	regex_t *preg;
@@ -6239,7 +6321,12 @@
 	    return -1;
 	}
 	    
-	chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspidel")) {  /* delete response header from a regex ignoring case */
 	regex_t *preg;
@@ -6259,7 +6346,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspideny")) {  /* block response header from a regex ignoring case */
 	regex_t *preg;
@@ -6279,7 +6371,12 @@
 	    return -1;
 	}
 	
-	chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
+	err = chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
+	if (err) {
+	    Alert("parsing [%s:%d] : invalid character or unterminated sequence in replacement string near '%c'.\n",
+		  file, linenum, *err);
+	    return -1;
+	}
     }
     else if (!strcmp(args[0], "rspadd")) {  /* add response header */
 	if (curproxy == &defproxy) {
@@ -6299,8 +6396,10 @@
 	
 	curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
     }
-    else if (!strcmp(args[0], "errorloc")) { /* error location */
-	int errnum;
+    else if (!strcmp(args[0], "errorloc") ||
+	     !strcmp(args[0], "errorloc302") ||
+	     !strcmp(args[0], "errorloc303")) { /* error location */
+	int errnum, errlen;
 	char *err;
 
 	// if (curproxy == &defproxy) {
@@ -6314,8 +6413,13 @@
 	}
 
 	errnum = atol(args[1]);
-	err = malloc(strlen(HTTP_302) + strlen(args[2]) + 5);
-	sprintf(err, "%s%s\r\n\r\n", HTTP_302, args[2]);
+	if (!strcmp(args[0], "errorloc303")) {
+	    err = malloc(strlen(HTTP_303) + strlen(args[2]) + 5);
+	    errlen = sprintf(err, "%s%s\r\n\r\n", HTTP_303, args[2]);
+	} else {
+	    err = malloc(strlen(HTTP_302) + strlen(args[2]) + 5);
+	    errlen = sprintf(err, "%s%s\r\n\r\n", HTTP_302, args[2]);
+	}
 
 	if (errnum == 400) {
 	    if (curproxy->errmsg.msg400) {
@@ -6323,7 +6427,7 @@
 		free(curproxy->errmsg.msg400);
 	    }
 	    curproxy->errmsg.msg400 = err;
-	    curproxy->errmsg.len400 = strlen(err);
+	    curproxy->errmsg.len400 = errlen;
 	}
 	else if (errnum == 403) {
 	    if (curproxy->errmsg.msg403) {
@@ -6331,7 +6435,7 @@
 		free(curproxy->errmsg.msg403);
 	    }
 	    curproxy->errmsg.msg403 = err;
-	    curproxy->errmsg.len403 = strlen(err);
+	    curproxy->errmsg.len403 = errlen;
 	}
 	else if (errnum == 408) {
 	    if (curproxy->errmsg.msg408) {
@@ -6339,7 +6443,7 @@
 		free(curproxy->errmsg.msg408);
 	    }
 	    curproxy->errmsg.msg408 = err;
-	    curproxy->errmsg.len408 = strlen(err);
+	    curproxy->errmsg.len408 = errlen;
 	}
 	else if (errnum == 500) {
 	    if (curproxy->errmsg.msg500) {
@@ -6347,7 +6451,7 @@
 		free(curproxy->errmsg.msg500);
 	    }
 	    curproxy->errmsg.msg500 = err;
-	    curproxy->errmsg.len500 = strlen(err);
+	    curproxy->errmsg.len500 = errlen;
 	}
 	else if (errnum == 502) {
 	    if (curproxy->errmsg.msg502) {
@@ -6355,7 +6459,7 @@
 		free(curproxy->errmsg.msg502);
 	    }
 	    curproxy->errmsg.msg502 = err;
-	    curproxy->errmsg.len502 = strlen(err);
+	    curproxy->errmsg.len502 = errlen;
 	}
 	else if (errnum == 503) {
 	    if (curproxy->errmsg.msg503) {
@@ -6363,7 +6467,7 @@
 		free(curproxy->errmsg.msg503);
 	    }
 	    curproxy->errmsg.msg503 = err;
-	    curproxy->errmsg.len503 = strlen(err);
+	    curproxy->errmsg.len503 = errlen;
 	}
 	else if (errnum == 504) {
 	    if (curproxy->errmsg.msg504) {
@@ -6371,7 +6475,7 @@
 		free(curproxy->errmsg.msg504);
 	    }
 	    curproxy->errmsg.msg504 = err;
-	    curproxy->errmsg.len504 = strlen(err);
+	    curproxy->errmsg.len504 = errlen;
 	}
 	else {
 	    Warning("parsing [%s:%d] : error %d relocation will be ignored.\n", file, linenum, errnum);
@@ -6443,14 +6547,21 @@
 		    *line = '\t';
 		    skip = 1;
 		}
-		else if (line[1] == 'x' && (line + 3 < end )) {
-		    unsigned char hex1, hex2;
-		    hex1 = toupper(line[2]) - '0'; hex2 = toupper(line[3]) - '0';
-		    if (hex1 > 9) hex1 -= 'A' - '9' - 1;
-		    if (hex2 > 9) hex2 -= 'A' - '9' - 1;
-		    *line = (hex1<<4) + hex2;
-		    skip = 3;
-		} 
+		else if (line[1] == 'x') {
+		    if ((line + 3 < end ) && ishex(line[2]) && ishex(line[3])) {
+			unsigned char hex1, hex2;
+			hex1 = toupper(line[2]) - '0';
+			hex2 = toupper(line[3]) - '0';
+			if (hex1 > 9) hex1 -= 'A' - '9' - 1;
+			if (hex2 > 9) hex2 -= 'A' - '9' - 1;
+			*line = (hex1<<4) + hex2;
+			skip = 3;
+		    }
+		    else {
+			Alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]);
+			return -1;
+		    }
+		}
 		if (skip) {
 		    memmove(line + 1, line + 1 + skip, end - (line + skip + 1));
 		    end -= skip;