diff -Naur tinyproxy-1.6.2/doc/tinyproxy.conf tinyproxy_fec/doc/tinyproxy.conf
--- tinyproxy-1.6.2/doc/tinyproxy.conf	2003-06-23 23:14:32.000000000 +0200
+++ tinyproxy_fec/doc/tinyproxy.conf	2007-01-22 14:06:47.000000000 +0100
@@ -64,8 +64,8 @@
 # Where to log the information. Either LogFile or Syslog should be set,
 # but not both.
 #
-Logfile "/var/log/tinyproxy.log"
-# Syslog On
+# Logfile "/var/log/tinyproxy.log"
+Syslog On
 
 #
 # Set the logging level. Allowed settings are:
@@ -85,7 +85,7 @@
 # PidFile: Write the PID of the main tinyproxy thread to this file so it
 # can be used for signalling purposes.
 #
-PidFile "/var/run/tinyproxy.pid"
+PidFile "/tmp/tinyproxy.pid"
 
 #
 # Include the X-Tinyproxy header, which has the client's IP address when
@@ -226,3 +226,5 @@
 #
 ConnectPort 443
 ConnectPort 563
+
+CCheckErrFile "/tmp/ccheck.html"
diff -Naur tinyproxy-1.6.2/src/buffer.c tinyproxy_fec/src/buffer.c
--- tinyproxy-1.6.2/src/buffer.c	2002-05-24 06:45:32.000000000 +0200
+++ tinyproxy_fec/src/buffer.c	2007-01-22 14:06:47.000000000 +0100
@@ -312,3 +312,33 @@
 		}
 	}
 }
+
+
+ssize_t
+write_buffer_fd(int fd, struct buffer_s * buffptr)
+{
+	ssize_t bytessent;
+	struct bufline_s *line;
+
+	assert(fd >= 0);
+	assert(buffptr != NULL);
+
+	if (buffptr->size == 0)
+		return 0;
+
+	/* Sanity check. It would be bad to be using a NULL pointer! */
+	assert(BUFFER_HEAD(buffptr) != NULL);
+	line = BUFFER_HEAD(buffptr);
+
+	bytessent =
+	    write(fd, line->string + line->pos, line->length - line->pos);
+
+	if (bytessent >= 0) {
+		/* bytes sent, adjust buffer */
+		line->pos += bytessent;
+		if (line->pos == line->length)
+			free_line(remove_from_buffer(buffptr));
+		return bytessent;
+	}
+	return(0);
+}
diff -Naur tinyproxy-1.6.2/src/child.c tinyproxy_fec/src/child.c
--- tinyproxy-1.6.2/src/child.c	2003-10-14 20:01:05.000000000 +0200
+++ tinyproxy_fec/src/child.c	2007-01-22 14:06:47.000000000 +0100
@@ -436,3 +436,35 @@
 {
 	close(listenfd);
 }
+
+void
+remove_child(pid_t pid)
+{
+	int i;
+	int ok = 0;
+
+	SERVER_COUNT_LOCK();
+	*servers_waiting = 0;
+	for (i = 0; i != child_config.maxclients; i++) {
+		if(pid == child_ptr[i].tid) {
+			child_ptr[i].status = T_EMPTY;
+			child_ptr[i].connects = 0;
+			log_message(LOG_INFO, "remove_child(): deleted child "
+			  "%d from list", pid);
+			ok = 1;
+		}
+		else
+		{
+			if(child_ptr[i].status == T_WAITING)
+				*servers_waiting += 1;
+		}
+	}
+	SERVER_COUNT_UNLOCK();
+	if(!ok)
+	{
+		log_message(LOG_INFO,
+		  "remove_child(): could not find pid %d", pid);
+	}
+	log_message(LOG_INFO, "remove_child(): set servers_waiting to %d",
+	  *servers_waiting);
+}
diff -Naur tinyproxy-1.6.2/src/child.h tinyproxy_fec/src/child.h
--- tinyproxy-1.6.2/src/child.h	2002-05-26 20:45:26.000000000 +0200
+++ tinyproxy_fec/src/child.h	2007-01-22 14:06:47.000000000 +0100
@@ -34,4 +34,6 @@
 
 extern short int child_configure(child_config_t type, unsigned int val);
 
+extern void remove_child(pid_t pid);
+
 #endif
diff -Naur tinyproxy-1.6.2/src/conns.c tinyproxy_fec/src/conns.c
--- tinyproxy-1.6.2/src/conns.c	2003-06-01 01:02:21.000000000 +0200
+++ tinyproxy_fec/src/conns.c	2007-01-22 14:06:47.000000000 +0100
@@ -77,6 +77,10 @@
 
 	update_stats(STAT_OPEN);
 
+	connptr->server_headers = NULL;
+
+	connptr->forwarded_for = NULL;
+
 	return connptr;
 
 error_exit:
@@ -133,6 +137,12 @@
 	if (connptr->client_string_addr)
 		safefree(connptr->client_string_addr);
 
+	if(connptr->server_headers)
+		hashmap_delete(connptr->server_headers);
+
+	if(connptr->forwarded_for)
+		free(connptr->forwarded_for);
+
 	safefree(connptr);
 
 	update_stats(STAT_CLOSE);
diff -Naur tinyproxy-1.6.2/src/conns.h tinyproxy_fec/src/conns.h
--- tinyproxy-1.6.2/src/conns.h	2003-05-04 06:35:10.000000000 +0200
+++ tinyproxy_fec/src/conns.h	2007-01-22 14:06:47.000000000 +0100
@@ -19,6 +19,7 @@
 #define TINYPROXY_CONNS_H
 
 #include "tinyproxy.h"
+#include "hashmap.h"
 
 /*
  * Connection Definition
@@ -70,6 +71,10 @@
 		unsigned int major;
 		unsigned int minor;
 	} protocol;
+
+	hashmap_t server_headers;
+	int check_content;
+	char *forwarded_for;
 };
 
 /*
diff -Naur tinyproxy-1.6.2/src/grammar.y tinyproxy_fec/src/grammar.y
--- tinyproxy-1.6.2/src/grammar.y	2003-06-26 20:17:09.000000000 +0200
+++ tinyproxy_fec/src/grammar.y	2007-01-22 14:42:36.000000000 +0100
@@ -56,6 +56,9 @@
 %token KW_ERRORPAGE KW_DEFAULT_ERRORPAGE
 %token KW_STATPAGE
 %token KW_VIA_PROXY_NAME
+%token KW_CLAMAV_PORT KW_CCHECK_ERR_FILE KW_ADMIN_EMAIL KW_CCHECK_MIME_EXCLUDE
+%token KW_CHROOT KW_CCHECK_MAX_SIZE KW_CCHECK_MIN_FREE KW_CCHECK_TEMP_DIR
+%token KW_CCHECK_VIRT_DIR KW_CCHECK_MAX_SIZE_PASS
 
 /* yes/no switches */
 %token KW_YES KW_NO
@@ -69,6 +72,7 @@
 %token <cptr> STRING
 %token <cptr> NUMERIC_ADDRESS
 %token <cptr> NETMASK_ADDRESS
+%token <cptr> MIME_TYPE
 
 %type <num> yesno
 %type <cptr> string
@@ -219,6 +223,19 @@
 		  log_message(LOG_INFO, "Stathost is set to \"%s\"", $2);
 		  config.stathost = $2;
 	  }
+	| KW_CLAMAV_PORT NUMBER         { config.clamav_port = $2; }
+	| KW_CCHECK_ERR_FILE string     { config.ccheck_err_file = $2; }
+	| KW_ADMIN_EMAIL string		{ config.admin_email = $2; }
+	| KW_CCHECK_MIME_EXCLUDE MIME_TYPE
+					{ add_exclude_mime_type($2); }
+	| KW_CHROOT string              { config.chroot = $2; }
+	| KW_CCHECK_MAX_SIZE NUMBER     { config.ccheck_max_size =
+	                                   1024 * 1024 * $2; }
+	| KW_CCHECK_MAX_SIZE_PASS NUMBER	{ config.ccheck_max_size_pass = $2; }
+	| KW_CCHECK_MIN_FREE NUMBER     { config.ccheck_min_free =  
+	                                  1024 * 1024 * $2; }
+	| KW_CCHECK_TEMP_DIR string     { config.ccheck_temp_dir = $2; }
+	| KW_CCHECK_VIRT_DIR string     { config.ccheck_virt_dir = $2; }
 	;
 
 loglevels
diff -Naur tinyproxy-1.6.2/src/htmlerror.c tinyproxy_fec/src/htmlerror.c
--- tinyproxy-1.6.2/src/htmlerror.c	2003-07-14 19:42:43.000000000 +0200
+++ tinyproxy_fec/src/htmlerror.c	2007-01-22 14:06:47.000000000 +0100
@@ -244,6 +244,7 @@
 	ADD_VAR_RET("version", VERSION);
 	ADD_VAR_RET("package", PACKAGE);
 	ADD_VAR_RET("date", timebuf);
+	ADD_VAR_RET("admin", config.admin_email);
 	return(0);
 }
 
diff -Naur tinyproxy-1.6.2/src/Makefile.in tinyproxy_fec/src/Makefile.in
--- tinyproxy-1.6.2/src/Makefile.in	2003-10-17 17:50:00.000000000 +0200
+++ tinyproxy_fec/src/Makefile.in	2007-01-22 14:06:47.000000000 +0100
@@ -116,13 +116,13 @@
 
 sbin_PROGRAMS = tinyproxy
 
-tinyproxy_SOURCES =  	acl.c acl.h 	anonymous.c anonymous.h 	buffer.c buffer.h 	child.c child.h 	common.h 	conns.c conns.h 	daemon.c daemon.h 	hashmap.c hashmap.h 	heap.c heap.h 	htmlerror.c htmlerror.h 	http_message.c http_message.h 	log.c log.h 	network.c network.h 	reqs.c reqs.h 	sock.c sock.h 	stats.c stats.h 	text.c text.h 	tinyproxy.c tinyproxy.h 	utils.c utils.h 	vector.c vector.h 	grammar.y scanner.l 	regexp.h
+tinyproxy_SOURCES =  	acl.c acl.h 	anonymous.c anonymous.h 	buffer.c buffer.h 	child.c child.h 	common.h 	conns.c conns.h 	daemon.c daemon.h 	hashmap.c hashmap.h 	heap.c heap.h 	htmlerror.c htmlerror.h 	http_message.c http_message.h 	log.c log.h 	network.c network.h 	reqs.c reqs.h 	sock.c sock.h 	stats.c stats.h 	text.c text.h 	tinyproxy.c tinyproxy.h 	utils.c utils.h 	vector.c vector.h 	grammar.y scanner.l 	regexp.h virus.c virus.h
 
 
 EXTRA_DIST = gnuregex.c gnuregex.h
 EXTRA_tinyproxy_SOURCES = filter.c filter.h grammar.h
 tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@
-tinyproxy_LDADD = @ADDITIONAL_OBJECTS@
+tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpabaselib
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = ../config.h
 CONFIG_CLEAN_FILES = 
@@ -136,7 +136,7 @@
 heap.$(OBJEXT) htmlerror.$(OBJEXT) http_message.$(OBJEXT) log.$(OBJEXT) \
 network.$(OBJEXT) reqs.$(OBJEXT) sock.$(OBJEXT) stats.$(OBJEXT) \
 text.$(OBJEXT) tinyproxy.$(OBJEXT) utils.$(OBJEXT) vector.$(OBJEXT) \
-grammar.$(OBJEXT) scanner.$(OBJEXT)
+grammar.$(OBJEXT) scanner.$(OBJEXT) virus.$(OBJEXT)
 tinyproxy_LDFLAGS = 
 LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LEXLIB = @LEXLIB@
diff -Naur tinyproxy-1.6.2/src/reqs.c tinyproxy_fec/src/reqs.c
--- tinyproxy-1.6.2/src/reqs.c	2003-06-26 20:19:57.000000000 +0200
+++ tinyproxy_fec/src/reqs.c	2007-01-22 14:39:17.000000000 +0100
@@ -40,6 +40,13 @@
 #include "text.h"
 #include "utils.h"
 #include "vector.h"
+#include "virus.h"
+
+#include <sys/vfs.h>
+#include <sys/socket.h>
+#include <linux/tcp.h>
+
+
 
 /*
  * Maximum length of a HTTP line
@@ -88,6 +95,45 @@
 	char *path;
 };
 
+static int write_via_header(int fd, hashmap_t hashofheaders,
+  unsigned int major, unsigned int minor);
+
+
+int send_server_headers(struct conn_s *connptr)
+{
+	char *data, *header;
+	hashmap_iter iter;
+	int ret;
+	ret = write_via_header(connptr->client_fd, connptr->server_headers,
+	  connptr->protocol.major, connptr->protocol.minor);
+	if (ret < 0)
+		return(-1);
+	
+	iter = hashmap_first(connptr->server_headers);
+	if (iter >= 0)
+	{
+		for ( ; !hashmap_is_end(connptr->server_headers,
+		  iter); ++iter)
+		{
+			hashmap_return_entry(connptr->server_headers,
+					     iter,
+					     &data,
+					     (void **)&header);
+
+			ret = write_message(connptr->client_fd,
+						  "%s: %s\r\n",
+						  data, header);
+			if (ret < 0)
+				return(-1);
+		}
+	}
+	if (safe_write(connptr->client_fd, "\r\n", 2) < 0)
+		return -1;
+	return(0);
+}
+
+
+
 /*
  * Now, this routine adds a "port" to the list.  It also creates the list if
  * it hasn't already by done.
@@ -1076,6 +1122,14 @@
 		log_message(LOG_INFO, "Not sending client headers to remote machine");
 		return 0;
 	}
+	/*
+	 * Store X-Forwarded-For information
+	 */
+	if(hashmap_entry_by_key(hashofheaders, "X-Forwarded-For",
+	  (void **)&data) > 0)
+		connptr->forwarded_for = strdup(data);
+	else
+		connptr->forwarded_for = strdup("unknown");
 
 	/*
 	 * See if there is a "Content-Length" header.  If so, again we need
@@ -1244,41 +1298,10 @@
 		hashmap_remove(hashofheaders, skipheaders[i]);
 	}
 
-	/* Send, or add the Via header */
-	ret = write_via_header(connptr->client_fd, hashofheaders,
-			       connptr->protocol.major,
-			       connptr->protocol.minor);
-	if (ret < 0)
-		goto ERROR_EXIT;
-
-	/*
-	 * All right, output all the remaining headers to the client.
-	 */
-	iter = hashmap_first(hashofheaders);
-	if (iter >= 0) {
-		for ( ; !hashmap_is_end(hashofheaders, iter); ++iter) {
-			hashmap_return_entry(hashofheaders,
-					     iter,
-					     &data,
-					     (void **)&header);
-
-			ret = write_message(connptr->client_fd,
-						  "%s: %s\r\n",
-						  data, header);
-			if (ret < 0)
-				goto ERROR_EXIT;
-		}
-	}
-	hashmap_delete(hashofheaders);
-
-	/* Write the final blank line to signify the end of the headers */
-	if (safe_write(connptr->client_fd, "\r\n", 2) < 0)
-		return -1;
-
+	connptr->server_headers = hashofheaders;
 	return 0;
 
   ERROR_EXIT:
-	hashmap_delete(hashofheaders);
 	return -1;
 }
 
@@ -1324,8 +1347,8 @@
 			FD_SET(connptr->client_fd, &rset);
 
 		ret = select(maxfd, &rset, &wset, NULL, &tv);
-
-		if (ret == 0) {
+	
+                if (ret == 0) {
 			tdiff = difftime(time(NULL), last_access);
 			if (tdiff > config.idletimeout) {
 				log_message(LOG_INFO,
@@ -1394,6 +1417,327 @@
 	return;
 }
 
+
+/*
+static int do_content_check(struct conn_s *connptr, char *filename)
+{
+	int fd[2];
+	int pid;
+	char msg_buffer[500];
+
+	if(pipe(fd) < 0)
+	{
+		internal_error(connptr);
+		printf("Unable to create pipe: %s\n", strerror(errno));
+		return(-1);
+	}
+	if((pid = fork()) < 0)
+	{
+		internal_error(connptr);
+		printf("Unable to fork: %s\n", strerror(errno));
+		return(-1);
+	}
+	if(pid == 0)
+	{
+		close(fd[0]);
+		close(1);
+		dup2(fd[1], 1);
+		if(execlp("/tmp/filter", "filter", filename) < 0)
+		{
+			printf("execlp failed(): %s\n", strerror(errno));
+			exit(2);
+		}
+	}
+	else
+	{
+		FILE *file;
+		char buf[500];
+		int lc = 0;
+		int status;
+
+		close(fd[1]);
+		file = fdopen(fd[0], "r");
+		while(fgets(buf, sizeof(buf), file))
+		{
+			if(lc++ == 0)
+				strncpy(msg_buffer, buf, sizeof(msg_buffer));
+		}
+		fclose(file);
+		close(fd[0]);
+		waitpid(pid, &status, 0);
+		if(!WIFEXITED(status))
+		{
+			internal_error(connptr);
+			printf("check program failed\n");
+			return(-1);
+		}
+
+		if(WEXITSTATUS(status) == 2)
+		{
+			internal_error(connptr);
+			printf("check program failed\n");
+			return(-1);
+		}
+		else if(WEXITSTATUS(status) == 1)
+		{
+			indicate_http_error(connptr, 503, "Malicious Content ",
+			  "detail", msg_buffer, NULL);
+			send_http_error_message(connptr);
+			return(0);
+		}
+		else if(WEXITSTATUS(status) != 0)
+		{
+			internal_error(connptr);
+			printf("unexpected return code (%d)\n",
+			  WEXITSTATUS(status));
+			return(-1);
+		}
+	}
+
+	return(1);
+}
+
+*/
+
+
+static void
+relay_check_content(struct conn_s *connptr)
+{
+	fd_set rset, wset;
+	struct timeval tv;
+	time_t last_access;
+	time_t last_client_talk;
+	int ret;
+	double tdiff;
+	int maxfd = max(connptr->client_fd, connptr->server_fd) + 1;
+	ssize_t bytes_received;
+	int tmp_file;
+	int tmp_file2;
+	int connection_timeout = 0;
+	char filename[255];
+	int i, len, k, j;
+	char sendbuf[20000];
+	char one_byte[1];
+	char client_timeout_buf[2000];
+	int content_result;
+	char content_msg[200];
+	char *sock_str_int;
+
+	if(config.ccheck_max_size != 0 &&
+	  connptr->content_length.server > config.ccheck_max_size)
+	{
+		if(config.ccheck_max_size_pass == 1) {
+			log_message(LOG_WARNING, "Maximum allowed file size exceeded "
+			  "(content-length: %d)",connptr->content_length.server); 
+			log_message(LOG_WARNING, "Piping Message through without scanning....");
+			relay_connection(connptr);
+			return;
+		}
+		send_ccheck_response(connptr, "Content Rejected",
+		  "The requested file exceeds the maximum allowed size");
+		log_message(LOG_WARNING, "Maximum allowed file size exceeded "
+		  "(content-length: %d)",connptr->content_length.server); 
+		return;
+	}
+	snprintf(filename, sizeof(filename), "%s/buffer.%d", 
+	  config.virt_ccheck_temp_dir, getpid());
+	if((tmp_file = open(filename, O_CREAT|O_WRONLY, 0666)) < 0)
+	{
+		send_ccheck_response(connptr, "Internal Error",
+		  "Unable to create temporary buffer file");
+		log_message(LOG_ERR, "Unable to create buffer file (%s): %s",
+		  filename, strerror(errno));
+		return;
+	}
+
+	socket_nonblocking(connptr->client_fd);
+	socket_nonblocking(connptr->server_fd);
+
+	last_access = time(NULL);
+	last_client_talk = time(NULL);
+	len = 0;
+	
+	for (;;) {
+		FD_ZERO(&rset);
+		FD_ZERO(&wset);
+
+		tv.tv_sec =
+		    config.idletimeout - difftime(time(NULL), last_access);
+		tv.tv_usec = 0;
+
+		if (buffer_size(connptr->sbuffer) > 0)
+			FD_SET(connptr->client_fd, &wset);
+		if (buffer_size(connptr->cbuffer) > 0)
+			FD_SET(connptr->server_fd, &wset);
+		if (buffer_size(connptr->sbuffer) < MAXBUFFSIZE)
+			FD_SET(connptr->server_fd, &rset);
+		if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE)
+			FD_SET(connptr->client_fd, &rset);
+
+		tdiff = difftime(time(NULL), last_client_talk);
+		if (tdiff > config.idletimeout) {
+			if (len <= connection_timeout) {
+
+				if((tmp_file2 = open(filename, O_RDONLY)) < 0)
+				{
+					if (connection_timeout == 0)
+						send_ccheck_response(connptr, "Internal Error",
+						  "Unable to open temporary buffer file");
+					else
+						send_ccheck_response_nohead(connptr, "Internal Error",
+						  "Unable to open temporary buffer file");
+					log_message(LOG_ERR, "unable to open buffer file: %s", strerror(errno));
+				}
+				len = read(tmp_file2, client_timeout_buf, sizeof(client_timeout_buf));
+				close(tmp_file2);
+			}
+			//Sending Closing Header
+			if (connection_timeout == 0) {
+				send_server_headers(connptr);
+			} else {
+				one_byte[0] = client_timeout_buf[connection_timeout -1];
+				send(connptr->client_fd, one_byte, 1 , MSG_DONTWAIT);
+			}
+			connection_timeout++;
+			log_message(LOG_WARNING, "Client getting into Timeout... Sending bytes");
+			last_client_talk = time(NULL);
+		}
+		ret = select(maxfd, &rset, &wset, NULL, &tv);
+		if (ret == 0) {
+			tdiff = difftime(time(NULL), last_access);
+			if (tdiff > config.idletimeout) {
+				log_message(LOG_INFO,
+					    "Idle Timeout (after select) as %g > %u.",
+					    tdiff, config.idletimeout);
+				return;
+			} else {
+				continue;
+			}
+		} else if (ret < 0) {
+			log_message(LOG_ERR,
+				    "relay_connection: select() error \"%s\". Closing connection (client_fd:%d, server_fd:%d)",
+				    strerror(errno), connptr->client_fd,
+				    connptr->server_fd);
+			return;
+		} else {
+			/*
+			 * All right, something was actually selected so mark it.
+			 */
+			last_access = time(NULL);
+		}
+
+		if (FD_ISSET(connptr->server_fd, &rset)) {
+			bytes_received = read_buffer(connptr->server_fd, connptr->sbuffer);
+			if (bytes_received < 0)
+				break;
+			write_buffer_fd(tmp_file, connptr->sbuffer);
+			if(config.ccheck_min_free > 0)
+			{
+				struct statfs fs;
+
+				if(fstatfs(tmp_file, &fs) < 0 ||
+				  (fs.f_bavail * 1024 < config.ccheck_min_free))
+				{
+					if(connection_timeout == 0)
+						send_ccheck_response(connptr,
+						  "Content Rejected",
+						  "Free diskspace too low");
+					else
+						send_ccheck_response_nohead(connptr,
+                                                  "Content Rejected",
+                                                  "Free diskspace too low");
+					log_message(LOG_WARNING,
+					  "Free diskspace too low");
+					close(tmp_file);
+					unlink(filename);
+					return;
+				}
+			}
+					
+			connptr->content_length.server -= bytes_received;
+			if (connptr->content_length.server == 0)
+				break;
+		}
+		if (FD_ISSET(connptr->client_fd, &rset)
+		    && read_buffer(connptr->client_fd, connptr->cbuffer) < 0) {
+			break;
+		}
+		if (FD_ISSET(connptr->server_fd, &wset)
+		    && write_buffer(connptr->server_fd, connptr->cbuffer) < 0) {
+			break;
+		}
+	}
+
+	/*
+	 * Here the server has closed the connection... write the
+	 * remainder to the client and then exit.
+	 */
+	socket_blocking(connptr->client_fd);
+	while (buffer_size(connptr->sbuffer) > 0) {
+		if (write_buffer_fd(tmp_file, connptr->sbuffer) < 0)
+			break;
+	}
+
+	/*
+	 * Try to send any remaining data to the server if we can.
+	 */
+	socket_blocking(connptr->server_fd);
+	while (buffer_size(connptr->cbuffer) > 0) {
+		if (write_buffer(connptr->server_fd, connptr->cbuffer) < 0)
+			break;
+	}
+
+	close(tmp_file);
+	content_result = scan_file(filename, connptr, content_msg,
+	  sizeof(content_msg));
+	if(content_result < 0)
+	{
+		if (connection_timeout == 0)
+			send_ccheck_response(connptr, "Internal Error", content_msg);
+		else
+			send_ccheck_response_nohead(connptr, "Internal Error", content_msg);
+		log_message(LOG_ERR, "Content scanner error: %s", content_msg);
+	}
+	else if(content_result == 0)
+	{
+		if (connection_timeout == 0)
+			send_ccheck_response(connptr, "Content Rejected", content_msg);
+		else
+			send_ccheck_response_nohead(connptr, "Content Rejected", content_msg);
+		log_message(LOG_WARNING, "Content Rejected: %s", content_msg);
+	}
+	else
+	{
+		if((tmp_file = open(filename, O_RDONLY)) < 0)
+		{
+			if (connection_timeout == 0)
+				send_ccheck_response(connptr, "Internal Error",
+					"Unable to open temporary buffer file");
+			else
+				send_ccheck_response_nohead(connptr, "Internal Error",
+					"Unable to open temporary buffer file");
+			log_message(LOG_ERR, "unable to open buffer file: %s",
+			  strerror(errno));
+		}
+		if (connection_timeout == 0)
+			send_server_headers(connptr);
+		if (connection_timeout != 0)
+		{
+			j = read(tmp_file, sendbuf, connection_timeout -1);
+			log_message(LOG_WARNING, "Time_Out happend %d -> Discarding %d Bytes", connection_timeout, j);
+		}
+		while(i = read(tmp_file, sendbuf, sizeof(sendbuf)))
+		{
+			send(connptr->client_fd, sendbuf, i, MSG_NOSIGNAL);
+		}
+		close(tmp_file);
+	}
+	unlink(filename);
+	shutdown(connptr->client_fd, SHUT_WR);
+
+	return;
+}
+
 /*
  * Establish a connection to the upstream proxy server.
  */
@@ -1608,6 +1952,11 @@
 			destroy_conn(connptr);
 			return;
 		}
+		content_check_required(connptr);
+		if(!connptr->check_content)
+		{
+			send_server_headers(connptr);
+		}
 	} else {
 		if (send_ssl_response(connptr) < 0) {
 			log_message(LOG_ERR,
@@ -1618,7 +1967,10 @@
 		}
 	}
 
-	relay_connection(connptr);
+	if(connptr->check_content && !connptr->connect_method)
+		relay_check_content(connptr);
+	else
+		relay_connection(connptr);
 
 	log_message(LOG_INFO, "Closed connection between local client (fd:%d) and remote client (fd:%d)",
 		    connptr->client_fd, connptr->server_fd);
diff -Naur tinyproxy-1.6.2/src/scanner.l tinyproxy_fec/src/scanner.l
--- tinyproxy-1.6.2/src/scanner.l	2003-06-26 20:26:10.000000000 +0200
+++ tinyproxy_fec/src/scanner.l	2007-01-22 14:43:27.000000000 +0100
@@ -18,6 +18,8 @@
  */
 %{
 
+#include "common.h"
+
 #include "tinyproxy.h"
 
 #include "grammar.h"
@@ -59,6 +61,16 @@
 	{ "errorfile",	         KW_ERRORPAGE },
 	{ "defaulterrorfile",    KW_DEFAULT_ERRORPAGE },
 	{ "statfile",            KW_STATPAGE },
+	{ "clamavport",          KW_CLAMAV_PORT },
+	{ "ccheckerrfile",       KW_CCHECK_ERR_FILE },
+	{ "adminemail",          KW_ADMIN_EMAIL },
+	{ "ccheckexclude",       KW_CCHECK_MIME_EXCLUDE },
+	{ "chroot",              KW_CHROOT },
+	{ "ccheckmaxsize",       KW_CCHECK_MAX_SIZE },
+	{ "ccheckmaxsizepass",   KW_CCHECK_MAX_SIZE_PASS },
+	{ "ccheckminfree",       KW_CCHECK_MIN_FREE },
+	{ "cchecktempdir",       KW_CCHECK_TEMP_DIR },
+	{ "ccheckvirtdir",       KW_CCHECK_VIRT_DIR },
 
         /* loglevel and the settings */
         { "loglevel",            KW_LOGLEVEL },
@@ -97,6 +109,7 @@
 alpha		[a-zA-Z]
 alphanum	[a-zA-Z0-9]
 word		[^ \#'"\(\)\{\}\\;\n\t,|\.]
+alphamime	[a-zA-Z0-9\.\+\-]
 
 %x string
 
@@ -131,6 +144,11 @@
 
 ({digit}{1,3}\.){3}{digit}{1,3} { yylval.cptr = strdup(yytext); return NUMERIC_ADDRESS; }
 ({digit}{1,3}\.){3}{digit}{1,3}\/{digit}+ { yylval.cptr = strdup(yytext); return NETMASK_ADDRESS; }
+\"{alphamime}+\/(({alphamime}+)|(\*))\" {
+					yylval.cptr = strdup(yytext + 1);
+					yylval.cptr[yyleng - 3] = 0;
+					return MIME_TYPE;
+				}
 
 
 %%
diff -Naur tinyproxy-1.6.2/src/tinyproxy.c tinyproxy_fec/src/tinyproxy.c
--- tinyproxy-1.6.2/src/tinyproxy.c	2003-03-17 05:24:19.000000000 +0100
+++ tinyproxy_fec/src/tinyproxy.c	2007-01-22 14:06:47.000000000 +0100
@@ -35,6 +35,7 @@
 #include "sock.h"
 #include "stats.h"
 #include "utils.h"
+#include "virus.h"
 
 void takesig(int sig);
 
@@ -68,8 +69,15 @@
 		break;
 
 	case SIGCHLD:
-		while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
-			;
+		while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+			if(pid > 0 && WIFSIGNALED(status))
+			{
+				log_message(LOG_WARNING,
+				  "Child terminated with signal %s",
+				  strsignal(WTERMSIG(status)));
+				remove_child(pid);
+			}
+		}
 		break;
 	}
 
@@ -158,6 +166,7 @@
 	 */
 #if defined(HAVE_SETRLIMIT) && defined(NDEBUG)
 	struct rlimit core_limit = { 0, 0 };
+	memset(&config, 0, sizeof(struct config_s));
 	if (setrlimit(RLIMIT_CORE, &core_limit) < 0) {
 		fprintf(stderr, "%s: Could not set the core limit to zero.\n",
 			argv[0]);
@@ -266,6 +275,37 @@
 			    MAX_IDLE_TIME);
 		config.idletimeout = MAX_IDLE_TIME;
 	}
+	if(config.clamav_port == 0)
+		config.clamav_port = 3310;
+	if(config.ccheck_temp_dir == NULL)
+		config.ccheck_temp_dir = strdup("/tmp");
+	if(config.chroot)
+	{
+		trim_slash(config.ccheck_temp_dir);
+		trim_slash(config.chroot);
+		if(!config.syslog)
+		{
+			trim_slash(config.logf_name);
+			if(!path_inside_chroot(config.logf_name))
+			{
+				fprintf(stderr, "%s: Logfile path not "
+				  "inside chroot dir\n", argv[0]);
+				exit(EX_SOFTWARE);
+			}
+			if(!path_inside_chroot(config.ccheck_temp_dir))
+			{
+				fprintf(stderr, "%s: Content check temp "
+				  "directory not inside chroot dir\n", argv[0]);
+				exit(EX_SOFTWARE);
+			}
+			config.virt_ccheck_temp_dir = config.ccheck_temp_dir +
+			  strlen(config.chroot);
+		}
+	}
+	else
+	{
+		config.virt_ccheck_temp_dir = config.ccheck_temp_dir;
+	}
 
 	init_stats();
 
@@ -312,47 +352,77 @@
 		exit(EX_OSERR);
 	}
 
-	/*
-	 * Switch to a different user.
-	 */
-	if (geteuid() == 0) {
-		if (config.group && strlen(config.group) > 0) {
+	/* do chroot if requested */
+	if (geteuid() == 0)
+	{
+		/* first get userid and groupid of non-privileged user
+		 * because, after chroot we can't access the
+		 * passwd and group file
+		 */
+		if(config.group && strlen(config.group) > 0)
+		{
 			thisgroup = getgrnam(config.group);
-			if (!thisgroup) {
+			if (!thisgroup)
+			{
 				fprintf(stderr,
-					"%s: Unable to find group \"%s\".\n",
-					argv[0], config.group);
+				  "%s: Unable to find group \"%s\".\n",
+				  argv[0], config.group);
 				exit(EX_NOUSER);
 			}
-			if (setgid(thisgroup->gr_gid) < 0) {
-				fprintf(stderr,
-					"%s: Unable to change to group \"%s\".\n",
-					argv[0], config.group);
-				exit(EX_CANTCREAT);
-			}
-			log_message(LOG_INFO, "Now running as group \"%s\".",
-				    config.group);
 		}
-		if (config.username && strlen(config.username) > 0) {
+		if(config.username && strlen(config.username) > 0)
+		{
 			thisuser = getpwnam(config.username);
-			if (!thisuser) {
+			if(!thisuser)
+			{
 				fprintf(stderr,
-					"%s: Unable to find user \"%s\".",
-					argv[0], config.username);
+				  "%s: Unable to find user \"%s\".",
+				  argv[0], config.username);
 				exit(EX_NOUSER);
 			}
-			if (setuid(thisuser->pw_uid) < 0) {
+		}
+	}
+	if(config.chroot != NULL && chroot(config.chroot) < 0)
+	{
+		fprintf(stderr, "%s: unable to chroot to %s, %s\n",
+		  argv[0], config.chroot, strerror(errno));
+		exit(EX_SOFTWARE);
+	}
+
+	/*
+	 * Switch to a different user.
+	 */
+	if(geteuid() == 0)
+	{
+		if(thisgroup)
+		{
+			if(setgid(thisgroup->gr_gid) < 0)
+			{
 				fprintf(stderr,
-					"%s: Unable to change to user \"%s\".",
-					argv[0], config.username);
+				  "%s: Unable to change to group \"%s\".\n",
+				  argv[0], config.group);
+				exit(EX_CANTCREAT);
+			}
+			log_message(LOG_INFO, "Now running as group \"%s\".",
+			  config.group);
+		}
+		if(thisuser)
+		{
+			if(setuid(thisuser->pw_uid) < 0)
+			{
+				fprintf(stderr,
+				  "%s: Unable to change to user \"%s\".",
+				  argv[0], config.username);
 				exit(EX_CANTCREAT);
 			}
 			log_message(LOG_INFO, "Now running as user \"%s\".",
-				    config.username);
+			  config.username);
 		}
-	} else {
+	}
+	else
+	{
 		log_message(LOG_WARNING,
-			    "Not running as root, so not changing UID/GID.");
+		  "Not running as root, so not changing UID/GID.");
 	}
 
 	if (child_pool_create() < 0) {
diff -Naur tinyproxy-1.6.2/src/tinyproxy.h tinyproxy_fec/src/tinyproxy.h
--- tinyproxy-1.6.2/src/tinyproxy.h	2003-06-20 19:02:12.000000000 +0200
+++ tinyproxy_fec/src/tinyproxy.h	2007-01-22 14:45:36.000000000 +0100
@@ -86,6 +86,25 @@
 	 * The HTML statistics page. 
 	 */
 	char *statpage;
+
+	/*
+	 * Content Check
+	 */
+	int clamav_port;
+	char *ccheck_err_file;
+	char *admin_email;
+	char *chroot;
+	int ccheck_max_size;
+	int ccheck_max_size_pass;
+	int ccheck_min_free;
+	char *ccheck_temp_dir;
+	char *ccheck_virt_dir;
+	char *ccheck_admin;
+	char *virt_ccheck_temp_dir;
+	struct exclude_mtype_s {
+		char *main;
+		char *sub;
+	} **exclude_mtypes;
 };
 
 /* Global Structures used in the program */
diff -Naur tinyproxy-1.6.2/src/virus.c tinyproxy_fec/src/virus.c
--- tinyproxy-1.6.2/src/virus.c	1970-01-01 01:00:00.000000000 +0100
+++ tinyproxy_fec/src/virus.c	2007-01-22 14:06:47.000000000 +0100
@@ -0,0 +1,186 @@
+#include "common.h"
+#include "tinyproxy.h"
+#include "conns.h"
+#include "htmlerror.h"
+#include "heap.h"
+#include <pa/scanqclient.h>
+
+
+int scan_file(char *filename, struct conn_s *connptr, char *msg, int msg_size)
+{
+	int ret;
+	int res;
+	char info[1000];
+
+	snprintf(info, sizeof(info), "Client: %s, Request: %s",
+	  connptr->forwarded_for, connptr->request_line);
+	res = pa_virus_scan("HTTP", filename, info);
+	if(res < 0)
+	{
+		snprintf(msg, msg_size, "Virus scan failed: %s",
+		  pa_virus_errstr());
+	}
+	else if(res == 0)
+	{
+		snprintf(msg, msg_size, "Found virus: %s",
+		  pa_virus_name());
+	}
+	else
+	{
+		snprintf(msg, msg_size, "No virus found");
+	}
+	ret = res;
+	return(ret);
+}
+
+
+void send_ccheck_response(struct conn_s *connptr, char *head, char *error_text)
+{
+	char *headers =
+	  "Server: %s/%s\r\n"
+	  "Content-Type: text/html\r\n" \
+	  "Connection: close\r\n" \
+	"\r\n";
+	char *fallback_error =
+	  "<html><head><title>%s</title></head><body>"
+	  "<h1>%s</h1>"
+	  "The following error occured, but the proxy server was unable "
+	  "to find the error page<br/><br/>"
+	  "<b>%s</b><br/><br/>"
+	  "Please contact your administrator: <a href='mailto:%s'>%s</a>"
+	  "</body></html>";
+	FILE *msg_file;
+
+	write_message(connptr->client_fd, headers, PACKAGE, VERSION);
+	if(config.ccheck_err_file == NULL ||
+	  (msg_file = fopen(config.ccheck_err_file, "r")) == NULL)
+	{
+		write_message(connptr->client_fd, fallback_error,
+		  head, head, error_text, config.admin_email,
+		  config.admin_email);
+	}
+	else
+	{
+		add_standard_vars(connptr);
+		add_error_variable(connptr, "cc_head", safestrdup(head));
+		add_error_variable(connptr, "cc_error_text",
+		  safestrdup(error_text));
+		send_html_file(msg_file, connptr);
+		fclose(msg_file);
+	}
+}
+
+void send_ccheck_response_nohead(struct conn_s *connptr, char *head, char *error_text)
+{
+	char *fallback_error =
+	  "<html><head><title>%s</title></head><body>"
+	  "<h1>%s</h1>"
+	  "The following error occured, but the proxy server was unable "
+	  "to find the error page<br/><br/>"
+	  "<b>%s</b><br/><br/>"
+	  "Please contact your administrator: <a href='mailto:%s'>%s</a>"
+	  "</body></html>";
+	FILE *msg_file;
+
+	if(config.ccheck_err_file == NULL ||
+	  (msg_file = fopen(config.ccheck_err_file, "r")) == NULL)
+	{
+		write_message(connptr->client_fd, fallback_error,
+		  head, head, error_text, config.admin_email,
+		  config.admin_email);
+	}
+	else
+	{
+		add_standard_vars(connptr);
+		add_error_variable(connptr, "cc_head", safestrdup(head));
+		add_error_variable(connptr, "cc_error_text",
+		  safestrdup(error_text));
+		send_html_file(msg_file, connptr);
+		fclose(msg_file);
+	}
+}
+
+
+int add_exclude_mime_type(char *mime_type)
+{
+	static int count = 0;
+	char *p;
+	char temp;
+	int i;
+
+	if((p = index(mime_type, '/')) == NULL)
+		return(-1);
+	p++;
+	config.exclude_mtypes = saferealloc(config.exclude_mtypes,
+	  sizeof(struct exclude_mtype_s *) * (count + 2));
+	config.exclude_mtypes[count] =
+	  (struct exclude_mtype_s *)safemalloc(sizeof(struct exclude_mtype_s));
+	temp = *p;
+	*p = 0;
+	config.exclude_mtypes[count]->main = safestrdup(mime_type);
+	*p = temp;
+	config.exclude_mtypes[count]->sub = *p == '*' ? NULL :
+	  safestrdup(p);
+	config.exclude_mtypes[count + 1] = NULL;
+	++count;
+	return(0);
+}
+
+
+int content_check_required(struct conn_s *connptr)
+{
+	char *header;
+	char *sub;
+	char *p;
+	int l, sublen;
+	struct exclude_mtype_s **type;
+
+	connptr->check_content = 1;
+	if(hashmap_entry_by_key(connptr->server_headers, "Content-Type",
+	  (void **)&header) <= 0)
+	{
+		return(0);
+	}
+	if((sub = index(header, '/')) == NULL)
+		return(0);
+	++sub;
+	if((p = index(sub, ';')) == NULL)
+		sublen = strlen(sub);
+	else
+		sublen = p - sub;
+	type = config.exclude_mtypes;
+	if(type == NULL)
+		return(0);
+	while(*type != NULL)
+	{
+		if(strncmp(header, (*type)->main, strlen((*type)->main)) == 0 &&
+		  ((*type)->sub == NULL ||
+		  strncmp(sub, (*type)->sub, sublen) == 0))
+		{
+			connptr->check_content = 0;
+			return(0);
+		}
+		++type;
+	}
+	return(0);
+}
+
+
+void trim_slash(char *path)
+{
+	int len;
+
+	len = strlen(path);
+	while(path[len - 1] == '/' && len > 0)
+		path[--len] = 0;
+}
+
+
+int path_inside_chroot(char *path)
+{
+	if(strncmp(path, config.chroot, strlen(config.chroot)) == 0)
+		return(1);
+	return(0);
+
+}
+
diff -Naur tinyproxy-1.6.2/src/virus.h tinyproxy_fec/src/virus.h
--- tinyproxy-1.6.2/src/virus.h	1970-01-01 01:00:00.000000000 +0100
+++ tinyproxy_fec/src/virus.h	2007-01-22 14:06:47.000000000 +0100
@@ -0,0 +1,9 @@
+#include "conns.h"
+
+void send_ccheck_response(struct conn_s *connptr, char *head,
+  char *error_text);
+int scan_file(char *filename, struct conn_s *connptr, char *msg, int msg_size);
+int add_exclude_mime_type(char *mime_type);
+int content_check_required(struct conn_s *connptr);
+void trim_slash(char *path);
+int path_inside_chroot(char *path);

