* released 1.1.7
* added option forwardfor
* added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel
* added "log global" in "listen" section.
* added a new "global" section :
  - logs
  - debug, quiet, daemon modes
  - uid, gid, chroot, nbproc, maxconn
* added a TODO file
* updated example files
diff --git a/NOTES b/NOTES
index d462195..382018c 100644
--- a/NOTES
+++ b/NOTES
@@ -4,10 +4,12 @@
   * chain regex in a list
   * reply 502 when no server is available
 1.1.6 -> 1.1.7
-  - implement global logging
-  - have a single log function
+  * implement global logging
+  * have a single log function
+  * add x-forwarded-for
+  * log http requests on demand, and destination server name
+1.1.7 -> 1.1.8
+  - log destination server IP
   - handle parametrable HTTP health-checks replies
   - differentiate http headers and http uris
-  - log http requests on demand, and destination server IP
-  - add x-forwarded-for
-
+  - support keep-alive
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..41827a2
--- /dev/null
+++ b/TODO
@@ -0,0 +1,21 @@
+* x-forwarded-for
+* implémenter l'option "log global" au niveau proxy pour utiliser les logs
+  globaux.
+* matching case-insensitive
++ factoriser la fonction de log (send_log = send_syslog+warning+alert)
++ désactivation du keep-alive (suppression des ^Connection: et ajout des Connection: close)
+  -> 4 lignes (2 del, 2 add) suffisent.
+
+- loguer t_cnx, t_data, t_total
+- mesurer le tps consommé entre deux select, et fournir la conso CPU :
+  %cpu = 100 * (tpreselect(n+1)-tpostselect(n)) / (tpreselect(n+1)-tpreselect(n))
+- implémenter limitation fd dans la conf : setrlimit(RLIMIT_NOFILE, ...)
+- implémenter core/no-core dans la conf : setrlimit(RLIMIT_CORE, ...)
+- implémenter outgoing addr
+- optimiser les regex pour accélérer les matches :
+  - compter les matches
+  - si match(n) & ([n].cpt > [n-1].cpt) & ([n].action == [n-1].action), swap(n,n-1)
+  - régulièrement, diviser tous les compteurs (lors d'un dépassement par exemple)
+- filtrage sur l'adresse IP source
+- ne pas loguer certaines adresses IP sources
+- gestion keep-alive
diff --git a/examples/cfg b/examples/cfg
index 423ca2c..ab63dba 100644
--- a/examples/cfg
+++ b/examples/cfg
@@ -1,5 +1,20 @@
+global
+	log	127.0.0.1 local0
+#	log	127.0.0.1 local1
+	maxconn	4000
+	uid	0
+	gid	0
+#	chroot	/tmp
+#	nbproc	4
+#	daemon
+#	debug
+#	quiet
+
 listen proxy1 0.0.0.0:8000
 	mode	http
+#	log	127.0.0.1 local0
+#	log	127.0.0.1 local1
+	log global
 	#mode	tcp
         cookie SERVERID insert indirect
 	balance roundrobin
@@ -7,11 +22,13 @@
 	#dispatch 127.0.0.1:31300
 	#dispatch 127.0.0.1:80
 	#dispatch 127.0.0.1:22
-	#server tuxlocal 127.0.0.1:80 cookie cookie1 check
+	#server nc 127.0.0.1:8080 cookie cookie1 check
+	server tuxlocal 127.0.0.1:80 cookie cookie1 check
 	#server tuxceleron 10.101.0.1:80 cookie cookie2 check
 	#server telnet 127.0.0.1:23
 	#server ssh 127.0.0.1:22
-	server local 127.0.0.1:3130 cookie cookie3 check
+	#server local 127.0.0.1:3130 cookie cookie3 check
+	#server ko 127.0.0.1:0 cookie cookie3 check
 	#server local 127.0.0.1:8001 cookie cookie3 check
 	#server local 127.0.0.1:3130
 	#server celeron 10.101.0.1:80 cookie srv1
@@ -28,6 +45,15 @@
 	#rspdel	^Server.*
 	#rspadd Set-Cookie:\ mycookie=0;\ path=/
 	#rsprep ^(Date:\ )([^,]*)(,\ )(.*) LaDate\ est:\ \4\ (\2)
+	# force connection:close
+	#reqidel ^Connection:
+	#rspidel	^Connection:
+	#reqadd	Connection:\ close
+	#rspadd	Connection:\ close
+	# processing options
+	#option	keepalive
+	option	forwardfor
+	option httplog
 	
 listen proxy1 0.0.0.0:8001
 	mode	http
diff --git a/haproxy.c b/haproxy.c
index bc17e3e..0d3d9e8 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -13,6 +13,15 @@
  *
  * ChangeLog :
  *
+ * 2002/04/12 : 1.1.7
+ *   - added option forwardfor
+ *   - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel
+ *   - added "log global" in "listen" section.
+ * 2002/04/09 :
+ *   - added a new "global" section :
+ *     - logs
+ *     - debug, quiet, daemon modes
+ *     - uid, gid, chroot, nbproc, maxconn
  * 2002/04/08 : 1.1.6
  *   - regex are now chained and not limited anymore.
  *   - unavailable server now returns HTTP/502.
@@ -108,8 +117,8 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.1.6pre4"
-#define HAPROXY_DATE	"2002/04/07"
+#define HAPROXY_VERSION "1.1.7"
+#define HAPROXY_DATE	"2002/04/12"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -128,6 +137,7 @@
 
 // reserved buffer space for header rewriting
 #define	MAXREWRITE	4096
+#define REQURI_LEN	1024
 
 // max # args on a configuration line
 #define MAX_LINE_ARGS	40
@@ -143,12 +153,14 @@
 #define SERVERID_LEN	16
 #define CONN_RETRIES	3
 
-/* FIXME: this should be user-configurable */
 #define	CHK_CONNTIME	2000
 #define	DEF_CHKINTR	2000
 #define DEF_FALLTIME	3
 #define DEF_RISETIME	2
 
+/* default connections limit */
+#define DEFAULT_MAXCONN	2000
+
 /* how many bits are needed to code the size of an int (eg: 32bits -> 5) */
 #define	INTBITS		5
 
@@ -236,8 +248,7 @@
 #define sizeof_session	sizeof(struct session)
 #define sizeof_buffer	sizeof(struct buffer)
 #define sizeof_fdtab	sizeof(struct fdtab)
-#define sizeof_str256	256
-
+#define sizeof_requri	REQURI_LEN
 
 /* different possible states for the sockets */
 #define FD_STCLOSE	0
@@ -270,6 +281,10 @@
 #define PR_O_COOK_ANY	(PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS)
 #define PR_O_BALANCE_RR	32	/* balance in round-robin mode */
 #define PR_O_BALANCE	(PR_O_BALANCE_RR)
+#define	PR_O_KEEPALIVE	64	/* follow keep-alive sessions */
+#define	PR_O_FWDFOR	128	/* insert x-forwarded-for with client address */
+#define PR_O_LOGHTTP	256	/* generate a full HTTP log */
+
 
 /* various session flags */
 #define SN_DIRECT	1	/* connection made on the server matching the client cookie */
@@ -300,7 +315,7 @@
 #define	RES_NULL	2	/* result is 0 (read == 0), or connect without need for writing */
 #define RES_ERROR	3	/* result -1 or error on the socket (eg: connect()) */
 
-/* modes of operation (global variable "mode") */
+/* modes of operation (global.mode) */
 #define	MODE_DEBUG	1
 #define	MODE_STATS	2
 #define	MODE_LOG	4
@@ -316,6 +331,21 @@
 #define ACT_REMOVE	2	/* remove the matching header */
 #define ACT_DENY	3	/* deny the request */
 
+/* configuration sections */
+#define CFG_NONE	0
+#define CFG_GLOBAL	1
+#define CFG_LISTEN	2
+
+/* fields that need to be logged. They appear as flags in session->logwait */
+#define LW_DATE		1	/* date */
+#define LW_CLIP		2	/* CLient IP */
+#define LW_SVIP		4	/* SerVer IP */
+#define LW_SVID		8	/* server ID */
+#define	LW_REQ		16	/* http REQuest */
+#define LW_RESP		32	/* http RESPonse */
+#define LW_PXIP		64	/* proxy IP */
+#define LW_PXID		128	/* proxy ID */
+
 /*********************************************************************/
 
 #define LIST_HEAD(a)	((void *)(&(a)))
@@ -379,11 +409,13 @@
     int srv_state;			/* state of the server side */
     int conn_retries;			/* number of connect retries left */
     int flags;				/* some flags describing the session */
+    int logwait;			/* log things waiting to be collected : LW_* */
     struct buffer *req;			/* request buffer */
     struct buffer *rep;			/* response buffer */
     struct sockaddr_in cli_addr;	/* the client address */
     struct sockaddr_in srv_addr;	/* the address to connect to */
     struct server *srv;			/* the server being used */
+    char *requri;			/* first line if log needed, NULL otherwise */
 };
 
 struct proxy {
@@ -406,6 +438,7 @@
     struct proxy *next;
     struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
     char logfac1, logfac2;		/* log facility for both servers. -1 = disabled */
+    int to_log;				/* things to be logged (LW_*) */
     struct timeval stop_time;		/* date to stop listening, when stopping != 0 */
     int nb_reqadd, nb_rspadd;
     struct hdr_exp *req_exp;		/* regular expressions for request headers */
@@ -424,12 +457,28 @@
 
 /*********************************************************************/
 
-int cfg_maxconn = 2000;		/* # of simultaneous connections, (-n) */
 int cfg_maxpconn = 2000;	/* # of simultaneous connections per proxy (-N) */
-int cfg_maxsock = 0;		/* max # of sockets */
 char *cfg_cfgfile = NULL;	/* configuration file */
 char *progname = NULL;		/* program name */
 int  pid;			/* current process id */
+
+/* global options */
+static struct {
+    int uid;
+    int gid;
+    int nbproc;
+    int maxconn;
+    int maxsock;		/* max # of sockets */
+    int mode;
+    char *chroot;
+    int logfac1, logfac2;
+    struct sockaddr_in logsrv1, logsrv2;
+} global = {
+    logfac1 : -1,
+    logfac2 : -1,
+    /* others NULL OK */
+};
+
 /*********************************************************************/
 
 fd_set	*ReadEvent,
@@ -440,7 +489,7 @@
 void **pool_session = NULL,
     **pool_buffer   = NULL,
     **pool_fdtab    = NULL,
-    **pool_str256   = NULL,
+    **pool_requri   = NULL,
     **pool_task	    = NULL;
 
 struct proxy *proxy  = NULL;	/* list of all existing proxies */
@@ -451,7 +500,6 @@
     next:LIST_HEAD(wait_queue)
 };
 
-static int mode = 0;		/* MODE_DEBUG, ... */
 static int totalconn = 0;	/* total # of terminated sessions */
 static int actconn = 0;		/* # of active sessions */
 static int maxfd = 0;		/* # of the highest fd + 1 */
@@ -562,7 +610,7 @@
 	    "        -q quiet mode : don't display messages\n"
 	    "        -n sets the maximum total # of connections (%d)\n"
 	    "        -N sets the default, per-proxy maximum # of connections (%d)\n\n",
-	    name, cfg_maxconn, cfg_maxpconn);
+	    name, DEFAULT_MAXCONN, cfg_maxpconn);
     exit(1);
 }
 
@@ -575,7 +623,7 @@
     struct timeval tv;
     struct tm *tm;
 
-    if (!(mode & MODE_QUIET)) {
+    if (!(global.mode & MODE_QUIET)) {
 	va_start(argp, fmt);
 
 	gettimeofday(&tv, NULL);
@@ -597,7 +645,7 @@
     struct timeval tv;
     struct tm *tm;
 
-    if (!(mode & MODE_QUIET)) {
+    if (!(global.mode & MODE_QUIET)) {
 	va_start(argp, fmt);
 
 	gettimeofday(&tv, NULL);
@@ -616,7 +664,7 @@
 void qfprintf(FILE *out, char *fmt, ...) {
     va_list argp;
 
-    if (!(mode & MODE_QUIET)) {
+    if (!(global.mode & MODE_QUIET)) {
 	va_start(argp, fmt);
 	vfprintf(out, fmt, argp);
 	fflush(out);
@@ -708,6 +756,9 @@
     /* 20011216/WT : other progs don't set the hostname, and syslogd
      * systematically repeats it which is contrary to RFC3164.
      */
+    /*
+     * warning: buffer overflow possible on progname.
+     */
     p += sprintf(p, "<%d>%s %2d %02d:%02d:%02d %s[%d]: ",
 		 facility * 8 + level,
 		 monthname[tm->tm_mon],
@@ -728,6 +779,107 @@
     sendto(logfd, logmsg, p - logmsg, MSG_DONTWAIT | MSG_NOSIGNAL,
 	   (struct sockaddr *)sa, sizeof(*sa));
 #endif
+}
+
+
+/*
+ * This function sends a syslog message to both log servers of a proxy,
+ * or to global log servers if the proxy is NULL.
+ * It also tries not to waste too much time computing the message header.
+ * It doesn't care about errors nor does it report them.
+ * WARNING! no check is made on the prog+hostname+date length, so the
+ * local hostname + the prog name must be shorter than MAX_SYSLOG_LEN-19.
+ * the message will be truncated to fit the maximum length.
+ */
+void send_log(struct proxy *p, int level, char *message, ...) {
+    static int logfd = -1;	/* syslog UDP socket */
+    static long tvsec = -1;	/* to force the string to be initialized */
+    struct timeval tv;
+    va_list argp;
+    static char logmsg[MAX_SYSLOG_LEN];
+    static char *dataptr = NULL;
+    int fac_level;
+    int hdr_len, data_len;
+    struct sockaddr_in *sa[2];
+    int facilities[2];
+    int nbloggers = 0;
+    char *log_ptr;
+
+    if (logfd < 0) {
+	if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+	    return;
+    }
+    
+    if (level < 0 || progname == NULL || message == NULL)
+	return;
+
+    gettimeofday(&tv, NULL);
+    if (tv.tv_sec != tvsec) {
+	/* this string is rebuild only once a second */
+	struct tm *tm = localtime(&tv.tv_sec);
+	tvsec = tv.tv_sec;
+
+	/*
+	 * warning: buffer overflow possible on progname.
+	 */
+	dataptr = logmsg + snprintf(logmsg, sizeof(logmsg),
+				   "<<<<>%s %2d %02d:%02d:%02d %s[%d]: ",
+				   monthname[tm->tm_mon],
+				   tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
+				   progname, pid);
+    }
+
+    va_start(argp, message);
+    data_len = vsnprintf(dataptr, logmsg + sizeof(logmsg) - dataptr, message, argp);
+    dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
+    va_end(argp);
+
+    if (p == NULL) {
+	if (global.logfac1 >= 0) {
+	    sa[nbloggers] = &global.logsrv1;
+	    facilities[nbloggers] = global.logfac1;
+	    nbloggers++;
+	}
+	if (global.logfac2 >= 0) {
+	    sa[nbloggers] = &global.logsrv2;
+	    facilities[nbloggers] = global.logfac2;
+	    nbloggers++;
+	}
+    } else {
+	if (p->logfac1 >= 0) {
+	    sa[nbloggers] = &p->logsrv1;
+	    facilities[nbloggers] = p->logfac1;
+	    nbloggers++;
+	}
+	if (p->logfac2 >= 0) {
+	    sa[nbloggers] = &p->logsrv2;
+	    facilities[nbloggers] = p->logfac2;
+	    nbloggers++;
+	}
+    }
+
+    while (nbloggers-- > 0) {
+	/* do this for each log target */
+	fac_level = (facilities[nbloggers] << 3) + level;
+	log_ptr = logmsg + 3; /* last digit of the log level */
+	do {
+	    *log_ptr = '0' + fac_level % 10;
+	    fac_level /= 10;
+	    log_ptr--;
+	} while (fac_level && log_ptr > logmsg);
+	*log_ptr = '<';
+	hdr_len = dataptr - log_ptr;
+	
+	/* the total syslog message now starts at p, for hdr_len+data_len */
+
+#ifndef MSG_NOSIGNAL
+	sendto(logfd, log_ptr, hdr_len + data_len, MSG_DONTWAIT,
+	       (struct sockaddr *)sa[nbloggers], sizeof(**sa));
+#else
+	sendto(logfd, log_ptr, hdr_len + data_len, MSG_DONTWAIT | MSG_NOSIGNAL,
+	       (struct sockaddr *)sa[nbloggers], sizeof(**sa));
+#endif
+    }
 }
 
 
@@ -1093,6 +1245,9 @@
 	pool_free(buffer, s->req);
     if (s->rep)
 	pool_free(buffer, s->rep);
+    if (s->requri)
+	pool_free(requri, s->requri);
+
     pool_free(session, s);
 }
 
@@ -1151,7 +1306,7 @@
 	return -1;
     }
 	
-    if (fd >= cfg_maxsock) {
+    if (fd >= global.maxsock) {
 	Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
 	close(fd);
 	return -1;
@@ -1574,6 +1729,40 @@
     s->req->l = 0;
 }
 
+/*
+ * send a log for the session when we have enough info about it
+ */
+void sess_log(struct session *s) {
+    unsigned char *pn;
+    struct proxy *p = s->proxy;
+    int log;
+    char *uri;
+    char *pxid;
+    char *srv;
+
+    /* This is a first attempt at a better logging system.
+     * For now, we rely on send_log() to provide the date, although it obviously
+     * is the date of the log and not of the request, and most fields are not
+     * computed.
+     */
+
+    log = p->to_log & ~s->logwait;
+
+    pn = (log & LW_CLIP) ?
+	 (unsigned char *)&s->cli_addr.sin_addr :
+	 (unsigned char *)"\0\0\0\0";
+
+    uri = (log & LW_REQ) ? s->requri : "<requri>";
+    pxid = p->id;
+    //srv = (log & LW_SVID) ? s->srv->id : "<svid>";
+    srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<svid>";
+	
+    send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s \"%s\"\n",
+	     pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
+	     pxid, srv, uri);
+    s->logwait = 0;
+}
+
 
 /*
  * this function is called on a read event from a listen socket, corresponding
@@ -1611,7 +1800,7 @@
 	}
 
 	s->cli_addr = addr;
-	if (cfd >= cfg_maxsock) {
+	if (cfd >= global.maxsock) {
 	    Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
 	    close(cfd);
 	    pool_free(task, t);
@@ -1629,6 +1818,25 @@
 	    return 0;
 	}
 
+	t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
+	t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
+	t->state = TASK_IDLE;
+	t->process = process_session;
+	t->context = s;
+
+	s->task = t;
+	s->proxy = p;
+	s->cli_state = (p->mode == PR_MODE_HTTP) ?  CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
+	s->srv_state = SV_STIDLE;
+	s->req = s->rep = NULL; /* will be allocated later */
+	s->flags = 0;
+	s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
+	s->cli_fd = cfd;
+	s->srv_fd = -1;
+	s->conn_retries = p->conn_retries;
+	s->requri = NULL;
+	s->logwait = p->to_log;
+
 	if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
 	    && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
 	    struct sockaddr_in sockname;
@@ -1641,40 +1849,25 @@
 	    sn = (unsigned char *)&sockname.sin_addr;
 	    pn = (unsigned char *)&s->cli_addr.sin_addr;
 
-	    sprintf(trash, "Connect from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (%s/%s)\n",
-		    pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
-		    sn[0], sn[1], sn[2], sn[3], ntohs(sockname.sin_port),
-		    p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
-
-	    if (p->logfac1 >= 0)
-		send_syslog(&p->logsrv1, p->logfac1, LOG_INFO, trash);
-	    if (p->logfac2 >= 0)
-		send_syslog(&p->logsrv2, p->logfac2, LOG_INFO, trash);
+	    if (p->to_log) {
+		/* we have the client ip */
+		if (s->logwait & LW_CLIP)
+		    if (!(s->logwait &= ~LW_CLIP))
+			sess_log(s);
+	    }
+	    else
+		send_log(p, LOG_INFO, "Connect from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (%s/%s)\n",
+			 pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
+			 sn[0], sn[1], sn[2], sn[3], ntohs(sockname.sin_port),
+			 p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
 	}
 
-	if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+	if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 	    int len;
 	    len = sprintf(trash, "accept(%04x)=%04x\n", (unsigned short)fd, (unsigned short)cfd);
 	    write(1, trash, len);
 	}
 
-	t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
-	t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
-	t->state = TASK_IDLE;
-	t->process = process_session;
-	t->context = s;
-
-	s->task = t;
-	s->proxy = p;
-	s->cli_state = (p->mode == PR_MODE_HTTP) ?  CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
-	s->srv_state = SV_STIDLE;
-	s->req = s->rep = NULL; /* will be allocated later */
-	s->flags = 0;
-	s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
-	s->cli_fd = cfd;
-	s->srv_fd = -1;
-	s->conn_retries = p->conn_retries;
-
 	if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
 	    close(cfd); /* nothing can be done for this fd without memory */
 	    pool_free(task, t);
@@ -1859,6 +2052,7 @@
     return dst - old_dst;
 }
 
+
 /*
  * manages the client FSM and its socket. BTW, it also tries to handle the
  * cookie. It returns 1 if a state has changed (and a resync may be needed),
@@ -1903,6 +2097,15 @@
 		    buffer_replace2(req, req->h, req->h, newhdr, len);
 		}
 
+		if (t->proxy->options & PR_O_FWDFOR) {
+		    /* insert an X-Forwarded-For header */
+		    unsigned char *pn;
+		    pn = (unsigned char *)&t->cli_addr.sin_addr;
+		    len = sprintf(newhdr, "X-Forwarded-For: %d.%d.%d.%d\r\n",
+				  pn[0], pn[1], pn[2], pn[3]);
+		    buffer_replace2(req, req->h, req->h, newhdr, len);
+		}
+
 		t->cli_state = CL_STDATA;
 		req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
 
@@ -1938,9 +2141,31 @@
 	     *   req->r  = end of data (not used at this stage)
 	     */
 
+	    if (t->logwait & LW_REQ &&
+		t->proxy->mode & PR_MODE_HTTP &&
+		t->proxy->options & PR_O_LOGHTTP) {
+		/* we have a complete HTTP request that we must log */
+		int urilen;
+
+		if ((t->requri = pool_alloc(requri)) == NULL) {
+		    Alert("HTTP logging : out of memory.\n");
+		    client_retnclose(t, strlen(HTTP_403), HTTP_403);
+		    return 1;
+		}
+		
+		urilen = ptr - req->h;
+		if (urilen >= REQURI_LEN)
+		    urilen = REQURI_LEN - 1;
+		memcpy(t->requri, req->h, urilen);
+		t->requri[urilen] = 0;
+
+		if (!(t->logwait &= ~LW_REQ))
+		    sess_log(t);
+	    }
+
 	    delete_header = 0;
 
-	    if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+	    if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 		int len, max;
 		len = sprintf(trash, "clihdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
 		max = ptr - req->h;
@@ -2225,7 +2450,7 @@
 	return 0;
     }
     else { /* CL_STCLOSE: nothing to do */
-	if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+	if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 	    int len;
 	    len = sprintf(trash, "clicls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
 	    write(1, trash, len);
@@ -2407,7 +2632,7 @@
 
 	    delete_header = 0;
 
-	    if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+	    if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 		int len, max;
 		len = sprintf(trash, "srvhdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
 		max = ptr - rep->h;
@@ -2716,7 +2941,7 @@
 	return 0;
     }
     else { /* SV_STCLOSE : nothing to do */
-	if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+	if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 	    int len;
 	    len = sprintf(trash, "srvcls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
 	    write(1, trash, len);
@@ -2763,12 +2988,16 @@
     s->proxy->nbconn--;
     actconn--;
     
-    if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+    if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
 	int len;
 	len = sprintf(trash, "closed[%04x:%04x]\n", (unsigned short)s->cli_fd, (unsigned short)s->srv_fd);
 	write(1, trash, len);
     }
 
+    /* let's do a final log if we need it */
+    if (s->logwait)
+	sess_log(s);
+
     /* the task MUST not be in the run queue anymore */
     task_delete(t);
     session_free(s);
@@ -2799,7 +3028,7 @@
 	/* we'll initiate a new check */
 	s->result = 0; /* no result yet */
 	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
-	    if ((fd < cfg_maxsock) &&
+	    if ((fd < global.maxsock) &&
 		(fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
 		(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
 		//fprintf(stderr, "process_chk: 3\n");
@@ -2841,16 +3070,17 @@
 	    s->health--; /* still good */
 	else {
 	    if (s->health == s->rise) {
-		if (!(mode & MODE_QUIET))
+		if (!(global.mode & MODE_QUIET))
 		    Warning("server %s DOWN.\n", s->id);
 
-		sprintf(trash, "Server %s/%s is DOWN.\n",
-			s->proxy->id, s->id);
-		
-		if (s->proxy->logfac1 >= 0)
-		    send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
-		if (s->proxy->logfac2 >= 0)
-		    send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
+//		  sprintf(trash, "Server %s/%s is DOWN.\n",
+//			  s->proxy->id, s->id);
+//		  
+//		  if (s->proxy->logfac1 >= 0)
+//		      send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
+//		  if (s->proxy->logfac2 >= 0)
+//		      send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
+		send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
 	    }
 
 	    s->health = 0; /* failure */
@@ -2869,14 +3099,15 @@
 	    s->health++; /* was bad, stays for a while */
 	    if (s->health >= s->rise) {
 		if (s->health == s->rise) {
-		    if (!(mode & MODE_QUIET))
+		    if (!(global.mode & MODE_QUIET))
 			Warning("server %s UP.\n", s->id);
-		    sprintf(trash, "Server %s/%s is UP.\n", s->proxy->id, s->id);
+//		        sprintf(trash, "Server %s/%s is UP.\n", s->proxy->id, s->id);
 		    
-		    if (s->proxy->logfac1 >= 0)
-			send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_NOTICE, trash);
-		    if (s->proxy->logfac2 >= 0)
-			send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_NOTICE, trash);
+//		      if (s->proxy->logfac1 >= 0)
+//			  send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_NOTICE, trash);
+//		      if (s->proxy->logfac2 >= 0)
+//			  send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_NOTICE, trash);
+		    send_log(s->proxy, LOG_NOTICE, "Server %s/%s is UP.\n", s->proxy->id, s->id);
 		}
 
 		s->health = s->rise + s->fall - 1; /* OK now */
@@ -2894,15 +3125,17 @@
 		s->health--; /* still good */
 	    else {
 		if (s->health == s->rise) {
-		    if (!(mode & MODE_QUIET))
+		    if (!(global.mode & MODE_QUIET))
 			Warning("server %s DOWN.\n", s->id);
-		    sprintf(trash, "Server %s/%s is DOWN.\n",
-			    s->proxy->id, s->id);
-		
-		    if (s->proxy->logfac1 >= 0)
-			send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
-		    if (s->proxy->logfac2 >= 0)
-			send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
+//		      sprintf(trash, "Server %s/%s is DOWN.\n",
+//			      s->proxy->id, s->id);
+//		  
+//		      if (s->proxy->logfac1 >= 0)
+//			  send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
+//		      if (s->proxy->logfac2 >= 0)
+//			  send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
+
+		    send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
 		}
 
 		s->health = 0; /* failure */
@@ -3017,7 +3250,7 @@
       /* let's restore fdset state */
 
       readnotnull = 0; writenotnull = 0;
-      for (i = 0; i < (cfg_maxsock + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
+      for (i = 0; i < (global.maxsock + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
 	  readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
 	  writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
       }
@@ -3089,8 +3322,8 @@
 	deltatime = (tv_delta(&now, &lastevt)?:1);
 	totaltime = (tv_delta(&now, &starttime)?:1);
 	
-	if (mode & MODE_STATS) {	
-		if ((lines++ % 16 == 0) && !(mode & MODE_LOG))
+	if (global.mode & MODE_STATS) {	
+		if ((lines++ % 16 == 0) && !(global.mode & MODE_LOG))
 		    qfprintf(stderr,
 			    "\n active   total  tsknew tskgood tskleft tskrght tsknsch tsklsch tskrsch\n");
 		if (lines>1) {
@@ -3126,7 +3359,7 @@
     tleft = -1; /* infinite time */
 
     /* if there are enough free sessions, we'll activate proxies */
-    if (actconn < cfg_maxconn) {
+    if (actconn < global.maxconn) {
 	while (p) {
 	    if (p->nbconn < p->maxconn) {
 		if (p->state == PR_STIDLE) {
@@ -3161,12 +3394,13 @@
 		t = tv_remain(&now, &p->stop_time);
 		if (t == 0) {
 		    Warning("Proxy %s stopped.\n", p->id);
-		    sprintf(trash, "Proxy %s stopped.\n", p->id);
+//		    sprintf(trash, "Proxy %s stopped.\n", p->id);
 		
-		    if (p->logfac1 >= 0)
-			send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
-		    if (p->logfac2 >= 0)
-			send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
+//		      if (p->logfac1 >= 0)
+//			  send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
+//		      if (p->logfac2 >= 0)
+//			  send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
+		    send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
 
 		    fd_delete(p->listen_fd);
 		    p->state = PR_STDISABLED;
@@ -3195,12 +3429,14 @@
     while (p) {
 	if (p->state != PR_STDISABLED) {
 	    Warning("Stopping proxy %s in %d ms.\n", p->id, p->grace);
-	    sprintf(trash, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
+//	    sprintf(trash, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
 	    
-	    if (p->logfac1 >= 0)
-		send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
-	    if (p->logfac2 >= 0)
-		send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
+//	      if (p->logfac1 >= 0)
+//		  send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
+//	      if (p->logfac2 >= 0)
+//		  send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
+
+	    send_log(p, LOG_WARNING, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
 	    tv_delayfrom(&p->stop_time, &now, p->grace);
 	}
 	p = p->next;
@@ -3253,373 +3489,432 @@
     *head = exp;
 }
 
+
 /*
- * This function reads and parses the configuration file given in the argument.
- * returns 0 if OK, -1 if error.
+ * parse a line in a <global> section. Returns 0 if OK, -1 if error.
  */
-int readcfgfile(char *file) {
-    char thisline[256];
-    char *line;
-    FILE *f;
-    int linenum = 0;
-    char *end;
-    char *args[MAX_LINE_ARGS];
-    int arg;
-    int cfgerr = 0;
-
-    struct proxy *curproxy = NULL;
-    struct server *newsrv = NULL;
+int cfg_parse_global(char *file, int linenum, char **args) {
 
-    if ((f=fopen(file,"r")) == NULL)
-	return -1;
-
-    while (fgets(line = thisline, sizeof(thisline), f) != NULL) {
-	linenum++;
-
-	end = line + strlen(line);
-
-	/* skip leading spaces */
-	while (isspace(*line))
-	    line++;
-	
-	arg = 0;
-	args[arg] = line;
-
-	while (*line && arg < MAX_LINE_ARGS) {
-	    /* first, we'll replace \\, \<space>, \#, \r, \n, \t, \xXX with their
-	     * C equivalent value. Other combinations left unchanged (eg: \1).
-	     */
-	    if (*line == '\\') {
-		int skip = 0;
-		if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') {
-		    *line = line[1];
-		    skip = 1;
-		}
-		else if (line[1] == 'r') {
-		    *line = '\r';
-		    skip = 1;
-		} 
-		else if (line[1] == 'n') {
-		    *line = '\n';
-		    skip = 1;
-		}
-		else if (line[1] == 't') {
-		    *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;
-		} 
-		if (skip) {
-		    memmove(line + 1, line + 1 + skip, end - (line + skip + 1));
-		    end -= skip;
-		}
-		line++;
-	    }
-	    else {
-		if (*line == '#' || *line == '\n' || *line == '\r')
-		    *line = 0; /* end of string, end of loop */
-		else
-		    line++;
-		
-		/* a non-escaped space is an argument separator */
-		if (isspace(*line)) {
-		    *line++ = 0;
-		    while (isspace(*line))
-			line++;
-		    args[++arg] = line;
-		}
-	    }
+    if (!strcmp(args[0], "global")) {  /* new section */
+	/* no option, nothing special to do */
+	return 0;
+    }
+    else if (!strcmp(args[0], "daemon")) {
+	global.mode |= MODE_DAEMON;
+    }
+    else if (!strcmp(args[0], "debug")) {
+	global.mode |= MODE_DEBUG;
+    }
+    else if (!strcmp(args[0], "quiet")) {
+	global.mode |= MODE_QUIET;
+    }
+    else if (!strcmp(args[0], "stats")) {
+	global.mode |= MODE_STATS;
+    }
+    else if (!strcmp(args[0], "uid")) {
+	if (global.uid != 0) {
+	    Alert("parsing [%s:%d] : <uid> already specified. Continuing.\n", file, linenum);
+	    return 0;
 	}
-
-	/* empty line */
-	if (!**args)
-	    continue;
-
-	/* zero out remaining args */
-	while (++arg < MAX_LINE_ARGS) {
-	    args[arg] = line;
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <uid> expects an integer argument.\n", file, linenum);
+	    return -1;
 	}
-
-	if (!strcmp(args[0], "listen")) {  /* new proxy */
-	    if (strchr(args[2], ':') == NULL) {
-		Alert("parsing [%s:%d] : <listen> expects <id> and <addr:port> as arguments.\n",
-		      file, linenum);
-		return -1;
-	    }
-
-	    if ((curproxy = (struct proxy *)calloc(1, sizeof(struct proxy)))
-		== NULL) {
-		Alert("parsing [%s:%d] : out of memory\n", file, linenum);
-		exit(1);
-	    }
-	    curproxy->next = proxy;
-	    proxy = curproxy;
-	    curproxy->id = strdup(args[1]);
-	    curproxy->listen_addr = *str2sa(args[2]);
-	    curproxy->state = PR_STNEW;
-	    /* set default values */
-	    curproxy->maxconn = cfg_maxpconn;
-	    curproxy->conn_retries = CONN_RETRIES;
-	    curproxy->options = 0;
-	    curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
-	    curproxy->mode = PR_MODE_TCP;
-	    curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
-	    continue;
+	global.uid = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "gid")) {
+	if (global.gid != 0) {
+	    Alert("parsing [%s:%d] : <gid> already specified. Continuing.\n", file, linenum);
+	    return 0;
 	}
-	else if (curproxy == NULL) {
-	    Alert("parsing [%s:%d] : <listen> expected.\n",
-		  file, linenum);
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <gid> expects an integer argument.\n", file, linenum);
 	    return -1;
 	}
-    
-	if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
-	    if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
-	    else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
-	    else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
-	    else {
-		Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]);
-		return -1;
-	    }
+	global.gid = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "nbproc")) {
+	if (global.nbproc != 0) {
+	    Alert("parsing [%s:%d] : <nbproc> already specified. Continuing.\n", file, linenum);
+	    return 0;
 	}
-	else if (!strcmp(args[0], "disabled")) {  /* disables this proxy */
-	    curproxy->state = PR_STDISABLED;
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <gid> expects an integer argument.\n", file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "cookie")) {  /* cookie name */
-	    int cur_arg;
-	    if (curproxy->cookie_name != NULL) {
-		Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
-		      file, linenum);
-		continue;
-	    }
-
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <cookie> expects <cookie_name> as argument.\n",
-		      file, linenum);
-		return -1;
-	    }
-	    curproxy->cookie_name = strdup(args[1]);
-
-	    cur_arg = 2;
-	    while (*(args[cur_arg])) {
-		if (!strcmp(args[cur_arg], "rewrite")) {
-		    curproxy->options |= PR_O_COOK_RW;
-		}
-		else if (!strcmp(args[cur_arg], "indirect")) {
-		    curproxy->options |= PR_O_COOK_IND;
-		}
-		else if (!strcmp(args[cur_arg], "insert")) {
-		    curproxy->options |= PR_O_COOK_INS;
-		}
-		else {
-		    Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
-			  file, linenum);
-		    return -1;
-		}
-		cur_arg++;
-	    }
-	    if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) {
-		Alert("parsing [%s:%d] : <cookie> 'rewrite' and 'indirect' mode are incompatibles.\n",
-		      file, linenum);
-		return -1;
-	    }
+	global.nbproc = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "maxconn")) {
+	if (global.maxconn != 0) {
+	    Alert("parsing [%s:%d] : <maxconn> already specified. Continuing.\n", file, linenum);
+	    return 0;
 	}
-	else if (!strcmp(args[0], "contimeout")) {  /* connect timeout */
-	    if (curproxy->contimeout != 0) {
-		Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n",
-		      file, linenum);
-		continue;
-	    }
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <contimeout> expects an integer <time_in_ms> as argument.\n",
-		      file, linenum);
-		return -1;
-	    }
-	    curproxy->contimeout = atol(args[1]);
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n", file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "clitimeout")) {  /*  client timeout */
-	    if (curproxy->clitimeout != 0) {
-		Alert("parsing [%s:%d] : clitimeout already specified. Continuing.\n",
-		      file, linenum);
-		continue;
+	global.maxconn = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "chroot")) {
+	if (global.chroot != NULL) {
+	    Alert("parsing [%s:%d] : <chroot> already specified. Continuing.\n", file, linenum);
+	    return 0;
+	}
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <chroot> expects a directory as an argument.\n", file, linenum);
+	    return -1;
+	}
+	global.chroot = strdup(args[1]);
+    }
+    else if (!strcmp(args[0], "log")) {  /* syslog server address */
+	struct sockaddr_in *sa;
+	int facility;
+	
+	if (*(args[1]) == 0 || *(args[2]) == 0) {
+	    Alert("parsing [%s:%d] : <log> expects <address> and <facility> as arguments.\n", file, linenum);
+	    return -1;
+	}
+	
+	for (facility = 0; facility < NB_LOG_FACILITIES; facility++)
+	    if (!strcmp(log_facilities[facility], args[2]))
+		break;
+	
+	if (facility >= NB_LOG_FACILITIES) {
+	    Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]);
+	    exit(1);
+	}
+	
+	sa = str2sa(args[1]);
+	if (!sa->sin_port)
+	    sa->sin_port = htons(SYSLOG_PORT);
+
+	if (global.logfac1 == -1) {
+	    global.logsrv1 = *sa;
+	    global.logfac1 = facility;
+	}
+	else if (global.logfac2 == -1) {
+	    global.logsrv2 = *sa;
+	    global.logfac2 = facility;
+	}
+	else {
+	    Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
+	    return -1;
+	}
+	
+    }
+    else {
+	Alert("parsing [%s:%d] : unknown keyword <%s> in <global> section\n", file, linenum, args[0]);
+	return -1;
+    }
+    return 0;
+}
+
+
+/*
+ * parse a line in a <listen> section. Returns 0 if OK, -1 if error.
+ */
+int cfg_parse_listen(char *file, int linenum, char **args) {
+    static struct proxy *curproxy = NULL;
+    struct server *newsrv = NULL;
+
+    if (!strcmp(args[0], "listen")) {  /* new proxy */
+	if (strchr(args[2], ':') == NULL) {
+	    Alert("parsing [%s:%d] : <listen> expects <id> and <addr:port> as arguments.\n",
+		  file, linenum);
+	    return -1;
+	}
+	
+	if ((curproxy = (struct proxy *)calloc(1, sizeof(struct proxy))) == NULL) {
+	    Alert("parsing [%s:%d] : out of memory\n", file, linenum);
+	    return -1;
+	}
+	curproxy->next = proxy;
+	proxy = curproxy;
+	curproxy->id = strdup(args[1]);
+	curproxy->listen_addr = *str2sa(args[2]);
+	curproxy->state = PR_STNEW;
+	/* set default values */
+	curproxy->maxconn = cfg_maxpconn;
+	curproxy->conn_retries = CONN_RETRIES;
+	curproxy->options = 0;
+	curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
+	curproxy->mode = PR_MODE_TCP;
+	curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
+	curproxy->to_log = 0;
+	return 0;
+    }
+    else if (curproxy == NULL) {
+	Alert("parsing [%s:%d] : <listen> expected.\n", file, linenum);
+	return -1;
+    }
+    
+    if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
+	if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
+	else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
+	else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
+	else {
+	    Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+    }
+    else if (!strcmp(args[0], "disabled")) {  /* disables this proxy */
+	curproxy->state = PR_STDISABLED;
+    }
+    else if (!strcmp(args[0], "cookie")) {  /* cookie name */
+	int cur_arg;
+	if (curproxy->cookie_name != NULL) {
+	    Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
+		  file, linenum);
+	    return 0;
+	}
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <cookie> expects <cookie_name> as argument.\n",
+		  file, linenum);
+	    return -1;
+	}
+	curproxy->cookie_name = strdup(args[1]);
+	
+	cur_arg = 2;
+	while (*(args[cur_arg])) {
+	    if (!strcmp(args[cur_arg], "rewrite")) {
+		curproxy->options |= PR_O_COOK_RW;
 	    }
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <clitimeout> expects an integer <time_in_ms> as argument.\n",
-		      file, linenum);
-		return -1;
+	    else if (!strcmp(args[cur_arg], "indirect")) {
+		curproxy->options |= PR_O_COOK_IND;
 	    }
-	    curproxy->clitimeout = atol(args[1]);
-	}
-	else if (!strcmp(args[0], "srvtimeout")) {  /*  server timeout */
-	    if (curproxy->srvtimeout != 0) {
-		Alert("parsing [%s:%d] : srvtimeout already specified. Continuing.\n",
-		      file, linenum);
-		continue;
+	    else if (!strcmp(args[cur_arg], "insert")) {
+		curproxy->options |= PR_O_COOK_INS;
 	    }
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <srvtimeout> expects an integer <time_in_ms> as argument.\n",
+	    else {
+		Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
 		      file, linenum);
 		return -1;
 	    }
-	    curproxy->srvtimeout = atol(args[1]);
+	    cur_arg++;
+	}
+	if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) {
+	    Alert("parsing [%s:%d] : <cookie> 'rewrite' and 'indirect' mode are incompatibles.\n",
+		  file, linenum);
+	    return -1;
+	}
+    }
+    else if (!strcmp(args[0], "contimeout")) {  /* connect timeout */
+	if (curproxy->contimeout != 0) {
+	    Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n", file, linenum);
+	    return 0;
+	}
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <contimeout> expects an integer <time_in_ms> as argument.\n",
+		  file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "retries")) {  /* connection retries */
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <retries> expects an integer argument (dispatch counts for one).\n",
+	curproxy->contimeout = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "clitimeout")) {  /*  client timeout */
+	if (curproxy->clitimeout != 0) {
+	    Alert("parsing [%s:%d] : clitimeout already specified. Continuing.\n",
+		  file, linenum);
+	    return 0;
+	}
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <clitimeout> expects an integer <time_in_ms> as argument.\n",
+		  file, linenum);
+	    return -1;
+	}
+	curproxy->clitimeout = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "srvtimeout")) {  /*  server timeout */
+	if (curproxy->srvtimeout != 0) {
+	    Alert("parsing [%s:%d] : srvtimeout already specified. Continuing.\n", file, linenum);
+	    return 0;
+	}
+	if (*(args[1]) == 0) {
+		Alert("parsing [%s:%d] : <srvtimeout> expects an integer <time_in_ms> as argument.\n",
 		      file, linenum);
 		return -1;
-	    }
-	    curproxy->conn_retries = atol(args[1]);
 	}
-	else if (!strcmp(args[0], "redispatch") || !strcmp(args[0], "redisp")) {
+	curproxy->srvtimeout = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "retries")) {  /* connection retries */
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <retries> expects an integer argument (dispatch counts for one).\n",
+		  file, linenum);
+	    return -1;
+	}
+	curproxy->conn_retries = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "option")) {
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <option> expects an option name.\n", file, linenum);
+	    return -1;
+	}
+	if (!strcmp(args[1], "redispatch"))
 	    /* enable reconnections to dispatch */
 	    curproxy->options |= PR_O_REDISP;
-	}
 #ifdef TRANSPARENT
-	else if (!strcmp(args[0], "transparent")) {
+	else if (!strcmp(args[1], "transparent"))
 	    /* enable transparent proxy connections */
 	    curproxy->options |= PR_O_TRANSP;
+#endif
+	else if (!strcmp(args[1], "keepalive"))
+	    /* enable keep-alive */
+	    curproxy->options |= PR_O_KEEPALIVE;
+	else if (!strcmp(args[1], "forwardfor"))
+	    /* insert x-forwarded-for field */
+	    curproxy->options |= PR_O_FWDFOR;
+	else if (!strcmp(args[1], "httplog")) {
+	    /* generate a complete HTTP log */
+	    curproxy->options |= PR_O_LOGHTTP;
+	    curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID;
 	}
+	else {
+	    Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	return 0;
+    }
+    else if (!strcmp(args[0], "redispatch") || !strcmp(args[0], "redisp")) {
+	/* enable reconnections to dispatch */
+	curproxy->options |= PR_O_REDISP;
+    }
+#ifdef TRANSPARENT
+    else if (!strcmp(args[0], "transparent")) {
+	/* enable transparent proxy connections */
+	curproxy->options |= PR_O_TRANSP;
+    }
 #endif
-	else if (!strcmp(args[0], "maxconn")) {  /* maxconn */
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n",
-		      file, linenum);
-		return -1;
-	    }
-	    curproxy->maxconn = atol(args[1]);
+    else if (!strcmp(args[0], "maxconn")) {  /* maxconn */
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n", file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "grace")) {  /* grace time (ms) */
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <grace> expects a time in milliseconds.\n",
-		      file, linenum);
-		return -1;
-	    }
-	    curproxy->grace = atol(args[1]);
+	curproxy->maxconn = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "grace")) {  /* grace time (ms) */
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <grace> expects a time in milliseconds.\n", file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "dispatch")) {  /* dispatch address */
-	    if (strchr(args[1], ':') == NULL) {
-		Alert("parsing [%s:%d] : <dispatch> expects <addr:port> as argument.\n",
-		      file, linenum);
-		return -1;
-	    }
-	    curproxy->dispatch_addr = *str2sa(args[1]);
+	curproxy->grace = atol(args[1]);
+    }
+    else if (!strcmp(args[0], "dispatch")) {  /* dispatch address */
+	if (strchr(args[1], ':') == NULL) {
+	    Alert("parsing [%s:%d] : <dispatch> expects <addr:port> as argument.\n", file, linenum);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "balance")) {  /* set balancing with optionnal algorithm */
-	    if (*(args[1])) {
-		if (!strcmp(args[1], "roundrobin")) {
-		    curproxy->options |= PR_O_BALANCE_RR;
-		}
-		else {
-		    Alert("parsing [%s:%d] : <balance> supports 'roundrobin' options.\n",
-			  file, linenum);
-		    return -1;
-		}
-	    }
-	    else /* if no option is set, use round-robin by default */
+	curproxy->dispatch_addr = *str2sa(args[1]);
+    }
+    else if (!strcmp(args[0], "balance")) {  /* set balancing with optionnal algorithm */
+	if (*(args[1])) {
+	    if (!strcmp(args[1], "roundrobin")) {
 		curproxy->options |= PR_O_BALANCE_RR;
+	    }
+	    else {
+		Alert("parsing [%s:%d] : <balance> supports 'roundrobin' options.\n", file, linenum);
+		return -1;
+	    }
 	}
-	else if (!strcmp(args[0], "server")) {  /* server address */
-	    int cur_arg;
+	else /* if no option is set, use round-robin by default */
+	    curproxy->options |= PR_O_BALANCE_RR;
+    }
+    else if (!strcmp(args[0], "server")) {  /* server address */
+	int cur_arg;
 
-	    if (strchr(args[2], ':') == NULL) {
-		Alert("parsing [%s:%d] : <server> expects <name> and <addr:port> as arguments.\n",
-		      file, linenum);
-		return -1;
+	if (strchr(args[2], ':') == NULL) {
+	    Alert("parsing [%s:%d] : <server> expects <name> and <addr:port> as arguments.\n",
+		  file, linenum);
+	    return -1;
+	}
+	if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) {
+	    Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+	    return -1;
+	}
+	newsrv->next = curproxy->srv;
+	curproxy->srv = newsrv;
+	newsrv->proxy = curproxy;
+	newsrv->id = strdup(args[1]);
+	newsrv->addr = *str2sa(args[2]);
+	newsrv->state = SRV_RUNNING; /* early server setup */
+	newsrv->curfd = -1; /* no health-check in progress */
+	newsrv->inter = DEF_CHKINTR;
+	newsrv->rise = DEF_RISETIME;
+	newsrv->fall = DEF_FALLTIME;
+	newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
+	cur_arg = 3;
+	while (*args[cur_arg]) {
+	    if (!strcmp(args[cur_arg], "cookie")) {
+		newsrv->cookie = strdup(args[cur_arg + 1]);
+		newsrv->cklen = strlen(args[cur_arg + 1]);
+		cur_arg += 2;
 	    }
-	    if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) {
-		Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-		exit(1);
+	    else if (!strcmp(args[cur_arg], "rise")) {
+		newsrv->rise = atol(args[cur_arg + 1]);
+		newsrv->health = newsrv->rise;
+		cur_arg += 2;
 	    }
-	    newsrv->next = curproxy->srv;
-	    curproxy->srv = newsrv;
-	    newsrv->proxy = curproxy;
-	    newsrv->id = strdup(args[1]);
-	    newsrv->addr = *str2sa(args[2]);
-	    newsrv->state = SRV_RUNNING; /* early server setup */
-	    newsrv->curfd = -1; /* no health-check in progress */
-	    newsrv->inter = DEF_CHKINTR;
-	    newsrv->rise = DEF_RISETIME;
-	    newsrv->fall = DEF_FALLTIME;
-	    newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
-	    cur_arg = 3;
-	    while (*args[cur_arg]) {
-		if (!strcmp(args[cur_arg], "cookie")) {
-		    newsrv->cookie = strdup(args[cur_arg + 1]);
-		    newsrv->cklen = strlen(args[cur_arg + 1]);
-		    cur_arg += 2;
-		}
-		else if (!strcmp(args[cur_arg], "rise")) {
-		    newsrv->rise = atol(args[cur_arg + 1]);
-		    newsrv->health = newsrv->rise;
-		    cur_arg += 2;
-		}
-		else if (!strcmp(args[cur_arg], "fall")) {
-		    newsrv->fall = atol(args[cur_arg + 1]);
-		    cur_arg += 2;
-		}
-		else if (!strcmp(args[cur_arg], "inter")) {
-		    newsrv->inter = atol(args[cur_arg + 1]);
-		    cur_arg += 2;
-		}
-		else if (!strcmp(args[cur_arg], "check")) {
-		    struct task *t;
-
-		    if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
-			Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-			return -1;
-		    }
-
-		    t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
-		    t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
-		    t->state = TASK_IDLE;
-		    t->process = process_chk;
-		    t->context = newsrv;
-
-		    if (curproxy->state != PR_STDISABLED) {
-			tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
-			task_queue(t);
-			task_wakeup(&rq, t);
-		    }
-
-		    cur_arg += 1;
-		}
-		else {
-		    Alert("parsing [%s:%d] : server %s only supports options 'cookie' and 'check'.\n",
-			  file, linenum, newsrv->id);
+	    else if (!strcmp(args[cur_arg], "fall")) {
+		newsrv->fall = atol(args[cur_arg + 1]);
+		cur_arg += 2;
+	    }
+	    else if (!strcmp(args[cur_arg], "inter")) {
+		newsrv->inter = atol(args[cur_arg + 1]);
+		cur_arg += 2;
+	    }
+	    else if (!strcmp(args[cur_arg], "check")) {
+		struct task *t;
+		
+		if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
+		    Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
 		    return -1;
 		}
+		
+		t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
+		t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
+		t->state = TASK_IDLE;
+		t->process = process_chk;
+		t->context = newsrv;
+		
+		if (curproxy->state != PR_STDISABLED) {
+		    tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
+		    task_queue(t);
+		    task_wakeup(&rq, t);
+		}
+		
+		cur_arg += 1;
 	    }
-	    curproxy->nbservers++;
-	}
-	else if (!strcmp(args[0], "log")) {  /* syslog server address */
-	    struct sockaddr_in *sa;
-	    int facility;
-
-	    if (*(args[1]) == 0 || *(args[2]) == 0) {
-		Alert("parsing [%s:%d] : <log> expects <address> and <facility> as arguments.\n",
-		      file, linenum);
+	    else {
+		Alert("parsing [%s:%d] : server %s only supports options 'cookie' and 'check'.\n",
+		      file, linenum, newsrv->id);
 		return -1;
 	    }
-
+	}
+	curproxy->nbservers++;
+    }
+    else if (!strcmp(args[0], "log")) {  /* syslog server address */
+	struct sockaddr_in *sa;
+	int facility;
+	
+	if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
+	    curproxy->logfac1 = global.logfac1;
+	    curproxy->logsrv1 = global.logsrv1;
+	    curproxy->logfac2 = global.logfac2;
+	    curproxy->logsrv2 = global.logsrv2;
+	}
+	else if (*(args[1]) && *(args[2])) {
 	    for (facility = 0; facility < NB_LOG_FACILITIES; facility++)
 		if (!strcmp(log_facilities[facility], args[2]))
 		    break;
-
+	
 	    if (facility >= NB_LOG_FACILITIES) {
 		Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]);
 		exit(1);
 	    }
-
+	    
 	    sa = str2sa(args[1]);
 	    if (!sa->sin_port)
 		sa->sin_port = htons(SYSLOG_PORT);
-
+	    
 	    if (curproxy->logfac1 == -1) {
 		curproxy->logsrv1 = *sa;
 		curproxy->logfac1 = facility;
@@ -3630,15 +3925,163 @@
 	    }
 	    else {
 		Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
-		exit(1);
+		return -1;
 	    }
-
+	}
+	else {
+	    Alert("parsing [%s:%d] : <log> expects either <address[:port]> and <facility> or 'global' as arguments.\n",
+		  file, linenum);
+	    return -1;
+	}
+    }
+    else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0 || *(args[2]) == 0) {
+	    Alert("parsing [%s:%d] : <reqrep> expects <search> and <replace> as arguments.\n",
+		  file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+    }
+    else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqdel> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL);
+    }
+    else if (!strcmp(args[0], "reqdeny")) {  /* deny a request if a header matches this regex */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqdeny> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL);
+    }
+    else if (!strcmp(args[0], "reqallow")) {  /* allow a request if a header matches this regex */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqallow> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
+    }
+    else if (!strcmp(args[0], "reqirep")) {  /* replace request header from a regex, ignoring case */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0 || *(args[2]) == 0) {
+	    Alert("parsing [%s:%d] : <reqirep> expects <search> and <replace> as arguments.\n",
+		  file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+    }
+    else if (!strcmp(args[0], "reqidel")) {  /* delete request header from a regex ignoring case */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqidel> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL);
+    }
+    else if (!strcmp(args[0], "reqideny")) {  /* deny a request if a header matches this regex ignoring case */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqideny> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL);
+    }
+    else if (!strcmp(args[0], "reqiallow")) {  /* allow a request if a header matches this regex ignoring case */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqiallow> expects <regex> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	
+	chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
+    }
+    else if (!strcmp(args[0], "reqadd")) {  /* add request header */
+	if (curproxy->nb_reqadd >= MAX_NEWHDR) {
+	    Alert("parsing [%s:%d] : too many `reqadd'. Continuing.\n", file, linenum);
+	    return 0;
+	}
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <reqadd> expects <header> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	    curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
 	}
-	else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
+	else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) {  /* replace response header from a regex */
 	    regex_t *preg;
 
 	    if (*(args[1]) == 0 || *(args[2]) == 0) {
-		Alert("parsing [%s:%d] : <reqrep> expects <search> and <replace> as arguments.\n",
+		Alert("parsing [%s:%d] : <rsprep> expects <search> and <replace> as arguments.\n",
 		      file, linenum);
 		return -1;
 	    }
@@ -3648,128 +4091,194 @@
 		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
 		return -1;
 	    }
+	    
+	    chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	}
+    else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <rspdel> expects <search> as an argument.\n", file, linenum);
+	    return -1;
+	}
 
-	    chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
+	
+	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+    }
+    else if (!strcmp(args[0], "rspirep")) {  /* replace response header from a regex ignoring case */
 	    regex_t *preg;
 
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <reqdel> expects <regex> as an argument.\n",
+	    if (*(args[1]) == 0 || *(args[2]) == 0) {
+		Alert("parsing [%s:%d] : <rspirep> expects <search> and <replace> as arguments.\n",
 		      file, linenum);
 		return -1;
 	    }
 
 	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+	    if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
 		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
 		return -1;
 	    }
+	    
+	    chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	}
+    else if (!strcmp(args[0], "rspidel")) {  /* delete response header from a regex ignoring case */
+	regex_t *preg;
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <rspidel> expects <search> as an argument.\n", file, linenum);
+	    return -1;
+	}
 
-	    chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL);
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+	    return -1;
 	}
-	else if (!strcmp(args[0], "reqdeny")) {  /* deny a request if a header matches this regex */
-	    regex_t *preg;
+	
+	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+    }
+    else if (!strcmp(args[0], "rspadd")) {  /* add response header */
+	if (curproxy->nb_rspadd >= MAX_NEWHDR) {
+	    Alert("parsing [%s:%d] : too many `rspadd'. Continuing.\n", file, linenum);
+	    return 0;
+	}
+	
+	if (*(args[1]) == 0) {
+	    Alert("parsing [%s:%d] : <rspadd> expects <header> as an argument.\n", file, linenum);
+	    return -1;
+	}
+	
+	curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
+    }
+    else {
+	Alert("parsing [%s:%d] : unknown keyword <%s> in <listen> section\n", file, linenum, args[0]);
+	return -1;
+    }
+    return 0;
+}
 
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <reqdeny> expects <regex> as an argument.\n",
-		      file, linenum);
-		return -1;
-	    }
 
-	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
-		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
-		return -1;
-	    }
-
-	    chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL);
-	}
-	else if (!strcmp(args[0], "reqallow")) {  /* allow a request if a header matches this regex */
-	    regex_t *preg;
+/*
+ * This function reads and parses the configuration file given in the argument.
+ * returns 0 if OK, -1 if error.
+ */
+int readcfgfile(char *file) {
+    char thisline[256];
+    char *line;
+    FILE *f;
+    int linenum = 0;
+    char *end;
+    char *args[MAX_LINE_ARGS];
+    int arg;
+    int cfgerr = 0;
+    int confsect = CFG_NONE;
 
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <reqallow> expects <regex> as an argument.\n",
-		      file, linenum);
-		return -1;
-	    }
+    struct proxy *curproxy = NULL;
+    struct server *newsrv = NULL;
 
-	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
-		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
-		return -1;
-	    }
+    if ((f=fopen(file,"r")) == NULL)
+	return -1;
 
-	    chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
-	}
-	else if (!strcmp(args[0], "reqadd")) {  /* add request header */
-	    if (curproxy->nb_reqadd >= MAX_NEWHDR) {
-		Alert("parsing [%s:%d] : too many `reqadd'. Continuing.\n",
-		      file, linenum);
-		continue;
-	    }
+    while (fgets(line = thisline, sizeof(thisline), f) != NULL) {
+	linenum++;
 
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <reqadd> expects <header> as an argument.\n",
-		      file, linenum);
-		return -1;
-	    }
+	end = line + strlen(line);
 
-	    curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
-	}
-	else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) {  /* replace response header from a regex */
-	    regex_t *preg;
+	/* skip leading spaces */
+	while (isspace(*line))
+	    line++;
+	
+	arg = 0;
+	args[arg] = line;
 
-	    if (*(args[1]) == 0 || *(args[2]) == 0) {
-		Alert("parsing [%s:%d] : <rsprep> expects <search> and <replace> as arguments.\n",
-		      file, linenum);
-		return -1;
+	while (*line && arg < MAX_LINE_ARGS) {
+	    /* first, we'll replace \\, \<space>, \#, \r, \n, \t, \xXX with their
+	     * C equivalent value. Other combinations left unchanged (eg: \1).
+	     */
+	    if (*line == '\\') {
+		int skip = 0;
+		if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') {
+		    *line = line[1];
+		    skip = 1;
+		}
+		else if (line[1] == 'r') {
+		    *line = '\r';
+		    skip = 1;
+		} 
+		else if (line[1] == 'n') {
+		    *line = '\n';
+		    skip = 1;
+		}
+		else if (line[1] == 't') {
+		    *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;
+		} 
+		if (skip) {
+		    memmove(line + 1, line + 1 + skip, end - (line + skip + 1));
+		    end -= skip;
+		}
+		line++;
 	    }
-
-	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
-		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
-		return -1;
+	    else {
+		if (*line == '#' || *line == '\n' || *line == '\r')
+		    *line = 0; /* end of string, end of loop */
+		else
+		    line++;
+		
+		/* a non-escaped space is an argument separator */
+		if (isspace(*line)) {
+		    *line++ = 0;
+		    while (isspace(*line))
+			line++;
+		    args[++arg] = line;
+		}
 	    }
-
-	    chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
 	}
-	else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
-	    regex_t *preg;
-
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <rspdel> expects <search> as an argument.\n",
-		      file, linenum);
-		return -1;
-	    }
 
-	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
-		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
-		return -1;
-	    }
+	/* empty line */
+	if (!**args)
+	    continue;
 
-	    chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
+	/* zero out remaining args */
+	while (++arg < MAX_LINE_ARGS) {
+	    args[arg] = line;
 	}
-	else if (!strcmp(args[0], "rspadd")) {  /* add response header */
-	    if (curproxy->nb_rspadd >= MAX_NEWHDR) {
-		Alert("parsing [%s:%d] : too many `rspadd'. Continuing.\n",
-		      file, linenum);
-		continue;
-	    }
 
-	    if (*(args[1]) == 0) {
-		Alert("parsing [%s:%d] : <rspadd> expects <header> as an argument.\n",
-		      file, linenum);
-		return -1;
-	    }
+	if (!strcmp(args[0], "listen"))  /* new proxy */
+	    confsect = CFG_LISTEN;
+	else if (!strcmp(args[0], "global"))  /* global config */
+	    confsect = CFG_GLOBAL;
+	/* else it's a section keyword */
 
-	    curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
-	}
-	else {
-	    Alert("parsing [%s:%d] : unknown keyword <%s>\n", file, linenum, args[0]);
-	    exit(1);
+	switch (confsect) {
+	case CFG_LISTEN:
+	    if (cfg_parse_listen(file, linenum, args) < 0)
+		return -1;
+	    break;
+	case CFG_GLOBAL:
+	    if (cfg_parse_global(file, linenum, args) < 0)
+		return -1;
+	    break;
+	default:
+	    Alert("parsing [%s:%d] : unknown keyword <%s> out of section.\n", file, linenum, args[0]);
+	    return -1;
 	}
+	    
+	    
     }
     fclose(f);
 
@@ -3859,8 +4368,10 @@
  */
 void init(int argc, char **argv) {
     int i;
+    int arg_mode = 0;	/* MODE_DEBUG, ... */
     char *old_argv = *argv;
     char *tmp;
+    int cfg_maxconn = 0;	/* # of simultaneous connections, (-n) */
 
     if (1<<INTBITS != sizeof(int)*8) {
 	qfprintf(stderr,
@@ -3887,16 +4398,16 @@
 		exit(0);
 	    }
 	    else if (*flag == 'd')
-		mode |= MODE_DEBUG;
+		arg_mode |= MODE_DEBUG;
 	    else if (*flag == 'D')
-		mode |= MODE_DAEMON | MODE_QUIET;
+		arg_mode |= MODE_DAEMON | MODE_QUIET;
 	    else if (*flag == 'q')
-		mode |= MODE_QUIET;
+		arg_mode |= MODE_QUIET;
 #if STATTIME > 0
 	    else if (*flag == 's')
-		mode |= MODE_STATS;
+		arg_mode |= MODE_STATS;
 	    else if (*flag == 'l')
-		mode |= MODE_LOG;
+		arg_mode |= MODE_LOG;
 #endif
 	    else { /* >=2 args */
 		argv++; argc--;
@@ -3916,8 +4427,6 @@
 	    argv++; argc--;
     }
 
-    cfg_maxsock = cfg_maxconn * 2; /* each connection needs two sockets */
-
     if (!cfg_cfgfile)
 	usage(old_argv);
 
@@ -3928,22 +4437,49 @@
 	exit(1);
     }
 
+    if (cfg_maxconn > 0)
+	global.maxconn = cfg_maxconn;
+
+    if (global.maxconn == 0)
+	global.maxconn = DEFAULT_MAXCONN;
+
+    global.maxsock = global.maxconn * 2; /* each connection needs two sockets */
+
+    if (arg_mode & MODE_DEBUG) {
+	/* command line debug mode inhibits configuration mode */
+	global.mode &= ~(MODE_DAEMON | MODE_QUIET);
+    }
+    global.mode |= (arg_mode & (MODE_DAEMON | MODE_QUIET | MODE_DEBUG));
+
+    if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
+	Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");
+	global.mode &= ~(MODE_DAEMON | MODE_QUIET);
+    }
+
+    if ((global.nbproc > 1) && !(global.mode & MODE_DAEMON)) {
+	Warning("<nbproc> is only meaningful in daemon mode. Setting limit to 1 process.\n");
+	global.nbproc = 1;
+    }
+
+    if (global.nbproc < 1)
+	global.nbproc = 1;
+
     ReadEvent = (fd_set *)calloc(1,
 		sizeof(fd_set) *
-		(cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
+		(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     WriteEvent = (fd_set *)calloc(1,
 		sizeof(fd_set) *
-		(cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
+		(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     StaticReadEvent = (fd_set *)calloc(1,
 		sizeof(fd_set) *
-		(cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
+		(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     StaticWriteEvent = (fd_set *)calloc(1,
 		sizeof(fd_set) *
-		(cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
+		(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
 
     fdtab = (struct fdtab *)calloc(1,
-		sizeof(struct fdtab) * (cfg_maxsock));
-    for (i = 0; i < cfg_maxsock; i++) {
+		sizeof(struct fdtab) * (global.maxsock));
+    for (i = 0; i < global.maxsock; i++) {
 	fdtab[i].state = FD_STCLOSE;
     }
 }
@@ -3968,7 +4504,7 @@
 	    return -1;
 	}
 	
-	if (fd >= cfg_maxsock) {
+	if (fd >= global.maxsock) {
 	    Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
 		  curproxy->id);
 	    close(fd);
@@ -4016,12 +4552,14 @@
 	listeners++;
 //	fprintf(stderr,"Proxy %s : socket bound.\n", curproxy->id);
 
-	sprintf(trash, "Proxy %s started.\n", curproxy->id);
-	
-	if (curproxy->logfac1 >= 0)
-	    send_syslog(&curproxy->logsrv1, curproxy->logfac1, LOG_INFO, trash);
-	if (curproxy->logfac2 >= 0)
-	    send_syslog(&curproxy->logsrv2, curproxy->logfac2, LOG_INFO, trash);
+//	  sprintf(trash, "Proxy %s started.\n", curproxy->id);
+//	  
+//	  if (curproxy->logfac1 >= 0)
+//	      send_syslog(&curproxy->logsrv1, curproxy->logfac1, LOG_INFO, trash);
+//	  if (curproxy->logfac2 >= 0)
+//	      send_syslog(&curproxy->logsrv2, curproxy->logfac2, LOG_INFO, trash);
+
+	send_log(curproxy, LOG_INFO, "Proxy %s started.\n", curproxy->id);
 	
     }
     return 0;
@@ -4031,21 +4569,7 @@
 int main(int argc, char **argv) {
     init(argc, argv);
 
-    if (mode & MODE_DAEMON) {
-	int ret;
-
-	ret = fork();
-
-	if (ret > 0)
-	    exit(0); /* parent must leave */
-	else if (ret < 0) {
-	    Alert("[%s.main()] Cannot fork\n", argv[0]);
-	    exit(1); /* there has been an error */
-	}
-	setpgid(1, 0);
-    }
-
-    if (mode & MODE_QUIET) {
+    if (global.mode & MODE_QUIET) {
 	/* detach from the tty */
 	fclose(stdin); fclose(stdout); fclose(stderr);
 	close(0); close(1); close(2);
@@ -4064,6 +4588,48 @@
     if (start_proxies() < 0)
 	exit(1);
 
+    /* open log files */
+
+    /* chroot if needed */
+    if (global.chroot != NULL) {
+	if (chroot(global.chroot) == -1) {
+	    Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
+	    exit(1);
+	}
+	chdir("/");
+    }
+
+    /* setgid / setuid */
+    if (global.gid && setregid(global.gid, global.gid) == -1) {
+	Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
+	exit(1);
+    }
+
+    if (global.uid && setreuid(global.uid, global.uid) == -1) {
+	Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
+	exit(1);
+    }
+
+    if (global.mode & MODE_DAEMON) {
+	int ret = 0;
+	int proc;
+
+	/* the father launches the required number of processes */
+	for (proc = 0; proc < global.nbproc; proc++) {
+	    ret = fork();
+	    if (ret < 0) {
+		Alert("[%s.main()] Cannot fork.\n", argv[0]);
+		exit(1); /* there has been an error */
+	    }
+	    else if (ret == 0) /* child breaks here */
+		break;
+	}
+	if (proc == global.nbproc)
+	    exit(0); /* parent must leave */
+
+	setpgid(1, 0);
+    }
+
     select_loop();
 
     exit(0);