Sophie

Sophie

distrib > Mandriva > 2007.1 > i586 > by-pkgid > be582f70178e2486a21b0bb291fcca02 > files > 2

DansGuardian-2.8.0.6-4mdv2007.1.src.rpm

diff -Naur dansguardian-2.8.0.4/ClamAV.cpp dansguardian-2.8.0.4.oden/ClamAV.cpp
--- dansguardian-2.8.0.4/ClamAV.cpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ClamAV.cpp	2005-06-17 12:30:35.300932239 +0200
@@ -0,0 +1,88 @@
+//  Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br)
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#include "ClamAV.hpp"
+#include "OptionContainer.hpp"
+#include <syslog.h>
+#include <time.h>
+
+extern OptionContainer o;
+extern struct cl_limits limits;
+extern struct cl_stat dbstat;
+
+ClamAV::ClamAV()
+:virname(0),root(NULL) {}
+
+int ClamAV::scanFile(char* filename) {
+
+    int isinfected = 0;
+    
+    isinfected = cl_scanfile(filename, &virname, NULL, root, &limits, CL_ARCHIVE| CL_MAIL | CL_OLE2 | CL_SCAN_PE | CL_SCAN_BLOCKBROKEN | CL_SCAN_HTML);
+    return isinfected;
+}
+
+int ClamAV::loadDB() {
+
+    int ret = 0;
+    unsigned int no = 0;
+    const char* dbdir = cl_retdbdir();
+    
+#ifdef DGDEBUG
+system("date");
+std::cout << "Start ClamAV DB load." << std::endl;
+#endif
+
+    if (root != NULL) {
+	cl_free(root);
+	root = NULL;
+    }
+    if ((ret = cl_loaddbdir(dbdir, &root, &no))) {
+	syslog(LOG_ERR, "Error loading ClamAV DB: %s", cl_perror(ret));
+	return ret;
+    }
+    else {
+	syslog(LOG_INFO, "ClamAV DB loaded: %i signatures known", no);
+	cl_build(root);
+	cl_statfree(&dbstat);
+	cl_statinidir(dbdir, &dbstat);
+    }
+    
+#ifdef DGDEBUG
+std::cout << "End of ClamAV DB load." << std::endl;
+system("date");
+#endif
+    
+    limits.maxfiles = o.cl_max_files;
+    limits.maxfilesize = o.cl_max_file_size;
+    limits.maxreclevel = o.cl_max_rec_level;
+
+    return 0;
+}
+
+int ClamAV::checkDB() {
+
+    int ret = 0;
+    
+    if (cl_statchkdir(&dbstat) == 1) {
+    
+#ifdef DGDEBUG
+std::cout << "ClamAV virus database has changed. Reloading..." << std::endl;
+#endif
+
+	ret = loadDB();
+    }
+    
+    return ret;
+}
diff -Naur dansguardian-2.8.0.4/ClamAV.hpp dansguardian-2.8.0.4.oden/ClamAV.hpp
--- dansguardian-2.8.0.4/ClamAV.hpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ClamAV.hpp	2005-06-17 12:30:35.300932239 +0200
@@ -0,0 +1,36 @@
+//  Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br)
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifndef __HPP_CLAMAV
+#define __HPP_CLAMAV
+//#include "OptionContainer.hpp"
+#include <clamav.h>
+
+class ClamAV {
+
+public:
+    const char* virname;
+    struct cl_node* root;
+
+    ClamAV();
+//    ~ClamAV();
+
+    int scanFile(char* filename);
+    int loadDB();
+    int checkDB();
+};
+
+#endif
+
diff -Naur dansguardian-2.8.0.4/ClamDScan.cpp dansguardian-2.8.0.4.oden/ClamDScan.cpp
--- dansguardian-2.8.0.4/ClamDScan.cpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ClamDScan.cpp	2005-06-17 12:30:35.300932239 +0200
@@ -0,0 +1,125 @@
+
+#include "ClamDScan.hpp"
+#include "OptionContainer.hpp"
+#include <stdio.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+extern OptionContainer o;
+
+// Constructor
+ClamDScan::ClamDScan()
+:virname(NULL),errorMsg(NULL) {};
+
+// Deconstructor
+ClamDScan::~ClamDScan() {};
+
+int ClamDScan::dsfile(int sockd, const char *filename) {
+    int infected = 0;
+    char buff[4096], scancmd[256];
+    FILE *fd;
+#ifdef DGDEBUG
+    std::cout << "Entering dsfile" << std::endl;
+#endif
+
+    sprintf(scancmd, "CONTSCAN %s", filename);
+
+#ifdef DGDEBUG
+    std::cout << "Before sending clamd command: " << scancmd << std::endl;
+#endif
+
+    if(write(sockd, scancmd, strlen(scancmd)) <= 0) {
+	// Error writing to clamd socket
+	infected = 2;
+    }
+
+    // reading response from clamd
+    if((fd = fdopen(dup(sockd), "r")) == NULL) {
+	// Error opening socket for reading
+#ifdef DGDEBUG
+	std::cout << "Error opening clamd socket for reading" << std::endl;
+#endif
+	infected = 2;
+	return infected;
+    }
+
+    while(fgets(buff, sizeof(buff), fd)) {
+	if(strstr(buff, "FOUND\n")) {
+	    infected = 1;
+	}
+	if (strstr(buff, "ERROR\n")) {
+	    infected = 2;
+	}
+    }
+#ifdef DGDEBUG
+	std::cout << "Got reply from clamd: '" << buff << "'" << std::endl;
+#endif
+
+    if (infected == 1) {
+	virname = new char[256];
+	strcpy(virname, buff);
+	virname += (strlen(filename) + 2); // clamd adds " :" to the returning string
+	virname[strlen(virname) - 7] = '\0'; // Removing " FOUND\n" from returning string
+#ifdef DGDEBUG
+	std::cout << "Virus found: '" << virname << "'" << std::endl;
+#endif
+    }
+    else if (infected == 2) {
+	errorMsg = new char[256];
+	strcpy(errorMsg, buff);
+	errorMsg += (strlen(filename) + 2);  // clamd adds " :" to the returning text
+	errorMsg[strlen(errorMsg) - 7] = '\0'; // Removing " ERROR\n" from returning string
+#ifdef DGDEBUG
+	std::cout << "Error found: '" << errorMsg << "'" << std::endl;
+#endif
+    }
+    fclose(fd);
+    
+    return infected;
+}
+
+int ClamDScan::dconnect(void) {
+
+    struct sockaddr_un server;
+    int sockd;
+    const char *LocalSocket = o.localsocket.c_str();
+    
+    server.sun_family = AF_UNIX;
+    strncpy(server.sun_path, LocalSocket, sizeof(server.sun_path));
+
+    if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+	return -1;
+    }
+
+    if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+	return -1;
+    }
+
+    return sockd;
+}
+
+int ClamDScan::scanFile(const char *filename) {
+    int sockd = 0;
+    int infected = 0;
+
+#ifdef DGDEBUG
+    std::cout << "Entering ClamDScan scanFile" << std::endl;
+    std::cout << "Before calling dconnect" << std::endl;
+#endif
+    if((sockd = dconnect()) < 0) {
+#ifdef DGDEBUG
+    std::cout << "Error connecting to clamd daemon" << std::endl;
+#endif
+	infected = 2;
+    }
+    else {
+#ifdef DGDEBUG
+    std::cout << "Before calling dsfile" << std::endl;
+#endif
+	infected = dsfile(sockd, filename);
+    }
+    close(sockd);
+
+    return infected;
+}
diff -Naur dansguardian-2.8.0.4/ClamDScan.hpp dansguardian-2.8.0.4.oden/ClamDScan.hpp
--- dansguardian-2.8.0.4/ClamDScan.hpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ClamDScan.hpp	2005-06-17 12:30:35.301932260 +0200
@@ -0,0 +1,36 @@
+//  Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br)
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifndef __HPP_CLAMDSCAN
+#define __HPP_CLAMDSCAN
+
+#define CL_CLEAN	0	// virus not found
+#define CL_VIRUS	1	// virus found
+
+class ClamDScan {
+
+public:
+    char *virname;
+    char *errorMsg;
+
+    ClamDScan();
+    ~ClamDScan();
+
+    int dsfile(int sockd, const char *filename);
+    int dconnect(void);
+    int scanFile(const char *filename);
+};
+
+#endif
diff -Naur dansguardian-2.8.0.4/ConnectionHandler.cpp dansguardian-2.8.0.4.oden/ConnectionHandler.cpp
--- dansguardian-2.8.0.4/ConnectionHandler.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ConnectionHandler.cpp	2005-06-17 12:30:35.304932326 +0200
@@ -22,6 +22,7 @@
 #include "ImageContainer.hpp"
 #include "ConnectionHandler.hpp"
 #include "DataBuffer.hpp"
+#include "VirusScanner.hpp"
 #include "Socket.hpp"
 #include "UDSocket.hpp"
 #include "Ident.hpp"
@@ -52,555 +53,758 @@
 extern OptionContainer o;
 
 void ConnectionHandler::handleConnection(int peerfd, String ip, int port) {
+
+    Socket proxysock;  // to hold connection to proxy
+
     Socket peerconn;
     peerconn.close();
     peerconn.setFD(peerfd);
-
-    struct timeval thestart;
-    struct timezone notused;
-    gettimeofday(&thestart, &notused);
-
     peerconn.setTimeout(10);
 
     HTTPHeader header;  // to hold the incoming client request header
-
     header.setTimeout(10);  // set a timeout as we don't want blocking 4 eva
 
     HTTPHeader docheader;  // to hold the returned page header from proxy
-
-    docheader.setTimeout(20);
+    docheader.setTimeout(120);
 
     DataBuffer docbody;  // to hold the returned page
-
     docbody.setTimeout(120);
 
+    VirusScanner virusscan;
+
+    Ident ident;  // for holding
+
     bool waschecked = false;  // flags
     bool wasrequested = false;
     bool isexception = false;
+    bool novirusscan = false;
     bool isourwebserver = false;
     bool wasclean = false;
     bool cachehit = false;
     bool forceauthrequest = false;
     bool isbypass = false;
     bool iscookiebypass = false;
-    int bypasstimestamp = 0;
     bool ispostblock = false;
     bool pausedtoobig = false;
 
+    int docsize = 0;  // to store the size of the returned document for loggin
+    int bypasstimestamp = 0;
+    int contentlength = 0;
+	int rc = 0;
+
     if (o.preemptive_banning == 0) {
-            forceauthrequest = true;
+		forceauthrequest = true;
     }
 
+    std::string exceptionreason;  // to hold the reason for not blocking
+    std::string clientip = ip.toCharArray();  // hold the clients ip
     std::string mimetype = "-";
 
     String url;
     String urld;
     String urldomain;
 
-    std::string exceptionreason;  // to hold the reason for not blocking
-
-    int docsize = 0;  // to store the size of the returned document for loggin
-
-    Ident ident;  // for holding
-
-    std::string clientip = ip.toCharArray();  // hold the clients ip
+    struct timeval thestart;
+    struct timezone notused;
+    gettimeofday(&thestart, &notused);
 
     #ifdef DGDEBUG  // debug stuff surprisingly enough
         std::cout << "got connection" << std::endl;
         std::cout << clientip << std::endl;
     #endif
 
-    Socket proxysock;  // to hold connection to proxy
-
     try {
 
-        // connect to proxy
-        int rc = proxysock.connect(o.proxy_ip, o.proxy_port);
-
-        if (rc) {
-            #ifdef DGDEBUG
-                std::cerr << "Error connecting to proxy" << std::endl;
-            #endif
-            syslog(LOG_ERR, "%s","Error connecting to proxy");
-            return;  // if we can't connect to the proxy, there is no point
-                 // in continuing
-        }
-
-        header.in(&peerconn);  // get header from client
-        url = header.url();
-        urld = header.decode(url);
-        if (url.after("://").contains("/")) {
-            urldomain = url.after("//").before("/");
-        }
-        else {
-            urldomain = url.after("//");
-        }
-
-        if (header.malformedURL(url)) {
-            // checks for bad URLs to prevent security hole
-            try { // writestring throws exception on error/timeout
-                peerconn.writeString("HTTP/1.0 400 Bad Request\n");
-                peerconn.writeString("Content-Type: text/html\n\n");
-                peerconn.writeString("<HTML><HEAD><TITLE>DansGuardian - 400 Bad Request</TITLE></HEAD><BODY><H1>DansGuardian - 400 Bad Request</H1> ");
-                peerconn.writeString(o.language_list.getTranslation(200));
-                // The requested URL is malformed.
-                peerconn.writeString("</BODY></HTML>\n");
-            } catch (exception& e) {}
-            try {
-                proxysock.close();  // close connection to proxy
-            } catch (exception& e) {}
-            return;
-        }
-
-        if (o.use_xforwardedfor == 1) {
-            std::string xforwardip = header.getXForwardedForIP();
-            if (xforwardip.length() > 6) {
-                clientip = xforwardip;
-            }
-            #ifdef DGDEBUG
-                std::cout << "using x-forwardedfor:" << clientip << std::endl;
-            #endif
-        }
-
-        std::string clientuser = ident.getUsername(&header, &clientip, port);
-                                                   // extract username
-
-        #ifdef DGDEBUG
-            std::cout << "About to determine group" << std::endl;
-        #endif
-
-        int filtergroup = determineGroup(&clientuser);
-        if (filtergroup < 0) {
-            filtergroup = determineGroup(&clientip);
-        }
-        if (filtergroup < 0) {
-            filtergroup = 0; //default group - one day configurable?
-        }
-
-        #ifdef DGDEBUG
-            std::cout << "filtergroup:" << filtergroup << std::endl;
-        #endif
-
-        if (o.forwarded_for == 1) {
-            header.addXForwardedFor(clientip);  // add squid-like entry
-        }
-        if ((*o.fg[filtergroup]).bypass_mode > 0) {
-            #ifdef DGDEBUG
-                std::cout << "About to check for bypass..." << std::endl;
-            #endif
-            bypasstimestamp = header.isBypassURL(&url, (*o.fg[filtergroup]).magic.c_str(), clientip.c_str());
-            if (bypasstimestamp > 0) {
-                #ifdef DGDEBUG
-                    std::cout << "Bypass URL match" << std::endl;
-                #endif
-                header.chopBypass(url);
-                url = header.url();
-                urld = header.decode(url);
-                if (bypasstimestamp > 1) {  // not expired
-                    isbypass = true;
-                    exceptionreason = o.language_list.getTranslation(606);
-                }
-            }
-            else if (header.isBypassCookie(&urldomain, (*o.fg[filtergroup]).cookie_magic.c_str(), clientip.c_str())) {
-                #ifdef DGDEBUG
-                    std::cout << "Bypass cookie match" << std::endl;
-                #endif
-                iscookiebypass = true;
-                isbypass = true;
-                exceptionreason = o.language_list.getTranslation(607);
-            }
-            #ifdef DGDEBUG
-                std::cout << "Finished bypass checks." << std::endl;
-            #endif
-        }
-        if (isbypass) {
-            #ifdef DGDEBUG
-                std::cout << "Bypass activated!" << std::endl;
-            #endif
-        }
-        else if (o.inipexceptions(&clientip)) {  // admin pc
-            isexception = true;
-            exceptionreason = o.language_list.getTranslation(600);
-            // Exception client IP match.
-        }
-        else if (o.inuserexceptions(&clientuser)) { // admin user
-            isexception = true;
-            exceptionreason = o.language_list.getTranslation(601);
-            // Exception client user match.
-        }
-        else if ((*o.fg[filtergroup]).inexceptions(urld)) {  // allowed site
-            if ((*o.fg[0]).iswebserver(url)) {
-                isourwebserver = true;
-            }
-            else {
-                isexception = true;
-                exceptionreason = o.language_list.getTranslation(602);
-                // Exception site match.
-            }
-        }
-        else if ((*o.fg[filtergroup]).inurlexceptions(urld)) {  // allowed url
-            isexception = true;
-            exceptionreason = o.language_list.getTranslation(603);
-            // Exception url match.
-        }
-
-
-
-        #ifdef DGDEBUG
-            std::cout << "extracted url:" << urld << std::endl;
-        #endif
-
-
-	if ( (isourwebserver || isexception || iscookiebypass)
-	    // don't filter exception and local web server
-	    // Cookie bypass so don't need to add cookie so just CONNECT
-	    && !o.inBannedIPList(&clientip)		// bad users pc
-	    && !o.inBannedUserList(&clientuser) ) { 	// bad user
-
-	    proxysock.readyForOutput(10);  // exception on timeout or error
-            header.out(&proxysock);  // send proxy the request
-            try {
-                FDTunnel fdt;  // make a tunnel object
-                // tunnel from client to proxy and back
-                fdt.tunnel(proxysock.getFD(), peerconn.getFD()); // not expected to exception
-                docsize = fdt.throughput;
-                if (!isourwebserver) {  // don't log requests to the web server
-                    decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype);
-                }
-
-            } catch (exception& e) {}
-            try {
-                proxysock.close();  // close connection to proxy
-            } catch (exception& e) {}
-            return;  // connection dealt with so exit
-        }
-
-
-        NaughtyFilter checkme;  // our filter object
-        checkme.filtergroup = filtergroup;
-
-        char* i;
-
-        // Improved IF structure as suggested by AFN
-
-        if ((!forceauthrequest || header.requesttype().startsWith("CONNECT")) && !isbypass) {
-            // if its a connect and we don't do filtering on it now then
-            // it will get tunneled and not filtered.  We can't tunnel later
-            // as its ssl so we can't see the return header etc
-            // So preemptive banning is forced on with ssl unfortunately.
-            // It is unlikely to cause many problems though.
-            requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock);
-        }
-
-
-        if (!checkme.isItNaughty && header.requesttype().startsWith("CONNECT")) {
-            // can't filter content of CONNECT
-            proxysock.readyForOutput(10);  // exception on timeout or error
-            header.out(&proxysock);  // send proxy the request
-            try {
-                FDTunnel fdt;  // make a tunnel object
-                // tunnel from client to proxy and back
-                fdt.tunnel(proxysock.getFD(), peerconn.getFD()); // not expected to exception
-                docsize = fdt.throughput;
-                decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype);
-            } catch (exception& e) {}
-            try {
-                proxysock.close();  // close connection to proxy
-            } catch (exception& e) {}
-            return;  // connection dealt with so exit
-        }
-
-        if (!checkme.isItNaughty) {
-            proxysock.readyForOutput(10);
-            header.out(&proxysock);  // send header to proxy
-            proxysock.checkForInput(120);
-            docheader.in(&proxysock);  // get header from proxy
-            #ifdef DGDEBUG
-                std::cout << "got header from proxy" << std::endl;
-            #endif
-            wasrequested = true;  // so we know where we are later
-
-            if (isbypass) {
-	        docheader.setCookie("GBYPASS", hashedCookie(&urldomain, filtergroup, &clientip, bypasstimestamp).toCharArray());
-	    }
-
-            mimetype = docheader.getcontenttype().toCharArray();
-            unsigned int p = (*o.fg[filtergroup]).banned_mimetype_list;
-//            i = o.banned_mimetype_list.findInList((char*)mimetype.c_str());
-            if ((i = (*o.lm.l[p]).findInList((char*)mimetype.c_str())) != NULL) {
-                checkme.whatIsNaughty = o.language_list.getTranslation(800);
-                // Banned MIME Type:
-                checkme.whatIsNaughty += i;
-                checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
-                checkme.isItNaughty = true;
-            }
-            #ifdef DGDEBUG
-                std::cout << mimetype.length() << std::endl;
-                std::cout << ":" << mimetype;
-                std::cout << ":" << std::endl;
-            #endif
-
-            if (!checkme.isItNaughty && !docheader.isRedirection()) {
-                // Can't ban file extensions of URLs that just redirect
-                String tempurl = urld;
-                String tempdispos = docheader.disposition();
-                if (tempdispos.length() > 1) {
-                    // dispos filename must take presidense
-                    #ifdef DGDEBUG
-                         std::cout << "Disposition filename:" << tempdispos << ":" << std::endl;
-                    #endif
-                    // The function expects a url so we have to
-                    // generate a psudo one.
-                    tempdispos = "http://foo.bar/" + tempdispos;
-                    if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempdispos)) != NULL) {
-                        checkme.whatIsNaughty = o.language_list.getTranslation(900);
-                        // Banned extension:
-                        checkme.whatIsNaughty += i;
-                        checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
-                        checkme.isItNaughty = true;
-                    }
-                }
-                else {
-                    if (!tempurl.contains("?")) {
-//                        i = o.inBannedExtensionList(tempurl);
-                        if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) {
-                            checkme.whatIsNaughty =  o.language_list.getTranslation(900);
-                            // Banned extension:
-                            checkme.whatIsNaughty += i;
-                            checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
-                            checkme.isItNaughty = true;
-                        }
-                    }
-                    if (String(mimetype.c_str()).contains("application/")) {
-                        while (tempurl.endsWith("?")) {
-                            tempurl.chop();
-                        }
-                        while(tempurl.contains("/")) {  // no slash no url
-                            if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) {
-                                checkme.whatIsNaughty =  o.language_list.getTranslation(900);
-                                // Banned extension:
-                                checkme.whatIsNaughty += i;
-                                checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
-                                checkme.isItNaughty = true;
-                                break;
-                            }
-                            while (tempurl.contains("/") && !tempurl.endsWith("?")) {
-                                tempurl.chop();
-                            }
-                            tempurl.chop();  // get rid of the ?
-
-                        }
-                    }
-                }
-            }
-
-            if (!checkme.isItNaughty && forceauthrequest && !docheader.authRequired()) {
-                requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock);
-            }
-
-            if (docheader.iscontenttype("text") && !checkme.isItNaughty) {
-                // here we check if its a known good one
-                // if so we skip content checking
-                waschecked = true;
-                proxysock.checkForInput(120);
-                if (docheader.isCompressed()) {
-                    docbody.setDecompress(docheader.contentEncoding());
-                }
-                #ifdef DGDEBUG
-                    std::cout << docheader.contentEncoding() << std::endl;
-                    std::cout << "about to get body from proxy" << std::endl;
-                #endif
-                pausedtoobig = docbody.in(&proxysock);  // get body from proxy
-                #ifdef DGDEBUG
-                    if (pausedtoobig) {
-                        std::cout << "got PARTIAL body from proxy" << std::endl;
-                    }
-                    else {
-                        std::cout << "got body from proxy" << std::endl;
-                    }
-                #endif
-
-                int dblen = docbody.length();
-                docsize = dblen;
-
-                if (o.url_cache_number > 0) {
-                    if (wasClean(urld)) {
-                        wasclean = true;
-                        cachehit = true;
-                        #ifdef DGDEBUG
-                            std::cout << "url was clean skipping content checking" << std::endl;
-                        #endif
-                    }
-                    // was not clean
-                    else {
-                        #ifdef DGDEBUG
-                            system("date");
-                        #endif
-                        if (dblen <= o.max_content_filter_size
-                            || o.max_content_filter_size == 0) {
-                            checkme.checkme(&docbody);  // content filtering
-                        }
-                        #ifdef DGDEBUG
-                            else {
-                                std::cout << "content length large so skipping content filtering" << std::endl;
-                            }
-                            system("date");
-                        #endif
-                    }
-                }
-                // urlcache is not used
-                else {
-                        #ifdef DGDEBUG
-                            system("date");
-                        #endif
-                        if (dblen <= o.max_content_filter_size
-                            || o.max_content_filter_size == 0) {
-                            checkme.checkme(&docbody);  // content filtering
-                        }
-                        #ifdef DGDEBUG
-                            else {
-                                std::cout << "content length large so skipping content filtering" << std::endl;
-                            }
-                            system("date");
-                        #endif
-                }
-                bool contentmodified = false;
-                if (dblen <= o.max_content_filter_size || o.max_content_filter_size == 0) {
-                    contentmodified = docbody.contentRegExp(filtergroup);  // content modifying
-                                          // uses global variable
-                }
-                #ifdef DGDEBUG
-                    else {
-                        std::cout << "content length large so skipping content modifying" << std::endl;
-                    }
-                    system("date");
-                #endif
-
-                if (contentmodified) {
-                    #ifdef DGDEBUG
-                        std::cout << "content modification made" << std::endl;
-                    #endif
-                    if (docheader.isCompressed()) {
-                        docheader.removeEncoding(dblen);
-                        // need to modify header to mark as not compressed
-                        // it also modifies Content-Length as well
-                    }
-                    else {
-                        docheader.setContentLength(docbody.buffer_length);
-                    }
-                }
-                else {
-                    docbody.swapbacktocompressed();
-                    // if we've not modified it might as well go back to
-                    // the original compressed version (if there) and send
-                    // that to the browser
-                }
-                // here if its OK then request addition to good url database
-            }
-        }
-
-        if (checkme.isException) {
-            isexception = true;
-            exceptionreason = checkme.whatIsNaughtyLog;
-        }
-
-        if (docheader.isRedirection()) {
-            checkme.isItNaughty = false;
-        }
-
-        if (o.url_cache_number > 0) {
-            if (!wasclean && !checkme.isItNaughty && docheader.iscontenttype("text") && header.requesttype() == "GET") {
+		header.in(&peerconn);  // get header from client
+		url = header.url();
+		urld = header.decode(url);
+		if (url.after("://").contains("/")) {
+			urldomain = url.after("//").before("/");
+		}
+		else {
+			urldomain = url.after("//");
+		}
+
+		if (header.malformedURL(url)) {
+			// checks for bad URLs to prevent security hole
+			try { // writestring throws exception on error/timeout
+				peerconn.writeString("HTTP/1.0 400 Bad Request\n");
+				peerconn.writeString("Content-Type: text/html\n\n");
+				peerconn.writeString("<HTML><HEAD><TITLE>DansGuardian - 400 Bad Request</TITLE></HEAD><BODY><H1>DansGuardian - 400 Bad Request</H1> ");
+				peerconn.writeString(o.language_list.getTranslation(200));
+				// The requested URL is malformed.
+				peerconn.writeString("</BODY></HTML>\n");
+			} catch (exception& e) {}
+			return;
+		}
+
+		if (o.use_xforwardedfor == 1) {
+			std::string xforwardip = header.getXForwardedForIP();
+			if (xforwardip.length() > 6) {
+				clientip = xforwardip;
+			}
+			#ifdef DGDEBUG
+				std::cout << "using x-forwardedfor:" << clientip << std::endl;
+			#endif
+		}
+
+		std::string clientuser = ident.getUsername(&header, &clientip, port);
+		// extract username
+
+		#ifdef DGDEBUG
+			std::cout << "About to determine group" << std::endl;
+		#endif
+
+		int filtergroup = determineGroup(&clientuser);
+		if (filtergroup < 0) {
+			filtergroup = determineGroup(&clientip);
+		}
+		if (filtergroup < 0) {
+			filtergroup = 0; //default group - one day configurable?
+		}
+
+		#ifdef DGDEBUG
+			std::cout << "filtergroup:" << filtergroup << std::endl;
+		#endif
+
+		if (!(o.virus_scan && o.fg[filtergroup]->virusscan)) {
+			novirusscan = true;
+			#ifdef DGDEBUG
+				std::cout << "Virus scan is disabled." << std::endl;
+			#endif
+		}
+		
+		if (o.forwarded_for == 1) {
+			header.addXForwardedFor(clientip);  // add squid-like entry
+		}
+
+		if ((*o.fg[filtergroup]).bypass_mode > 0) {
+			#ifdef DGDEBUG
+				std::cout << "About to check for bypass..." << std::endl;
+			#endif
+			bypasstimestamp = header.isBypassURL(&url, (*o.fg[filtergroup]).magic.c_str(), clientip.c_str());
+			if (bypasstimestamp > 0) {
+				#ifdef DGDEBUG
+					std::cout << "Bypass URL match" << std::endl;
+				#endif
+				header.chopBypass(url);
+				url = header.url();
+				urld = header.decode(url);
+				if (bypasstimestamp > 1) {  // not expired
+					isbypass = true;
+					exceptionreason = o.language_list.getTranslation(606);
+				}
+			}
+			else if (header.isBypassCookie(&urldomain, (*o.fg[filtergroup]).cookie_magic.c_str(), clientip.c_str())) {
+				#ifdef DGDEBUG
+					std::cout << "Bypass cookie match" << std::endl;
+				#endif
+				iscookiebypass = true;
+				isbypass = true;
+				exceptionreason = o.language_list.getTranslation(607);
+			}
+			#ifdef DGDEBUG
+				std::cout << "Finished bypass checks." << std::endl;
+			#endif
+		}
+		if (isbypass) {
+			#ifdef DGDEBUG
+				std::cout << "Bypass activated!" << std::endl;
+			#endif
+		}
+		else if (o.inipexceptions(&clientip)) {  // admin pc
+			isexception = true;
+			exceptionreason = o.language_list.getTranslation(600);
+			// Exception client IP match.
+		}
+		else if (o.inuserexceptions(&clientuser)) { // admin user
+			isexception = true;
+			exceptionreason = o.language_list.getTranslation(601);
+			// Exception client user match.
+		}
+		else if ((*o.fg[filtergroup]).inexceptions(urld)) {  // allowed site
+			if ((*o.fg[0]).iswebserver(url)) {
+				isourwebserver = true;
+			}
+			else {
+				isexception = true;
+				exceptionreason = o.language_list.getTranslation(602);
+				// Exception site match.
+			}
+		}
+		else if ((*o.fg[filtergroup]).inurlexceptions(urld)) {  // allowed url
+			isexception = true;
+			exceptionreason = o.language_list.getTranslation(603);
+			// Exception url match.
+		}
+
+		#ifdef DGDEBUG
+			std::cout << "extracted url:" << urld << std::endl;
+		#endif
+
+		// Speed improvement to take advantage of dgav url_cache_clean_only
+		// Aecio F. Neto - dgav 6.3.7
+		if (!novirusscan && o.url_cache_clean_only && (o.url_cache_number > 0)) {
+			if (wasClean(urld)) {
+				novirusscan = true;
+				#ifdef DGDEBUG
+					std::cout << "url was found in cache. Scanning disabled" << std::endl;
+				#endif
+				wasclean = true;
+				cachehit = true;
+			}
+		}
+
+		// connect to proxy
+		if ((rc = proxysock.connect(o.proxy_ip, o.proxy_port)) > 0) {
+			#ifdef DGDEBUG
+        		std::cerr << "Error connecting to proxy" << std::endl;
+			#endif
+			syslog(LOG_ERR, "%s","Error connecting to proxy");
+			return; // if we can't connect to the proxy, there is no point
+					// in continuing
+		}
+
+		if ( (isourwebserver || isexception || iscookiebypass)
+		    // don't filter exception and local web server
+		    // Cookie bypass so don't need to add cookie so just CONNECT
+	    	&& !o.inBannedIPList(&clientip)		// bad users pc
+		    && !o.inBannedUserList(&clientuser) ) { 	// bad user
+
+			proxysock.readyForOutput(10);  // exception on timeout or error
+			header.out(&proxysock);  // send proxy the request
+
+			#ifdef DGDEBUG
+				std::cout << "sent request header to proxy" << std::endl;
+			#endif
+
+			proxysock.checkForInput(120);
+			docheader.in(&proxysock);  // get body header from proxy
+			contentlength = docheader.contentlength();
+
+			#ifdef DGDEBUG
+				std::cout << "got body header from proxy" << std::endl;
+				std::cout << "contentlength = " << contentlength << std::endl;
+				std::cout << "sending reply header to client" << std::endl;
+			#endif
+
+			peerconn.readyForOutput(10);  // exceptions on error/timeout
+			docheader.out(&peerconn);  // send body header to client
+
+			#ifdef DGDEBUG
+				std::cout << "sent body header to client" << std::endl;
+			#endif
+
+			if (!novirusscan) { // see if we do not need to virus scan the content.
+				novirusscan = noVirusScanCheck(urld, filtergroup, &header, &docheader);
+			}
+	    
+			if (novirusscan) {
+				makeTunnel(&docsize, &proxysock, &peerconn);
+				try {
+					proxysock.close();
+				} catch (exception& e) {}
+			}
+			else {
+        		#ifdef DGDEBUG
+					std::cout << "calling 1st scanning routine" << std::endl;
+					std::cout << "docheader.contentlength = " << contentlength << std::endl;
+				#endif
+				try {
+					virusscan.handleScanning(&proxysock, &peerconn, url, 0, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 0);
+				} catch (exception& e) {
+					#ifdef DGDEBUG
+						std::cout << "exception caught - scanning/sending url" << std::endl;
+					#endif
+					exceptionreason = "*ERROR* - exception caught scanning/sending data" + exceptionreason;
+					decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype);
+					try {
+						proxysock.close();
+					} catch (exception& e2) {}
+					return;
+				}
+				docsize = virusscan.file_length;
+				#ifdef DGDEBUG
+					std::cout << "handlescanning properly ran" << std::endl;
+					std::cout << "docsize: " << docsize << std::endl;
+				#endif
+			}
+
+			if (!novirusscan && (virusscan.isInfected == 1)) {
+				exceptionreason = virusscan.virname;
+			}
+			
+			if (!isourwebserver) {  // don't log requests to the web server
+				decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype);
+			}
+			return;  // connection dealt with so exit
+		}
+
+		NaughtyFilter checkme;  // our filter object
+		checkme.filtergroup = filtergroup;
+
+		char* i;
+
+		// Improved IF structure as suggested by AFN
+
+		if ((!forceauthrequest || header.requesttype().startsWith("CONNECT")) && !isbypass) {
+			// if its a connect and we don't do filtering on it now then
+			// it will get tunneled and not filtered.  We can't tunnel later
+			// as its ssl so we can't see the return header etc
+			// So preemptive banning is forced on with ssl unfortunately.
+			// It is unlikely to cause many problems though.
+			requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock);
+		}
+
+		if (!checkme.isItNaughty && header.requesttype().startsWith("CONNECT")) {
+			// can't filter content of CONNECT
+			proxysock.readyForOutput(10);  // exception on timeout or error
+			header.out(&proxysock);  // send proxy the request
+			makeTunnel(&docsize, &proxysock, &peerconn);
+			decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype);
+			try {
+			    proxysock.close();  // close connection to proxy
+			} catch (exception& e) {}
+			return;  // connection dealt with so exit
+		}
+
+		if (!checkme.isItNaughty && !wasclean) {
+			proxysock.readyForOutput(10);
+			header.out(&proxysock);  // send header to proxy
+			proxysock.checkForInput(120);
+			docheader.in(&proxysock);  // get header from proxy
+			contentlength = docheader.contentlength();
+
+			#ifdef DGDEBUG
+			    std::cout << "got header from proxy" << std::endl;
+			#endif
+
+			wasrequested = true;  // so we know where we are later
+
+			if (isbypass) {
+			    docheader.setCookie("GBYPASS", hashedCookie(&urldomain, filtergroup, &clientip, bypasstimestamp).toCharArray());
+			}
+
+			mimetype = docheader.getcontenttype().toCharArray();
+			unsigned int p = (*o.fg[filtergroup]).banned_mimetype_list;
+//			i = o.banned_mimetype_list.findInList((char*)mimetype.c_str());
+			if ((i = (*o.lm.l[p]).findInList((char*)mimetype.c_str())) != NULL) {
+				checkme.whatIsNaughty = o.language_list.getTranslation(800);
+				// Banned MIME Type:
+				checkme.whatIsNaughty += i;
+				checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
+				checkme.isItNaughty = true;
+			}
+			#ifdef DGDEBUG
+				std::cout << mimetype.length() << std::endl;
+				std::cout << ":" << mimetype;
+				std::cout << ":" << std::endl;
+			#endif
+
+			if (!checkme.isItNaughty && !docheader.isRedirection()) {
+				// Can't ban file extensions of URLs that just redirect
+				String tempurl = urld;
+				String tempdispos = docheader.disposition();
+				if (tempdispos.length() > 1) {
+					// dispos filename must take presidense
+					#ifdef DGDEBUG
+						std::cout << "Disposition filename:" << tempdispos << ":" << std::endl;
+					#endif
+					// The function expects a url so we have to
+					// generate a psudo one.
+					tempdispos = "http://foo.bar/" + tempdispos;
+					if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempdispos)) != NULL) {
+						checkme.whatIsNaughty = o.language_list.getTranslation(900);
+						// Banned extension:
+						checkme.whatIsNaughty += i;
+						checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
+						checkme.isItNaughty = true;
+					}
+				}
+				else {
+					if (!tempurl.contains("?")) {
+//					i = o.inBannedExtensionList(tempurl);
+						if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) {
+							checkme.whatIsNaughty =  o.language_list.getTranslation(900);
+							// Banned extension:
+							checkme.whatIsNaughty += i;
+							checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
+							checkme.isItNaughty = true;
+						}
+					}
+					if (String(mimetype.c_str()).contains("application/")) {
+						while (tempurl.endsWith("?")) {
+							tempurl.chop();
+						}
+						while(tempurl.contains("/")) {  // no slash no url
+							if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) {
+								checkme.whatIsNaughty =  o.language_list.getTranslation(900);
+								// Banned extension:
+								checkme.whatIsNaughty += i;
+								checkme.whatIsNaughtyLog = checkme.whatIsNaughty;
+								checkme.isItNaughty = true;
+								break;
+							}
+							while (tempurl.contains("/") && !tempurl.endsWith("?")) {
+								tempurl.chop();
+							}
+							tempurl.chop();  // get rid of the ?
+						}
+					}
+				}
+			}
+
+			if (!checkme.isItNaughty && forceauthrequest && !docheader.authRequired()) {
+				requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock);
+			}
+
+			if (docheader.iscontenttype("text") && !checkme.isItNaughty) {
+				// here we check if its a known good one
+				// if so we skip content checking
+				waschecked = true;
+				proxysock.checkForInput(120);
+				if (docheader.isCompressed()) {
+					docbody.setDecompress(docheader.contentEncoding());
+				}
+				#ifdef DGDEBUG
+					std::cout << docheader.contentEncoding() << std::endl;
+					std::cout << "about to get body from proxy" << std::endl;
+				#endif
+				pausedtoobig = docbody.in(&proxysock);  // get body from proxy
+				#ifdef DGDEBUG
+					if (pausedtoobig) {
+						std::cout << "got PARTIAL body from proxy" << std::endl;
+					}
+					else {
+						std::cout << "got body from proxy" << std::endl;
+					}
+				#endif
+
+				int dblen = docbody.length();
+				docsize = dblen;
+
+				if (o.url_cache_number > 0) {
+					if (wasClean(urld)) {
+						wasclean = true;
+						cachehit = true;
+						#ifdef DGDEBUG
+							std::cout << "url was clean skipping content checking" << std::endl;
+						#endif
+					}
+	                // was not clean
+					else {
+						#ifdef DGDEBUG
+							system("date");
+						#endif
+						if (dblen <= o.max_content_filter_size
+						|| o.max_content_filter_size == 0) {
+							checkme.checkme(&docbody);  // content filtering
+						}
+						#ifdef DGDEBUG
+							else {
+								std::cout << "content length large so skipping content filtering" << std::endl;
+							}
+							system("date");
+						#endif
+					}
+				}
+				// urlcache is not used
+				else {
+					#ifdef DGDEBUG
+						system("date");
+					#endif
+					if (dblen <= o.max_content_filter_size
+					|| o.max_content_filter_size == 0) {
+						checkme.checkme(&docbody);  // content filtering
+					}
+					#ifdef DGDEBUG
+						else {
+							std::cout << "content length large so skipping content filtering" << std::endl;
+						}
+						system("date");
+					#endif
+				}
+				bool contentmodified = false;
+				if (dblen <= o.max_content_filter_size || o.max_content_filter_size == 0) {
+					contentmodified = docbody.contentRegExp(filtergroup);	// content modifying
+																			// uses global variable
+				}
+				#ifdef DGDEBUG
+					else {
+						std::cout << "content length large so skipping content modifying" << std::endl;
+					}
+					system("date");
+				#endif
+
+				if (contentmodified) {
+					#ifdef DGDEBUG
+						std::cout << "content modification made" << std::endl;
+					#endif
+					if (docheader.isCompressed()) {
+						docheader.removeEncoding(dblen);
+						// need to modify header to mark as not compressed
+						// it also modifies Content-Length as well
+					}
+					else {
+						docheader.setContentLength(docbody.buffer_length);
+					}
+				}
+				else {
+					docbody.swapbacktocompressed();
+					// if we've not modified it might as well go back to
+					// the original compressed version (if there) and send
+					// that to the browser
+				}
+			}
+		}
+
+		if (checkme.isException) {
+			isexception = true;
+			exceptionreason = checkme.whatIsNaughtyLog;
+		}
+
+		if (docheader.isRedirection()) {
+			checkme.isItNaughty = false;
+		}
+
+		if (checkme.isItNaughty && !isbypass) {  // then we deny, unless we were told to bypass the block
+			decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, checkme.whatIsNaughtyLog, header.requesttype().toCharArray(), docsize, o.ll, true, false, true, 0, false, false, &thestart, cachehit, 403, mimetype);
+			if (denyAccess(&peerconn, &proxysock, &header, &docheader, &url, &checkme, &clientuser, &clientip, filtergroup, ispostblock)) {
+				return;  // not stealth mode
+			}
+			// if get here in stealth mode
+		}
+
+		if (!wasrequested) {
+			proxysock.readyForOutput(10);
+			header.out(&proxysock); // send header to proxy
+			proxysock.checkForInput(120);
+			docheader.in(&proxysock);  // get reply header from proxy
+			contentlength = docheader.contentlength();
+		}
+
+		#ifdef DGDEBUG
+			std::cout << "sending header to client" << std::endl;
+		#endif
+		peerconn.readyForOutput(10);
+		docheader.out(&peerconn);  // send body header to client
+		#ifdef DGDEBUG
+			std::cout << "sent header to client" << std::endl;
+		#endif
+
+		if (!novirusscan) { // see if we do not need to virus scan the content.
+			novirusscan = noVirusScanCheck(urld, filtergroup, &header, &docheader);
+		}
+
+		if (waschecked) {
+			if (novirusscan) {
+				// Original Dansguardian method
+				#ifdef DGDEBUG
+					std::cout << "sending body to client" << std::endl;
+				#endif
+				peerconn.readyForOutput(10);
+				docbody.out(&peerconn); // send doc body to client
+				if (pausedtoobig) {
+					#ifdef DGDEBUG
+						std::cout << "sent PARTIAL body to client" << std::endl;
+						std::cout << "about to start tunnel to send the rest" << std::endl;
+					#endif
+					
+					makeTunnel(&docsize, &proxysock, &peerconn);
+				}
+				#ifdef DGDEBUG
+				else {
+					std::cout << "sent body to client" << std::endl;
+				}
+				#endif
+			}
+			else {
+				#ifdef DGDEBUG
+					std::cout << "creating tempfile for docbody" << std::endl;
+				#endif
+				try {
+					virusscan.createTempFile();
+					docbody.writeFile(virusscan.tempFileName);
+				} catch (exception& e) {
+					#ifdef DGDEBUG
+						std::cout << "exception caught - writing file" << std::endl;
+					#endif
+					exceptionreason = "*ERROR* - exception caught writing file" + exceptionreason;
+					decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
+					try {
+						proxysock.close();
+					} catch (exception& e2) {}
+					return;
+				}
+				#ifdef DGDEBUG
+					std::cout << "tempfile for docbody created" << std::endl;
+					std::cout << "calling 2nd scanning routine" << std::endl;
+					std::cout << "docheader.contentlength = " << contentlength << std::endl;
+				#endif
+				try {
+					virusscan.handleScanning(&proxysock, &peerconn, url, docsize, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 1);
+				} catch (exception& e) {
+					#ifdef DGDEBUG
+						std::cout << "exception caught - scanning/sending url" << std::endl;
+					#endif
+					docsize = virusscan.file_length;
+					exceptionreason = "*ERROR* - exception caught scanning/sending url" + exceptionreason;
+					decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
+					try {
+						proxysock.close();
+					} catch (exception& e2) {}
+					return;
+				}
+				docsize = virusscan.file_length;
+				if (virusscan.isInfected == 1) {
+					exceptionreason = virusscan.virname;
+				}
+				#ifdef DGDEBUG
+					std::cout << "handlescanning properly ran" << std::endl;
+					std::cout << "docsize: " << docsize << std::endl;
+				#endif
+			}
+		}
+		else { // was not supposed to be checked
+			if (novirusscan) {
+				#ifdef DGDEBUG
+					std::cout << "non-text content" << std::endl;
+					std::cout << "before checkforinput" << std::endl;
+				#endif
+				makeTunnel(&docsize, &proxysock, &peerconn);
+			}
+			else {
+				#ifdef DGDEBUG
+					std::cout << "calling 3rd scanning routine" << std::endl;
+					std::cout << "docheader.contentlength = " << contentlength << std::endl;
+				#endif
+				try {
+					virusscan.handleScanning(&proxysock, &peerconn, url, 0, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 0);
+				} catch (exception& e) {
+					#ifdef DGDEBUG
+						std::cout << "exception caught - scanning/sending url" << std::endl;
+					#endif
+					docsize = virusscan.file_length;
+					exceptionreason = "*ERROR* - exception caught scanning/sending url" + exceptionreason;
+					decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
+					try {
+						proxysock.close();
+					} catch (exception& e2) {}
+					return;
+				}
+				docsize = virusscan.file_length;
+				if (virusscan.isInfected == 1) {
+					exceptionreason = virusscan.virname;
+				}
+				#ifdef DGDEBUG
+					std::cout << "handlescanning properly ran" << std::endl;
+					std::cout << "docsize: " << docsize << std::endl;
+				#endif
+			}
+		}
+		decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
+
+		if ((o.url_cache_number > 0) && (header.requesttype() == "GET")) {
+			if (!checkme.isItNaughty && !wasclean) {
+				if (!novirusscan && o.url_cache_clean_only && !virusscan.isInfected) {
+				// Both content AND virus scanning is necessary to insert
+				// an entry into urlcache.
+
+					addToClean(urld);
+					// insert new entry in urlcache
+				}
+				else if (docheader.iscontenttype("text")) {
+					addToClean(urld);
+						// insert new entry in urlcache
+				}
+			}
+		}
+	} catch (exception& e) {
+		#ifdef DGDEBUG
+			std::cout << "connection handler caught an exception" << std::endl;
+		#endif
+		try {
+			proxysock.close();  // close connection to proxy
+		} catch (exception& e) {}
+		return;
+	}
+
+	try {
+		proxysock.close();  // close conection to squid
+	} catch (exception& e) {}
+	try {
+		peerconn.readyForOutput(10);
+	}
+	catch (exception& e) {
+		return;
+	}
+	return;
+}
 
-                addToClean(urld);
-            }
-        }
+void ConnectionHandler::makeTunnel(int *docsize, Socket *proxysock, Socket *peerconn) {
+	FDTunnel fdt;  // make a tunnel object
+	
+	try {
+		// tunnel from client to proxy and back
+		fdt.tunnel((*proxysock).getFD(), (*peerconn).getFD()); // not expected to exception
+	} catch (exception& e) {}
 
-        if (checkme.isItNaughty && !isbypass) {  // then we deny, unless we were told to bypass the block
-            decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, checkme.whatIsNaughtyLog, header.requesttype().toCharArray(), docsize, o.ll, true, false, false, false, &thestart, cachehit, 403, mimetype);
-            if (denyAccess(&peerconn, &proxysock, &header, &docheader, &url, &checkme, &clientuser, &clientip, filtergroup, ispostblock)) {
-                return;  // not stealth mode
-            }
-            // if get here in stealth mode
-        }
+	*docsize = fdt.throughput;
+}
 
-        if (wasrequested == false) {
-            proxysock.readyForOutput(10); // exceptions on error/timeout
-            header.out(&proxysock); // exceptions on error/timeout
-            proxysock.checkForInput(120); // exceptions on error/timeout
-            docheader.in(&proxysock);  // get reply header from proxy
-        }
+bool ConnectionHandler::noVirusScanCheck(String urld, int filtergroup, HTTPHeader *header, HTTPHeader *docheader) {
 
-        #ifdef DGDEBUG
-            std::cout << "sending header to client" << std::endl;
-        #endif
-        peerconn.readyForOutput(10);  // exceptions on error/timeout
-        docheader.out(&peerconn);  // send header to client
-        #ifdef DGDEBUG
-             std::cout << "sent header to client" << std::endl;
-        #endif
-        if (waschecked) {
-            if(!docheader.authRequired() && !pausedtoobig) {
-                decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
-            }
+	int contentlength = (*docheader).contentlength();
+	String contenttype = (*docheader).getcontenttype();
 
-            #ifdef DGDEBUG
-                std::cout << "sending body to client" << std::endl;
-            #endif
-            peerconn.readyForOutput(10); // check for error/timeout needed
-            docbody.out(&peerconn); // send doc body to client
-            #ifdef DGDEBUG
-                if (pausedtoobig) {
-                    std::cout << "sent PARTIAL body to client" << std::endl;
-                }
-                else {
-                    std::cout << "sent body to client" << std::endl;
-                }
-            #endif
-            if (pausedtoobig) {
-                #ifdef DGDEBUG
-                    std::cout << "about to start tunnel to send the rest" << std::endl;
-                #endif
+	if (o.virus_scan && o.fg[filtergroup]->virusscan) { // see if we do not need to virus scan the content.
+		#ifdef DGDEBUG
+			std::cout << "checking to see if we don't have to virus scan - case 2" << std::endl;
+			std::cout << "url:" << urld << std::endl;
+		#endif
+
+		if ((*header).requesttype().startsWith("CONNECT")) {
+			#ifdef DGDEBUG
+				std::cout << "http method is CONNECT - virus scan skipped." << std::endl;
+			#endif
+			return true;
+		}
+		else if ((*o.fg[filtergroup]).invirussiteexceptions(urld)) {
+			#ifdef DGDEBUG
+				std::cout << "address found in virus scan exception site" << std::endl;
+			#endif
+			return true;
+		}
+		else if ((*o.fg[filtergroup]).invirusurlexceptions(urld)) {
+			#ifdef DGDEBUG
+				std::cout << "address found in virus scan exception url" << std::endl;
+			#endif
+			return true;
+		}
+		else if ((o.max_content_scan_size > 0) && (contentlength > o.max_content_scan_size)) {
+			#ifdef DGDEBUG
+				std::cout << "contentlength is bigger than content size to be scanned" << std::endl;
+			#endif
+			return true;
+		}
+		else if (o.exception_virus_mimetype_list.AVfindInList(contenttype.toCharArray()) >= 0) {
+			#ifdef DGDEBUG
+				std::cout << "mimetype '" << contenttype.toCharArray() << "' in exception virus mime-type list" << std::endl;
+			#endif
+			return true;
+		}
+		else if (o.inVirusExceptionExtensionList(urld) >= 0) {
+			#ifdef DGDEBUG
+				std::cout << "file extension in exception virus extension list" << std::endl;
+			#endif
+			return true;
+		}
+	}
+	else {
+		return true;
+	}
 
-                FDTunnel fdt;
-                #ifdef DGDEBUG
-                    std::cout << "tunnel activated" << std::endl;
-                #endif
-                fdt.tunnel(proxysock.getFD(), peerconn.getFD());
-                docsize += fdt.throughput;
-                decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
-            }
-        }
-        else {  // was not supposed to be checked
-            FDTunnel fdt;
-            #ifdef DGDEBUG
-                std::cout << "tunnel activated" << std::endl;
-            #endif
-            fdt.tunnel(proxysock.getFD(), peerconn.getFD());
-            docsize = fdt.throughput;
-
-            decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype);
-
-        }
-    } catch (exception& e) {
-        #ifdef DGDEBUG
-            std::cout << "connection handler caught an exception" << std::endl;
-        #endif
-        try {
-            proxysock.close();  // close connection to proxy
-        } catch (exception& e) {}
-        return;
-    }
-
-    try {
-        proxysock.close();  // close conection to squid
-    } catch (exception& e) {}
-    try {
-        peerconn.readyForOutput(10);
-    }
-    catch (exception& e) {
-        return;
-    }
-    return;
+	return false;
 }
 
 // if we don't do this the browsers complain
@@ -710,7 +914,7 @@
 
 
 
-void ConnectionHandler::decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughty, bool isexception, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype) {
+void ConnectionHandler::decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughty, bool isexception, bool novirusscan, int infected, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype) {
 
     if (loglevel == 0) {
         return;
@@ -742,16 +946,23 @@
         // make it stand out in the logs and also
         // more easily findable with a search
     }
+
     if (isexception) {
        if (logexceptions == 1) {
             what = "*EXCEPTION* " + what;
         }
-        else {
-            what = "";
-        }
     }
+
+    if (infected == 1) {
+		what = "*INFECTED* FOUND:" + what;
+	}
+	else if (!novirusscan) {
+		what = "*SCANNED* " + what;
+	}
+
     if ((isexception && logexceptions == 1)
          || isnaughty
+         || (infected == 1)
          || loglevel == 3
          || (loglevel == 2 && istext)) {
         doTheLogMan(who, from, where, what, how, size, thestart, cachehit, code, mimetype);
diff -Naur dansguardian-2.8.0.4/ConnectionHandler.hpp dansguardian-2.8.0.4.oden/ConnectionHandler.hpp
--- dansguardian-2.8.0.4/ConnectionHandler.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ConnectionHandler.hpp	2005-06-17 12:30:35.304932326 +0200
@@ -32,11 +32,13 @@
     void handleConnection(int peerfd, String ip, int port);
 
 private:
+	void makeTunnel(int *docsize, Socket *proxysock, Socket *peerconn);
+	bool noVirusScanCheck(String urld, int filtergroup, HTTPHeader *header, HTTPHeader *docheader);
+	std::string miniURLEncode(std::string s);
     void doTheLogMan(std::string who, std::string from, std::string where, std::string what, std::string how, int size, struct timeval *thestart, bool cachehit, int code, std::string mimetype);
-    std::string miniURLEncode(std::string s);
-    void decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughtly, bool isexception, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype);
-    bool wasClean(String url);
+    void decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughtly, bool isexception, bool novirusscan, int infected, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype);
     void addToClean(String url);
+    bool wasClean(String url);
     void requestChecks(HTTPHeader *header, NaughtyFilter *checkme, String *urld, std::string *clientip, std::string *clientuser, int filtergroup, bool *ispostblock);
     bool isIPHostnameStrip(String url);
     int determineGroup(std::string *user);
diff -Naur dansguardian-2.8.0.4/DataBuffer.cpp dansguardian-2.8.0.4.oden/DataBuffer.cpp
--- dansguardian-2.8.0.4/DataBuffer.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/DataBuffer.cpp	2005-06-17 12:30:35.305932348 +0200
@@ -234,7 +234,7 @@
     }
     while (true) {
         #ifdef DGDEBUG
-            std::cerr << "inflate loop" << std::endl;
+            std::cout << "inflate loop" << std::endl;
         #endif
         err = inflate(&d_stream, Z_NO_FLUSH);
         bout = newsize - bytesgot - d_stream.avail_out;
@@ -340,3 +340,25 @@
         compressed_buffer_length = 0;
     } // drop the decompressed version if there
 }
+
+// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+void DataBuffer::writeFile(char* tempFileName) throw(exception) {
+    int i = 0;
+    fstream tmpFile;
+    
+    tmpFile.open(tempFileName, ios::out);
+
+    #ifdef DGDEBUG
+	std::cout << "DataBuffer - filename: " << tempFileName << std::endl;
+        std::cout << "DataBuffer - writing to file " << tempFileName << ", length = '" << buffer_length << "'" << std::endl;
+    #endif
+
+    for (i = 0; i < buffer_length; i++) 
+	tmpFile.put(data[i]);
+
+    #ifdef DGDEBUG
+        std::cout << "DataBuffer - wrote to " << tempFileName << ", length = '" << i << "'" << std::endl;
+    #endif
+    tmpFile.close();
+}
+ // End of Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
diff -Naur dansguardian-2.8.0.4/DataBuffer.hpp dansguardian-2.8.0.4.oden/DataBuffer.hpp
--- dansguardian-2.8.0.4/DataBuffer.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/DataBuffer.hpp	2005-06-17 12:30:35.305932348 +0200
@@ -42,7 +42,10 @@
     void setDecompress(String d);
     bool contentRegExp(int filtergroup);
     void swapbacktocompressed();
-
+    // Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+    void writeFile(char* tempFileName) throw(exception);
+    // End of Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+    
 private:
     int timeout;
     String decompress;
diff -Naur dansguardian-2.8.0.4/FOptionContainer.cpp dansguardian-2.8.0.4.oden/FOptionContainer.cpp
--- dansguardian-2.8.0.4/FOptionContainer.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/FOptionContainer.cpp	2005-06-17 12:30:35.306932369 +0200
@@ -47,6 +47,8 @@
     o.lm.deRefList(grey_url_list);
     o.lm.deRefList(banned_regexpurl_list);
     o.lm.deRefList(content_regexp_list);
+    o.lm.deRefList(exception_virus_site_list);
+    o.lm.deRefList(exception_virus_url_list);
 }
 
 void FOptionContainer::reset() {
@@ -61,6 +63,10 @@
     o.lm.deRefList(grey_url_list);
     o.lm.deRefList(banned_regexpurl_list);
     o.lm.deRefList(content_regexp_list);
+
+    o.lm.deRefList(exception_virus_site_list);
+    o.lm.deRefList(exception_virus_url_list);
+
     banned_phrase_list_index.clear();
     conffile.clear();
     banned_regexpurl_list_comp.clear();
@@ -148,6 +154,20 @@
             enable_PICS = 1;
         }
 
+	// per filtergroup virusscan option
+        if (findoptionS("virusscan") == "off") {
+		#ifdef DGDEBUG
+		    std::cout << "virusscan for group is: off" << std::endl;
+		#endif
+            virusscan = 0;
+        }
+        else {
+		#ifdef DGDEBUG
+		    std::cout << "virusscan for group is: on" << std::endl;
+		#endif
+            virusscan = 1;
+        }
+
         naughtyness_limit = findoptionI("naughtynesslimit");
         if (!realitycheck(String(naughtyness_limit), 1, 4, "naughtynesslimit"))
             { return false; }
@@ -165,6 +185,9 @@
         exceptions_site_list_location = findoptionS("exceptionsitelist");
         exceptions_url_list_location = findoptionS("exceptionurllist");
 
+        exceptions_virus_site_list_location = findoptionS("exceptionvirussitelist");
+        exceptions_virus_url_list_location = findoptionS("exceptionvirusurllist");
+
         pics_rsac_nudity = findoptionI("RSACnudity");
         pics_rsac_language = findoptionI("RSAClanguage");
         pics_rsac_sex = findoptionI("RSACsex");
@@ -274,7 +297,6 @@
         if (!readeslfile(exceptions_site_list_location.c_str())) {
             return false;
         }  // site exceptions
-
         if (!readeurllfile(exceptions_url_list_location.c_str())) {
             return false;
         }  // url exceptions
@@ -304,6 +326,14 @@
         if (!readcrelfile(content_regexp_list_location.c_str())) {
             return false;
         }  // content replacement regular expressions
+
+        if (!readevslfile(exceptions_virus_site_list_location.c_str())) {
+            return false;
+        }  // site exceptions
+
+        if (!readevurllfile(exceptions_virus_url_list_location.c_str())) {
+            return false;
+        }  // url exceptions
         #ifdef DGDEBUG
             std::cout << "lists into memory" << filename << std::endl;
         #endif
@@ -1127,3 +1157,113 @@
     return false;
 }
 
+bool FOptionContainer::readevslfile(const char* filename) {
+    int result = o.lm.newItemList(filename, false, 0, true);
+    if (result < 0) {
+        if (!isDaemonised) {
+            std::cerr << "Error opening exceptionvirussitelist" << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error opening exceptionvirussitelist");
+        return false;
+    }
+    exception_virus_site_list = (unsigned)result;
+    if (!(*o.lm.l[exception_virus_site_list]).used) {
+        (*o.lm.l[exception_virus_site_list]).endsWithSort();
+        if (createlistcachefiles == 1) {
+            if (!(*o.lm.l[exception_virus_site_list]).createCacheFile()) {
+                return false;
+            }
+        }
+        (*o.lm.l[exception_virus_site_list]).used = true;
+    }  // idea is that if the list has already been used it is already
+    // compiled, sorted, etc so no point doing it again
+    return true;
+}
+
+bool FOptionContainer::readevurllfile(const char* filename) {
+    int result = o.lm.newItemList(filename, false, 0, true);
+    if (result < 0) {
+        if (!isDaemonised) {
+            std::cerr << "Error opening exceptionvirusurllist" << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error opening exceptionvirusurllist");
+        return false;
+    }
+    exception_virus_url_list = (unsigned)result;
+    if (!(*o.lm.l[exception_virus_url_list]).used) {
+        (*o.lm.l[exception_virus_url_list]).startsWithSort();
+        if (createlistcachefiles == 1) {
+            if (!(*o.lm.l[exception_virus_url_list]).createCacheFile()) {
+                return false;
+            }
+        }
+        (*o.lm.l[exception_virus_url_list]).used = true;
+    }
+    return true;
+}
+
+bool FOptionContainer::invirussiteexceptions(String url) {
+    if (iswebserver(url)) {  // don't filter our web server
+        return true;
+    }
+    url.toLower();
+    url.removePTP();  // chop off the ht(f)tp(s)://
+    if (url.contains("/")) {
+        url = url.before("/");  // chop off any path after the domain
+    }
+    char *i;
+    while (url.contains(".")) {
+        i = (*o.lm.l[exception_virus_site_list]).findInList(url.toCharArray());
+        if (i != NULL) {
+            return true;  // exact match
+        }
+        url = url.after(".");  // check for being in higher level domains
+    }
+    if (url.length() > 1) {  // allows matching of .tld
+        url = "." + url;
+        i = (*o.lm.l[exception_virus_site_list]).findInList(url.toCharArray());
+        if (i != NULL) {
+            return i;  // exact match
+        }
+    }
+    return false;  // and our survey said "UUHH UURRGHH"
+}
+
+
+bool FOptionContainer::invirusurlexceptions(String url) {
+    int fl;
+    char *i;
+    String foundurl;
+    url.removeWhiteSpace();  // just in case of weird browser crap
+    url.toLower();
+    url.removePTP();  // chop off the ht(f)tp(s)://
+    if (url.contains("/")) {
+        String tpath = "/";
+        tpath += url.after("/");
+        url = url.before("/");
+        tpath.hexDecode();
+        tpath.realPath();
+        url += tpath;  // will resolve ../ and %2e2e/ and // etc
+    }
+    if (url.endsWith("/")) {
+        url.chop();  // chop off trailing / if any
+    }
+    while (url.before("/").contains(".")) {
+        i = (*o.lm.l[exception_virus_url_list]).findStartsWith(url.toCharArray());
+        if (i != NULL) {
+            foundurl = i;
+            fl = foundurl.length();
+            if (url.length() > fl) {
+                unsigned char c = url[fl];
+                if (c == '/' || c == '?' || c == '&' || c == '=') {
+                    return true; // matches /blah/ or /blah/foo but not /blahfoo
+                }
+            }
+            else {
+                return true;  // exact match
+            }
+        }
+        url = url.after(".");  // check for being in higher level domains
+    }
+    return false;
+}
diff -Naur dansguardian-2.8.0.4/FOptionContainer.hpp dansguardian-2.8.0.4.oden/FOptionContainer.hpp
--- dansguardian-2.8.0.4/FOptionContainer.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/FOptionContainer.hpp	2005-06-17 12:30:35.307932391 +0200
@@ -32,20 +32,25 @@
 
 public:
     ~FOptionContainer();
-    bool read(std::string filename);
     void reset();
+    bool read(std::string filename);
     bool inexceptions(String url);
     bool iswebserver(String url);
     bool inurlexceptions(String url);
-    char* inBannedSiteList(String url);
-    char* inBannedURLList(String url);
     bool inGreySiteList(String url);
     bool inGreyURLList(String url);
-    int inBannedRegExpURLList(String url);
-    char* inBannedExtensionList(String url);
     bool isIPHostname(String url);
+
+    bool invirussiteexceptions(String url);
+    bool invirusurlexceptions(String url);
+
+    char* inBannedSiteList(String url);
+    char* inBannedURLList(String url);
+    char* inBannedExtensionList(String url);
+    int inBannedRegExpURLList(String url);
     int weighted_phrase_mode;
     int naughtyness_limit;
+    int virusscan;
     int createlistcachefiles;
     int enable_PICS;
     int blanketblock;
@@ -138,6 +143,10 @@
     std::string banned_mimetype_list_location;
     std::string exceptions_site_list_location;
     std::string exceptions_url_list_location;
+
+    std::string exceptions_virus_site_list_location;
+    std::string exceptions_virus_url_list_location;
+
     unsigned int banned_phrase_list;
     unsigned int exception_site_list;
     unsigned int exception_url_list;
@@ -149,6 +158,10 @@
     unsigned int grey_url_list;
     unsigned int banned_regexpurl_list;
     unsigned int content_regexp_list;
+
+    unsigned int exception_virus_site_list;
+    unsigned int exception_virus_url_list;
+
     std::deque<int> banned_phrase_list_index;
     std::deque<RegExp> banned_regexpurl_list_comp;
     std::deque<String> banned_regexpurl_list_source;
@@ -178,13 +191,15 @@
     bool readbreulfile(const char* filename);
     bool compilebreulfile(unsigned int list);
     bool readcrelfile(const char* filename);
+
+    bool readevslfile(const char* filename);
+    bool readevurllfile(const char* filename);
+
     void bwlfilehelper(String line, int index);
     void bwlfilehelperhelper(String line, int index);
     int findoptionI(const char* option);
     std::string findoptionS(const char* option);
     bool realitycheck(String s, int minl, int maxl, char* emessage);
-
-
 };
 
 #endif
diff -Naur dansguardian-2.8.0.4/ListContainer.cpp dansguardian-2.8.0.4.oden/ListContainer.cpp
--- dansguardian-2.8.0.4/ListContainer.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ListContainer.cpp	2005-06-17 12:30:35.308932413 +0200
@@ -1314,3 +1314,106 @@
     }
     return true;
 }
+
+// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+int ListContainer::AVfindEndsWith(char* string) {
+    if (items < 1) {
+        return -1;
+    }
+    return searchREW(0, items-1, string);
+}
+
+int ListContainer::AVfindInList(char* string) {
+    if (items < 1) {
+        return -1;
+    }
+    if (isSW) {
+        return searchRSWF(0, items-1, string);
+    }
+    return searchREWF(0, items-1, string);
+}
+
+bool ListContainer::AVreadItemList(const char* filename, bool startswith, int filters) {
+    std::string linebuffer;
+    RegExp re;
+    re.comp("^.*\\:[0-9]+\\/.*");
+    #ifdef DGDEBUG
+        std::cout << filename << std::endl;
+    #endif
+    if (isCacheFileNewer(filename)) {  // see if cached .process file
+        linebuffer = filename; //         is available and up to date
+        linebuffer += ".processed";
+
+        if (getFileLength(linebuffer.c_str()) >= 4000) {
+                                      // don't bother with small files
+            if (!readProcessedItemList(linebuffer.c_str(), startswith, filters)) {  // read cached
+                return false;
+            }
+            issorted = true;  // don't bother sorting cached file
+            return true;
+        }
+    }
+    int len = getFileLength(filename);
+    if (len < 0) {
+        if (!isDaemonised) {
+            std::cerr << "Error reading file: " << filename << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error reading file:");
+        syslog(LOG_ERR, "%s",filename);
+        return false;
+    }
+    if (len < 4) {
+        return true;  // its blank - perhaps due to webmin editing
+                    // just return
+    }
+    increaseMemoryBy(len);  // Allocate some memory to hold file
+    ifstream listfile(filename, ios::in);
+    if (!listfile.good()) {
+        if (!isDaemonised) {
+            std::cerr << "Error opening :"<< filename << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error opening :");
+        syslog(LOG_ERR, "%s", filename);
+        return false;
+    }
+    String temp, inc, hostname, url;
+    while (!listfile.eof()) {
+        getline(listfile, linebuffer);
+        if (linebuffer.length() < 2) continue;  // its jibberish
+        if (linebuffer[0] == '#') continue;  // its a comment
+        temp = (char*)linebuffer.c_str();
+        if (temp.contains("#")) {
+            temp = temp.before("#");  // tidy up
+        }
+        temp.removeWhiteSpace();  // tidy up and make it handle CRLF files
+        if (temp.startsWith(".Include<")) {  // see if we have another list
+            inc = temp.after(".Include<");  // to include
+            inc = inc.before(">");
+            if (!readItemList(inc.toCharArray(), startswith, filters)) {  // read it
+                listfile.close();
+                return false;
+            }
+            continue;
+        }
+        if (temp.endsWith("/")) {
+            temp.chop();  // tidy up
+        }
+        if (temp.startsWith("ftp://")) {
+            temp = temp.after("ftp://");  // tidy up
+        }
+        if (filters == 1) {  // remove port addresses
+            if (temp.contains(":")) {  // quicker than full regexp
+                if (re.match(temp.toCharArray())) {
+                    hostname = temp.before(":");
+                    url = temp.after("/");
+                    temp = hostname + "/" + url;
+                }
+            }
+        }
+        temp.toLower();  // tidy up
+        addToItemList(temp.toCharArray(), temp.length());  // add to unsorted
+                                                           // list
+    }
+    listfile.close();
+    return true;  // sucessful read
+}
diff -Naur dansguardian-2.8.0.4/ListContainer.hpp dansguardian-2.8.0.4.oden/ListContainer.hpp
--- dansguardian-2.8.0.4/ListContainer.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/ListContainer.hpp	2005-06-17 12:30:35.309932435 +0200
@@ -78,6 +78,11 @@
     std::deque<unsigned int> morelists;  // has to be non private as reg
     // exp compiler needs to access these
 
+    // Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+    int AVfindInList(char* string);
+    int AVfindEndsWith(char* string);
+    bool AVreadItemList(const char* filename, bool startswith, int filters);
+
 private:
 
     bool sourceisexception;
diff -Naur dansguardian-2.8.0.4/Makefile.in dansguardian-2.8.0.4.oden/Makefile.in
--- dansguardian-2.8.0.4/Makefile.in	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/Makefile.in	2005-06-17 12:30:35.309932435 +0200
@@ -3,4 +3,3 @@
 #Please email me (daniel@/jadeb .com) if you have modified this file for any
 #particular distribution or if I have got anything wrong.
 #Don't forget the trailing /
-
diff -Naur dansguardian-2.8.0.4/OptionContainer.cpp dansguardian-2.8.0.4.oden/OptionContainer.cpp
--- dansguardian-2.8.0.4/OptionContainer.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/OptionContainer.cpp	2005-06-17 12:30:35.310932456 +0200
@@ -45,6 +45,8 @@
     deleteFilterGroups();
     exception_user_list.reset();
     exception_ip_list.reset();
+    exception_virus_mimetype_list.reset();
+    exception_virus_extension_list.reset();
     banned_ip_list.reset();
     banned_user_list.reset();
     html_template.reset();
@@ -317,6 +319,101 @@
            reverse_client_ip_lookups = 0;
         }
 
+	// Virus scanning options
+	if (findoptionS("virusscan") == "on") {
+           virus_scan = 1;
+        }
+        else {
+           virus_scan = 0;
+        }
+
+	std::string enginefound;
+	if ((enginefound = findoptionS("virusengine")) == "") {
+	    virus_scan = 0;
+	}
+	else if (enginefound == "clamav")
+	    virus_engine = 1;
+	else if (enginefound == "kav")
+	    virus_engine = 2;
+	else if (enginefound == "clamdscan")
+	    virus_engine = 3;
+	else
+	    virus_scan = 0;
+
+	trickle_length = findoptionI("tricklelength");
+        if (!realitycheck(String(trickle_length), 1, 6, "tricklelength"))
+            { return false; }
+	if (trickle_length <= 0)
+	    trickle_length = -1;
+
+	first_trickle_delay = findoptionI("firsttrickledelay");
+        if (!realitycheck(String(first_trickle_delay), 1, 3, "firsttrickledelay"))
+            { return false; }
+	if (first_trickle_delay < 1)
+	    first_trickle_delay = 60;
+
+	following_trickle_delay = findoptionI("followingtrickledelay");
+        if (!realitycheck(String(following_trickle_delay), 1, 3, "followingtrickledelay"))
+            { return false; }
+	if (following_trickle_delay < 1)
+	    following_trickle_delay = 30;
+
+        max_content_scan_size = findoptionI("maxcontentscansize") * 1024;
+        if (!realitycheck(String(max_content_filter_size), 1, 8, "maxcontentscansize"))
+            { return false; }
+	if (max_content_scan_size < 1)
+	    max_content_scan_size = 0; // assumes default: no limit
+
+	if (findoptionS("virusscanexceptions") == "on") {
+	    virus_scan_exceptions = 1;
+	}
+	else {
+	    virus_scan_exceptions = 0;
+	}
+
+	if (findoptionS("urlcachecleanonly") == "on") {
+	    url_cache_clean_only = 1;
+	}
+	else {
+	    url_cache_clean_only = 0;
+	}
+
+	if ((download_dir = findoptionS("downloaddir")) == "")
+	    download_dir = "/tmp/dgvirus";
+
+	cl_max_files = findoptionI("clmaxfiles");
+        if (!realitycheck(String(cl_max_files), 1, 5, "clmaxfiles"))
+            { return false; }
+	if (cl_max_files < 1)
+	    cl_max_files = 1500; // assumes default: 1500 files
+
+	cl_max_rec_level = findoptionI("clmaxreclevel");
+        if (!realitycheck(String(cl_max_rec_level), 1, 1, "clmaxreclevel"))
+            { return false; }
+	if (cl_max_rec_level < 1)
+	    cl_max_rec_level = 3; // assumes default: 3 levels
+
+	cl_max_file_size = findoptionI("clmaxfilesize");
+        if (!realitycheck(String(cl_max_file_size), 1, 10, "cl_maxfilesize"))
+            { return false; }
+	if (cl_max_file_size < 1)
+	    cl_max_file_size = 10485760; // assumes default: 10 megabytes
+
+	virus_scanner_timeout = findoptionI("virusscannertimeout");
+
+        notify = findoptionI("notify");
+        if (!realitycheck(notify, 1, 1, "notify")) { 
+	    notify = 0; // disabled
+	}
+	postmaster = findoptionS("postmaster");
+	email_domain = findoptionS("emaildomain");
+	email_server = findoptionS("emailserver");
+
+
+	if ((localsocket = findoptionS("localsocket")) == "") {
+	    localsocket = "/tmp/clamd";
+	}
+	// End of Virus scanning options
 
         if (findoptionS("usexforwardedfor") == "on") {
 	   use_xforwardedfor = 1;
@@ -342,6 +439,10 @@
         banned_user_list_location = findoptionS("banneduserlist");
         exceptions_user_list_location = findoptionS("exceptionuserlist");
         exceptions_ip_list_location = findoptionS("exceptioniplist");
+	
+	exceptions_virus_mimetype_list_location = findoptionS("exceptionvirusmimetypelist");
+	exceptions_virus_extension_list_location = findoptionS("exceptionvirusextensionlist"); 
+
         language_list_location = findoptionS("languagedir") + "/" + findoptionS("language") + "/messages";
         access_denied_address = findoptionS("accessdeniedaddress");
         ada = access_denied_address.c_str();
@@ -382,6 +483,16 @@
             return false;
         }  // site exceptions
 
+       if (virus_scan) { 
+           if (!readevmlfile(exceptions_virus_mimetype_list_location.c_str())) { 
+               return false; 
+           }  // virus mimetype exceptions 
+            
+           if (!readevelfile(exceptions_virus_extension_list_location.c_str())) { 
+               return false; 
+           }  // virus extensions exceptions 
+       } 
+
         if (!language_list.readLanguageList(language_list_location.c_str())) {
             return false;
         }  // messages language file
@@ -679,4 +790,45 @@
     return true;
 }
 
+// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+int OptionContainer::inVirusExceptionExtensionList(String url) {
+    url.removeWhiteSpace();  // just in case of weird browser crap
+    url.toLower();
+    url.removePTP();  // chop off the ht(f)tp(s)://
+    url = url.after("/");  // chop off any domain before the path
+    if (url.length() < 2) {  // will never match
+        return -1;
+    }
+    return exception_virus_extension_list.AVfindEndsWith(url.toCharArray());
+}
+
+bool OptionContainer::readevmlfile(const char* filename) {
+
+    bool result = exception_virus_mimetype_list.AVreadItemList(filename, false, 0);
+    if (!result) {
+        if (!isDaemonised) {
+            std::cerr << "Error opening exceptionvirusmimetypelist" << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error opening exceptionvirusmimetypelist");
+        return false;
+    }
+    exception_virus_mimetype_list.endsWithSort();
+    return true;
+}
+
+bool OptionContainer::readevelfile(const char* filename) {
+
+    bool result = exception_virus_extension_list.AVreadItemList(filename, false, 0);
+    if (!result) {
+        if (!isDaemonised) {
+            std::cerr << "Error opening exceptionvirusextensionlist" << std::endl;
+        }
+        syslog(LOG_ERR, "%s","Error opening exceptionvirusextensionlist");
+        return false;
+    }
+    exception_virus_extension_list.endsWithSort();
+    return true;
+}
+
+
 
diff -Naur dansguardian-2.8.0.4/OptionContainer.hpp dansguardian-2.8.0.4.oden/OptionContainer.hpp
--- dansguardian-2.8.0.4/OptionContainer.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/OptionContainer.hpp	2005-06-17 12:30:35.310932456 +0200
@@ -44,6 +44,7 @@
     bool inBannedUserList(const std::string *user);
     bool readFilterGroupConf();
     bool readfgfile(const char* filename);
+    int inVirusExceptionExtensionList(String url);
     int filter_groups;
     int log_exception_hits;
     int non_standard_delimiter;
@@ -87,12 +88,33 @@
     int proxy_group;
     int root_user;
 
+    int virus_scan;         // signals if we are using the Virus Scanner Code. 
+    int virus_engine;
+    int first_trickle_delay;
+    int following_trickle_delay;
+    int max_content_scan_size;
+    int trickle_length;
+    int virus_scan_exceptions;
+    int url_cache_clean_only;
+    std::string download_dir;
+    int cl_max_files;
+    int cl_max_rec_level;
+    unsigned long int cl_max_file_size;
+    int virus_scanner_timeout;
+    int notify;
+    std::string postmaster;
+    std::string email_domain;
+    std::string email_server;
+    std::string localsocket;
+
     std::string filter_groups_list_location;
     std::string html_template_location;
     std::string banned_ip_list_location;
     std::string banned_user_list_location;
     std::string exceptions_user_list_location;
     std::string exceptions_ip_list_location;
+    std::string exceptions_virus_mimetype_list_location;
+    std::string exceptions_virus_extension_list_location;
     std::string language_list_location;
     std::string access_denied_address;
     std::string log_location;
@@ -110,6 +132,8 @@
     ListContainer filter_groups_list;
     ListContainer exception_user_list;
     ListContainer exception_ip_list;
+    ListContainer exception_virus_mimetype_list;
+    ListContainer exception_virus_extension_list;
     ListContainer banned_ip_list;
     ListContainer banned_user_list;
     LanguageContainer language_list;
@@ -132,6 +156,8 @@
     bool realitycheck(String s, int minl, int maxl, char* emessage);
     bool readAnotherFilterGroupConf(const char *filename);
 
+    bool readevmlfile(const char* filename);  // exception virus mimetype list
+    bool readevelfile(const char* filename);  // exception virus extension list
 };
 
 #endif
diff -Naur dansguardian-2.8.0.4/README.AV dansguardian-2.8.0.4.oden/README.AV
--- dansguardian-2.8.0.4/README.AV	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/README.AV	2005-06-17 12:30:35.311932478 +0200
@@ -0,0 +1,306 @@
+For all support, instructions and copyright go to:
+http://www.sourceforge.net/projects/dgav
+
+DansGuardian is a content filtering proxy that works in conjunction with
+another caching proxy such as Squid or Oops.
+
+DansGuardian Anti-Virus Plugin is an add-on to provide real-time virus scanning
+capabilities for content access through DansGuardian.
+
+Original code was written by
+James A. Pattie (james@pcxperience.com)
+(c) 2002 by Xperience, Inc. (http://www.pcxperience.com)
+Licensed under GPL.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    
+This add-on is a modified version, developed and maintained by:
+Aecio F. Neto (afn@harvest.com.br)
+Harvest Consultoria (http://www.harvest.com.br)
+-----
+Nerijus Baliunas (nerijus@users.sourceforge.net)
+http://www.sat.lt
+-----
+Alexander Marx (mad-ml@madness.at)
+http://www.madness.at
+
+
+CONTENTS
+-----------------------------------------
+Instructions
+References
+Configuration options
+Configuration options for CLAMDSCAN
+Configuration options for CLAMAV
+Known issues
+Enabling CLAMD daemon
+Enabling KASPERSKY
+Information for developers
+
+
+Instructions
+-----------------------------------------
+To be able to compile this program it is necessary to have ClamAV or Kaspersky
+antivirus software installed. No other engine is supported up to now.
+Because this program uses library calls to one of these engines, it is
+necessary to have also their respective development libraries and
+include files available before compiling.
+
+
+References
+-----------------------------------------
+1. Anti-Virus plugin
+Sourceforge project
+http://www.sourceforge.net/projects/dgav
+
+Web content filtering portal at Harvest Consultoria
+http://www.harvest.com.br/asp/afn/wcfp.nsf
+
+Antivirus plugin at pcxperience
+http://www.pcxperience.com/dgvirus/index.html
+
+
+2. ClamAV and ClamDScan
+Official web site:
+http://www.clamav.net
+
+Debian package:
+apt-get install libclamav1-dev
+
+(also recommended)
+apt-get install clamav-freshclam
+
+RPM based systems:
+Install clamav from source or check available versions at
+http://www.rpmfind.net
+
+
+3. Kaspersky
+Official web site:
+http://www.kasperskylabs.com
+
+KAV Client
+http://kavclient.sourceforge.net
+
+
+4. libesmtp
+Official web site:
+http://www.stafford.uklinux.net/libesmtp
+
+Debian package:
+apt-get install libesmtp5 libesmtp-dev
+
+RPM based systems:
+ftp://chbm.nu/pub/
+or install libesmtp from source or check available versions at
+http://www.rpmfind.net
+
+
+Configuration options
+-----------------------------------------
+1. virusscan
+If on, we scan all downloaded content using embedded virus engine.
+Only supported engines of this version are ClamAV or Kaspersky.
+If off, we don't scan any downloaded content.
+
+Default value: on
+
+2. tricklelength
+If off (value = -1), the scanner will send 1 byte per delay period
+to the client to keep a download connection alive.
+When the whole file is downloaded and scanned, the client will receive 
+all remaining bytes, if the file is clean.
+Set to a positive integer value to enable immediate delivery to the client. 
+Value set means minimum number of bytes of the downloaded file 
+that will be held and delivered after virus scan.
+If clean, the remaining bytes will be sent to the client.
+If infected, file downloaded will be incomplete and a warning message 
+will be sent to the postmaster and possibly the user.
+
+Default value: 32768
+
+3. firsttrickledelay
+Delay in seconds to deliver the first byte to the client.
+This option only applies if tricklelength is set to -1 (off).
+
+Default value: 30
+
+4. follwingtrickledelay
+Delay in seconds to deliver subsequent bytes to the client.
+This option only applies if tricklelength is set to -1 (off).
+
+Defaul value: 60
+
+5. exceptionvirusmimetypelist
+Full path to file that defines mime types that will *not* be virus scanned.
+
+Default value: /<config dir>/exceptionvirusmimetypelist
+
+6. exceptionvirusextensionlist
+File that defines file extensions that will *not* be virus scanned.
+
+Default value: /<config dir>/exceptionvirusextensionlist
+
+7. downloaddir
+Set where the files are downloaded to before they are scanned.
+
+Default value: /tmp/dgvirus
+
+8. virusscanexceptions
+If off, antivirus scanner will ignore exception sites and urls.
+
+Default value: on
+
+9. urlcachecleanonly
+If off, url cache will contain entries of text only urls.
+Keeping it off, preserves original Dansguardian feature and
+downloaded content will be always scanned by antivirus.
+When turned on, urlcache will be loaded only with content
+found to be good and that is virus free.
+Thus, content of urls will not be scanned for each request while it is found
+in urlcache.
+
+Default value: on
+
+10. virusscannertimeout
+The maximum length of time the virus scanner is allowed to run (in seconds).
+
+Default value: 60
+
+11. notify
+Sets who receives email notification when a virus is found. Users must be 
+authenticated to be able to receive messages. Email address for users will be
+formed by the authentication name received by DG plus @emaildomain
+(see option below)
+0 = disabled
+1 = user only
+2 = postmaster only
+3 = postmaster and users (default)
+
+Default value: 3
+
+12. emaildomain
+Set email domain to use when notifying users of an infected file.
+This is just the domain name part, after @.
+It is *highly* recommended to set this option before running dansguardian.
+
+Default value: your.domain.com
+
+13. postmaster
+Set email address of who to notify about any infections found.
+Should put your full domain name here too.
+It is *highly* recommended to set this option before running dansguardian.
+
+Default value: postmaster@your.domain.com
+
+14. emailserver
+Set the address and port of the mail server to send notifications through.
+It is *highly* recommended to set this option before running dansguardian.
+
+Default value: 127.0.0.1:25
+
+virusengine
+Set the embedded virus scan engine to be used (clamav or kaspersky).
+
+Default value: clamav
+
+Configuration options for CLAMDSCAN
+-----------------------------------------
+1. localsocket
+Defines local socket file path that will be used to communicate with clamd
+daemon.
+
+Default value: '/tmp/clamd'
+
+
+Configuration options for CLAMAV
+-----------------------------------------
+1. maxfiles
+Indicates maximum number of files inside a downloaded compressed file.
+
+Default value: 1500
+
+2. maxreclevel
+Indicates maximum recursion level that will be performed when it finds a
+compressed file inside downloaded compressed file.
+
+Default value: 3
+
+3. maxfilesize
+Indicates maximum size, in bytes,  of each file inside a downloaded compressed
+file.
+
+Default value: 10485760
+(10 Mbytes)
+
+
+Known issues
+-----------------------------------------
+1. If a big file is downloaded and virus scanning takes longer than browser
+timeout value, such file will not be downloaded properly.
+The only workaround to this issue is:
+    1. Insert file URL in exception list.
+    2. Change virusscanexceptions to off.
+    3. Gently restart Dansguardian.
+    4. Download file and perform virus scanning locally.
+    5. Remove file URL from exception list.
+    6. Change virusscanexceptions to your previous value.
+    7. Gently restart Dansguardian.
+
+2. Enabling Kasperksy instead of clamAV and vice-versa requires changes of
+libraries and define statements in Makefile (or .in files for all systems).
+This situation is already planned to be solved in some later release.
+
+Enabling CLAMD daemon
+-----------------------------------------
+To enable clamd daemon, you must apply the patch before configuring:
+
+patch -p1 < clamdscan.patch
+
+Also, the local socket file path used by clamd must be properly setup in
+dansguardian.conf.
+
+Dansguardian must be allowed to communicate with clamd, so clamd must run
+with same user and group that Dansguardian does (usually nobody).
+
+
+Enabling KASPERSKY
+-----------------------------------------
+Install kavclient (http://kavclient.sourceforge.net/) and apply the patch:
+
+patch -p1 < kav.patch
+
+If you get "libesmtp.so: undefined reference to `pthread_getspecific'"
+error while linking, add -pthread at the end of LIBS line in autoconf/linux.in.
+
+
+Information for developers
+-----------------------------------------
+How to update to a new DG version:
+"diff -urN dansguardian-2.8.0.2 dansguardian-2.8.0.3 > dg.diff"
+"cd dgav"
+"patch -p1 < ../dg.diff"
+Update failed hunks manually.
+It would be better to import newer version with cvs.
+
+What to do before the release:
+Update version information in DGVirus.spec, dansguardian.cpp and
+configure files. Commit all uncommitted changes. Tag a new release
+in cvs tree: "cvs tag DGAV_6_3".
+
+How to create antivirus patch which can be released:
+"diff -urN -x CVS -x .cvsignore -x phraselists -X dgav/.cvsignore dansguardian-2.8.0.3 dgav > dgav.patch"
+
+<EOF>
\ No newline at end of file
diff -Naur dansguardian-2.8.0.4/String.cpp dansguardian-2.8.0.4.oden/String.cpp
--- dansguardian-2.8.0.4/String.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/String.cpp	2005-06-17 12:30:35.312932500 +0200
@@ -102,6 +102,29 @@
     }
 #endif
 
+// Bodge to handle GCC3.x - improvements very welcome!!!
+// Sun 1st December 2002 - daniel@ //jadeb.com
+#ifdef __GCCVER3
+    String::String(const long unsigned num) {
+        std::ostringstream buf;
+        buf << num << std::ends;
+        std::string s = buf.str();
+        char* bs = (char*)s.c_str();
+        int l = strlen(bs);
+        data = new char[l + 1];
+        memcpy(data, bs, l);
+        sl = l;
+        data[sl] = '\0';
+    }
+#else
+    String::String(const long unsigned num) {
+        std::ostrstream buf;
+        buf << num << std::ends;
+        data = buf.str();  // with side effect: it calls buf.freeze()
+        sl = buf.pcount() - 1;
+    }
+#endif
+
 String::String(const char* bs, int len) {
     data = new char[len + 1];
     memcpy(data, bs, len);
diff -Naur dansguardian-2.8.0.4/String.hpp dansguardian-2.8.0.4.oden/String.hpp
--- dansguardian-2.8.0.4/String.hpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/String.hpp	2005-06-17 12:30:35.312932500 +0200
@@ -38,6 +38,7 @@
     String(const String& s);
     String(const int num);
     String(const long num);
+    String(const long unsigned num);
     String(const char bs[], int len);
     String(const char bs[], int start, int len);
     friend ostream &  operator << (ostream & out, const String& s);
diff -Naur dansguardian-2.8.0.4/VirusEngine.cpp dansguardian-2.8.0.4.oden/VirusEngine.cpp
--- dansguardian-2.8.0.4/VirusEngine.cpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/VirusEngine.cpp	2005-06-17 12:30:35.312932500 +0200
@@ -0,0 +1,103 @@
+// Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+// Additions by Nerijus Baliunas (nerijus@users.sourceforge.net)
+
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#include "autoconf/platform.h"
+#include "VirusEngine.hpp"
+#include "OptionContainer.hpp"
+#include <unistd.h>
+#include <fstream>
+
+#ifdef __CLAMDSCAN
+#include "ClamDScan.hpp"
+#endif
+
+#ifdef __CLAMAV
+#include "ClamAV.hpp"
+#include <clamav.h>
+extern ClamAV clamav;
+#endif
+
+#ifdef __KAV
+#include "KAV.hpp"
+extern KAV kav;
+#endif
+
+extern OptionContainer o;
+
+VirusEngine::VirusEngine()
+:virname(0), strError(0) {}
+// virname - holds the virus name, if found
+// strError - holds the error message text from the engine library
+
+VirusEngine::~VirusEngine() {
+// delete statements removed according to Alexander Marx mem leak patch
+}
+
+int VirusEngine::scan(char* tempFileName) {
+
+    int ret = 0;
+
+    if (o.virus_engine == 1) { // ClamAV
+#ifdef __CLAMAV
+	ret = clamav.scanFile(tempFileName);
+	virname = clamav.virname;
+		
+	if (ret == CL_CLEAN)
+	    ret = AV_CLEAN;
+	else if (ret == CL_VIRUS)
+	    ret = AV_VIRUS;
+	else {
+	    strError = cl_strerror(ret);
+	}
+#endif
+    }
+    else if (o.virus_engine == 2) { // KAV
+#ifdef __KAV
+	ret = kav.scanFile(tempFileName);
+	virname = kav.virname;
+
+	if (ret == KAV_STATUS_CLEAN || ret == KAV_STATUS_CORRUPTED_FOUND)
+	    ret = AV_CLEAN;
+	else {
+	    //if (ret == KAV_STATUS_VIRUSES_FOUND || ret == KAV_STATUS_SUSPICIOUS_FOUND
+	    //|| ret == KAV_STATUS_CORRUPTED_VIRUSES_FOUND)
+	    ret = AV_VIRUS;
+	}
+#endif
+    }
+    else if (o.virus_engine == 3) { // ClamDScan
+#ifdef __CLAMDSCAN
+	ClamDScan clamdscan;
+	ret = clamdscan.scanFile(tempFileName);
+	virname = clamdscan.virname;
+		
+	if (ret == CL_CLEAN)
+	    ret = AV_CLEAN;
+	else if (ret == CL_VIRUS)
+	    ret = AV_VIRUS;
+	else {
+	    strError = clamdscan.errorMsg;
+	}
+#endif
+    }
+    else {
+	virname = "";
+	ret = AV_CLEAN;
+    }
+
+    return ret;
+}
diff -Naur dansguardian-2.8.0.4/VirusEngine.hpp dansguardian-2.8.0.4.oden/VirusEngine.hpp
--- dansguardian-2.8.0.4/VirusEngine.hpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/VirusEngine.hpp	2005-06-17 12:30:35.313932522 +0200
@@ -0,0 +1,35 @@
+// Written by Aecio F. Neto(afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifndef __HPP_VIRUSENGINE
+#define __HPP_VIRUSENGINE
+
+#define AV_CLEAN	0 /* virus not found */
+#define AV_VIRUS	1 /* virus found */
+
+class VirusEngine {
+
+public:
+    const char* virname;
+    const char* strError;
+
+    VirusEngine();
+    ~VirusEngine();
+
+    int scan(char* tempFileName);
+};
+
+#endif
diff -Naur dansguardian-2.8.0.4/VirusScanner.cpp dansguardian-2.8.0.4.oden/VirusScanner.cpp
--- dansguardian-2.8.0.4/VirusScanner.cpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/VirusScanner.cpp	2005-06-17 12:30:35.313932522 +0200
@@ -0,0 +1,482 @@
+// Written by James A. Pattie (james@pcxperience.com) Xperience, Inc. (www.pcxperience.com)
+// Additions by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+// Additions by Nerijus Baliunas (nerijus@users.sourceforge.net)
+
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#include "autoconf/platform.h"
+#include "VirusEngine.hpp"
+#include "VirusScanner.hpp"
+#include "String.hpp"
+#include <libesmtp.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#define MAX_BUF 262144 // 256k
+#define SIZE 32768 // 32k
+
+extern OptionContainer o;
+
+VirusScanner::VirusScanner()
+:file_length(0),isInfected(0),virname(0),tempFileName(new char[0]),tempfileFD(0),timeout(60),trickle_pos(0) {}
+
+VirusScanner::~VirusScanner() {
+    unlink(tempFileName);
+    
+    delete[] tempFileName;
+    // delete temporary file when class is destroyed
+    // basically this is for when an exception is caught
+}
+
+void VirusScanner::handleScanning(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength,
+				  std::string username, std::string clientip, int filtergroup,
+				  std::string location, int reporting_level, int scantype) throw(exception) {
+    if (scantype == 0) {
+	// scantype 0 indicates that a temporary file has to be created and
+	// its content will be retrieved by this class
+	createTempFile();
+    }
+    
+    if (contentLength >= docsize) {
+	// this means that some bytes were already written to disk
+	// but not all of them. So, continue receiving them from proxy
+	try {
+	    in(proxysock, peerconn, url, docsize, contentLength, username, clientip, scantype);
+	} catch (exception& e) {
+	    #ifdef DGDEBUG
+		std::cout << "***exception on Viruscanner::in***" << std::endl;
+	    #endif
+	    throw e;
+	}
+    }
+
+    // scantype indicates that a temporary file already exists and
+    // its content has a FD in this class
+    scan(peerconn, url, username, clientip, filtergroup, location, reporting_level, scantype);
+}
+
+void VirusScanner::createTempFile(void) throw (exception) {
+    fstream tmpFile;
+    
+    tempFileName = new char[256];
+    strcpy(tempFileName, o.download_dir.c_str());
+    strcat(tempFileName, "/tfXXXXXX");
+    
+
+    if ((tempfileFD = mkstemp(tempFileName)) < 0) {
+	#ifdef DGDEBUG
+    	    std::cout << "VirusScanner could not create temp file " << tempFileName <<  std::endl;
+        #endif
+	
+	if (mkdir(o.download_dir.c_str(), S_IRWXU) == -1) {
+	    if (errno != EEXIST) {
+		#ifdef DGDEBUG
+    		    std::cout << "VirusScanner could not re-create temp dir" <<  std::endl;
+    		#endif
+		throw exception();
+	    }
+	}
+
+	strcpy(tempFileName, o.download_dir.c_str());
+	strcat(tempFileName, "/tfXXXXXX");
+	if ((tempfileFD = mkstemp(tempFileName)) < 0) {
+	    #ifdef DGDEBUG
+    		std::cout << "VirusScanner could not create temp file " << tempFileName <<  std::endl;
+    	    #endif
+	    throw exception();
+	}
+    }
+}
+
+bool VirusScanner::out(Socket *sock, char *block, int size) throw(exception) {
+
+    // exceptions on timeout or error
+    (*sock).readyForOutput(timeout);  
+
+    // need exception or something for a bad write
+    if (!(*sock).writeToSocket(block, size, 0, timeout)) {
+	#ifdef DGDEBUG
+	    std::cout << "Error sending data to socket" << endl;
+	#endif
+        throw exception();
+	return false;
+    }  // write the data block out to the stream
+
+    return true;
+}
+
+void VirusScanner::in(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength,
+		      std::string username, std::string clientip, int scantype) throw(exception) {
+
+    // ConnectionHandler had received all data. Bye... we will only scan it.
+    if ((contentLength > 0) && (docsize == contentLength)) {
+    	#ifdef DGDEBUG
+    	    std::cout << "ConnectionHandler had received all data. returning..." << std::endl;
+        #endif
+	return;
+    }
+        
+    time_t lastWrite;
+    time_t current;
+
+    char input[MAX_BUF];  // buffer for storing a grabbed block from the input stream
+    char output[SIZE];
+
+    int tricklelength = o.trickle_length;
+    int followingtrickledelay = o.following_trickle_delay;
+    int delay = o.first_trickle_delay;
+    int size = 1;
+    int bufferlength = 0;
+    int lastsize = 0;
+    int flush = 0;
+    int rci = 0;
+    int rco = 0;
+
+    bool flushed = false;
+        
+    if (contentLength > 0) {
+	file_length = contentLength;
+	// lastsize is only used for positive tricklelength 
+	lastsize = contentLength - tricklelength;
+    }
+    else {
+	// bad web server don't provide file sizes.
+	// switches back to 1 byte trickle method
+	// because we don't know when to stop sending data in trickle mode
+    	tricklelength = 1;
+	lastsize = 0;
+    }
+
+    if (scantype == 1) {
+	lseek(tempfileFD, 0, SEEK_SET);
+	
+	while(true) {
+	    rci = read(tempfileFD, input, tricklelength);
+	    
+	    if (rci < 1) {
+		break;
+	    }
+	    
+	    if (rci > 0) {
+		bufferlength += rci;
+
+		if (out(peerconn, input, rci)) {
+		    trickle_pos += rci;
+		}
+		else {
+		    #ifdef DGDEBUG
+			std::cout << "Could not write block to socket." << std::endl;
+		    #endif
+		    throw exception();
+		}
+	    }
+	    
+	    if ((tricklelength > 0) && (trickle_pos + tricklelength) >= lastsize) {
+		break;
+	    }
+	}
+    }
+
+    // position file pointer to append more data
+    lseek(tempfileFD, 0, SEEK_END);
+    
+    lastWrite = time(NULL);
+    current = lastWrite;
+    while(true) {
+	flushed = false;
+	try {
+    	    // grab a fixed amount of input
+            (*proxysock).checkForInput(timeout);
+    	    rci = (*proxysock).readFromSocket(&input[flush], SIZE, 0, timeout);
+        } catch (exception& e) {
+            break;
+        }
+
+        if (rci < 1) {
+            break;  // nothing received or an error ocurred
+        }
+
+	if ((flush + rci + SIZE) >= MAX_BUF) {
+	    // write direct to block instead of looping to insert 1 byte each time
+	    // based on code from Alexander Marx
+	    lseek(tempfileFD, 0, SEEK_END);
+	    write(tempfileFD, input, flush + rci);
+    	    bufferlength += (flush + rci);  // update data size counter
+	    flushed = true;
+    	    #ifdef DGDEBUG
+    	        std::cout << "flushed data to disk due to a full buffer" << std::endl;
+            #endif
+	}
+
+	if (tricklelength > 0) {
+	    if ((trickle_pos + SIZE + tricklelength) < lastsize) {
+		if (out(peerconn, &input[flush], rci)) {
+		    trickle_pos += rci;
+		}
+		else {
+		    break;
+		}
+	    }
+	}
+	else if (bufferlength > trickle_pos) {
+	    if (trickle_pos > 0) {
+		delay = followingtrickledelay;
+	    }
+
+    	    current = time(NULL);
+	    if ((current - lastWrite) >= delay) {
+    		#ifdef DGDEBUG
+    	    	    std::cout << "It is time to send some data to client" << std::endl;
+    		#endif
+
+		lseek(tempfileFD, trickle_pos, SEEK_SET);
+		rco = read(tempfileFD, output, size);
+
+		// write the data block out to the stream
+          	if (out(peerconn, output, rco)) {
+		    lastWrite = current;
+		    trickle_pos += rco;
+		}
+              	#ifdef DGDEBUG
+		    else {
+            	        std::cout << "Could not trickle byte " << trickle_pos << " - filename: " << tempFileName << "'" << std::endl;
+          	    }
+          	#endif
+	    }
+	}
+
+	#ifdef DGDEBUG
+	    std::cout << "flush: " << flush << std::endl;
+	    std::cout << "rci: " << rci << std::endl;
+	    std::cout << "trickle_pos: " << trickle_pos << std::endl;
+    	    std::cout << "bufferlength: " << bufferlength << std::endl;
+    	    std::cout << "tricklelength: " << tricklelength << std::endl;
+	    std::cout << "size: " << size << std::endl;
+	#endif
+	
+	if (flushed) {
+	    flush = 0;
+	}
+	else {
+	    flush += rci;
+	}
+    }
+
+    // There is data left in memory block
+    if (!flushed && (flush > 0)) {
+	#ifdef DGDEBUG
+	    std::cout << "Writing data left in memory to disk" << std::endl;
+	#endif
+	lseek(tempfileFD, 0, SEEK_END);
+	write(tempfileFD, input, flush);
+    }
+}
+
+void VirusScanner::scan(Socket *peerconn, String url, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception) {
+    int rc = 0;
+    char block[MAX_BUF]; // buffer for storing a grabbed block from the input stream
+
+    VirusEngine avEngine;
+
+    #ifdef DGDEBUG
+        std::cout << "Scanning file: '" << tempFileName << "' for user '" << username << "'..." << std::endl;
+    #endif
+
+    if (file_length == 0) {
+	lseek(tempfileFD, 0, SEEK_SET);
+	file_length = lseek(tempfileFD, 0, SEEK_END);
+    }
+    lseek(tempfileFD, 0, SEEK_SET);
+        
+    #ifdef DGDEBUG
+        std::cout << "Starting file scanning..." << endl;
+        system("date");
+    #endif
+    isInfected = avEngine.scan(tempFileName);
+    #ifdef DGDEBUG
+        system("date");
+        std::cout << "End of file scanning" << endl;
+	std::cout << "VirusScanner - isInfected = " << isInfected << std::endl;
+    #endif
+
+    if (isInfected == AV_CLEAN) {
+        #ifdef DGDEBUG
+            std::cout << "sending data from trickle_pos = '" << trickle_pos << "', length = '" << file_length << std::endl;
+        #endif
+
+        // position ourselves.
+	lseek(tempfileFD, trickle_pos, SEEK_SET);
+	while(true) {
+	    rc = read(tempfileFD, block, MAX_BUF);
+	    #ifdef DGDEBUG
+		std::cout << "rc = " << rc << std::endl;
+	    #endif
+
+    	    if (rc < 1) {
+        	break;  // an error occured so end the while()
+                	// or none received so pipe is closed
+    	    }
+
+    	    if (rc > 0) {
+        	if (out(peerconn, block, rc)) {
+		#ifdef DGDEBUG
+		    std::cout << "Wrote block to client socket." << std::endl;
+		#endif
+		}
+		#ifdef DGDEBUG
+		    else {
+			std::cout << "Could not write block to client socket." << std::endl;
+		    }
+		#endif
+    	    }
+	}
+    }
+    else if (isInfected == AV_VIRUS) {
+	virname = avEngine.virname;
+        // send the browser an error html document stating that the file was infected with virus.
+	// Added feature to use dansguardian.pl
+        try {
+		// HTTP header was already sent to client
+		// Sending only HTTP body to client
+	    if (reporting_level == 3) {
+		String msg;
+		msg = "Virus ";
+		msg += virname;
+		msg += " found";
+		o.html_template.display(peerconn,
+		url.toCharArray(),
+		msg,
+		o.language_list.getTranslation(1100),
+		username.c_str(),
+		clientip.c_str(),
+		String (filtergroup + 1),
+		""
+		);
+	    }
+	    else {
+		(*peerconn).writeString("<html><head>");
+		(*peerconn).writeString("<script language=JavaScript>");
+		(*peerconn).writeString("location.href=");
+		(*peerconn).writeString(location.c_str());
+		(*peerconn).writeString("?USER==");
+		(*peerconn).writeString(username.c_str());
+		(*peerconn).writeString("::REASON==Virus%20'");
+		(*peerconn).writeString(virname);
+		(*peerconn).writeString("'%20found");
+		(*peerconn).writeString("::URL==");
+		(*peerconn).writeString(url.toCharArray());
+		(*peerconn).writeString("</script>");
+		(*peerconn).writeString("</head></html>");
+	    }
+
+	    if (o.notify) {
+		#ifdef DGDEBUG
+		    std::cout << "Sending email..." << endl;
+		#endif
+		sendmail(username, url, clientip);
+	    }
+        }   catch (exception& e) {
+            #ifdef DGDEBUG
+                std::cout << "sending infected message failed!" << std::endl;
+            #endif
+            throw e;
+        }
+	#ifdef DGDEBUG
+	    std::cout << "VirusName: " << virname << endl;
+	#endif
+    }
+    else {
+	try {
+	    (*peerconn).writeString("<html><head><title>DansGuardian - Antivirus Error</title></head>");
+    	    (*peerconn).writeString("<body><center><h1>DansGuardian Antivirus Patch - Error during scanning</h1>");
+	    (*peerconn).writeString("Error message: '");
+	    (*peerconn).writeString(avEngine.strError);
+    	    (*peerconn).writeString("'</center></body></html>");
+	} catch (exception& e) {
+	    throw e;
+	}
+    }
+
+    close(tempfileFD);
+    unlink(tempFileName);
+}
+
+void VirusScanner::sendmail (std::string username, String url, std::string clientip) {
+    smtp_session_t session;
+    smtp_message_t message;
+    smtp_recipient_t recipient;
+    char *host = (char *)o.email_server.c_str();
+    std::string from = "\"DansGuardian Anti-Virus\" <dgvirus@";
+    from += o.email_domain;
+    from += ">";
+    std::string return_path = "dgvirus@";
+    return_path += o.email_domain;
+    char *subject = "Virus Found by DansGuardian Anti-Virus";
+
+    session = smtp_create_session ();
+    message = smtp_add_message (session);
+
+    // Set the host running the SMTP server.  LibESMTP has a default port
+    // number of 587, however this is not widely deployed so the port
+    // is specified as 25 along with the default MTA host.
+    smtp_set_server (session, host);
+
+    // Set the reverse path for the mail envelope.  (NULL is ok)
+    smtp_set_reverse_path (message, return_path.c_str());
+
+    // RFC 2822 doesn't require recipient headers but a To: header would
+    // be nice to have if not present.
+    smtp_set_header (message, "To", NULL, NULL);
+
+    // Set the Subject: header.  For no reason, we want the supplied subject
+    // to override any subject line in the message headers.
+    if (subject != NULL) {
+	smtp_set_header (message, "Subject", subject);
+	smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1);
+    }
+  
+    std::string str = "MIME-Version: 1.0\r\n";
+    str += "Content-Type: text/plain; charset=iso-8859-1\r\n";
+    str += "Content-Transfer-Encoding: 7bit\r\n";
+    str += "From: ";
+    str += from;
+    str += "\r\n\r\n";
+    str += "Virus        : ";
+    str += virname;
+    str += "\r\n";
+    str += "User         : ";
+    str += username;
+    str += "\r\nURL          : ";
+    str += url.toCharArray();
+    str += "\r\nUser IP      : ";
+    str += clientip;
+    smtp_set_message_str(message, (char *)str.c_str());
+
+    if (o.notify > 1)
+	recipient = smtp_add_recipient (message, o.postmaster.c_str());
+	
+    if (o.notify != 2)
+	if (username != "-")
+	recipient = smtp_add_recipient (message, (username + "@" + o.email_domain).c_str());
+
+    // Initiate a connection to the SMTP server and transfer the message.
+    if (!smtp_start_session (session)) {
+	char buf[128];
+	syslog(LOG_ERR, "SMTP server problem: %s",
+	       smtp_strerror (smtp_errno (), buf, sizeof buf));
+    }
+
+    smtp_destroy_session (session);
+}
diff -Naur dansguardian-2.8.0.4/VirusScanner.hpp dansguardian-2.8.0.4.oden/VirusScanner.hpp
--- dansguardian-2.8.0.4/VirusScanner.hpp	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/VirusScanner.hpp	2005-06-17 12:30:35.314932544 +0200
@@ -0,0 +1,51 @@
+// Written by James A. Pattie (james@pcxperience.com) Xperience, Inc. (www.pcxperience.com)
+
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#ifndef __HPP_VIRUSSCANNER
+#define __HPP_VIRUSSCANNER
+#include "OptionContainer.hpp"
+#include "Socket.hpp"
+#include "String.hpp"
+#include <exception>
+#include <fstream>
+#include <sys/stat.h>
+
+class VirusScanner {
+
+public:
+    long unsigned int file_length;
+    int isInfected;
+    const char *virname;
+    char *tempFileName;
+    int tempfileFD;
+
+    VirusScanner();
+    ~VirusScanner();
+
+    void handleScanning(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception);
+    void setTimeout(int t);
+    void createTempFile(void) throw(exception);
+    bool out(Socket *sock, char *block, int size) throw(exception);
+    void in(Socket* sock, Socket* outSock, String url, int docsize, int contentLength, std::string username, std::string clientip, int scantype) throw(exception);
+    void scan(Socket *peerconn, String url, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception);
+    void sendmail (std::string username, String url, std::string clientip);
+
+private:
+    int timeout;
+    int trickle_pos;
+};
+
+#endif
diff -Naur dansguardian-2.8.0.4/autoconf/linux.in dansguardian-2.8.0.4.oden/autoconf/linux.in
--- dansguardian-2.8.0.4/autoconf/linux.in	2005-06-17 12:32:42.648709149 +0200
+++ dansguardian-2.8.0.4.oden/autoconf/linux.in	2005-06-17 12:32:23.725296145 +0200
@@ -12,15 +12,19 @@
       DataBuffer.o HTTPHeader.o NaughtyFilter.o RegExp.o Socket.o \
       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
-      FOptionContainer.o ListManager.o md5.o
+      FOptionContainer.o ListManager.o md5.o \
+      ClamAV.o VirusEngine.o VirusScanner.o
       
 # Why so? Already linked against zlib dynamically, -lz hereunder in DGCFLAGS
 #LIBS = -Wl,-Bstatic -lz -Wl,-Bdynamic
+LIBS = -lclamav -lesmtp
 PROG = dansguardian
 INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
                bannedphraselist exceptionsitelist dansguardian.pl \
                bannedextensionlist bannedmimetypelist pics exceptioniplist \
-               exceptionuserlist exceptionurllist contentregexplist
+               exceptionuserlist exceptionurllist contentregexplist \
+	       exceptionvirusmimetypelist exceptionvirusextensionlist \
+	       exceptionvirussitelist exceptionvirusurllist
 CONFIGUREFILES = autoconf/platform.h confdefs.h config.log conftest.subs \
                  dansguardian.conf config.cache Makefile logrotation weightedphraselist \
                  bannedphraselist exceptionphraselist dansguardian.sysv \
@@ -79,6 +83,10 @@
 	cp -f ./exceptionurllist $I$(CONFFILELOCATION)exceptionurllist
 	cp -f ./exceptionuserlist $I$(CONFFILELOCATION)exceptionuserlist
 	cp -f ./exceptioniplist $I$(CONFFILELOCATION)exceptioniplist
+	cp -f ./exceptionvirusmimetypelist $I$(CONFFILELOCATION)exceptionvirusmimetypelist
+	cp -f ./exceptionvirusextensionlist $I$(CONFFILELOCATION)exceptionvirusextensionlist
+	cp -f ./exceptionvirussitelist $I$(CONFFILELOCATION)exceptionvirussitelist
+	cp -f ./exceptionvirusurllist $I$(CONFFILELOCATION)exceptionvirusurllist
 	cp -f ./pics $I$(CONFFILELOCATION)pics
 	cp -f ./transparent1x1.gif $I$(CONFFILELOCATION)transparent1x1.gif
 	cp -f ./logrotation $I$(CONFFILELOCATION)logrotation
diff -Naur dansguardian-2.8.0.4/clamdscan.patch dansguardian-2.8.0.4.oden/clamdscan.patch
--- dansguardian-2.8.0.4/clamdscan.patch	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/clamdscan.patch	2005-06-17 12:30:35.314932544 +0200
@@ -0,0 +1,126 @@
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/fbsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/fbsd.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/fbsd.in	2004-10-23 16:19:25.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/fbsd.in	2005-02-06 22:20:01.000000000 -0200
+@@ -9,9 +9,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = $(libdir)/libz.a -lclamav -lesmtp
++LIBS = $(libdir)/libz.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/hpux.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/hpux.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/hpux.in	2004-10-23 16:20:20.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/hpux.in	2005-02-06 22:20:01.000000000 -0200
+@@ -13,9 +13,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = $(libdir)/libz.a -lclamav -lesmtp
++LIBS = $(libdir)/libz.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/linux.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/linux.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/linux.in	2004-10-23 15:16:26.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/linux.in	2005-02-06 22:20:01.000000000 -0200
+@@ -13,9 +13,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = $(libdir)/libz.a -lclamav -lesmtp
++LIBS = $(libdir)/libz.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/nbsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/nbsd.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/nbsd.in	2004-10-23 16:21:25.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/nbsd.in	2005-02-06 22:20:01.000000000 -0200
+@@ -9,9 +9,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = $(libdir)/libz.a -lclamav -lesmtp
++LIBS = $(libdir)/libz.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/obsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/obsd.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/obsd.in	2004-10-23 16:22:21.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/obsd.in	2005-02-06 22:20:01.000000000 -0200
+@@ -9,9 +9,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+ 
+-LIBS = $(libdir)/libz.a -lclamav -lesmtp
++LIBS = $(libdir)/libz.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/osx.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/osx.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/osx.in	2004-10-23 16:23:02.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/osx.in	2005-02-06 22:20:01.000000000 -0200
+@@ -9,9 +9,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = $(libdir)/libgcc.a -lclamav -lesmtp
++LIBS = $(libdir)/libgcc.a -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/solaris.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/solaris.in
+--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/solaris.in	2004-10-23 16:23:55.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/solaris.in	2005-02-06 22:20:01.000000000 -0200
+@@ -9,9 +9,9 @@
+       FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \
+       HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \
+       FOptionContainer.o ListManager.o md5.o \
+-      ClamAV.o VirusEngine.o VirusScanner.o
++      ClamDScan.o VirusEngine.o VirusScanner.o
+       
+-LIBS = -lstdc++ -lsocket -lnsl -lz -lresolv -lclamav -lesmtp
++LIBS = -lstdc++ -lsocket -lnsl -lz -lresolv -lesmtp
+ PROG = dansguardian
+ INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \
+                bannedphraselist exceptionsitelist dansguardian.pl \
+diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/configure dansguardian-2.8.0.3-antivirus-6.3.7-clamd/configure
+--- dansguardian-2.8.0.3-antivirus-6.3.7/configure	2004-10-23 14:13:16.000000000 -0300
++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/configure	2005-02-06 22:20:01.000000000 -0200
+@@ -2618,7 +2618,7 @@
+         ;;
+ esac
+ # Added by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+-echo "#define __CLAMAV 1" >> autoconf/platform.h
++echo "#define __CLAMDSCAN 1" >> autoconf/platform.h
+ 
+ 
+ 
+@@ -3197,7 +3197,7 @@
+ 
+ echo "# OPTION: virusengine">>dansguardian.conf
+ echo "# Set the embedded virus scan engine to be used (clamdscan, clamav or kav).">>dansguardian.conf
+-echo "virusengine = 'clamav'">>dansguardian.conf
++echo "virusengine = 'clamdscan'">>dansguardian.conf
+ echo "" >>dansguardian.conf
+ 
+ echo "# OPTION: tricklelength" >>dansguardian.conf
diff -Naur dansguardian-2.8.0.4/configure dansguardian-2.8.0.4.oden/configure
--- dansguardian-2.8.0.4/configure	2005-06-17 12:32:42.654709280 +0200
+++ dansguardian-2.8.0.4.oden/configure	2005-06-17 12:30:35.317932609 +0200
@@ -2417,6 +2417,7 @@
 	echo "Using IPv6 support"
 fi
 
+###AFN
 
 trap '' 1 2 15
 cat > confcache <<\EOF
@@ -2616,7 +2617,8 @@
 		$extendedecho  "#define __ENDIANH \"$endianh\"" >>autoconf/platform.h
         ;;
 esac
-
+# Added by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+echo "#define __CLAMAV 1" >> autoconf/platform.h
 
 
 
@@ -2786,7 +2788,7 @@
 	rm -f dansguardian.conf
 fi
 
-echo "# DansGuardian config file for version 2.8.0" >dansguardian.conf
+echo "# DansGuardian config file for version 2.8.0 with Anti-Virus plug-in 6.3.6" >dansguardian.conf
 echo "" >>dansguardian.conf
 echo "# **NOTE** as of version 2.7.5 most of the list files are now in dansguardianf1.conf" >>dansguardian.conf
 echo "" >>dansguardian.conf
@@ -2925,9 +2927,9 @@
 echo "# Positive result caching for text URLs" >>dansguardian.conf
 echo "# Caches good pages so they don't need to be scanned again" >>dansguardian.conf
 echo "# 0 = off (recommended for ISPs with users with disimilar browsing)" >>dansguardian.conf
-echo "# 1000 = recommended for most users" >>dansguardian.conf
+echo "# 2000 = recommended for most users" >>dansguardian.conf
 echo "# 5000 = suggested max upper limit" >>dansguardian.conf
-echo "urlcachenumber = 1000" >>dansguardian.conf
+echo "urlcachenumber = 2000" >>dansguardian.conf
 echo "#" >>dansguardian.conf
 echo "# Age before they are stale and should be ignored in seconds" >>dansguardian.conf
 echo "# 0 = never" >>dansguardian.conf
@@ -3177,11 +3179,159 @@
 echo "# on|off ( defaults to off )" >>dansguardian.conf
 echo "softrestart = off" >>dansguardian.conf
 echo "" >>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "" >>dansguardian.conf
 
+# Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+echo "# ANTIVIRUS SETTINGS" >>dansguardian.conf
+echo "# --------------------" >>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: virusscan" >>dansguardian.conf
+echo "# If on, we scan all downloaded content using embedded virus engine." >>dansguardian.conf
+echo "# Supported engines of this version are ClamAV, KAV or ClamDScan." >>dansguardian.conf
+echo "# If off, we don't scan any downloaded content." >>dansguardian.conf
+echo "# See http://www.pcxperience.org/dgvirus/ for more details." >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "virusscan = on" >>dansguardian.conf
+echo "" >>dansguardian.conf
 
+echo "# OPTION: virusengine">>dansguardian.conf
+echo "# Set the embedded virus scan engine to be used (clamdscan, clamav or kav).">>dansguardian.conf
+echo "virusengine = 'clamav'">>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: tricklelength" >>dansguardian.conf
+echo "# If off (value = -1), the scanner will send 1 byte per delay period" >>dansguardian.conf
+echo "# to the client to keep a download connection alive." >>dansguardian.conf
+echo "# When the whole file is downloaded and scanned, the client will receive " >>dansguardian.conf
+echo "# all remaining bytes, if the file is clean." >>dansguardian.conf
+echo "# Set to a positive integer value to enable immediate delivery to the client. " >>dansguardian.conf
+echo "# Value set means minimum number of bytes of the downloaded file " >>dansguardian.conf
+echo "# that will be held and delivered after virus scan." >>dansguardian.conf
+echo "# If clean, the remaining bytes will be sent to the client." >>dansguardian.conf
+echo "# If infected, file downloaded will be incomplete and a warning message " >>dansguardian.conf
+echo "# will be sent to the postmaster and possibly the user."  >>dansguardian.conf
+echo "# Recommended minimum positive value: 32768 (32 kbytes)" >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "tricklelength = 32768" >>dansguardian.conf
+echo "" >>dansguardian.conf
 
+echo "# OPTION: firsttrickledelay" >>dansguardian.conf
+echo "# Delay in seconds to deliver the first byte to the client." >>dansguardian.conf
+echo "# This option only applies if tricklelength is set to -1 (off)." >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "firsttrickledelay = 30" >>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: follwingtrickledelay" >>dansguardian.conf
+echo "# Delay in seconds to deliver subsequent bytes to the client." >>dansguardian.conf
+echo "# This option only applies if tricklelength is set to -1 (off)." >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "followingtrickledelay = 60" >>dansguardian.conf
+echo "" >>dansguardian.conf
 
+echo "# OPTION: exceptionvirusmimetypelist" >>dansguardian.conf
+echo "# The following file allow you to define mime types" >>dansguardian.conf
+echo "# that should not be virus scanned." >>dansguardian.conf
+$extendedecho "exceptionvirusmimetypelist = '$prefixdir$sysconfdir\c" >>dansguardian.conf
+echo "exceptionvirusmimetypelist'" >>dansguardian.conf
+echo "" >>dansguardian.conf
 
+echo "# OPTION: maxcontentscansize" >>dansguardian.conf
+echo "# Set the maximum size of a content to be virus scanned." >>dansguardian.conf
+echo "# Content size above this value will not be scanned against viruses." >>dansguardian.conf
+echo "# To have no limit, use 0 (zero)." >>dansguardian.conf
+echo "maxcontentscansize = 0" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: exceptionvirusextensionlist" >>dansguardian.conf
+echo "# The following file allow you to define file extensions" >>dansguardian.conf
+echo "# that should not be virus scanned." >>dansguardian.conf
+$extendedecho "exceptionvirusextensionlist = '$prefixdir$sysconfdir\c" >>dansguardian.conf
+echo "exceptionvirusextensionlist'" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: downloaddir">>dansguardian.conf
+echo "# Set where the files are downloaded to before they are scanned." >>dansguardian.conf
+echo "downloaddir = '/tmp/dgvirus'" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: virusscanexceptions" >>dansguardian.conf
+echo "# If off, antivirus scanner will ignore exception sites and urls." >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "virusscanexceptions = on" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: urlcachecleanonly" >>dansguardian.conf
+echo "# If off, url cache will contain entries of text only urls." >>dansguardian.conf
+echo "# Keeping it off, preserves original Dansguardian feature and" >>dansguardian.conf
+echo "# downloaded content will be always scanned by antivirus." >>dansguardian.conf
+echo "# When turned on, urlcache will be loaded only with content" >>dansguardian.conf
+echo "# found to be good and that is virus free." >>dansguardian.conf
+echo "# Thus, content of urls found in urlcache WILL NOT BE SCANNED AGAIN." >>dansguardian.conf
+echo "#" >>dansguardian.conf
+echo "urlcachecleanonly = on" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: virusscannertimeout">>dansguardian.conf
+echo "# The maximum length of time the commercial virus scanner is allowed to run">>dansguardian.conf
+echo "# for 1 batch of messages (in seconds).">>dansguardian.conf
+echo "virusscannertimeout = 60">>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# OPTION: notify">>dansguardian.conf
+echo "# Sets who receives email notification when a virus is found.">>dansguardian.conf
+echo "# Users must be authenticated to be able to receive messages.">>dansguardian.conf
+echo "# Email address for users will be formed by the authentication name received by DG">>dansguardian.conf
+echo "# plus @emaildomain (see option below)">>dansguardian.conf
+echo "# 0 = disabled">>dansguardian.conf
+echo "# 1 = user only">>dansguardian.conf
+echo "# 2 = postmaster only">>dansguardian.conf
+echo "# 3 = postmaster and users (default)">>dansguardian.conf
+echo "notify = 0">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: emaildomain">>dansguardian.conf
+echo "# Set email domain to use when notifying users of an infected file.">>dansguardian.conf
+echo "# This is just the domain name part, after the @">>dansguardian.conf
+echo "emaildomain = 'your.domain.com'">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: postmaster">>dansguardian.conf
+echo "# Set email address of who to notify about any infections found.">>dansguardian.conf
+echo "# Should put your full domain name here too.">>dansguardian.conf
+echo "postmaster = 'postmaster@your.domain.com'">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: emailserver">>dansguardian.conf
+echo "# Set the address and port of the Mail Server to send notifications through.">>dansguardian.conf
+echo "emailserver = '127.0.0.1:25'">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# CLAMDSCAN SETTINGS">>dansguardian.conf
+echo "# --------------------" >>dansguardian.conf
+echo "# OPTION: localsocket">>dansguardian.conf
+echo "# Set name of the local socket file">>dansguardian.conf
+echo "# default: '/tmp/clamd'">>dansguardian.conf
+echo "localsocket = '/tmp/clamd'">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "" >>dansguardian.conf
+
+echo "# CLAMAV SETTINGS">>dansguardian.conf
+echo "# --------------------" >>dansguardian.conf
+echo "# OPTION: maxfiles">>dansguardian.conf
+echo "# Set maximum number of files inside a compressed file">>dansguardian.conf
+echo "# default: 1500 files">>dansguardian.conf
+echo "clmaxfiles = 1500">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: maxreclevel">>dansguardian.conf
+echo "# Set maximum recursion level to perform scan on a compressed file">>dansguardian.conf
+echo "# that is inside a compressed file">>dansguardian.conf
+echo "# default: 3 levels">>dansguardian.conf
+echo "clmaxreclevel = 3">>dansguardian.conf
+echo "" >>dansguardian.conf
+echo "# OPTION: maxfilesize">>dansguardian.conf
+echo "# Set maximum file size of a file inside a compressed file">>dansguardian.conf
+echo "# default: 10485760 = 10 Mbytes">>dansguardian.conf
+echo "clmaxfilesize = 10485760">>dansguardian.conf
+echo "" >>dansguardian.conf
 
 echo "Generating platform specific dansguardianf1.conf..."
 if [ -f dansguardianf1.conf ]; then
@@ -3263,6 +3413,24 @@
 echo "# '76b42abc1cd0fdcaf6e943dcbc93b826' = an example" >>dansguardianf1.conf
 echo "bypasskey = ''" >>dansguardianf1.conf
 echo "" >>dansguardianf1.conf
+echo "" >>dansguardianf1.conf
+
+# Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br)
+echo "# ANTIVIRUS SETTINGS for this group" >>dansguardianf1.conf
+echo "# --------------------" >>dansguardianf1.conf
+echo "" >>dansguardianf1.conf
+echo "# OPTION: virusscan" >>dansguardianf1.conf
+echo "# If on, content will be scanned using virus engine setup in dansguardian.conf." >>dansguardianf1.conf
+echo "# More options are available in dansguardian.conf." >>dansguardianf1.conf
+echo "#" >>dansguardianf1.conf
+echo "virusscan = on" >>dansguardianf1.conf
+echo "" >>dansguardianf1.conf
+
+$extendedecho "exceptionvirussitelist = '$prefixdir$sysconfdir\c" >>dansguardianf1.conf
+echo "exceptionvirussitelist'" >>dansguardianf1.conf
+$extendedecho "exceptionvirusurllist = '$prefixdir$sysconfdir\c" >>dansguardianf1.conf
+echo "exceptionvirusurllist'" >>dansguardianf1.conf
+
 
 echo "Generating platform specific weightedphraselist..."
 if [ -f weightedphraselist ]; then
diff -Naur dansguardian-2.8.0.4/dansguardian.cpp dansguardian-2.8.0.4.oden/dansguardian.cpp
--- dansguardian-2.8.0.4/dansguardian.cpp	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/dansguardian.cpp	2005-06-17 12:30:35.318932631 +0200
@@ -37,6 +37,8 @@
 #include "SysV.hpp"
 #include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/stat.h>
+#include <cerrno>
 #ifdef __BSD
 	#include <sys/wait.h>
 #else
@@ -48,6 +50,19 @@
 #define FD_SETSIZE 256
 #endif
 
+#ifdef __CLAMAV
+#include "ClamAV.hpp"
+#include <clamav.h>
+struct cl_node *root = NULL;
+struct cl_limits limits;
+struct cl_stat dbstat;
+ClamAV clamav;
+#endif
+
+#ifdef __KAV
+#include "KAV.hpp"
+KAV kav;
+#endif
 
 OptionContainer o;
 
@@ -77,12 +92,13 @@
     std::string configfile = __CONFFILE;
     srand(time(NULL));
     SysV sysv;
-    int rc;
+    int rc,no,ret;
 
     for (int i=1; i<argc; i++) {
         int option = (argv[i][0] == '-')? argv[i][1] : 0;  // 0 will never be a valid option
         switch (option) {
             case 'P':
+                std::cout << "Anti-Virus plugin version 6.3.8" << std::endl;
                 return 0;
             case 'q':
                 ReadConfig(configfile.c_str(), 0);
@@ -204,8 +220,27 @@
         }
         logfiletest.close();
     }
-
-
+    
+    
+    if (o.virus_scan) {
+	if (mkdir(o.download_dir.c_str(), S_IRWXU) == -1)
+	    if (errno != EEXIST) {
+		std::cerr << std::endl << "Error creating temporary folder. " << std::endl;
+		return 1;
+	    }
+
+#ifdef __CLAMAV
+	if (o.virus_engine == 1) {
+	    int ret = 0;
+	    
+	    if ((ret = clamav.loadDB()) != 0) {
+		syslog(LOG_INFO, "loadDB ret: %i", ret);
+		return ret;
+	    }
+	}
+#endif
+    }
+    
     FatController f;  // Thomas The Tank Engine
 
 
@@ -261,6 +296,15 @@
                 std::cout << "conf file read." << std::endl;
             #endif
 
+#ifdef __CLAMAV
+			if (o.virus_scan && o.virus_engine == 1) {
+				if ((ret = clamav.checkDB()) != 0) {
+					syslog(LOG_INFO, "checkDB ret: %i", ret);
+					return ret;
+				}
+			}
+#endif
+
             if (nodaemon) {
                 o.no_daemon = 1;
             }
diff -Naur dansguardian-2.8.0.4/exceptionvirusextensionlist dansguardian-2.8.0.4.oden/exceptionvirusextensionlist
--- dansguardian-2.8.0.4/exceptionvirusextensionlist	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/exceptionvirusextensionlist	2005-06-17 12:30:35.319932652 +0200
@@ -0,0 +1,31 @@
+#Exception Virus extension list
+# The Virus scanning code will ignore files with these extensions.
+
+# File extensions with executable code 
+
+
+# Files which one normally things as non-executable but
+# can contain harmful macros and viruses
+
+
+# Other files which may contain files with executable code
+
+
+# Time/bandwidth wasting files
+
+.mp3  # Music file
+.mpeg # Movie file
+.mpg  # Movie file
+.avi  # Movie file
+.ra   # Real Audio
+.ram  #  "
+.rm   #  "
+
+# Image files not to scan
+.gif
+.png
+.ico
+# http://www.kb.cert.org/vuls/id/297462
+#.jpg
+#.jpeg
+
diff -Naur dansguardian-2.8.0.4/exceptionvirusmimetypelist dansguardian-2.8.0.4.oden/exceptionvirusmimetypelist
--- dansguardian-2.8.0.4/exceptionvirusmimetypelist	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/exceptionvirusmimetypelist	2005-06-17 12:30:35.319932652 +0200
@@ -0,0 +1,22 @@
+# MIME types the virus scanning code ignores.
+
+audio/mpeg
+audio/x-mpeg
+audio/x-pn-realaudio
+audio/x-wav
+audio/x-realaudio
+audio/x-pn-realaudio
+audio/vnd.rn-realaudio
+video/mpeg
+video/x-mpeg2
+video/acorn-replay
+video/quicktime
+video/x-msvideo
+video/msvideo
+video/vnd.rn-realvideo
+
+image/png
+image/gif
+image/tiff
+# http://www.kb.cert.org/vuls/id/297462
+# image/jpeg
diff -Naur dansguardian-2.8.0.4/exceptionvirussitelist dansguardian-2.8.0.4.oden/exceptionvirussitelist
--- dansguardian-2.8.0.4/exceptionvirussitelist	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/exceptionvirussitelist	2005-06-17 12:30:35.319932652 +0200
@@ -0,0 +1,14 @@
+#Sites in virus exception list will not be virus scanned
+#Don't bother with the www. or
+#the http://
+#
+#These are specifically domains and are not URLs.
+#For example 'foo.bar/porn/' is no good, you need
+#to just have 'foo.bar'.
+#
+#You can also match IPs here too.
+#
+#As of DansGuardian 2.7.3 you can now include
+#.tld so for example you can match .gov for example
+
+dansguardian.org
diff -Naur dansguardian-2.8.0.4/exceptionvirusurllist dansguardian-2.8.0.4.oden/exceptionvirusurllist
--- dansguardian-2.8.0.4/exceptionvirusurllist	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/exceptionvirusurllist	2005-06-17 12:30:35.320932674 +0200
@@ -0,0 +1,15 @@
+#URLs in exception virus list will not be virus scanned
+#Don't bother with the www. or
+#the http://
+#
+#These are parts of sites that filtering should
+#be switched off for.
+#
+#These should not be domains, i.e. entire sites,
+#they should be a domain with a path.
+#
+#For example 'foo.bar' is no good, you need
+#to just have 'foo.bar/porn/'.
+#
+#Another example:
+#generallybadsite.tld/partthatsok/
diff -Naur dansguardian-2.8.0.4/languages/lithuanian/messages dansguardian-2.8.0.4.oden/languages/lithuanian/messages
--- dansguardian-2.8.0.4/languages/lithuanian/messages	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/languages/lithuanian/messages	2005-06-17 12:30:35.320932674 +0200
@@ -42,3 +42,5 @@
 "900","Uþdraustas praplëtimas: "
 
 "1000","Virðytas ðio tinklalapio PICS vertinimo kriterijus."
+
+"1100","Turinys uþkrëstas virusu."
diff -Naur dansguardian-2.8.0.4/languages/lithuanian/messages.orig dansguardian-2.8.0.4.oden/languages/lithuanian/messages.orig
--- dansguardian-2.8.0.4/languages/lithuanian/messages.orig	1970-01-01 01:00:00.000000000 +0100
+++ dansguardian-2.8.0.4.oden/languages/lithuanian/messages.orig	2005-06-17 12:30:35.320932674 +0200
@@ -0,0 +1,44 @@
+# DansGuardian 2.8 messages file
+# Lithuanian version by Nerijus Baliûnas
+# charset=windows-1257
+
+"1","Tinklalapis uþdraustas"
+
+"100","Ið jûsø IP adreso negalima pasiekti tinklalapio: "
+"101","Draudþiama narðyti ið jûsø IP adreso."
+"102","Ðiam vartotojui negalima pasiekti tinklalapio: "
+
+"200","Neteisingas URL."
+
+"300","Rasta uþdrausta frazë: "
+"301","Rasta uþdrausta frazë."
+
+"400","Rasta uþdrausta fraziø kombinacija: "
+"401","Rasta uþdrausta fraziø kombinacija."
+"402","Sumuojamø fraziø limitas "
+"403","Virðytas sumuojamø fraziø limitas."
+
+"500","Uþdraustas adresas (site): "
+"501","Uþdraustas URL: "
+"502","Leidþiamas priëjimas tik prie tinklalapiø, esanèiø 'baltame' sàraðe, bet ðio tinklalapio jame nëra."
+"503","Draudþiamas URL regexp: "
+"504","Rastas draudþiamas URL regexp."
+"505","Priëjimas prie IP adresø uþdraustas, o ðis adresas yra tik IP adresas."
+
+"600","Kliento IP yra iðimèiø sàraðe."
+"601","Kliento vardas yra iðimèiø sàraðe."
+"602","Adresas yra iðimèiø sàraðe."
+"603","URL yra iðimèiø sàraðe."
+"604","Rasta frazë ið iðimèiø sàraðo: "
+"605","Rasta fraziø kombinacija ið iðimèiø sàraðo: "
+"606","URL apëjimo iðimtis."
+"607","Slapuko apëjimo iðimtis."
+
+"700","Nusiuntimas (upload) yra uþdraustas."
+"701","Virðytas nusiuntimo (upload) limitas."
+
+"800","Uþdraustas MIME tipas: "
+
+"900","Uþdraustas praplëtimas: "
+
+"1000","Virðytas ðio tinklalapio PICS vertinimo kriterijus."
diff -Naur dansguardian-2.8.0.4/languages/portuguese/messages dansguardian-2.8.0.4.oden/languages/portuguese/messages
--- dansguardian-2.8.0.4/languages/portuguese/messages	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/languages/portuguese/messages	2005-06-17 12:30:35.321932696 +0200
@@ -39,5 +39,6 @@
 "900","Extensão proibida: "
 
 "1000","Nível de rotulagem PICS excedido no sítio acima."
+"1100","Conteudo infectado por vírus."
 
 # 1,1000 by Andson Gomes - html internet
diff -Naur dansguardian-2.8.0.4/languages/portuguese/template.html dansguardian-2.8.0.4.oden/languages/portuguese/template.html
--- dansguardian-2.8.0.4/languages/portuguese/template.html	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/languages/portuguese/template.html	2005-06-17 12:30:35.321932696 +0200
@@ -1,35 +1,77 @@
-<HTML><HEAD><TITLE>DansGuardian - Acesso negado</TITLE></HEAD>
-<BODY>
-<CENTER><H2>O ACESSO FOI NEGADO AO USU&Aacute;RIO '-USER-'</H2>
-<br>O acesso &agrave; p&aacute;gina:<P>
-<strong><a href="-URL-">-URL-</a></strong>
-<P>... foi negado pela seguinte raz&atilde;o:<P>
-<strong><font color="#ff0000">-REASONGIVEN-</font></strong>
-<p><table border=1 bgcolor="#FFEE00"><tr><td>Voc&ecirc; est&aacute; vendo este erro porque a p&aacute;gina que voc&ecirc; tentou acessar<br>
-cont&eacute;m - ou &eacute; rotulada como contendo - material considerado<br>
-impr&oacute;prio.</td></tr></table>
-<P><table border=1 bgcolor="#44dd44"><tr><td>Se voc&ecirc; tem alguma d&uacute;vida, contacte o Coordenador de IT ou o Administrador da Rede.</td></tr></table>
-
-<P><font size=-3>Turbinado por <a href="http://dansguardian.org" target="_blank">DansGuardian</a></font>
-</center>
-</BODY>
-</HTML>
+<html>
+
+<head>
+<title>DansGuardian - Acesso negado</title>
+</head>
+
+<body bgcolor=#FFFFFF>
+
+<center>
+<table border=0 cellspacing=0 cellpadding=2 height=540 width=700>
+<tr>
+	<td colspan=2 bgcolor=#FEA700 height=100 align=center>
+	<font face=arial,helvetica size=6>
+	<b>O acesso foi negado!</b>
+	</td>
+</tr>
+<tr>
+	<td colspan=2 bgcolor=#FFFACD height=30 align=right>
+	<font face=arial,helvetica size=3 color=black>
+	<b>-USER-&nbsp;</b>
+	</td>
+</tr>
+<tr>
+	<td align=center valign=bottom width=150 bgcolor=#B0C4DE>
+	<font face=arial,helvetica size=1 color=black>
+	YOUR ORG NAME
+	</td>
+	<td width=550 bgcolor=#FFFFFF align=center valign=center>
+	<font face=arial,helvetica color=black>
+	<font size=4>
+	O acesso &agrave; p&aacute;gina:
+	<br><br>
+	<a href="-URL-" target="_blank">-URL-</a>
+	<br><br>
+	<font size=3>
+	... foi negado pela seguinte raz&atilde;:
+	<br><br>
+	<font color=red>
+	<b>-REASONGIVEN-</b>
+	<font color=black>
+	<br><br><br><br>
+	Voc&ecirc; est&aacute; vendo este erro porque o conte&uacute; que voc&ecirc; tentou
+	acessar parece ter, ou indica que cont&eacute;m, material classificado como impr&oacute;prio.
+	<br><br>
+	Se voc&ecirc; necessitar de aux&iacute; para este acesso, entre em contato com administrador da rede.
+	<br><br><br><br>
+	<font size=1>
+	Powered by <a href="http://www.dansguardian.org" target="_blank">DansGuardian</a>
+	</td>
+</tr>
+</table>
+
+</body>
+
+</html>
+
 <!--
-As variáveis disponíveis são as seguintes:
-- URL- informa a URL que o usuário estava tentando acessar.
-- REASONGIVEN- informa o motivo do bloqueio gentilmente, isto é, não mostra a frase proibida.
-- REASONLOGGED- informa o motivo que foi registrado, incluindo todos os detalhes.
-- USER- informa o nome de usuário, se conhecido.
-- IP- informa o IP de origem.
+The available variables are as follows:
+- URL- gives the URL the user was trying to get to.
+- REASONGIVEN- gives the nice reason (i.e. not quoting the banned phrase).
+- REASONLOGGED- gives the reason that gets logged including full details.
+- USER- gives the username if known.
+- IP- gives the originating IP.
 - FILTERGROUP- gives the group number.
 - BYPASS- gives URL which allows temporary bypass of denied page
 
+You need to remove the space between the - and the variable to use them
+in your HTML.  They are there above so extra processing is not required.
 
-Você precisa remover o espaço entre o hífen (-) e a variável para usá-la em seu HTML. Elas estão na definição da página acima, portanto processamento extra não é requerido.
-
-Mais moldes de exemplo serão provavalmente encontrados no sítio de DansGuardian, na página de Extras.
+More example templates are likely to be found on the DansGuardian web site
+on the Extras page.
 
-Daniel Barron 2002-03-27
+This page was designed by Paul Richards.  (http://www.ridney.com/)
 
-Traduzido/translated por/by Henrique Araujo (henrique@colegiosaogoncalo.g12.br)
+Daniel Barron 2003-09-01
 --!>
+
diff -Naur dansguardian-2.8.0.4/languages/ukenglish/messages dansguardian-2.8.0.4.oden/languages/ukenglish/messages
--- dansguardian-2.8.0.4/languages/ukenglish/messages	2005-02-20 16:25:10.000000000 +0100
+++ dansguardian-2.8.0.4.oden/languages/ukenglish/messages	2005-06-17 12:30:35.322932718 +0200
@@ -40,3 +40,5 @@
 "900","Banned extension: "
 
 "1000","PICS labeling level exceeded on the above site."
+
+"1100","Virus infected content found."