[Pkg-clamav-commits] [SCM] Debian repository for ClamAV branch, debian/unstable, updated. debian/0.95+dfsg-1-6634-g03ef06d

aCaB acab at clamav.net
Mon Feb 14 09:56:01 UTC 2011


The following commit has been merged in the debian/unstable branch:
commit 03ef06d4da2dfd94f5afae8db88a6ede239ab029
Author: aCaB <acab at clamav.net>
Date:   Mon Feb 14 10:54:31 2011 +0100

    merge --squash clamav-0.97

diff --git a/ChangeLog b/ChangeLog
index 9f127d0..e0a66f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,192 @@
+Mon Feb  7 17:45:49 CET 2011 (tk)
+---------------------------------
+ * V 0.97
+
+Mon Feb  7 17:24:52 CET 2011 (tk)
+---------------------------------
+ * libclamav/vba_extract.c: fix error path double free (bb#2486)
+
+Fri Feb  4 16:55:07 EET 2011 (edwin)
+------------------------------------
+ * libclamav/phishcheck.c: fix some missed safebrowsing URLs (bb #2514)
+
+Fri Feb  4 14:35:32 CET 2011 (tk)
+---------------------------------
+ * libclamav/matcher-bm.c: fix error message (bb#2513)
+
+Thu Feb  3 16:40:24 CET 2011 (acab)
+-----------------------------------
+ * libclamav/matcher-hash.c: stop leaking virusnames (nopool mode)
+
+Mon Jan 31 16:53:39 CET 2011 (tk)
+---------------------------------
+ * V 0.97rc
+
+Fri Jan 21 15:05:13 CET 2011 (acab)
+-----------------------------------
+ * libclamav/disasm.c: sign extend immeds, displacers and reljumps (bb#2341)
+
+Thu Jan 20 15:34:00 CET 2011 (tk)
+---------------------------------
+ * shared/optparser: when size limit is disabled set it to 4GB (bb#2471)
+
+Thu Jan 20 16:11:43 EET 2011 (edwin)
+------------------------------------
+ * shared/optparser.c: reduce BytecodeTimeout to 5s (instead of 60).
+
+Thu Jan 20 16:09:29 EET 2011 (edwin)
+-----------------------------------
+ * libclamav/bytecode_vm.c: fix memset on bigendian arch (bb #2478).
+
+Thu Jan 20 11:38:33 EET 2011 (edwin)
+------------------------------------
+ * libclamav/pdf.c: fix missed detection (bb #2455).
+
+Thu Jan 20 11:19:50 EET 2011 (edwin)
+-----------------------------------
+ * libclamav/pdf.c: fix another pdf uninit (bb #2404).
+
+Thu Jan 20 11:16:17 EET 2011 (edwin)
+------------------------------------
+ * libclamav/pdf.c: fix uninit value (bb #2455).
+
+Thu Jan 20 10:11:32 EET 2011 (edwin)
+------------------------------------
+ * libclamav/c++/llvm: port 2 upstream commits to fix bb#8723.
+
+Thu Jan 20 10:02:24 EET 2011 (edwin)
+------------------------------------
+ * unit_tests/check_bytecode.c: fix build on etch (bb #2399),
+    thanks to David F. Skoll <dfs*roaringpenguin.com>.
+
+Thu Jan 20 03:29:04 CET 2011 (acab)
+-----------------------------------
+ * win32/compat/resolv.c: fix nasty typo
+
+Wed Jan 19 12:16:23 CET 2011 (acab)
+-----------------------------------
+ * build system: add AM macro CLAMDSCAN_LIBS (bb#2400)
+
+Tue Jan 18 16:18:07 CET 2011 (tk)
+---------------------------------
+ * sigtool: handle .hs(b|u) and .ms(b|u) files
+
+Tue Jan 18 14:59:37 CET 2011 (tk)
+---------------------------------
+ * sigtool: add new options --sha1 and --sha256
+
+Mon Jan 17 19:05:25 CET 2011 (tk)
+---------------------------------
+ * freshclam: detect and fix corruptions of existing db files
+
+Fri Jan 14 23:33:25 CET 2011 (acab)
+-----------------------------------
+ * libclamav: merge multihash branch - we can now match on md5, sha1, sha256
+
+Fri Jan 14 15:52:30 CET 2011 (tk)
+---------------------------------
+ * freshclam: verify integrity of all *.cvd files during each session
+
+Mon Jan 10 23:51:08 EET 2011 (edwin)
+------------------------------------
+ * libclamav/c++/ClamBCRTChecks.cpp: fix paranoid mode crash (bb #2434).
+
+Mon Jan 10 18:43:02 CET 2011 (tk)
+---------------------------------
+ * sigtool: improve handling of spaces in cdiffs (bb#2090)
+
+Thu Jan  6 14:52:38 CET 2011 (acab)
+-----------------------------------
+ * libclamav/hashtab.c: properly clear the table
+
+Mon Jan  3 17:10:03 CET 2011 (tk)
+---------------------------------
+ * freshclam/manager.c: fix version warning (bb#2456)
+
+Mon Jan  3 16:38:46 CET 2011 (tk)
+---------------------------------
+ * libclamav: improve handling of HandlerType (bb#2298)
+
+Thu Dec 30 15:02:05 CET 2010 (tk)
+---------------------------------
+ * libclamav/filetypes.c: improve detection of tar files
+
+Wed Dec 29 14:36:46 CET 2010 (tk)
+---------------------------------
+ * libclamav/scanners.c: improve scanning of mail files in raw mode (bb#2244)
+
+Tue Dec 28 18:22:30 CET 2010 (tk)
+---------------------------------
+ * clamscan: add new options --follow-(dir|file)-symlinks (bb#1870)
+
+Tue Dec 28 13:03:14 CET 2010 (tk)
+---------------------------------
+ * libclamav/blob.c: properly scan files when LeaveTemporaryFiles
+		     is enabled (bb#2447)
+
+Mon Dec 27 15:21:43 CET 2010 (acab)
+-----------------------------------
+ * libclamav/disasm.c: fix movsx/movzx disasm src size (bb#2341)
+
+Wed Dec 22 12:10:06 CET 2010 (tk)
+---------------------------------
+ * freshclam: fix again handling of relative db paths (bb#2240)
+
+Tue Dec 21 16:00:26 CET 2010 (acab)
+-----------------------------------
+ * clamav-milter: add LogClean option (bb#2442)
+
+Mon Dec 20 16:30:57 EET 2010 (edwin)
+-----------------------------------
+ * clamd: bump default MaxConnectionQueueLength to 200
+
+Fri Dec 17 17:10:46 CET 2010 (acab)
+-----------------------------------
+ * shared/cdiff.c: Fix error path double free - missed in 4bc4581d
+		   thanks Michael Tautschnig <mt*debian.org>
+
+Fri Dec 17 14:21:21 CET 2010 (acab)
+-----------------------------------
+ * clamav-milter/whitelist.c, clamscan/manager.c, libclamav/sis.c:
+   error path leaks and similar - thanks Michael Tautschnig <mt*debian.org>
+	
+Thu Dec 16 15:31:38 CET 2010 (acab)
+---------------------------------
+ * clamav-milter: handle EINTR during waitpid()
+
+Thu Dec 16 14:15:33 CET 2010 (tk)
+---------------------------------
+ * freshclam, clamd: handle EINTR during waitpid() (bb#2422)
+
+Tue Dec 14 19:13:53 EET 2010 (edwin)
+------------------------------------
+ * libclamav/chmunpack.c: avoid crash on fstat failure (bb #2429).
+
+Tue Dec 14 16:07:42 CET 2010 (tk)
+---------------------------------
+ * libclamav: pass array with initial matches to bytecode (bb#2397)
+
+Tue Dec 14 12:20:34 CET 2010 (tk)
+---------------------------------
+ * libclamav/msexpand.c: fix name clash on HP-UX (bb#2398)
+
+Tue Dec 14 11:33:00 CET 2010 (tk)
+---------------------------------
+ * libclamav/scanners.c: ScanMail wasn't always honored (bb#2426)
+
+Thu Dec  9 13:40:55 CET 2010 (tk)
+---------------------------------
+ * clamd: don't report file hashes to clients (bb#2409)
+
+Thu Dec  9 13:29:16 CET 2010 (tk)
+---------------------------------
+ * libclamav: preserve callbacks during db reload (bb#2418)
+
+Thu Dec  2 18:44:12 CET 2010 (tk)
+---------------------------------
+ * libclamav/matcher-ac.c: optimize handling of multi-part signatures (bb#2322)
+			   Patch by finglenark <finglenark*gmail.com>
+
 Tue Nov 30 14:05:34 CET 2010 (tk)
 ---------------------------------
  * V 0.96.5
diff --git a/Makefile.in b/Makefile.in
index ecd0705..79fa8ad 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -199,6 +199,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/NEWS b/NEWS
index 477822d..2f4bf79 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,23 @@
-0.96.5
-------
+0.97
+----
+
+ClamAV 0.97 brings many improvements, including complete Windows support
+(all major components compile out-of-box under Visual Studio), support for
+signatures based on SHA1 and SHA256, better error detection, as well as 
+speed and memory optimizations. The complete list of changes is available 
+in the ChangeLog file. For upgrade notes and tips please see: 
+https://wiki.clamav.net/Main/UpgradeNotes097
+
+With Sourcefire, Inc. acquisition of Immunet Corp., ClamAV for Windows
+3.0 has been renamed Immunet 3.0, powered by ClamAV. This release
+contains the fully integrated LibClamAV 0.97 engine for offline,
+OnDemand, and OnAccess scanning. Immunet 3.0 users can now utilize
+the full power of the LibClamAV engine, all the ClamAV signatures,
+and creation of custom signatures on any platform running Immunet 3.0,
+powered by ClamAV. If you run Windows systems in your environment and
+need an AV solution to protect them, give Immunet 3.0, powered by ClamAV
+a try; you can download it from http://www.clamav.net/about/win32
 
-ClamAV 0.96.5 includes bugfixes and minor feature enhancements, such as
-improved handling of detection statistics, better file logging,
-and support for custom database URLs in freshclam. Please refer to the
-ChangeLog for details.
 
 --
 The ClamAV team (http://www.clamav.net/team)
diff --git a/README b/README
index fdc7be4..fef8ee3 100644
--- a/README
+++ b/README
@@ -2,6 +2,30 @@ Note: This README/NEWS file refers to the source tarball. Some things described
 here may not be available in binary packages.
 --
 
+0.97
+----
+
+ClamAV 0.97 brings many improvements, including complete Windows support
+(all major components compile out-of-box under Visual Studio), support for
+signatures based on SHA1 and SHA256, better error detection, as well as 
+speed and memory optimizations. The complete list of changes is available 
+in the ChangeLog file. For upgrade notes and tips please see: 
+https://wiki.clamav.net/Main/UpgradeNotes097
+
+With Sourcefire, Inc. acquisition of Immunet Corp., ClamAV for Windows
+3.0 has been renamed Immunet 3.0, powered by ClamAV. This release
+contains the fully integrated LibClamAV 0.97 engine for offline,
+OnDemand, and OnAccess scanning. Immunet 3.0 users can now utilize
+the full power of the LibClamAV engine, all the ClamAV signatures,
+and creation of custom signatures on any platform running Immunet 3.0,
+powered by ClamAV. If you run Windows systems in your environment and
+need an AV solution to protect them, give Immunet 3.0, powered by ClamAV
+a try; you can download it from http://www.clamav.net/about/win32
+
+
+--
+The ClamAV team (http://www.clamav.net/team)
+
 
 0.96.5
 ------
diff --git a/clamav-milter/Makefile.in b/clamav-milter/Makefile.in
index 9d37b93..161ff13 100644
--- a/clamav-milter/Makefile.in
+++ b/clamav-milter/Makefile.in
@@ -173,6 +173,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/clamav-milter/clamfi.c b/clamav-milter/clamfi.c
index 9f9573c..06e7bb7 100644
--- a/clamav-milter/clamfi.c
+++ b/clamav-milter/clamfi.c
@@ -58,11 +58,13 @@ static char *rejectfmt = NULL;
 int addxvirus = 0; /* 0 - don't add | 1 - replace | 2 - add */
 char xvirushdr[255];
 char *viraction = NULL;
-enum {
-    LOGINF_NONE,
-    LOGINF_BASIC,
-    LOGINF_FULL
-} loginfected;
+
+#define LOGINF_NONE 0
+#define LOGINF_BASIC 1
+#define LOGINF_FULL 2
+#define LOGCLN_BASIC 4
+#define LOGCLN_FULL 8
+int loginfected;
 
 #define CLAMFIBUFSZ 1424
 static const char *HDR_UNAVAIL = "UNKNOWN";
@@ -218,7 +220,7 @@ sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv) {
 
     if(!headerf) return SMFIS_CONTINUE; /* just in case */
 
-    if(loginfected == LOGINF_FULL || viraction) {
+    if((loginfected & (LOGINF_FULL | LOGCLN_FULL)) || viraction) {
 	if(!cf->msg_subj && !strcasecmp(headerf, "Subject"))
 	    cf->msg_subj = strdup(headerv ? headerv : "");
 	if(!cf->msg_date && !strcasecmp(headerf, "Date"))
@@ -317,10 +319,23 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
     len = strlen(reply);
     if(len>5 && !strcmp(reply + len - 5, ": OK\n")) {
 	if(addxvirus) add_x_header(ctx, "Clean", cf->scanned_count, cf->status_count);
+	if(loginfected & LOGCLN_FULL) {
+	    const char *id = smfi_getsymval(ctx, "{i}");
+	    const char *from = smfi_getsymval(ctx, "{mail_addr}");
+	    const char *to = smfi_getsymval(ctx, "{rcpt_addr}");
+	    const char *msg_subj = makesanehdr(cf->msg_subj);
+	    const char *msg_date = makesanehdr(cf->msg_date);
+	    const char *msg_id = makesanehdr(cf->msg_id);
+	    logg("~Clean message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s'\n", id, from, to, msg_subj, msg_id, msg_date);
+	} else if(loginfected & LOGCLN_BASIC) {
+	    const char *from = smfi_getsymval(ctx, "{mail_addr}");
+	    const char *to = smfi_getsymval(ctx, "{rcpt_addr}");
+	    logg("~Clean message from <%s> to <%s>\n", from, to);
+	}
 	ret = CleanAction(ctx);
     } else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) {
 	cf->virusname = NULL;
-	if(loginfected || addxvirus || rejectfmt || viraction) {
+	if((loginfected & (LOGINF_BASIC | LOGINF_FULL)) || addxvirus || rejectfmt || viraction) {
 	    char *vir;
 
 	    reply[len-7] = '\0';
@@ -344,7 +359,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
 
 		    if(!from) from = HDR_UNAVAIL;
 		    if(!to) to = HDR_UNAVAIL;
-		    if(loginfected == LOGINF_FULL || viraction) {
+		    if((loginfected & LOGINF_FULL) || viraction) {
 			const char *id = smfi_getsymval(ctx, "{i}");
 			const char *msg_subj = makesanehdr(cf->msg_subj);
 			const char *msg_date = makesanehdr(cf->msg_date);
@@ -352,7 +367,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
 
 			if(!id) id = HDR_UNAVAIL;
 			
-			if(loginfected == LOGINF_FULL)
+			if(loginfected & LOGINF_FULL)
 			    logg("~Message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s' infected by %s\n", id, from, to, msg_subj, msg_id, msg_date, vir);
 
 			if(viraction) {
@@ -382,8 +397,10 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
 				args[8] = NULL;
 				exit(execvp(viraction, args));
 			    } else if(pid > 0) {
+				int wret;
 				pthread_mutex_unlock(&virusaction_lock);
-				if(waitpid(pid, &ret, 0)<0)
+				while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
+				if(wret<0)
 				    logg("!VirusEvent: waitpid() failed: %s\n", cli_strerror(errno, er, sizeof(er)));
 				else {
 				    if(WIFEXITED(ret))
@@ -404,7 +421,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
 			    free(e_msg_id);
 			}
 		    }
-		    if(loginfected == LOGINF_BASIC)
+		    if(loginfected & LOGINF_BASIC)
 			logg("~Message from <%s> to <%s> infected by %s\n", from, to, vir);
 		}
 	    }
@@ -509,6 +526,17 @@ int init_actions(struct optstruct *opts) {
 	return 1;
     }
 
+    if((opt = optget(opts, "LogClean"))->enabled) {
+	if(!strcasecmp(opt->strarg, "Basic"))
+	    loginfected |= LOGCLN_BASIC;
+	else if(!strcasecmp(opt->strarg, "Full"))
+	    loginfected |= LOGCLN_FULL;
+	else if(strcasecmp(opt->strarg, "Off")) {
+	    logg("!Invalid setting %s for option LogInfected\n", opt->strarg);
+	    return 1;
+	}
+    }
+
     if((opt = optget(opts, "VirusAction"))->enabled)
 	viraction = strdup(opt->strarg);
 
diff --git a/clamav-milter/whitelist.c b/clamav-milter/whitelist.c
index 66f43f4..19263d7 100644
--- a/clamav-milter/whitelist.c
+++ b/clamav-milter/whitelist.c
@@ -90,6 +90,7 @@ int whitelist_init(const char *fname) {
 	if (!(w = (struct WHLST *)malloc(sizeof(*w)))) {
 	    logg("!Out of memory loading whitelist file\n");
 	    whitelist_free();
+	    fclose(f);
 	    return 1;
 	}
 	w->next = (*addto);
@@ -97,9 +98,11 @@ int whitelist_init(const char *fname) {
 	if (cli_regcomp(&w->preg, ptr, REG_ICASE|REG_NOSUB)) {
 	    logg("!Failed to compile regex '%s' in whitelist file\n", ptr);
 	    whitelist_free();
+	    fclose(f);
 	    return 1;
 	}
     }
+    fclose(f);
     return 0;
 }
 
diff --git a/clambc/Makefile.in b/clambc/Makefile.in
index f319777..7fd90bb 100644
--- a/clambc/Makefile.in
+++ b/clambc/Makefile.in
@@ -115,6 +115,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/clamconf/Makefile.in b/clamconf/Makefile.in
index a37d058..7322c1f 100644
--- a/clamconf/Makefile.in
+++ b/clamconf/Makefile.in
@@ -133,6 +133,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/clamd/Makefile.in b/clamd/Makefile.in
index 760da58..d16d81c 100644
--- a/clamd/Makefile.in
+++ b/clamd/Makefile.in
@@ -151,6 +151,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/clamd/others.c b/clamd/others.c
index 66a0d94..93d6c3c 100644
--- a/clamd/others.c
+++ b/clamd/others.c
@@ -146,7 +146,7 @@ void virusaction(const char *filename, const char *virname, const struct optstru
 	    exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
 	} else if(pid > 0) { /* parent */
 	    pthread_mutex_unlock(&virusaction_lock);
-	    waitpid(pid, NULL, 0);
+	    while(waitpid(pid, NULL, 0) == -1 && errno == EINTR);
 	} else {
 	    logg("!VirusEvent: fork failed.\n");
 	}
diff --git a/clamd/scanner.c b/clamd/scanner.c
index 1d1c5a5..2a084b1 100644
--- a/clamd/scanner.c
+++ b/clamd/scanner.c
@@ -239,7 +239,7 @@ int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw
 
     if (ret == CL_VIRUS) {
 	scandata->infected++;
-	if (conn_reply_virus(scandata->conn, filename, virname, context.virhash, context.virsize) == -1) {
+	if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
 	    free(filename);
 	    return CL_ETIMEOUT;
 	}
@@ -337,7 +337,7 @@ int scanfd(const int fd, const client_conn_t *conn, unsigned long int *scanned,
 	}
 
 	if(ret == CL_VIRUS) {
-		if (conn_reply_virus(conn, fdstr, virname, context.virhash, context.virsize) == -1)
+		if (conn_reply_virus(conn, fdstr, virname) == -1)
 		    ret = CL_ETIMEOUT;
 		if(context.virsize)
 		    detstats_add(virname, "NOFNAME", context.virsize, context.virhash);
diff --git a/clamd/session.c b/clamd/session.c
index f12eb22..e41c806 100644
--- a/clamd/session.c
+++ b/clamd/session.c
@@ -156,18 +156,12 @@ int conn_reply(const client_conn_t *conn, const char *path,
 }
 
 int conn_reply_virus(const client_conn_t *conn, const char *file,
-	       const char *virname, const char *virhash, unsigned int virsize)
+	       const char *virname)
 {
     if (conn->id) {
-	if (virsize)
-	    return mdprintf(conn->sd, "%u: %s: %s(%s:%u) FOUND%c", conn->id, file,
-			virname, virhash, virsize, conn->term);
 	return mdprintf(conn->sd, "%u: %s: %s FOUND%c", conn->id, file, virname,
 	    conn->term);
     }
-    if (virsize)
-	return mdprintf(conn->sd, "%s: %s(%s:%u) FOUND%c", file, virname, virhash,
-	    virsize, conn->term);
     return mdprintf(conn->sd, "%s: %s FOUND%c", file, virname, conn->term);
 }
 
diff --git a/clamd/session.h b/clamd/session.h
index 23fe976..183542d 100644
--- a/clamd/session.h
+++ b/clamd/session.h
@@ -96,7 +96,7 @@ int execute_or_dispatch_command(client_conn_t *conn, enum commands command, cons
 
 int conn_reply(const client_conn_t *conn, const char *path, const char *msg, const char *status);
 int conn_reply_single(const client_conn_t *conn, const char *path, const char *status);
-int conn_reply_virus(const client_conn_t *conn, const char *file, const char *virname, const char *virhash, unsigned int virsize);
+int conn_reply_virus(const client_conn_t *conn, const char *file, const char *virname);
 int conn_reply_error(const client_conn_t *conn, const char *msg);
 int conn_reply_errno(const client_conn_t *conn, const char *path, const char *msg);
 #endif
diff --git a/clamdscan/Makefile.am b/clamdscan/Makefile.am
index 5c514ab..5a0bd4e 100644
--- a/clamdscan/Makefile.am
+++ b/clamdscan/Makefile.am
@@ -43,7 +43,7 @@ endif
 
 DEFS = @DEFS@ -DCL_NOTHREADS -DCL_NOLIBCLAMAV
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/clamscan -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav
-LIBS = $(top_builddir)/libclamav/libclamav_internal_utils_nothreads.la  @FRESHCLAM_LIBS@
+LIBS = $(top_builddir)/libclamav/libclamav_internal_utils_nothreads.la  @CLAMDSCAN_LIBS@
 
 AM_INSTALLCHECK_STD_OPTIONS_EXEMPT=clamdscan$(EXEEXT)
 CLEANFILES=*.gcda *.gcno
diff --git a/clamdscan/Makefile.in b/clamdscan/Makefile.in
index a95acd4..71d11fb 100644
--- a/clamdscan/Makefile.in
+++ b/clamdscan/Makefile.in
@@ -144,6 +144,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
@@ -187,7 +188,7 @@ LIBCLAMAV_VERSION = @LIBCLAMAV_VERSION@
 LIBLTDL = @LIBLTDL@
 LIBM = @LIBM@
 LIBOBJS = @LIBOBJS@
-LIBS = $(top_builddir)/libclamav/libclamav_internal_utils_nothreads.la  @FRESHCLAM_LIBS@
+LIBS = $(top_builddir)/libclamav/libclamav_internal_utils_nothreads.la  @CLAMDSCAN_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
diff --git a/clamdtop/Makefile.in b/clamdtop/Makefile.in
index 09d89d8..09c5258 100644
--- a/clamdtop/Makefile.in
+++ b/clamdtop/Makefile.in
@@ -144,6 +144,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/clamscan/Makefile.am b/clamscan/Makefile.am
index c965af4..4a8e7d0 100644
--- a/clamscan/Makefile.am
+++ b/clamscan/Makefile.am
@@ -31,8 +31,6 @@ clamscan_SOURCES = \
     $(top_srcdir)/shared/misc.c \
     $(top_srcdir)/shared/misc.h \
     clamscan.c \
-    others.c \
-    others.h \
     global.h \
     manager.c \
     manager.h
diff --git a/clamscan/Makefile.in b/clamscan/Makefile.in
index 15aa33d..07c053d 100644
--- a/clamscan/Makefile.in
+++ b/clamscan/Makefile.in
@@ -79,7 +79,7 @@ am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
 am_clamscan_OBJECTS = output.$(OBJEXT) getopt.$(OBJEXT) \
 	optparser.$(OBJEXT) actions.$(OBJEXT) misc.$(OBJEXT) \
-	clamscan.$(OBJEXT) others.$(OBJEXT) manager.$(OBJEXT)
+	clamscan.$(OBJEXT) manager.$(OBJEXT)
 clamscan_OBJECTS = $(am_clamscan_OBJECTS)
 clamscan_LDADD = $(LDADD)
 AM_V_lt = $(am__v_lt_$(V))
@@ -135,6 +135,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
@@ -288,8 +289,6 @@ clamscan_SOURCES = \
     $(top_srcdir)/shared/misc.c \
     $(top_srcdir)/shared/misc.h \
     clamscan.c \
-    others.c \
-    others.h \
     global.h \
     manager.c \
     manager.h
@@ -405,7 +404,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/manager.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/misc.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/optparser.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/others.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/output.Po at am__quote@
 
 .c.o:
diff --git a/clamscan/clamscan.c b/clamscan/clamscan.c
index f1a1d94..b11a27e 100644
--- a/clamscan/clamscan.c
+++ b/clamscan/clamscan.c
@@ -210,6 +210,8 @@ void help(void)
     mprintf("    --log=FILE            -l FILE        Save scan report to FILE\n");
     mprintf("    --recursive[=yes/no(*)]  -r          Scan subdirectories recursively\n");
     mprintf("    --cross-fs[=yes(*)/no]               Scan files and directories on other filesystems\n");
+    mprintf("    --follow-dir-symlinks[=0/1(*)/2]     Follow directory symlinks (0 = never, 1 = direct, 2 = always)\n");
+    mprintf("    --follow-file-symlinks[=0/1(*)/2]    Follow file symlinks (0 = never, 1 = direct, 2 = always)\n");
     mprintf("    --file-list=FILE      -f FILE        Scan files from FILE\n");
     mprintf("    --remove[=yes/no(*)]                 Remove infected files. Be careful!\n");
     mprintf("    --move=DIRECTORY                     Move infected files into DIRECTORY\n");
@@ -221,7 +223,7 @@ void help(void)
     mprintf("\n");
     mprintf("    --bytecode[=yes(*)/no]               Load bytecode from the database\n");
     mprintf("    --bytecode-trust-all[=yes/no(*)]     Trust all loaded bytecode\n");
-    mprintf("    --bytecode-timeout=N		      Set bytecode timeout (in milliseconds)\n");
+    mprintf("    --bytecode-timeout=N                 Set bytecode timeout (in milliseconds)\n");
     mprintf("    --detect-pua[=yes/no(*)]             Detect Possibly Unwanted Applications\n");
     mprintf("    --exclude-pua=CAT                    Skip PUA sigs of category CAT\n");
     mprintf("    --include-pua=CAT                    Load PUA sigs of category CAT\n");
diff --git a/clamscan/manager.c b/clamscan/manager.c
index b4a5794..1672fcd 100644
--- a/clamscan/manager.c
+++ b/clamscan/manager.c
@@ -29,6 +29,9 @@
 #include <ctype.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
 #include <dirent.h>
 #ifndef _WIN32
 #include <sys/wait.h>
@@ -43,9 +46,9 @@
 #include <sys/types.h>
 #include <signal.h>
 #include <errno.h>
+#include <target.h>
 
 #include "manager.h"
-#include "others.h"
 #include "global.h"
 
 #include "shared/optparser.h"
@@ -64,25 +67,66 @@
 dev_t procdev;
 #endif
 
-void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options)
+#ifdef _WIN32
+/* FIXME: If possible, handle users correctly */
+static int checkaccess(const char *path, const char *username, int mode)
+{
+    return !_access(path, mode);
+}
+#else
+static int checkaccess(const char *path, const char *username, int mode)
+{
+	struct passwd *user;
+	int ret = 0, status;
+
+    if(!geteuid()) {
+
+	if((user = getpwnam(username)) == NULL) {
+	    return -1;
+	}
+
+	switch(fork()) {
+	    case -1:
+		return -2;
+
+	    case 0:
+		if(setgid(user->pw_gid)) {
+		    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
+		    exit(0);
+		}
+
+		if(setuid(user->pw_uid)) {
+		    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
+		    exit(0);
+		}
+
+		if(access(path, mode))
+		    exit(0);
+		else
+		    exit(1);
+
+	    default:
+		wait(&status);
+		if(WIFEXITED(status) && WEXITSTATUS(status) == 1)
+		    ret = 1;
+	}
+
+    } else {
+	if(!access(path, mode))
+	    ret = 1;
+    }
+
+    return ret;
+}
+#endif
+
+static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options)
 {
 	int ret = 0, fd, included, printclean = 1;
-	unsigned int fsize;
 	const struct optstruct *opt;
 	const char *virname;
-#ifdef C_LINUX
 	struct stat sb;
 
-    /* argh, don't scan /proc files */
-    if(procdev)
-	if(stat(filename, &sb) != -1)
-	    if(sb.st_dev == procdev) {
-		if(!printinfected)
-		    logg("~%s: Excluded (/proc)\n", filename);
-		return;
-	    }
-#endif    
-
     if((opt = optget(opts, "exclude"))->enabled) {
 	while(opt) {
 	    if(match_regex(filename, opt->strarg) == 1) {
@@ -110,13 +154,23 @@ void scanfile(const char *filename, struct cl_engine *engine, const struct optst
 	}
     }
 
-    fsize = fileinfo(filename, 1);
-    if(fsize == 0) {
-	if(!printinfected)
-	    logg("~%s: Empty file\n", filename);
-	return;
+    /* argh, don't scan /proc files */
+    if(stat(filename, &sb) != -1) {
+#ifdef C_LINUX
+	if(procdev && sb.st_dev == procdev) {
+	    if(!printinfected)
+		logg("~%s: Excluded (/proc)\n", filename);
+		return;
+	}
+#endif    
+	if(!sb.st_size) {
+	    if(!printinfected)
+		logg("~%s: Empty file\n", filename);
+	    return;
+	}
+	info.rblocks += sb.st_size / CL_COUNT_PRECISION;
     }
-    info.rblocks += fsize / CL_COUNT_PRECISION;
+
 #ifndef _WIN32
     if(geteuid())
 	if(checkaccess(filename, NULL, R_OK) != 1) {
@@ -159,14 +213,15 @@ void scanfile(const char *filename, struct cl_engine *engine, const struct optst
 	action(filename);
 }
 
-void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, unsigned int options, unsigned int depth, dev_t dev)
+static void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, unsigned int options, unsigned int depth, dev_t dev)
 {
 	DIR *dd;
 	struct dirent *dent;
-	struct stat statbuf;
+	struct stat sb;
 	char *fname;
 	int included;
 	const struct optstruct *opt;
+	unsigned int dirlnk, filelnk;
 
 
     if((opt = optget(opts, "exclude-dir"))->enabled) {
@@ -199,6 +254,9 @@ void scandirs(const char *dirname, struct cl_engine *engine, const struct optstr
     if(depth > (unsigned int) optget(opts, "max-dir-recursion")->numarg)
 	return;
 
+    dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
+    filelnk = optget(opts, "follow-file-symlinks")->numarg;
+
     if((dd = opendir(dirname)) != NULL) {
 	info.dirs++;
 	depth++;
@@ -214,19 +272,35 @@ void scandirs(const char *dirname, struct cl_engine *engine, const struct optstr
 			sprintf(fname, "%s"PATHSEP"%s", dirname, dent->d_name);
 
 		    /* stat the file */
-		    if(lstat(fname, &statbuf) != -1) {
+		    if(lstat(fname, &sb) != -1) {
 			if(!optget(opts, "cross-fs")->enabled) {
-			    if(statbuf.st_dev != dev) {
+			    if(sb.st_dev != dev) {
 				if(!printinfected)
 				    logg("~%s: Excluded\n", fname);
 				free(fname);
 				continue;
 			    }
 			}
-			if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode) && recursion)
-			    scandirs(fname, engine, opts, options, depth, dev);
-			else if(S_ISREG(statbuf.st_mode))
+			if(S_ISLNK(sb.st_mode)) {
+			    if(dirlnk != 2 && filelnk != 2) {
+				if(!printinfected)
+				    logg("%s: Symbolic link\n", fname);
+			    } else if(stat(fname, &sb) != -1) {
+				if(S_ISREG(sb.st_mode) && filelnk == 2) {
+				    scanfile(fname, engine, opts, options);
+				} else if(S_ISDIR(sb.st_mode) && dirlnk == 2) {
+				    if(recursion)
+					scandirs(fname, engine, opts, options, depth, dev);
+				} else {
+				    if(!printinfected)
+					logg("%s: Symbolic link\n", fname);
+				}
+			    }
+			} else if(S_ISREG(sb.st_mode)) {
 			    scanfile(fname, engine, opts, options);
+			} else if(S_ISDIR(sb.st_mode) && recursion) {
+			    scandirs(fname, engine, opts, options, depth, dev);
+			}
 		    }
 		    free(fname);
 		}
@@ -273,6 +347,7 @@ static int scanstdin(const struct cl_engine *engine, const struct optstruct *opt
 	if(fwrite(buff, 1, bread, fs) < bread) {
 	    logg("!Can't write to %s\n", file);
 	    free(file);
+	    fclose(fs);
 	    return 2;
 	}
     }
@@ -305,9 +380,8 @@ static int scanstdin(const struct cl_engine *engine, const struct optstruct *opt
 
 int scanmanager(const struct optstruct *opts)
 {
-	mode_t fmode;
-	int ret = 0, fmodeint, i;
-	unsigned int options = 0, dboptions = 0;
+	int ret = 0, i;
+	unsigned int options = 0, dboptions = 0, dirlnk = 1, filelnk = 1;
 	struct cl_engine *engine;
 	struct stat sb;
 	char *file, cwd[1024], *pua_cats = NULL;
@@ -317,6 +391,18 @@ int scanmanager(const struct optstruct *opts)
 	struct rlimit rlim;
 #endif
 
+    dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
+    if(dirlnk > 2) {
+	logg("!--follow-dir-symlinks: Invalid argument\n");
+	return 2;
+    }
+
+    filelnk = optget(opts, "follow-file-symlinks")->numarg;
+    if(filelnk > 2) {
+	logg("!--follow-file-symlinks: Invalid argument\n");
+	return 2;
+    }
+
     if(optget(opts, "phishing-sigs")->enabled)
 	dboptions |= CL_DB_PHISHING;
 
@@ -611,8 +697,8 @@ int scanmanager(const struct optstruct *opts)
 	    logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
 
 	while((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
-	    if((fmodeint = fileinfo(file, 2)) == -1) {
-		logg("^Can't access file %s\n", file);
+	    if(lstat(file, &sb) == -1) {
+		logg("^%s: Can't access file\n", file);
 		perror(file);
 		ret = 2;
 	    } else {
@@ -623,21 +709,27 @@ int scanmanager(const struct optstruct *opts)
 			break;
 		}
 
-		fmode = (mode_t) fmodeint;
-
-		switch(fmode & S_IFMT) {
-		    case S_IFREG:
-			scanfile(file, engine, opts, options);
-			break;
-
-		    case S_IFDIR:
-			stat(file, &sb);
-			scandirs(file, engine, opts, options, 1, sb.st_dev);
-			break;
-
-		    default:
-			logg("!Not supported file type (%s)\n", file);
-			ret = 2;
+		if(S_ISLNK(sb.st_mode)) {
+		    if(dirlnk == 0 && filelnk == 0) {
+			if(!printinfected)
+			    logg("%s: Symbolic link\n", file);
+		    } else if(stat(file, &sb) != -1) {
+			if(S_ISREG(sb.st_mode) && filelnk) {
+			    scanfile(file, engine, opts, options);
+			} else if(S_ISDIR(sb.st_mode) && dirlnk) {
+			    scandirs(file, engine, opts, options, 1, sb.st_dev);
+			} else {
+			    if(!printinfected)
+				logg("%s: Symbolic link\n", file);
+			}
+		    }
+		} else if(S_ISREG(sb.st_mode)) {
+		    scanfile(file, engine, opts, options);
+		} else if(S_ISDIR(sb.st_mode)) {
+		    scandirs(file, engine, opts, options, 1, sb.st_dev);
+		} else {
+		    logg("^%s: Not supported file type\n", file);
+		    ret = 2;
 		}
 	    }
 	    free(file);
@@ -655,4 +747,3 @@ int scanmanager(const struct optstruct *opts)
 
     return ret;
 }
-
diff --git a/clamscan/others.c b/clamscan/others.c
deleted file mode 100644
index 5fb8c5e..0000000
--- a/clamscan/others.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- *  Copyright (C) 2007-2009 Sourcefire, Inc.
- *
- *  Authors: Tomasz Kojm
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston,
- *  MA 02110-1301, USA.
- *
- */
-
-#if HAVE_CONFIG_H
-#include "clamav-config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/time.h>
-#endif
-#include <time.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <target.h>
-
-#include "shared/output.h"
-#include "others.h"
-
-int fileinfo(const char *filename, short i)
-{
-	struct stat infostruct;
-
-    if(stat(filename, &infostruct) == -1)
-	return(-1);
-
-    switch(i) {
-
-	case 1: /* size */
-	    return infostruct.st_size;
-	case 2: /* permissions */
-	    return (mode_t)infostruct.st_mode;
-	case 3: /* modification time */
-	    return infostruct.st_mtime;
-	case 4: /* UID */
-	    return infostruct.st_uid;
-	case 5: /* GID */
-	    return infostruct.st_gid;
-	default:
-	    logg("!fileinfo(): Unknown option.\n");
-	    exit(1);
-    }
-}
-
-#ifdef _WIN32
-/* FIXME: Handle users correctly */
-int checkaccess(const char *path, const char *username, int mode)
-{
-    return !_access(path, mode);
-}
-#else
-int checkaccess(const char *path, const char *username, int mode)
-{
-	struct passwd *user;
-	int ret = 0, status;
-
-    if(!geteuid()) {
-
-	if((user = getpwnam(username)) == NULL) {
-	    return -1;
-	}
-
-	switch(fork()) {
-	    case -1:
-		return -2;
-
-	    case 0:
-		if(setgid(user->pw_gid)) {
-		    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
-		    exit(0);
-		}
-
-		if(setuid(user->pw_uid)) {
-		    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
-		    exit(0);
-		}
-
-		if(access(path, mode))
-		    exit(0);
-		else
-		    exit(1);
-
-	    default:
-		wait(&status);
-		if(WIFEXITED(status) && WEXITSTATUS(status) == 1)
-		    ret = 1;
-	}
-
-    } else {
-	if(!access(path, mode))
-	    ret = 1;
-    }
-
-    return ret;
-}
-#endif
diff --git a/clamscan/others.h b/clamscan/others.h
deleted file mode 100644
index 934d838..0000000
--- a/clamscan/others.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- *  Copyright (C) 2007-2009 Sourcefire, Inc.
- *
- *  Authors: Tomasz Kojm
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston,
- *  MA 02110-1301, USA.
- */
-
-#ifndef __OTHERS_H
-#define __OTHERS_H
-
-int fileinfo(const char *filename, short i);
-int checkaccess(const char *path, const char *username, int mode);
-
-#endif
diff --git a/configure b/configure
index 78454ac..388c33d 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.67 for ClamAV 0.96.5.
+# Generated by GNU Autoconf 2.67 for ClamAV 0.97.
 #
 # Report bugs to <http://bugs.clamav.net/>.
 #
@@ -703,8 +703,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='ClamAV'
 PACKAGE_TARNAME='clamav'
-PACKAGE_VERSION='0.96.5'
-PACKAGE_STRING='ClamAV 0.96.5'
+PACKAGE_VERSION='0.97'
+PACKAGE_STRING='ClamAV 0.97'
 PACKAGE_BUGREPORT='http://bugs.clamav.net/'
 PACKAGE_URL='http://www.clamav.net/'
 
@@ -754,8 +754,6 @@ am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
-BUILD_SHA1_FALSE
-BUILD_SHA1_TRUE
 subdirs
 HAVE_CURSES_FALSE
 HAVE_CURSES_TRUE
@@ -770,6 +768,7 @@ BUILD_CLAMD_FALSE
 BUILD_CLAMD_TRUE
 THREAD_LIBS
 TH_SAFE
+CLAMDSCAN_LIBS
 FRESHCLAM_LIBS
 CLAMAV_MILTER_LIBS
 CLAMD_LIBS
@@ -1541,7 +1540,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ClamAV 0.96.5 to adapt to many kinds of systems.
+\`configure' configures ClamAV 0.97 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1612,7 +1611,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ClamAV 0.96.5:";;
+     short | recursive ) echo "Configuration of ClamAV 0.97:";;
    esac
   cat <<\_ACEOF
 
@@ -1769,7 +1768,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ClamAV configure 0.96.5
+ClamAV configure 0.97
 generated by GNU Autoconf 2.67
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -2238,7 +2237,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ClamAV $as_me 0.96.5, which was
+It was created by ClamAV $as_me 0.97, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   $ $0 $@
@@ -3359,7 +3358,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='clamav'
- VERSION='0.96.5'
+ VERSION='0.97'
 
 
 # Some tools Automake needs.
@@ -3488,7 +3487,7 @@ AM_BACKSLASH='\'
 $as_echo "#define PACKAGE PACKAGE_NAME" >>confdefs.h
 
 
-VERSION="0.96.5"
+VERSION="0.97"
 
 cat >>confdefs.h <<_ACEOF
 #define VERSION "$VERSION"
@@ -3496,7 +3495,7 @@ _ACEOF
 
 
 LC_CURRENT=7
-LC_REVISION=7
+LC_REVISION=9
 LC_AGE=1
 LIBCLAMAV_VERSION="$LC_CURRENT":"$LC_REVISION":"$LC_AGE"
 
@@ -5183,13 +5182,13 @@ if test "${lt_cv_nm_interface+set}" = set; then :
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:5186: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:5185: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5189: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:5188: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5192: output\"" >&5)
+  (eval echo "\"\$as_me:5191: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -6384,7 +6383,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 6387 "configure"' > conftest.$ac_ext
+  echo '#line 6386 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7911,11 +7910,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7914: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7913: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7918: \$? = $ac_status" >&5
+   echo "$as_me:7917: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -8250,11 +8249,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8253: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8252: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8257: \$? = $ac_status" >&5
+   echo "$as_me:8256: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -8355,11 +8354,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8358: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8357: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:8362: \$? = $ac_status" >&5
+   echo "$as_me:8361: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -8410,11 +8409,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8413: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8412: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:8417: \$? = $ac_status" >&5
+   echo "$as_me:8416: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -10794,7 +10793,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10797 "configure"
+#line 10796 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10890,7 +10889,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10893 "configure"
+#line 10892 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11562,7 +11561,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11565 "configure"
+#line 11564 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15002,6 +15001,8 @@ $as_echo "$ZLIB_HOME" >&6; }
 fi
 
 
+CLAMDSCAN_LIBS="$FRESHCLAM_LIBS"
+
 # Check whether --enable-zlib-vcheck was given.
 if test "${enable_zlib_vcheck+set}" = set; then :
   enableval=$enable_zlib_vcheck; zlib_check=$enableval
@@ -16845,6 +16846,7 @@ $as_echo "#define CLAMUKO 1" >>confdefs.h
     fi
     ;;
 solaris*)
+    CLAMDSCAN_LIBS="$CLAMDSCAN_LIBS -lresolv"
     FRESHCLAM_LIBS="$FRESHCLAM_LIBS -lresolv"
     if test "$have_pthreads" = "yes"; then
 	THREAD_LIBS="-lpthread"
@@ -16955,6 +16957,7 @@ $as_echo "#define BIND_8_COMPAT 1" >>confdefs.h
     use_netinfo="yes"
     ;;
 os2*)
+    CLAMDSCAN_LIBS="$CLAMDSCAN_LIBS -lsyslog"
     FRESHCLAM_LIBS="$FRESHCLAM_LIBS -lsyslog"
     CLAMD_LIBS="$CLAMD_LIBS -lsyslog"
     CLAMAV_MILTER_LIBS="$CLAMAV_MILTER_LIBS -lsyslog"
@@ -17050,6 +17053,7 @@ esac
 
 
 
+
 if test "$have_milter" = "yes"; then
 
         save_LIBS="$LIBS"
@@ -20038,14 +20042,6 @@ if test "$enable_sha_collector" != "no"; then
 $as_echo "#define HAVE__INTERNAL__SHA_COLLECT 1" >>confdefs.h
 
 fi
- if test "$enable_sha_collector" != "no"; then
-  BUILD_SHA1_TRUE=
-  BUILD_SHA1_FALSE='#'
-else
-  BUILD_SHA1_TRUE='#'
-  BUILD_SHA1_FALSE=
-fi
-
 
 ac_config_files="$ac_config_files clamscan/Makefile database/Makefile docs/Makefile clamd/Makefile clamdscan/Makefile clamav-milter/Makefile freshclam/Makefile sigtool/Makefile clamconf/Makefile etc/Makefile test/Makefile unit_tests/Makefile clamdtop/Makefile clambc/Makefile Makefile clamav-config libclamav.pc platform.h docs/man/clamav-milter.8 docs/man/clamav-milter.conf.5 docs/man/clamconf.1 docs/man/clamd.8 docs/man/clamd.conf.5 docs/man/clamdscan.1 docs/man/clamscan.1 docs/man/freshclam.1 docs/man/freshclam.conf.5 docs/man/sigtool.1 docs/man/clamdtop.1"
 
@@ -20225,10 +20221,6 @@ if test -z "${HAVE_CURSES_TRUE}" && test -z "${HAVE_CURSES_FALSE}"; then
   as_fn_error $? "conditional \"HAVE_CURSES\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${BUILD_SHA1_TRUE}" && test -z "${BUILD_SHA1_FALSE}"; then
-  as_fn_error $? "conditional \"BUILD_SHA1\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
 
 : ${CONFIG_STATUS=./config.status}
 ac_write_fail=0
@@ -20637,7 +20629,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ClamAV $as_me 0.96.5, which was
+This file was extended by ClamAV $as_me 0.97, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -20704,7 +20696,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ClamAV config.status 0.96.5
+ClamAV config.status 0.97
 configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
 
@@ -22820,10 +22812,6 @@ if test -z "${HAVE_CURSES_TRUE}" && test -z "${HAVE_CURSES_FALSE}"; then
   as_fn_error $? "conditional \"HAVE_CURSES\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${BUILD_SHA1_TRUE}" && test -z "${BUILD_SHA1_FALSE}"; then
-  as_fn_error $? "conditional \"BUILD_SHA1\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
 if test -z "${am__EXEEXT_TRUE}" && test -z "${am__EXEEXT_FALSE}"; then
   as_fn_error $? "conditional \"am__EXEEXT\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -23240,7 +23228,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ClamAV $as_me 0.96.5, which was
+This file was extended by ClamAV $as_me 0.97, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -23307,7 +23295,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ClamAV config.status 0.96.5
+ClamAV config.status 0.97
 configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.in b/configure.in
index e5a934b..1c95648 100644
--- a/configure.in
+++ b/configure.in
@@ -20,7 +20,7 @@ dnl   MA 02110-1301, USA.
 AC_PREREQ([2.59])
 dnl For a release change [devel] to the real version [0.xy]
 dnl also change VERSION below
-AC_INIT([ClamAV], [0.96.5], [http://bugs.clamav.net/], [clamav], [http://www.clamav.net/])
+AC_INIT([ClamAV], [0.97], [http://bugs.clamav.net/], [clamav], [http://www.clamav.net/])
 
 AH_BOTTOM([#include "platform.h"])
 dnl put configure auxiliary into config
@@ -42,11 +42,11 @@ dnl the date in the version
 AC_DEFINE([PACKAGE], PACKAGE_NAME, [Name of package])
 
 dnl change this on a release
-VERSION="0.96.5"
+VERSION="0.97"
 AC_DEFINE_UNQUOTED([VERSION],"$VERSION",[Version number of package])
 
 LC_CURRENT=7
-LC_REVISION=7
+LC_REVISION=9
 LC_AGE=1
 LIBCLAMAV_VERSION="$LC_CURRENT":"$LC_REVISION":"$LC_AGE"
 AC_SUBST([LIBCLAMAV_VERSION])
@@ -527,6 +527,8 @@ fi
 AC_MSG_RESULT([$ZLIB_HOME])
 ])
 
+CLAMDSCAN_LIBS="$FRESHCLAM_LIBS"
+
 AC_ARG_ENABLE([zlib-vcheck],
 [  --disable-zlib-vcheck	  do not check for buggy zlib version ],
 zlib_check=$enableval, zlib_check="yes")
@@ -971,6 +973,7 @@ kfreebsd*-gnu)
     fi
     ;;
 solaris*)
+    CLAMDSCAN_LIBS="$CLAMDSCAN_LIBS -lresolv"
     FRESHCLAM_LIBS="$FRESHCLAM_LIBS -lresolv"
     if test "$have_pthreads" = "yes"; then
 	THREAD_LIBS="-lpthread"
@@ -1049,6 +1052,7 @@ darwin*)
     use_netinfo="yes"
     ;;
 os2*)
+    CLAMDSCAN_LIBS="$CLAMDSCAN_LIBS -lsyslog"
     FRESHCLAM_LIBS="$FRESHCLAM_LIBS -lsyslog"
     CLAMD_LIBS="$CLAMD_LIBS -lsyslog"
     CLAMAV_MILTER_LIBS="$CLAMAV_MILTER_LIBS -lsyslog"
@@ -1125,6 +1129,7 @@ AC_SUBST([LIBCLAMAV_LIBS])
 AC_SUBST([CLAMD_LIBS])
 AC_SUBST([CLAMAV_MILTER_LIBS])
 AC_SUBST([FRESHCLAM_LIBS])
+AC_SUBST([CLAMDSCAN_LIBS])
 AC_SUBST([TH_SAFE])
 AC_SUBST([THREAD_LIBS])
 
@@ -1610,7 +1615,6 @@ AC_ARG_ENABLE([sha-collector-for-internal-use], [], [enable_sha_collector="yes"]
 if test "$enable_sha_collector" != "no"; then
     AC_DEFINE([HAVE__INTERNAL__SHA_COLLECT], 1, [For internal use only - DO NOT DEFINE])
 fi
-AM_CONDITIONAL([BUILD_SHA1],[test "$enable_sha_collector" != "no"])
 
 AC_OUTPUT([
 clamscan/Makefile
diff --git a/database/Makefile.in b/database/Makefile.in
index 2a03459..2f5f1e5 100644
--- a/database/Makefile.in
+++ b/database/Makefile.in
@@ -101,6 +101,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/docs/Makefile.in b/docs/Makefile.in
index b8e857c..fb8ca38 100644
--- a/docs/Makefile.in
+++ b/docs/Makefile.in
@@ -128,6 +128,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/docs/clamdoc.pdf b/docs/clamdoc.pdf
index 9817aba..b3bd9e7 100644
Binary files a/docs/clamdoc.pdf and b/docs/clamdoc.pdf differ
diff --git a/docs/clamdoc.tex b/docs/clamdoc.tex
index b2b6133..e034494 100644
--- a/docs/clamdoc.tex
+++ b/docs/clamdoc.tex
@@ -71,7 +71,7 @@
     \vspace{3cm}
     \begin{flushright}
 	\rule[-1ex]{8cm}{3pt}\\
-	\huge Clam AntiVirus 0.96.5\\
+	\huge Clam AntiVirus 0.97\\
 	\huge \emph{User Manual}\\
     \end{flushright}
 
@@ -83,7 +83,7 @@
     \noindent
     \begin{boxedminipage}[b]{\textwidth}
     ClamAV User Manual,
-    \copyright \  2007 - 2010 Sourcefire, Inc.
+    \copyright \  2007 - 2011 Sourcefire, Inc.
     Authors: Tomasz Kojm\\
     This document is distributed under the terms of the GNU General
     Public License v2.\\
diff --git a/docs/html/clamdoc.html b/docs/html/clamdoc.html
index e525949..7ef5637 100644
--- a/docs/html/clamdoc.html
+++ b/docs/html/clamdoc.html
@@ -56,7 +56,7 @@ original version by:  Nikos Drakos, CBLU, University of Leeds
 <BR>
 <BR>
     <DIV ALIGN="RIGHT">
-<BR>	<BIG CLASS="HUGE">Clam AntiVirus 0.96.5
+<BR>	<BIG CLASS="HUGE">Clam AntiVirus 0.97
 <BR>	<BIG CLASS="HUGE"><SPAN  CLASS="textit">User Manual</SPAN>
 <BR>    
 </BIG></BIG></DIV>
diff --git a/docs/html/index.html b/docs/html/index.html
index e525949..7ef5637 100644
--- a/docs/html/index.html
+++ b/docs/html/index.html
@@ -56,7 +56,7 @@ original version by:  Nikos Drakos, CBLU, University of Leeds
 <BR>
 <BR>
     <DIV ALIGN="RIGHT">
-<BR>	<BIG CLASS="HUGE">Clam AntiVirus 0.96.5
+<BR>	<BIG CLASS="HUGE">Clam AntiVirus 0.97
 <BR>	<BIG CLASS="HUGE"><SPAN  CLASS="textit">User Manual</SPAN>
 <BR>    
 </BIG></BIG></DIV>
diff --git a/docs/man/clamav-milter.conf.5.in b/docs/man/clamav-milter.conf.5.in
index 4786397..abdfbfe 100644
--- a/docs/man/clamav-milter.conf.5.in
+++ b/docs/man/clamav-milter.conf.5.in
@@ -212,15 +212,22 @@ Enable verbose logging.
 Default: no
 .TP 
 \fBLogInfected STRING\fR
-Specify the type of syslog messages \- please refer to 'man syslog' for facility names.
-.br 
-This option allows to tune what is logged when a message is infected. Possible values are Off (the default - nothing is logged), Basic (minimal info logged), Full (verbose info logged)
+This option allows to tune what is logged when a message is infected. Possible values are Off (the default \- nothing is logged), Basic (minimal info logged), Full (verbose info logged)
 .br
 Note: For this to work properly in sendmail, make sure the msg_id, mail_addr, rcpt_addr and i macroes are available in eom. In other words add a line like: Milter.macros.eom={msg_id}, {mail_addr}, {rcpt_addr}, i to your .cf file. Alternatively use the macro: define(`confMILTER_MACROS_EOM', `{msg_id}, {mail_addr}, {rcpt_addr}, i')
 .br
 Postfix should be working fine with the default settings.
 .br
 Default: disabled
+.TP 
+\fBLogClean STRING\fR
+This option allows to tune what is logged when no threat is found in a scanned message.
+.br
+See LogInfected for possible values and caveats.
+.br
+Useful in debugging but drastically increases the log size.
+.br
+Default: disabled
 .SH "NOTES"
 .LP 
 All options expressing a size are limited to max 4GB. Values in excess will be resetted to the maximum.
diff --git a/docs/man/clamd.conf.5.in b/docs/man/clamd.conf.5.in
index ac8c4bb..6b9cb84 100644
--- a/docs/man/clamd.conf.5.in
+++ b/docs/man/clamd.conf.5.in
@@ -122,7 +122,7 @@ Default: no
 \fBMaxConnectionQueueLength NUMBER\fR
 Maximum length the queue of pending connections may grow to.
 .br 
-Default: 15
+Default: 200
 .TP 
 \fBMaxThreads NUMBER\fR
 Maximum number of threads running at the same time.
@@ -257,7 +257,7 @@ Default: TrustSigned
 \fBBytecodeTimeout NUMBER\fR
 Set bytecode timeout in milliseconds.
 .br
-Default: 60000
+Default: 5000
 .TP 
 \fBDetectPUA BOOL\fR
 Detect Possibly Unwanted Applications.
diff --git a/docs/man/clamscan.1.in b/docs/man/clamscan.1.in
index 761fd48..3083131 100644
--- a/docs/man/clamscan.1.in
+++ b/docs/man/clamscan.1.in
@@ -54,6 +54,12 @@ Scan directories recursively. All the subdirectories in the given directory will
 \fB\-\-cross\-fs=[yes(*)/no]\fR
 Scan files and directories on other filesystems.
 .TP 
+\fB\-\-follow\-dir\-symlinks=[0/1(*)/2]\fR
+Follow directory symlinks. There are 3 options: 0 - never follow directory symlinks, 1 (default) - only follow directory symlinks, which are passed as direct arguments to clamscan. 2 - never follow directory symlinks.
+.TP 
+\fB\-\-follow\-file\-symlinks=[0/1(*)/2]\fR
+Follow file symlinks. There are 3 options: 0 - never follow file symlinks, 1 (default) - only follow file symlinks, which are passed as direct arguments to clamscan. 2 - never follow file symlinks.
+.TP 
 \fB\-\-bell\fR
 Sound bell on virus detection.
 .TP 
diff --git a/docs/man/sigtool.1.in b/docs/man/sigtool.1.in
index 684e571..75bfb00 100644
--- a/docs/man/sigtool.1.in
+++ b/docs/man/sigtool.1.in
@@ -30,6 +30,12 @@ Read data from stdin and write hex string to stdout.
 \fB\-\-md5 [FILES]\fR
 Generate MD5 checksum from stdin or MD5 sigs for FILES.
 .TP 
+\fB\-\-sha1 [FILES]\fR
+Generate SHA1 checksum from stdin or SHA1 sigs for FILES.
+.TP 
+\fB\-\-sha256 [FILES]\fR
+Generate SHA256 checksum from stdin or SHA256 sigs for FILES.
+.TP 
 \fB\-\-mdb [FILES]\fR
 Generate .mdb signatures for FILES.
 .TP 
diff --git a/etc/Makefile.in b/etc/Makefile.in
index 85a65ea..7158093 100644
--- a/etc/Makefile.in
+++ b/etc/Makefile.in
@@ -100,6 +100,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/etc/clamav-milter.conf b/etc/clamav-milter.conf
index e244240..c484840 100644
--- a/etc/clamav-milter.conf
+++ b/etc/clamav-milter.conf
@@ -266,3 +266,9 @@ ClamdSocket /var/run/clamav/clamd
 # Default: disabled
 #LogInfected Basic
 
+# This option allows to tune what is logged when no threat is found in a scanned message.
+# See LogInfected for possible values and caveats.
+# Useful in debugging but drastically increases the log size.
+# Default: disabled
+#LogClean Basic
+
diff --git a/etc/clamd.conf b/etc/clamd.conf
index 6cbb05e..67ebee1 100644
--- a/etc/clamd.conf
+++ b/etc/clamd.conf
@@ -103,7 +103,7 @@ Example
 #TCPAddr 127.0.0.1
 
 # Maximum length the queue of pending connections may grow to.
-# Default: 15
+# Default: 200
 #MaxConnectionQueueLength 30
 
 # Clamd uses FTP-like protocol to receive data from remote clients.
@@ -480,5 +480,5 @@ Example
 
 # Set bytecode timeout in miliseconds.
 # 
-# Default: 60000
-# BytecodeTimeout 60000
+# Default: 5000
+# BytecodeTimeout 1000
diff --git a/freshclam/Makefile.in b/freshclam/Makefile.in
index f129d83..1b8cfb2 100644
--- a/freshclam/Makefile.in
+++ b/freshclam/Makefile.in
@@ -137,6 +137,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/freshclam/freshclam.c b/freshclam/freshclam.c
index 4aae243..b7d6d34 100644
--- a/freshclam/freshclam.c
+++ b/freshclam/freshclam.c
@@ -63,7 +63,7 @@ static short terminate = 0;
 extern int active_children;
 
 static short foreground = 1;
-char updtmpdir[512];
+char updtmpdir[512], dbdir[512];
 int sigchld_wait = 1;
 
 static void sighandler(int sig) {
@@ -164,7 +164,7 @@ static void help(void)
     mprintf("\n");
 }
 
-static int download(const struct optstruct *opts, const char *datadir, const char *cfgfile)
+static int download(const struct optstruct *opts, const char *cfgfile)
 {
 	int ret = 0, try = 0, maxattempts = 0;
 	const struct optstruct *opt;
@@ -178,7 +178,7 @@ static int download(const struct optstruct *opts, const char *datadir, const cha
 	return 56;
     } else {
 	while(opt) {
-	    ret = downloadmanager(opts, opt->strarg, datadir, try == maxattempts - 1);
+	    ret = downloadmanager(opts, opt->strarg, try == maxattempts - 1);
 #ifndef _WIN32
 	    alarm(0);
 #endif
@@ -224,7 +224,7 @@ int main(int argc, char **argv)
 {
 	int ret = 52, retcl;
 	const char *cfgfile, *arg = NULL, *pidfile = NULL;
-	char *pt, dbdir[512];
+	char *pt;
 	struct optstruct *opts;
 	const struct optstruct *opt;
 #ifndef	_WIN32
@@ -471,7 +471,7 @@ int main(int argc, char **argv)
 	logg("#freshclam daemon %s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")\n", get_version());
 
 	while(!terminate) {
-	    ret = download(opts, dbdir, cfgfile);
+	    ret = download(opts, cfgfile);
 
 	    if(ret <= 1) {
 		if((opt = optget(opts, "SubmitDetectionStats"))->enabled)
@@ -529,7 +529,7 @@ int main(int argc, char **argv)
 		logg(" *** Virus databases are not updated in this mode ***\n");
 	    ret = submitstats(opt->strarg, opts);
 	} else {
-	    ret = download(opts, dbdir, cfgfile);
+	    ret = download(opts, cfgfile);
 
 	    if((opt = optget(opts, "SubmitDetectionStats"))->enabled)
 		submitstats(opt->strarg, opts);
diff --git a/freshclam/manager.c b/freshclam/manager.c
index f9a2f12..45e5137 100644
--- a/freshclam/manager.c
+++ b/freshclam/manager.c
@@ -83,7 +83,7 @@
 #include "libclamav/cvd.h"
 #include "libclamav/regex_list.h"
 
-extern char updtmpdir[512];
+extern char updtmpdir[512], dbdir[512];
 
 #define CHDIR_ERR(x)				\
 	if(chdir(x) == -1)			\
@@ -567,6 +567,7 @@ static char *proxyauth(const char *user, const char *pass)
     return auth;
 }
 
+#if BUILD_CLAMD
 int submitstats(const char *clamdcfg, const struct optstruct *opts)
 {
 	int sd, clamsockd, bread, cnt, ret;
@@ -744,6 +745,13 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
     }
     return ret;
 }
+#else
+int submitstats(const char *clamdcfg, const struct optstruct *opts)
+{
+    logg("clamd not built, no statistics");
+    return 52;
+}
+#endif
 
 static int Rfc2822DateTime(char *buf, time_t mtime)
 {
@@ -1132,7 +1140,7 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
 
     if(mdat) {
 	mirman_update_sf(mdat->currip, mdat->af, mdat, 0, 1);
-	mirman_write("mirrors.dat", optget(opts, "DatabaseDirectory")->strarg, mdat);
+	mirman_write("mirrors.dat", dbdir, mdat);
     }
 
     ret = getfile_mirman(srcfile, destfile, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist, ims, ipaddr, sd);
@@ -1140,7 +1148,7 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
 
     if(mdat) {
 	mirman_update_sf(mdat->currip, mdat->af, mdat, 0, -1);
-	mirman_write("mirrors.dat", optget(opts, "DatabaseDirectory")->strarg, mdat);
+	mirman_write("mirrors.dat", dbdir, mdat);
     }
 
     return ret;
@@ -1476,7 +1484,7 @@ static int test_database_wrap(const char *file, const char *newdb, int bytecode)
     char lastline[256];
     int pipefd[2];
     pid_t pid;
-    int status = 0;
+    int status = 0, ret;
     FILE *f;
 
     if (pipe(pipefd) == -1) {
@@ -1513,7 +1521,8 @@ static int test_database_wrap(const char *file, const char *newdb, int bytecode)
 	    }
 	    fclose(f);
 
-	    if (waitpid(pid, &status, 0) == -1 && errno != ECHILD)
+	    while ((ret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
+	    if (ret == -1 && errno != ECHILD)
 		logg("^waitpid() failed: %s\n", strerror(errno));
 	    cli_chomp(firstline);
 	    cli_chomp(lastline);
@@ -1523,7 +1532,7 @@ static int test_database_wrap(const char *file, const char *newdb, int bytecode)
 		     lastline);
 	    }
 	    if (WIFEXITED(status)) {
-		int ret = WEXITSTATUS(status);
+		ret = WEXITSTATUS(status);
 		if (ret) {
 		    logg("^Database load exited with status %d\n", ret);
 		    return ret;
@@ -1556,6 +1565,43 @@ static int test_database_wrap(const char *file, const char *newdb, int bytecode)
 }
 #endif
 
+static int checkdbdir(void)
+{
+	DIR *dir;
+	struct dirent *dent;
+	char fname[512], broken[513];
+	int ret, fret = 0;
+
+    if(!(dir = opendir(dbdir))) {
+	logg("!checkdbdir: Can't open directory %s\n", dbdir);
+	return -1;
+    }
+
+    while((dent = readdir(dir))) {
+	if(dent->d_ino) {
+	    if(cli_strbcasestr(dent->d_name, ".cld") || cli_strbcasestr(dent->d_name, ".cvd")) {
+		snprintf(fname, sizeof(fname), "%s"PATHSEP"%s", dbdir, dent->d_name);
+		if((ret = cl_cvdverify(fname))) {
+		    fret = -1;
+		    mprintf("!Corrupted database file %s: %s\n", fname, cl_strerror(ret));
+		    snprintf(broken, sizeof(broken), "%s.broken", fname);
+		    if(!access(broken, R_OK))
+			unlink(broken);
+		    if(rename(fname, broken)) {
+			if(unlink(fname))
+			    mprintf("!Can't remove broken database file %s, please delete it manually and restart freshclam\n", fname);
+		    } else {
+			mprintf("Corrupted database file renamed to %s\n", broken);
+		    }
+		}
+	    }
+	}
+    }
+
+    closedir(dir);
+    return fret;
+}
+
 extern int sigchld_wait;
 
 static int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, const struct optstruct *opts, const char *dnsreply, char *localip, int outdated, struct mirdat *mdat, int logerr, int extra)
@@ -2046,7 +2092,7 @@ static int updatecustomdb(const char *url, int *signo, const struct optstruct *o
     return 0;
 }
 
-int downloadmanager(const struct optstruct *opts, const char *hostname, const char *dbdir, int logerr)
+int downloadmanager(const struct optstruct *opts, const char *hostname, int logerr)
 {
 	time_t currtime;
 	int ret, updated = 0, outdated = 0, signo = 0;
@@ -2118,11 +2164,9 @@ int downloadmanager(const struct optstruct *opts, const char *hostname, const ch
 		    logg("*Software version from DNS: %s\n", newver);
 		    strncpy(vstr, get_version(), 32);
 		    vstr[31] = 0;
-		    if((pt = strstr(vstr, "-exp")) || (pt = strstr(vstr,"-broken")))
-			*pt = 0;
-
 		    if(vwarning && !strstr(vstr, "devel") && !strstr(vstr, "rc")) {
-			if(strcmp(vstr, newver)) {
+			pt = strchr(vstr, '-');
+			if((pt && strncmp(vstr, newver, pt - vstr)) || (!pt && strcmp(vstr, newver))) {
 			    logg("^Your ClamAV installation is OUTDATED!\n");
 			    logg("^Local version: %s Recommended version: %s\n", vstr, newver);
 			    logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n");
@@ -2269,6 +2313,12 @@ int downloadmanager(const struct optstruct *opts, const char *hostname, const ch
 
     cli_rmdirs(updtmpdir);
 
+    if(checkdbdir() < 0) {
+	if(newver)
+	    free(newver);
+	return 54;
+    }
+
     if(updated) {
 	if(optget(opts, "HTTPProxyServer")->enabled || !ipaddr[0]) {
 	    logg("Database updated (%d signatures) from %s\n", signo, hostname);
diff --git a/freshclam/manager.h b/freshclam/manager.h
index 9390ac9..8281048 100644
--- a/freshclam/manager.h
+++ b/freshclam/manager.h
@@ -25,7 +25,7 @@
 
 #include "shared/optparser.h"
 
-int downloadmanager(const struct optstruct *opts, const char *hostname, const char *dbdir, int logerr);
+int downloadmanager(const struct optstruct *opts, const char *hostname, int logerr);
 
 int submitstats(const char *clamdcfg, const struct optstruct *opts);
 
diff --git a/libclamav/Makefile.am b/libclamav/Makefile.am
index 401477a..94cd1e5 100644
--- a/libclamav/Makefile.am
+++ b/libclamav/Makefile.am
@@ -142,8 +142,8 @@ libclamav_la_SOURCES = \
         matcher-ac.h \
         matcher-bm.c \
         matcher-bm.h \
-        matcher-md5.c \
-        matcher-md5.h \
+        matcher-hash.c \
+        matcher-hash.h \
         matcher.c \
         matcher.h \
         others.c \
@@ -335,6 +335,8 @@ libclamav_la_SOURCES = \
 	default.h\
 	sha256.c\
 	sha256.h\
+	sha1.c\
+	sha1.h\
 	bignum.h\
 	bytecode.c\
 	bytecode.h\
@@ -367,10 +369,6 @@ libclamav_la_SOURCES += bignum.c \
 		     bignum_class.h
 endif
 
-if BUILD_SHA1
-libclamav_la_SOURCES += sha1.c sha1.h
-endif
-
 .PHONY: version.h.tmp
 version.c: version.h
 version.h: version.h.tmp
diff --git a/libclamav/Makefile.in b/libclamav/Makefile.in
index 06d7309..d66f7fb 100644
--- a/libclamav/Makefile.in
+++ b/libclamav/Makefile.in
@@ -66,7 +66,6 @@ target_triplet = @target@
 @LINK_TOMMATH_FALSE at am__append_7 = bignum.c \
 @LINK_TOMMATH_FALSE@		     bignum_class.h
 
- at BUILD_SHA1_TRUE@am__append_8 = sha1.c sha1.h
 subdir = libclamav
 DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
@@ -115,7 +114,7 @@ LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
 @ENABLE_LLVM_FALSE at am__DEPENDENCIES_2 = libclamav_nocxx.la
 @ENABLE_LLVM_TRUE at am__DEPENDENCIES_2 = c++/libclamavcxx.la
 am__libclamav_la_SOURCES_DIST = clamav.h matcher-ac.c matcher-ac.h \
-	matcher-bm.c matcher-bm.h matcher-md5.c matcher-md5.h \
+	matcher-bm.c matcher-bm.h matcher-hash.c matcher-hash.h \
 	matcher.c matcher.h others.c others.h readdb.c readdb.h cvd.c \
 	cvd.h dsig.c dsig.h scanners.c scanners.h textdet.c textdet.h \
 	filetypes.c filetypes.h filetypes_int.h rtf.c rtf.h blob.c \
@@ -153,17 +152,16 @@ am__libclamav_la_SOURCES_DIST = clamav.h matcher-ac.c matcher-ac.h \
 	jsparse/lexglobal.h jsparse/textbuf.h uniq.c uniq.h version.c \
 	version.h mpool.c mpool.h filtering.h filtering.c fmap.c \
 	fmap.h perflogging.c perflogging.h default.h sha256.c sha256.h \
-	bignum.h bytecode.c bytecode.h bytecode_vm.c bytecode_priv.h \
-	clambc.h cpio.c cpio.h macho.c macho.h ishield.c ishield.h \
-	type_desc.h bcfeatures.h bytecode_api.c bytecode_api_decl.c \
-	bytecode_api.h bytecode_api_impl.h bytecode_hooks.h cache.c \
-	cache.h bytecode_detect.c bytecode_detect.h \
-	builtin_bytecodes.h events.c events.h bignum.c bignum_class.h \
-	sha1.c sha1.h
+	sha1.c sha1.h bignum.h bytecode.c bytecode.h bytecode_vm.c \
+	bytecode_priv.h clambc.h cpio.c cpio.h macho.c macho.h \
+	ishield.c ishield.h type_desc.h bcfeatures.h bytecode_api.c \
+	bytecode_api_decl.c bytecode_api.h bytecode_api_impl.h \
+	bytecode_hooks.h cache.c cache.h bytecode_detect.c \
+	bytecode_detect.h builtin_bytecodes.h events.c events.h \
+	bignum.c bignum_class.h
 @LINK_TOMMATH_FALSE at am__objects_1 = libclamav_la-bignum.lo
- at BUILD_SHA1_TRUE@am__objects_2 = libclamav_la-sha1.lo
 am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
-	libclamav_la-matcher-bm.lo libclamav_la-matcher-md5.lo \
+	libclamav_la-matcher-bm.lo libclamav_la-matcher-hash.lo \
 	libclamav_la-matcher.lo libclamav_la-others.lo \
 	libclamav_la-readdb.lo libclamav_la-cvd.lo \
 	libclamav_la-dsig.lo libclamav_la-scanners.lo \
@@ -206,12 +204,13 @@ am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
 	libclamav_la-uniq.lo libclamav_la-version.lo \
 	libclamav_la-mpool.lo libclamav_la-filtering.lo \
 	libclamav_la-fmap.lo libclamav_la-perflogging.lo \
-	libclamav_la-sha256.lo libclamav_la-bytecode.lo \
-	libclamav_la-bytecode_vm.lo libclamav_la-cpio.lo \
-	libclamav_la-macho.lo libclamav_la-ishield.lo \
-	libclamav_la-bytecode_api.lo libclamav_la-bytecode_api_decl.lo \
-	libclamav_la-cache.lo libclamav_la-bytecode_detect.lo \
-	libclamav_la-events.lo $(am__objects_1) $(am__objects_2)
+	libclamav_la-sha256.lo libclamav_la-sha1.lo \
+	libclamav_la-bytecode.lo libclamav_la-bytecode_vm.lo \
+	libclamav_la-cpio.lo libclamav_la-macho.lo \
+	libclamav_la-ishield.lo libclamav_la-bytecode_api.lo \
+	libclamav_la-bytecode_api_decl.lo libclamav_la-cache.lo \
+	libclamav_la-bytecode_detect.lo libclamav_la-events.lo \
+	$(am__objects_1)
 libclamav_la_OBJECTS = $(am_libclamav_la_OBJECTS)
 AM_V_lt = $(am__v_lt_$(V))
 am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
@@ -386,6 +385,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
@@ -620,7 +620,7 @@ libclamav_la_LDFLAGS = @TH_SAFE@ -version-info @LIBCLAMAV_VERSION@ \
 	-no-undefined $(am__append_6)
 include_HEADERS = clamav.h
 libclamav_la_SOURCES = clamav.h matcher-ac.c matcher-ac.h matcher-bm.c \
-	matcher-bm.h matcher-md5.c matcher-md5.h matcher.c matcher.h \
+	matcher-bm.h matcher-hash.c matcher-hash.h matcher.c matcher.h \
 	others.c others.h readdb.c readdb.h cvd.c cvd.h dsig.c dsig.h \
 	scanners.c scanners.h textdet.c textdet.h filetypes.c \
 	filetypes.h filetypes_int.h rtf.c rtf.h blob.c blob.h mbox.c \
@@ -658,13 +658,13 @@ libclamav_la_SOURCES = clamav.h matcher-ac.c matcher-ac.h matcher-bm.c \
 	jsparse/lexglobal.h jsparse/textbuf.h uniq.c uniq.h version.c \
 	version.h mpool.c mpool.h filtering.h filtering.c fmap.c \
 	fmap.h perflogging.c perflogging.h default.h sha256.c sha256.h \
-	bignum.h bytecode.c bytecode.h bytecode_vm.c bytecode_priv.h \
-	clambc.h cpio.c cpio.h macho.c macho.h ishield.c ishield.h \
-	type_desc.h bcfeatures.h bytecode_api.c bytecode_api_decl.c \
-	bytecode_api.h bytecode_api_impl.h bytecode_hooks.h cache.c \
-	cache.h bytecode_detect.c bytecode_detect.h \
-	builtin_bytecodes.h events.c events.h $(am__append_7) \
-	$(am__append_8)
+	sha1.c sha1.h bignum.h bytecode.c bytecode.h bytecode_vm.c \
+	bytecode_priv.h clambc.h cpio.c cpio.h macho.c macho.h \
+	ishield.c ishield.h type_desc.h bcfeatures.h bytecode_api.c \
+	bytecode_api_decl.c bytecode_api.h bytecode_api_impl.h \
+	bytecode_hooks.h cache.c cache.h bytecode_detect.c \
+	bytecode_detect.h builtin_bytecodes.h events.c events.h \
+	$(am__append_7)
 noinst_LTLIBRARIES = libclamav_internal_utils.la libclamav_internal_utils_nothreads.la libclamav_nocxx.la
 COMMON_CLEANFILES = version.h version.h.tmp *.gcda *.gcno
 @MAINTAINER_MODE_TRUE at BUILT_SOURCES = jsparse/generated/operators.h jsparse/generated/keywords.h jsparse-keywords.gperf
@@ -838,7 +838,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-macho.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-matcher-ac.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-matcher-bm.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-matcher-md5.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-matcher-hash.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-matcher.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-mbox.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libclamav_la-message.Plo at am__quote@
@@ -936,13 +936,13 @@ libclamav_la-matcher-bm.lo: matcher-bm.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-matcher-bm.lo `test -f 'matcher-bm.c' || echo '$(srcdir)/'`matcher-bm.c
 
-libclamav_la-matcher-md5.lo: matcher-md5.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-matcher-md5.lo -MD -MP -MF $(DEPDIR)/libclamav_la-matcher-md5.Tpo -c -o libclamav_la-matcher-md5.lo `test -f 'matcher-md5.c' || echo '$(srcdir)/'`matcher-md5.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-matcher-md5.Tpo $(DEPDIR)/libclamav_la-matcher-md5.Plo
+libclamav_la-matcher-hash.lo: matcher-hash.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-matcher-hash.lo -MD -MP -MF $(DEPDIR)/libclamav_la-matcher-hash.Tpo -c -o libclamav_la-matcher-hash.lo `test -f 'matcher-hash.c' || echo '$(srcdir)/'`matcher-hash.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-matcher-hash.Tpo $(DEPDIR)/libclamav_la-matcher-hash.Plo
 @am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='matcher-md5.c' object='libclamav_la-matcher-md5.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='matcher-hash.c' object='libclamav_la-matcher-hash.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-matcher-md5.lo `test -f 'matcher-md5.c' || echo '$(srcdir)/'`matcher-md5.c
+ at am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-matcher-hash.lo `test -f 'matcher-hash.c' || echo '$(srcdir)/'`matcher-hash.c
 
 libclamav_la-matcher.lo: matcher.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-matcher.lo -MD -MP -MF $(DEPDIR)/libclamav_la-matcher.Tpo -c -o libclamav_la-matcher.lo `test -f 'matcher.c' || echo '$(srcdir)/'`matcher.c
@@ -1640,6 +1640,14 @@ libclamav_la-sha256.lo: sha256.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
 
+libclamav_la-sha1.lo: sha1.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-sha1.lo -MD -MP -MF $(DEPDIR)/libclamav_la-sha1.Tpo -c -o libclamav_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-sha1.Tpo $(DEPDIR)/libclamav_la-sha1.Plo
+ at am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='sha1.c' object='libclamav_la-sha1.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c
+
 libclamav_la-bytecode.lo: bytecode.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-bytecode.lo -MD -MP -MF $(DEPDIR)/libclamav_la-bytecode.Tpo -c -o libclamav_la-bytecode.lo `test -f 'bytecode.c' || echo '$(srcdir)/'`bytecode.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-bytecode.Tpo $(DEPDIR)/libclamav_la-bytecode.Plo
@@ -1728,14 +1736,6 @@ libclamav_la-bignum.lo: bignum.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-bignum.lo `test -f 'bignum.c' || echo '$(srcdir)/'`bignum.c
 
-libclamav_la-sha1.lo: sha1.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-sha1.lo -MD -MP -MF $(DEPDIR)/libclamav_la-sha1.Tpo -c -o libclamav_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-sha1.Tpo $(DEPDIR)/libclamav_la-sha1.Plo
- at am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='sha1.c' object='libclamav_la-sha1.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c
-
 libclamav_internal_utils_la-str.lo: str.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_internal_utils_la_CFLAGS) $(CFLAGS) -MT libclamav_internal_utils_la-str.lo -MD -MP -MF $(DEPDIR)/libclamav_internal_utils_la-str.Tpo -c -o libclamav_internal_utils_la-str.lo `test -f 'str.c' || echo '$(srcdir)/'`str.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_internal_utils_la-str.Tpo $(DEPDIR)/libclamav_internal_utils_la-str.Plo
diff --git a/libclamav/blob.c b/libclamav/blob.c
index 3daa648..bec7d16 100644
--- a/libclamav/blob.c
+++ b/libclamav/blob.c
@@ -396,12 +396,6 @@ fileblobCreate(void)
 int
 fileblobScanAndDestroy(fileblob *fb)
 {
-	if(fb->ctx && fb->ctx->engine->keeptmp) {
-		/* Can't remove the file, the caller must scan */
-		fileblobDestroy(fb);
-		return CL_CLEAN;
-	}
-		
 	switch(fileblobScan(fb)) {
 		case CL_VIRUS:
 			fileblobDestructiveDestroy(fb);
@@ -424,7 +418,8 @@ fileblobDestructiveDestroy(fileblob *fb)
 	if(fb->fp && fb->fullname) {
 		fclose(fb->fp);
 		cli_dbgmsg("fileblobDestructiveDestroy: %s\n", fb->fullname);
-		cli_unlink(fb->fullname);
+		if(!fb->ctx || !fb->ctx->engine->keeptmp)
+			cli_unlink(fb->fullname);
 		free(fb->fullname);
 		fb->fp = NULL;
 		fb->fullname = NULL;
diff --git a/libclamav/bytecode_vm.c b/libclamav/bytecode_vm.c
index 1691563..e3c61bd 100644
--- a/libclamav/bytecode_vm.c
+++ b/libclamav/bytecode_vm.c
@@ -226,7 +226,6 @@ static always_inline struct stack_entry *allocate_stack(struct stack *stack,
     entry->bb_inst = bb_inst;
     /* we allocated room for values right after stack_entry! */
     entry->values = values = (char*)&entry[1];
-
     memcpy(&values[func->numBytes - func->numConstants*8], func->constants,
 	   sizeof(*values)*func->numConstants*8);
     return entry;
@@ -1125,40 +1124,41 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
 		break;
 	    }
 	    DEFINE_OP(OP_BC_MEMCPY) {
-		int32_t arg3;
+		int64_t arg3;
 		void *arg1, *arg2;
 		int64_t res=0;
 
 		READ32(arg3, inst->u.three[2]);
 		READPOP(arg1, inst->u.three[0], arg3);
 		READPOP(arg2, inst->u.three[1], arg3);
-		memcpy(arg1, arg2, arg3);
+		memcpy(arg1, arg2, (int32_t)arg3);
 /*		READ64(res, inst->u.three[0]);*/
 		WRITE64(inst->dest, res);
 		break;
 	    }
 	    DEFINE_OP(OP_BC_MEMMOVE) {
-		int32_t arg3;
+		int64_t arg3;
 		void *arg1, *arg2;
 		int64_t res=0;
 
-		READ32(arg3, inst->u.three[2]);
+		READ64(arg3, inst->u.three[2]);
 		READPOP(arg1, inst->u.three[0], arg3);
 		READPOP(arg2, inst->u.three[1], arg3);
-		memmove(arg1, arg2, arg3);
+		memmove(arg1, arg2, (int32_t)arg3);
 /*		READ64(res, inst->u.three[0]);*/
 		WRITE64(inst->dest, res);
 		break;
 	    }
 	    DEFINE_OP(OP_BC_MEMSET) {
-		int32_t arg2, arg3;
+		int64_t arg3;
+		int32_t arg2;
 		void *arg1;
 		int64_t res=0;
 
-		READ32(arg3, inst->u.three[2]);
+		READ64(arg3, inst->u.three[2]);
 		READPOP(arg1, inst->u.three[0], arg3);
 		READ32(arg2, inst->u.three[1]);
-		memset(arg1, arg2, arg3);
+		memset(arg1, arg2, (int32_t)arg3);
 /*		READ64(res, inst->u.three[0]);*/
 		WRITE64(inst->dest, res);
 		break;
diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp
index 5c19dbd..9ab263b 100644
--- a/libclamav/c++/bytecode2llvm.cpp
+++ b/libclamav/c++/bytecode2llvm.cpp
@@ -1451,6 +1451,11 @@ public:
 			    assert(inst->u.ops.funcid < cli_apicall_maxapi && "APICall out of range");
 			    std::vector<Value*> args;
 			    Function *DestF = apiFuncs[inst->u.ops.funcid];
+			    if (!strcmp(cli_apicalls[inst->u.ops.funcid].name, "engine_functionality_level")) {
+				Store(inst->dest,
+				      ConstantInt::get(Type::getInt32Ty(Context),
+						       cl_retflevel()));
+			    } else {
 			    args.push_back(&*F->arg_begin()); // pass hidden arg
 			    for (unsigned a=0;a<inst->u.ops.numOps;a++) {
 				operand_t op = inst->u.ops.ops[a];
@@ -1459,6 +1464,7 @@ public:
 			    CallInst *CI = Builder.CreateCall(DestF, args.begin(), args.end());
 			    CI->setDoesNotThrow(true);
 			    Store(inst->dest, CI);
+			    }
 			    break;
 			}
 			case OP_BC_GEP1:
@@ -2103,8 +2109,8 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
 	// TODO: only run this on the untrusted bytecodes, not all of them...
 	if (has_untrusted)
 	    PM.add(createClamBCRTChecks());
-	PM.add(createCFGSimplificationPass());
 	PM.add(createSCCPPass());
+	PM.add(createCFGSimplificationPass());
 	PM.add(createGlobalOptimizerPass());
 	PM.add(createConstantMergePass());
 	PM.add(new RuntimeLimits());
@@ -2121,8 +2127,9 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
 	    // compile all functions now, not lazily!
 	    for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
 		Function *Fn = &*I;
-		if (!Fn->isDeclaration())
+		if (!Fn->isDeclaration()) {
 		    EE->getPointerToFunction(Fn);
+		}
 	    }
 	    codegenTimer.stopTimer();
 	}
@@ -2155,7 +2162,7 @@ int bytecode_init(void)
 {
     // If already initialized return
     if (llvm_is_multithreaded()) {
-	cli_warnmsg("bytecode_init: already initialized");
+	cli_warnmsg("bytecode_init: already initialized\n");
 	return CL_EARG;
     }
     llvm_install_error_handler(llvm_error_handler);
diff --git a/libclamav/c++/llvm/autoconf/configure.ac b/libclamav/c++/llvm/autoconf/configure.ac
index 3b1a6c0..01aba96 100644
--- a/libclamav/c++/llvm/autoconf/configure.ac
+++ b/libclamav/c++/llvm/autoconf/configure.ac
@@ -1272,9 +1272,25 @@ if test "$llvm_cv_enable_libffi" = "yes" ; then
   AC_CHECK_HEADERS([ffi.h ffi/ffi.h])
 fi
 
-dnl Try to find Darwin specific crash reporting library.
+dnl Try to find Darwin specific crash reporting libraries.
 AC_CHECK_HEADERS([CrashReporterClient.h])
 
+dnl Try to find Darwin specific crash reporting global.
+AC_MSG_CHECKING([__crashreporter_info__])
+AC_LINK_IFELSE(
+  AC_LANG_SOURCE(
+    [[extern const char *__crashreporter_info__;
+      int main() {
+        __crashreporter_info__ = "test";
+        return 0;
+      }
+    ]]),
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_CRASHREPORTER_INFO, 1, Can use __crashreporter_info__),
+  AC_MSG_RESULT(no)
+  AC_DEFINE(HAVE_CRASHREPORTER_INFO, 0,
+            Define if __crashreporter_info__ exists.))
+
 dnl===-----------------------------------------------------------------------===
 dnl===
 dnl=== SECTION 7: Check for types and structures
diff --git a/libclamav/c++/llvm/configure b/libclamav/c++/llvm/configure
index 0d8450f..eb53a25 100755
--- a/libclamav/c++/llvm/configure
+++ b/libclamav/c++/llvm/configure
@@ -11631,6 +11631,36 @@ fi
 done
 
 
+<<<<<<< HEAD
+=======
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking __crashreporter_info__" >&5
+$as_echo_n "checking __crashreporter_info__... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern const char *__crashreporter_info__;
+      int main() {
+        __crashreporter_info__ = "test";
+        return 0;
+      }
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_CRASHREPORTER_INFO 1" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define HAVE_CRASHREPORTER_INFO 0" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
+>>>>>>> clamav-0.97
 
 
 
diff --git a/libclamav/c++/llvm/include/llvm/Config/config.h.in b/libclamav/c++/llvm/include/llvm/Config/config.h.in
index a4cebb3..7f858f7 100644
--- a/libclamav/c++/llvm/include/llvm/Config/config.h.in
+++ b/libclamav/c++/llvm/include/llvm/Config/config.h.in
@@ -72,6 +72,9 @@
 /* Define to 1 if you have the <CrashReporterClient.h> header file. */
 #undef HAVE_CRASHREPORTERCLIENT_H
 
+/* Define if __crashreporter_info__ exists. */
+#undef HAVE_CRASHREPORTER_INFO
+
 /* Define to 1 if you have the <ctype.h> header file. */
 #undef HAVE_CTYPE_H
 
diff --git a/libclamav/c++/llvm/lib/Support/PrettyStackTrace.cpp b/libclamav/c++/llvm/lib/Support/PrettyStackTrace.cpp
index 3c8a108..57a9d51 100644
--- a/libclamav/c++/llvm/lib/Support/PrettyStackTrace.cpp
+++ b/libclamav/c++/llvm/lib/Support/PrettyStackTrace.cpp
@@ -55,7 +55,7 @@ static void PrintCurStackTrace(raw_ostream &OS) {
 }
 
 // Integrate with crash reporter libraries.
-#if defined (__APPLE__) && defined (HAVE_CRASHREPORTERCLIENT_H)
+#if defined (__APPLE__) && HAVE_CRASHREPORTERCLIENT_H
 //  If any clients of llvm try to link to libCrashReporterClient.a themselves,
 //  only one crash info struct will be used.
 extern "C" {
@@ -64,7 +64,7 @@ struct crashreporter_annotations_t gCRAnnotations
         __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) 
         = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0 };
 }
-#elif defined (__APPLE__)
+#elif defined (__APPLE__) && HAVE_CRASHREPORTER_INFO
 static const char *__crashreporter_info__ = 0;
 asm(".desc ___crashreporter_info__, 0x10");
 #endif
@@ -86,11 +86,11 @@ static void CrashHandler(void *) {
   }
   
   if (!TmpStr.empty()) {
-#ifndef HAVE_CRASHREPORTERCLIENT_H
-    __crashreporter_info__ = strdup(std::string(TmpStr.str()).c_str());
-#else
+#ifdef HAVE_CRASHREPORTERCLIENT_H
     // Cast to void to avoid warning.
     (void)CRSetCrashLogMessage(std::string(TmpStr.str()).c_str());
+#elif HAVE_CRASHREPORTER_INFO 
+    __crashreporter_info__ = strdup(std::string(TmpStr.str()).c_str());
 #endif
     errs() << TmpStr.str();
   }
diff --git a/libclamav/chmunpack.c b/libclamav/chmunpack.c
index 3f4a557..2ad9796 100644
--- a/libclamav/chmunpack.c
+++ b/libclamav/chmunpack.c
@@ -32,6 +32,7 @@
 #include <unistd.h>
 #endif
 #include <string.h>
+#include <errno.h>
 
 #include "fmap.h"
 #include "others.h"
@@ -678,6 +679,10 @@ int cli_chm_open(int fd, const char *dirname, chm_metadata_t *metadata, cli_ctx
 		if (!metadata->map) {
 			return CL_EMAP;
 		}
+	} else {
+	    char err[128];
+	    cli_warnmsg("fstat() failed: %s\n", cli_strerror(errno, err, sizeof(err)));
+	    return CL_ESTAT;
 	}
 
 	if (!itsf_read_header(metadata)) {
diff --git a/libclamav/clamav.h b/libclamav/clamav.h
index 862562c..e0423e8 100644
--- a/libclamav/clamav.h
+++ b/libclamav/clamav.h
@@ -69,6 +69,11 @@ typedef enum {
     CL_EBYTECODE,/* may be reported in testmode */
     CL_EBYTECODE_TESTFAIL, /* may be reported in testmode */
 
+    /* c4w error codes */
+    CL_ELOCK,
+    CL_EBUSY,
+    CL_ESTATE,
+
     /* no error codes below this line please */
     CL_ELAST_ERROR
 } cl_error_t;
diff --git a/libclamav/cvd.c b/libclamav/cvd.c
index 30dac75..564162b 100644
--- a/libclamav/cvd.c
+++ b/libclamav/cvd.c
@@ -515,7 +515,7 @@ static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int cld)
 	return CL_SUCCESS;
     }
 
-    md5 = cli_md5stream(fs, NULL);
+    md5 = cli_hashstream(fs, NULL, 1);
     cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
 
     if(strncmp(md5, cvd->md5, 32)) {
@@ -539,6 +539,7 @@ static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int cld)
 
 int cl_cvdverify(const char *file)
 {
+	struct cl_engine *engine;
 	FILE *fs;
 	int ret;
 
@@ -548,13 +549,20 @@ int cl_cvdverify(const char *file)
 	return CL_EOPEN;
     }
 
-    ret = cli_cvdverify(fs, NULL, 0);
-    fclose(fs);
+    if(!(engine = cl_engine_new())) {
+	cli_errmsg("cld_cvdverify: Can't create new engine\n");
+	fclose(fs);
+	return CL_EMEM;
+    }
+
+    ret = cli_cvdload(fs, engine, NULL, CL_DB_STDOPT | CL_DB_PUA, !!cli_strbcasestr(file, ".cld"), file, 1);
 
+    cl_engine_free(engine);
+    fclose(fs);
     return ret;
 }
 
-int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int cld, const char *filename)
+int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int cld, const char *filename, unsigned int chkonly)
 {
 	struct cl_cvd cvd, dupcvd;
 	FILE *dupfs;
@@ -622,6 +630,7 @@ int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigne
     }
 
     cfd = fileno(fs);
+    dbio.chkonly = 0;
     ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, NULL);
     if(ret != CL_SUCCESS)
 	return ret;
@@ -635,6 +644,7 @@ int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigne
     if(!dbinfo)
 	return CL_EMALFDB;
 
+    dbio.chkonly = chkonly;
     options |= CL_DB_SIGNED;
     ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, dbinfo);
 
diff --git a/libclamav/cvd.h b/libclamav/cvd.h
index e51c2b4..aee727f 100644
--- a/libclamav/cvd.h
+++ b/libclamav/cvd.h
@@ -33,10 +33,11 @@ struct cli_dbio {
     unsigned int size, bread;
     char *buf, *bufpt, *readpt;
     unsigned int usebuf, bufsize, readsize;
+    unsigned int chkonly;
     SHA256_CTX sha256ctx;
 };
 
-int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int cld, const char *filename);
+int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int cld, const char *filename, unsigned int chkonly);
 int cli_cvdunpack(const char *file, const char *dir);
 
 #endif
diff --git a/libclamav/disasm.c b/libclamav/disasm.c
index 61bd7cf..6f72fb4 100644
--- a/libclamav/disasm.c
+++ b/libclamav/disasm.c
@@ -1092,16 +1092,16 @@ static const struct OPCODES x86ops[2][256] = {{
   PUSHOP(0xb3, ADDR_MRM_GEN_EG, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_BTR),
   PUSHOP(0xb4, ADDR_MRM_GEN_GM, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_LFS), /* FIXME: mem size is F/D */
   PUSHOP(0xb5, ADDR_MRM_GEN_GM, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_LGS), /* FIXME: mem size is F/D */
-  PUSHOP(0xb6, ADDR_MRM_GEN_GE, SIZE_BYTE, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVZX), /* FIXME: dsize is always B */
-  PUSHOP(0xb7, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVZX), /* FIXME: dsize is always W */
+  PUSHOP(0xb6, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVZX),
+  PUSHOP(0xb7, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVZX),
   PUSHOP(0xb8, ADDR_NOADDR, SIZE_NOSIZE, ADDR_NOADDR, SIZE_NOSIZE, OP_INVALID),
   PUSHOP(0xb9, ADDR_NOADDR, SIZE_NOSIZE, ADDR_NOADDR, SIZE_NOSIZE, OP_UNSUP),
   PUSHOP(0xba, ADDR_MRM_EXTRA_1A, SIZE_WD, ADDR_IMMED, SIZE_BYTE, 24),
   PUSHOP(0xbb, ADDR_MRM_GEN_EG, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_BTC),
   PUSHOP(0xbc, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_BSF),
   PUSHOP(0xbd, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_BSR),
-  PUSHOP(0xbe, ADDR_MRM_GEN_GE, SIZE_BYTE, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVSX), /* FIXME: dsize is always B */
-  PUSHOP(0xbf, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVSX), /* FIXME: dsize is always W */
+  PUSHOP(0xbe, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVSX),
+  PUSHOP(0xbf, ADDR_MRM_GEN_GE, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_MOVSX),
 
   PUSHOP(0xc0, ADDR_MRM_GEN_EG, SIZE_BYTE, ADDR_NOADDR, SIZE_NOSIZE, OP_XADD),
   PUSHOP(0xc1, ADDR_MRM_GEN_EG, SIZE_WD, ADDR_NOADDR, SIZE_NOSIZE, OP_XADD),
@@ -1200,8 +1200,6 @@ static void spam_x86(struct DISASMED *s, char *hr) {
     case ACCESS_NOARG:
       break;
     case ACCESS_IMM:
-      hr += sprintf(hr, "%s %lx", comma, (long)s->args[i].arg.q);
-      break;
     case ACCESS_REL:
       if (s->args[i].arg.rq >=0)
 	hr += sprintf(hr, "%s %lx", comma, (long)s->args[i].arg.q);
@@ -1418,10 +1416,10 @@ static const uint8_t *disasm_x86(const uint8_t *command, unsigned int len, struc
 	  GETBYTE(b);
 	  s->args[0].arg.q+=(uint64_t)b<<(i*8);
 	}
-	if (x86ops[table][s->table_op].dmethod==ADDR_RELJ) {
+	/* if (x86ops[table][s->table_op].dmethod==ADDR_RELJ) { */
 	  s->args[0].arg.q<<=((8-sz)*8);
 	  s->args[0].arg.rq>>=((8-sz)*8);
-	}
+	/* } */
 	s->state = STATE_CHECKSTYPE;
 	continue;
       }
@@ -1490,6 +1488,10 @@ static const uint8_t *disasm_x86(const uint8_t *command, unsigned int len, struc
 	s->args[reversed^1].access = ACCESS_REG;
 	if ((s->args[reversed^1].reg = p[s->args[reversed].size][rop]) == REG_INVALID) INVALIDATE;
 
+	/* MOVZX size fixxup */
+	if(s->real_op == OP_MOVZX || s->real_op == OP_MOVSX)
+	    s->args[reversed].size = SIZEB + (s->table_op & 1);
+
 	if(mod==3) {
 	  if(x86ops[table][s->table_op].dmethod==ADDR_MRM_GEN_GM || x86ops[table][s->table_op].dmethod==ADDR_MRM_EXTRA_1A_M) INVALIDATE;
 	  s->args[reversed].access = ACCESS_REG;
@@ -1623,6 +1625,8 @@ static const uint8_t *disasm_x86(const uint8_t *command, unsigned int len, struc
 	  GETBYTE(b);
 	  s->args[s->cur].arg.q+=b<<(i*8);
 	}
+	  s->args[s->cur].arg.q<<=((8-sz)*8);
+	  s->args[s->cur].arg.rq>>=((8-sz)*8);
 	s->state = STATE_FINALIZE;
 	continue;
       }
diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c
index b992d4c..a40aa39 100644
--- a/libclamav/filetypes.c
+++ b/libclamav/filetypes.c
@@ -163,6 +163,17 @@ cli_file_t cli_filetype2(fmap_t *map, const struct cl_engine *engine)
 
     ret = cli_filetype(buff, bread, engine);
 
+    if(ret == CL_TYPE_BINARY_DATA) {
+	switch(is_tar(buff, bread)) {
+	    case 1:
+		cli_dbgmsg("Recognized old fashioned tar file\n");
+		return CL_TYPE_OLD_TAR;
+	    case 2:
+		cli_dbgmsg("Recognized POSIX tar file\n");
+		return CL_TYPE_POSIX_TAR;
+	}
+    }
+
     if(ret >= CL_TYPE_TEXT_ASCII && ret <= CL_TYPE_BINARY_DATA) {
 	/* HTML files may contain special characters and could be
 	 * misidentified as BINARY_DATA by cli_filetype()
@@ -234,18 +245,5 @@ cli_file_t cli_filetype2(fmap_t *map, const struct cl_engine *engine)
 	}
     }
 
-    if(ret == CL_TYPE_BINARY_DATA) {
-	switch(is_tar(buff, bread)) {
-	    case 1:
-		ret = CL_TYPE_OLD_TAR;
-		cli_dbgmsg("Recognized old fashioned tar file\n");
-		break;
-	    case 2:
-		ret = CL_TYPE_POSIX_TAR;
-		cli_dbgmsg("Recognized POSIX tar file\n");
-		break;
-	}
-    }
-
     return ret;
 }
diff --git a/libclamav/hashtab.c b/libclamav/hashtab.c
index e7fa897..11275c8 100644
--- a/libclamav/hashtab.c
+++ b/libclamav/hashtab.c
@@ -33,6 +33,7 @@
 #define MODULE_NAME "hashtab: "
 
 static const char DELETED_KEY[] = "";
+#define DELETED_HTU32_KEY ((uint32_t)(-1))
 
 static unsigned long nearest_power(unsigned long num)
 {
@@ -185,6 +186,24 @@ int cli_hashtab_init(struct cli_hashtable *s,size_t capacity)
 	return 0;
 }
 
+int cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool)
+{
+	if(!s)
+		return CL_ENULLARG;
+
+	PROFILE_INIT(s);
+
+	capacity = nearest_power(capacity);
+	s->htable = mpool_calloc(mempool, capacity, sizeof(*s->htable));
+	if(!s->htable)
+		return CL_EMEM;
+	s->capacity = capacity;
+	s->used = 0;
+	s->maxfill = 8*capacity/10;
+	return 0;
+}
+
+
 static inline uint32_t hash32shift(uint32_t key)
 {
   key = ~key + (key << 15);
@@ -210,6 +229,14 @@ static inline size_t hash(const unsigned char* k,const size_t len,const size_t S
 	return Hash & (SIZE - 1);
 }
 
+static inline size_t hash_htu32(uint32_t k, const size_t SIZE)
+{
+	/* mixing function */
+	size_t Hash = hash32shift(k);
+	/* SIZE is power of 2 */
+	return Hash & (SIZE - 1);
+}
+
 /* if returned element has key==NULL, then key was not found in table */
 struct cli_element* cli_hashtab_find(const struct cli_hashtable *s,const char* key,const size_t len)
 {
@@ -241,6 +268,61 @@ struct cli_element* cli_hashtab_find(const struct cli_hashtable *s,const char* k
 	return NULL; /* not found */
 }
 
+
+const struct cli_htu32_element *cli_htu32_find(const struct cli_htu32 *s, uint32_t key)
+{
+	struct cli_htu32_element* element;
+	size_t tries = 1;
+	size_t idx;
+
+	if(!s)
+		return NULL;
+	PROFILE_CALC_HASH(s);
+	PROFILE_FIND_ELEMENT(s);
+	idx = hash_htu32(key, s->capacity);
+	element = &s->htable[idx];
+	do {
+		if(!element->key) {
+			PROFILE_FIND_NOTFOUND(s, tries);
+			return NULL; /* element not found, place is empty */
+		}
+		else if(key == element->key) {
+			PROFILE_FIND_FOUND(s, tries);
+			return element;/* found */
+		}
+		else {
+			idx = (idx + tries++) & (s->capacity-1);
+			element = &s->htable[idx];
+		}
+	} while (tries <= s->capacity);
+	PROFILE_HASH_EXHAUSTED(s);
+	return NULL; /* not found */
+}
+
+/* linear enumeration - start with current = NULL, returns next item if present or NULL if not */
+const struct cli_htu32_element *cli_htu32_next(const struct cli_htu32 *s, const struct cli_htu32_element *current) {
+	size_t ncur;
+	if(!s || !s->capacity)
+		return NULL;
+
+	if(!current)
+		ncur = 0;
+	else {
+		ncur = current - s->htable;
+		if(ncur >= s->capacity)
+			return NULL;
+
+		ncur++;
+	}
+	for(; ncur<s->capacity; ncur++) {
+		const struct cli_htu32_element *item = &s->htable[ncur & (s->capacity - 1)];
+		if(item->key && item->key != DELETED_HTU32_KEY)
+			return item;
+	}
+	return NULL;
+}
+
+
 static int cli_hashtab_grow(struct cli_hashtable *s)
 {
 	const size_t new_capacity = nearest_power(s->capacity + 1);
@@ -262,7 +344,7 @@ static int cli_hashtab_grow(struct cli_hashtable *s)
 			element = &htable[idx];
 
 			while(element->key && tries <= new_capacity) {
-				idx = (idx + tries++) % new_capacity;
+				idx = (idx + tries++) & (new_capacity-1);
 				element = &htable[idx];
 			}
 			if(!element->key) {
@@ -287,6 +369,57 @@ static int cli_hashtab_grow(struct cli_hashtable *s)
 	return CL_SUCCESS;
 }
 
+#ifndef USE_MPOOL
+#define cli_htu32_grow(A, B) cli_htu32_grow(A)
+#endif
+
+static int cli_htu32_grow(struct cli_htu32 *s, mpool_t *mempool)
+{
+	const size_t new_capacity = nearest_power(s->capacity + 1);
+	struct cli_htu32_element* htable = mpool_calloc(mempool, new_capacity, sizeof(*s->htable));
+	size_t i,idx, used = 0;
+	cli_dbgmsg("hashtab.c: new capacity: %lu\n",new_capacity);
+	if(new_capacity == s->capacity || !htable)
+		return CL_EMEM;
+
+	PROFILE_GROW_START(s);
+
+	for(i=0; i < s->capacity; i++) {
+		if(s->htable[i].key && s->htable[i].key != DELETED_HTU32_KEY) {
+			struct cli_htu32_element* element;
+			size_t tries = 1;
+
+			PROFILE_CALC_HASH(s);
+			idx = hash_htu32(s->htable[i].key, new_capacity);
+			element = &htable[idx];
+
+			while(element->key && tries <= new_capacity) {
+				idx = (idx + tries++) & (new_capacity-1);
+				element = &htable[idx];
+			}
+			if(!element->key) {
+				/* copy element from old hashtable to new */
+				PROFILE_GROW_FOUND(s, tries);
+				*element = s->htable[i];
+				used++;
+			}
+			else {
+				cli_errmsg("hashtab.c: Impossible - unable to rehash table");
+				return CL_EMEM;/* this means we didn't find enough room for all elements in the new table, should never happen */ 
+			}
+		}
+	}
+	mpool_free(mempool, s->htable);
+	s->htable = htable;
+	s->used = used;
+	s->capacity = new_capacity;
+	s->maxfill = new_capacity*8/10;
+	cli_dbgmsg("Table %p size after grow:%ld\n",(void*)s,s->capacity);
+	PROFILE_GROW_DONE(s);
+	return CL_SUCCESS;
+}
+
+
 const struct cli_element* cli_hashtab_insert(struct cli_hashtable *s, const char* key, const size_t len, const cli_element_data data)
 {
 	struct cli_element* element;
@@ -349,6 +482,64 @@ const struct cli_element* cli_hashtab_insert(struct cli_hashtable *s, const char
 	return NULL;
 }
 
+
+int cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool)
+{
+	struct cli_htu32_element* element;
+	struct cli_htu32_element* deleted_element = NULL;
+	size_t tries = 1;
+	size_t idx;
+	int ret;
+
+	if(!s)
+		return CL_ENULLARG;
+	if(s->used > s->maxfill) {
+		cli_dbgmsg("hashtab.c:Growing hashtable %p, because it has exceeded maxfill, old size:%ld\n",(void*)s,s->capacity);
+		cli_htu32_grow(s, mempool);
+	}
+	do {
+		PROFILE_CALC_HASH(s);
+		idx = hash_htu32(item->key, s->capacity);
+		element = &s->htable[idx];
+
+		do {
+			if(!element->key) {
+				/* element not found, place is empty, insert*/
+				if(deleted_element) {
+					/* reuse deleted elements*/
+					element = deleted_element;
+					PROFILE_DELETED_REUSE(s, tries);
+				}
+				else {
+					PROFILE_INSERT(s, tries);
+				}
+				*element = *item;
+				s->used++;
+				return 0;
+			}
+			else if(element->key == DELETED_HTU32_KEY) {
+				deleted_element = element;
+				element->key = 0;
+			}
+			else if(item->key == element->key) {
+				PROFILE_DATA_UPDATE(s, tries);
+				element->data = item->data;/* key found, update */
+				return 0;
+			}
+			else {
+				idx = (idx + tries++) % s->capacity;
+				element = &s->htable[idx];
+			}
+		} while (tries <= s->capacity);
+		/* no free place found*/
+		PROFILE_HASH_EXHAUSTED(s);
+		cli_dbgmsg("hashtab.c: Growing hashtable %p, because its full, old size:%ld.\n",(void*)s,s->capacity);
+	} while( (ret = cli_htu32_grow(s, mempool)) >= 0 );
+	cli_warnmsg("hashtab.c: Unable to grow hashtable\n");
+	return ret;
+}
+
+
 void cli_hashtab_delete(struct cli_hashtable *s,const char* key,const size_t len)
 {
     struct cli_element *el = cli_hashtab_find(s, key, len);
@@ -358,6 +549,13 @@ void cli_hashtab_delete(struct cli_hashtable *s,const char* key,const size_t len
     el->key = DELETED_KEY;
 }
 
+void cli_htu32_delete(struct cli_htu32 *s, uint32_t key)
+{
+	struct cli_htu32_element *el = (struct cli_htu32_element *)cli_htu32_find(s, key);
+	if(el)
+		el->key = DELETED_HTU32_KEY;
+}
+
 void cli_hashtab_clear(struct cli_hashtable *s)
 {
 	size_t i;
@@ -367,7 +565,15 @@ void cli_hashtab_clear(struct cli_hashtable *s)
 			free((void *)s->htable[i].key);
 	}
 	if(s->htable)
-		memset(s->htable, 0, s->capacity);
+		memset(s->htable, 0, s->capacity * sizeof(*s->htable));
+	s->used = 0;
+}
+
+void cli_htu32_clear(struct cli_htu32 *s)
+{
+	PROFILE_HASH_CLEAR(s);
+	if(s->htable)
+		memset(s->htable, 0, s->capacity * sizeof(struct cli_htu32_element));
 	s->used = 0;
 }
 
@@ -379,6 +585,18 @@ void cli_hashtab_free(struct cli_hashtable *s)
 	s->capacity = 0;
 }
 
+void cli_htu32_free(struct cli_htu32 *s, mpool_t *mempool)
+{
+	mpool_free(mempool, s->htable);
+	s->htable = NULL;
+	s->capacity = 0;
+}
+
+size_t cli_htu32_numitems(struct cli_htu32 *s) {
+	if(!s) return 0;
+	return s->capacity;
+}
+
 int cli_hashtab_store(const struct cli_hashtable *s,FILE* out)
 {
 	size_t i;
diff --git a/libclamav/hashtab.h b/libclamav/hashtab.h
index 7672f4d..97ccffc 100644
--- a/libclamav/hashtab.h
+++ b/libclamav/hashtab.h
@@ -86,6 +86,39 @@ void cli_hashtab_free(struct cli_hashtable *s);
 int cli_hashtab_load(FILE* in, struct cli_hashtable *s);
 int cli_hashtab_store(const struct cli_hashtable *s,FILE* out);
 
+
+struct cli_htu32_element {
+    uint32_t key;
+    union {
+	unsigned long as_ulong;
+	void *as_ptr;
+    } data;
+};
+
+struct cli_htu32 {
+    struct cli_htu32_element* htable;
+    size_t capacity;
+    size_t used;
+    size_t maxfill;/* 80% */
+
+    STRUCT_PROFILE
+};
+
+#ifndef USE_MPOOL
+#define cli_htu32_init(A, B, C) cli_htu32_init(A, B)
+#define cli_htu32_insert(A, B, C) cli_htu32_insert(A, B)
+#define cli_htu32_free(A, B) cli_htu32_free(A)
+#endif
+int cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool);
+int cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool);
+const struct cli_htu32_element *cli_htu32_find(const struct cli_htu32 *s, uint32_t key);
+void cli_htu32_delete(struct cli_htu32 *s, uint32_t key);
+void cli_htu32_clear(struct cli_htu32 *s);
+void cli_htu32_free(struct cli_htu32 *s, mpool_t *mempool);
+const struct cli_htu32_element *cli_htu32_next(const struct cli_htu32 *s, const struct cli_htu32_element *current);
+size_t cli_htu32_numitems(struct cli_htu32 *s);
+
+
 /* a hashtable that stores the values too */
 struct cli_map_value {
     void *value;
diff --git a/libclamav/libclamav.map b/libclamav/libclamav.map
index 9b2c74d..3aed4c5 100644
--- a/libclamav/libclamav.map
+++ b/libclamav/libclamav.map
@@ -66,8 +66,8 @@ CLAMAV_PRIVATE {
     cli_wm_decrypt_macro;
     cli_readn;
     cli_str2hex;
-    cli_md5file;
-    cli_md5stream;
+    cli_hashfile;
+    cli_hashstream;
     html_normalise_map;
     cli_utf16toascii;
 
diff --git a/libclamav/matcher-ac.c b/libclamav/matcher-ac.c
index 17b83f2..4e01f44 100644
--- a/libclamav/matcher-ac.c
+++ b/libclamav/matcher-ac.c
@@ -82,7 +82,7 @@ static char boundary[256] = {
 int cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
 {
 	struct cli_ac_node *pt, *next;
-	struct cli_ac_patt *ph;
+	struct cli_ac_patt *ph, *ph_prev, *ph_add_after;
 	void *newtable;
 	struct cli_ac_special *a1, *a2;
 	uint8_t i, match;
@@ -162,7 +162,10 @@ int cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
     pattern->depth = i;
 
     ph = pt->list;
+    ph_add_after = ph_prev = NULL;
     while(ph) {
+	if(!ph_add_after && ph->partno <= pattern->partno && (!ph->next || ph->next->partno > pattern->partno))
+	    ph_add_after = ph;
 	if((ph->length == pattern->length) && (ph->prefix_length == pattern->prefix_length) && (ph->ch[0] == pattern->ch[0]) && (ph->ch[1] == pattern->ch[1])) {
 	    if(!memcmp(ph->pattern, pattern->pattern, ph->length * sizeof(uint16_t)) && !memcmp(ph->prefix, pattern->prefix, ph->prefix_length * sizeof(uint16_t))) {
 		if(!ph->special && !pattern->special) {
@@ -207,17 +210,35 @@ int cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
 		}
 
 		if(match) {
-		    pattern->next_same = ph->next_same;
-		    ph->next_same = pattern;
-		    return CL_SUCCESS;
+		    if(pattern->partno < ph->partno) {
+			pattern->next_same = ph;
+			if(ph_prev)
+			    ph_prev->next = ph->next;
+			else
+			    pt->list = ph->next;
+			ph->next = NULL;
+			break;
+		    } else {
+			while(ph->next_same && ph->next_same->partno < pattern->partno)
+			    ph = ph->next_same;
+			pattern->next_same = ph->next_same;
+			ph->next_same = pattern;
+			return CL_SUCCESS;
+		    }
 		}
 	    }
 	}
+	ph_prev = ph;
 	ph = ph->next;
     }
 
-    pattern->next = pt->list;
-    pt->list = pattern;
+    if(ph_add_after) {
+	pattern->next = ph_add_after->next;
+	ph_add_after->next = pattern;
+    } else {
+	pattern->next = pt->list;
+	pt->list = pattern;
+    }
 
     return CL_SUCCESS;
 }
@@ -305,16 +326,6 @@ static int ac_maketrans(struct cli_matcher *root)
 
 		child->fail = fail->trans[i];
 
-		if(child->list) {
-		    patt = child->list;
-		    while(patt->next)
-			patt = patt->next;
-
-		    patt->next = child->fail->list;
-		} else {
-		    child->list = child->fail->list;
-		}
-
 		if((ret = bfs_enqueue(&bfs, &bfs_last, child)) != 0)
 		    return ret;
 	    }
@@ -935,40 +946,54 @@ int cli_ac_initdata(struct cli_ac_data *data, uint32_t partsigs, uint32_t lsigs,
 	    data->lsigcnt[i] = data->lsigcnt[0] + 64 * i;
 
 	/* subsig offsets */
-	data->lsigsuboff = (uint32_t **) cli_malloc(lsigs * sizeof(uint32_t *));
-	if(!data->lsigsuboff) {
+	data->lsigsuboff_last = (uint32_t **) cli_malloc(lsigs * sizeof(uint32_t *));
+	data->lsigsuboff_first = (uint32_t **) cli_malloc(lsigs * sizeof(uint32_t *));
+	if(!data->lsigsuboff_last || !data->lsigsuboff_first) {
+	    free(data->lsigsuboff_last);
+	    free(data->lsigsuboff_first);
 	    free(data->lsigcnt[0]);
 	    free(data->lsigcnt);
 	    if(partsigs)
 		free(data->offmatrix);
 	    if(reloffsigs)
 		free(data->offset);
-	    cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff\n");
+	    cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff_(last|first)\n");
 	    return CL_EMEM;
 	}
-	data->lsigsuboff[0] = (uint32_t *) cli_calloc(lsigs * 64, sizeof(uint32_t));
-	if(!data->lsigsuboff[0]) {
-	    free(data->lsigsuboff);
+	data->lsigsuboff_last[0] = (uint32_t *) cli_calloc(lsigs * 64, sizeof(uint32_t));
+	data->lsigsuboff_first[0] = (uint32_t *) cli_calloc(lsigs * 64, sizeof(uint32_t));
+	if(!data->lsigsuboff_last[0] || !data->lsigsuboff_first[0]) {
+	    free(data->lsigsuboff_last[0]);
+	    free(data->lsigsuboff_first[0]);
+	    free(data->lsigsuboff_last);
+	    free(data->lsigsuboff_first);
 	    free(data->lsigcnt[0]);
 	    free(data->lsigcnt);
 	    if(partsigs)
 		free(data->offmatrix);
 	    if(reloffsigs)
 		free(data->offset);
-	    cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff[0]\n");
+	    cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff_(last|first)[0]\n");
 	    return CL_EMEM;
 	}
-	for(j = 0; j < 64; j++)
-	    data->lsigsuboff[0][j] = CLI_OFF_NONE;
+	for(j = 0; j < 64; j++) {
+	    data->lsigsuboff_last[0][j] = CLI_OFF_NONE;
+	    data->lsigsuboff_first[0][j] = CLI_OFF_NONE;
+	}
 	for(i = 1; i < lsigs; i++) {
-	    data->lsigsuboff[i] = data->lsigsuboff[0] + 64 * i;
-	    for(j = 0; j < 64; j++)
-		data->lsigsuboff[i][j] = CLI_OFF_NONE;
+	    data->lsigsuboff_last[i] = data->lsigsuboff_last[0] + 64 * i;
+	    data->lsigsuboff_first[i] = data->lsigsuboff_first[0] + 64 * i;
+	    for(j = 0; j < 64; j++) {
+		data->lsigsuboff_last[i][j] = CLI_OFF_NONE;
+		data->lsigsuboff_first[i][j] = CLI_OFF_NONE;
+	    }
 	}
     }
     for (i=0;i<32;i++)
 	data->macro_lastmatch[i] = CLI_OFF_NONE;
 
+    data->min_partno = 1;
+
     return CL_SUCCESS;
 }
 
@@ -1014,8 +1039,10 @@ void cli_ac_freedata(struct cli_ac_data *data)
     if(data && data->lsigs) {
 	free(data->lsigcnt[0]);
 	free(data->lsigcnt);
-	free(data->lsigsuboff[0]);
-	free(data->lsigsuboff);
+	free(data->lsigsuboff_last[0]);
+	free(data->lsigsuboff_last);
+	free(data->lsigsuboff_first[0]);
+	free(data->lsigsuboff_first);
 	data->lsigs = 0;
     }
 
@@ -1062,11 +1089,13 @@ static inline void lsig_sub_matched(const struct cli_matcher *root, struct cli_a
 	const struct cli_lsig_tdb *tdb = &root->ac_lsigtable[lsigid1]->tdb;
 
     if(realoff != CLI_OFF_NONE) {
-	if(mdata->lsigsuboff[lsigid1][lsigid2] != CLI_OFF_NONE && ((!partial && realoff <= mdata->lsigsuboff[lsigid1][lsigid2]) || (partial && realoff < mdata->lsigsuboff[lsigid1][lsigid2])))
+	if(mdata->lsigsuboff_first[lsigid1][lsigid2] == CLI_OFF_NONE)
+	    mdata->lsigsuboff_first[lsigid1][lsigid2] = realoff;
+	if(mdata->lsigsuboff_last[lsigid1][lsigid2] != CLI_OFF_NONE && ((!partial && realoff <= mdata->lsigsuboff_last[lsigid1][lsigid2]) || (partial && realoff < mdata->lsigsuboff_last[lsigid1][lsigid2])))
 	    return;
 	mdata->lsigcnt[lsigid1][lsigid2]++;
 	if(mdata->lsigcnt[lsigid1][lsigid2] <= 1 || !tdb->macro_ptids || !tdb->macro_ptids[lsigid2])
-	    mdata->lsigsuboff[lsigid1][lsigid2] = realoff;
+	    mdata->lsigsuboff_last[lsigid1][lsigid2] = realoff;
     }
 
     if (mdata->lsigcnt[lsigid1][lsigid2] > 1) {
@@ -1085,7 +1114,7 @@ static inline void lsig_sub_matched(const struct cli_matcher *root, struct cli_a
 	/* start of last macro match */
 	last_macro_match = mdata->macro_lastmatch[macropt->sigid];
 	/* start of previous lsig subsig match */
-	last_macroprev_match = mdata->lsigsuboff[lsigid1][lsigid2];
+	last_macroprev_match = mdata->lsigsuboff_last[lsigid1][lsigid2];
 	if (last_macro_match != CLI_OFF_NONE)
 	    cli_dbgmsg("Checking macro match: %u + (%u - %u) == %u\n",
 		       last_macroprev_match, smin, smax, last_macro_match);
@@ -1095,11 +1124,11 @@ static inline void lsig_sub_matched(const struct cli_matcher *root, struct cli_a
 	    cli_dbgmsg("Canceled false lsig macro match\n");
 	    /* Previous match was false - cancel it */
 	    mdata->lsigcnt[lsigid1][lsigid2]--;
-	    mdata->lsigsuboff[lsigid1][lsigid2] = realoff;
+	    mdata->lsigsuboff_last[lsigid1][lsigid2] = realoff;
 	} else {
 	    /* mark the macro sig itself matched */
 	    mdata->lsigcnt[lsigid1][lsigid2+1]++;
-	    mdata->lsigsuboff[lsigid1][lsigid2+1] = last_macro_match;
+	    mdata->lsigsuboff_last[lsigid1][lsigid2+1] = last_macro_match;
 	}
     }
 }
@@ -1119,7 +1148,7 @@ void cli_ac_chkmacro(struct cli_matcher *root, struct cli_ac_data *data, unsigne
 int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, const cli_ctx *ctx)
 {
 	struct cli_ac_node *current;
-	struct cli_ac_patt *patt, *pt;
+	struct cli_ac_patt *patt, *pt, *faillist;
         uint32_t i, bp, realoff, matchend;
 	uint16_t j;
 	int32_t **offmatrix, swp;
@@ -1141,10 +1170,22 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
 	current = current->trans[buffer[i]];
 
 	if(IS_FINAL(current)) {
+	    faillist = NULL;
 	    patt = current->list;
-	    if (IS_LEAF(current))
+	    if(IS_LEAF(current)) {
 		current = current->fail;
-	    while(patt) {
+		faillist = current->list;
+	    }
+	    while(1) {
+		if(!patt) {
+		    if(!(patt = faillist))
+			break;
+		    faillist = NULL;
+		}
+		if(patt->partno > mdata->min_partno) {
+		    patt = NULL;
+		    continue;
+		}
 		bp = i + 1 - patt->depth;
 		if(patt->offdata[0] != CLI_OFF_VERSION && patt->offdata[0] != CLI_OFF_MACRO && !patt->next_same && (patt->offset_min != CLI_OFF_ANY) && (!patt->sigid || patt->partno == 1)) {
 		    if(patt->offset_min == CLI_OFF_NONE) {
@@ -1167,6 +1208,8 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
 		pt = patt;
 		if(ac_findmatch(buffer, bp, offset + bp - patt->prefix_length, length, patt, &matchend)) {
 		    while(pt) {
+			if(pt->partno > mdata->min_partno)
+			    break;
 			if((pt->type && !(mode & AC_SCAN_FT)) || (!pt->type && !(mode & AC_SCAN_VIR))) {
 			    pt = pt->next_same;
 			    continue;
@@ -1206,6 +1249,9 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
 				continue;
 			    }
 
+			    if(pt->partno + 1 > mdata->min_partno)
+				mdata->min_partno = pt->partno + 1;
+
 			    if(!mdata->offmatrix[pt->sigid - 1]) {
 				mdata->offmatrix[pt->sigid - 1] = cli_malloc(pt->parts * sizeof(int32_t *));
 				if(!mdata->offmatrix[pt->sigid - 1]) {
diff --git a/libclamav/matcher-ac.h b/libclamav/matcher-ac.h
index 8370aec..048f64a 100644
--- a/libclamav/matcher-ac.h
+++ b/libclamav/matcher-ac.h
@@ -37,11 +37,12 @@ struct cli_ac_data {
     int32_t ***offmatrix;
     uint32_t partsigs, lsigs, reloffsigs;
     uint32_t **lsigcnt;
-    uint32_t **lsigsuboff;
+    uint32_t **lsigsuboff_last, **lsigsuboff_first;
     uint32_t *offset;
     uint32_t macro_lastmatch[32];
     /** Hashset for versioninfo matching */
     const struct cli_hashset *vinfo;
+    uint32_t min_partno;
 };
 
 struct cli_ac_special {
diff --git a/libclamav/matcher-bm.c b/libclamav/matcher-bm.c
index 696e6d0..094fc7f 100644
--- a/libclamav/matcher-bm.c
+++ b/libclamav/matcher-bm.c
@@ -355,6 +355,10 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
 		if(found && p->length + p->prefix_length == j) {
 		    if(!offdata && (p->offset_min != CLI_OFF_ANY)) {
 			if(p->offdata[0] != CLI_OFF_ABSOLUTE) {
+			    if(!info) {
+				p = p->next;
+				continue;
+			    }
 			    ret = cli_caloff(NULL, info, root->type, p->offdata, &off_min, &off_max);
 			    if(ret != CL_SUCCESS) {
 				cli_errmsg("cli_bm_scanbuff: Can't calculate relative offset in signature for %s\n", p->virname);
diff --git a/libclamav/matcher-hash.c b/libclamav/matcher-hash.c
new file mode 100644
index 0000000..93a120f
--- /dev/null
+++ b/libclamav/matcher-hash.c
@@ -0,0 +1,267 @@
+/*
+ *  Copyright (C) 2010 Sourcefire, Inc.
+ *
+ *  Authors: aCaB
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA 02110-1301, USA.
+ */
+
+#include "matcher.h"
+#include "others.h"
+#include "str.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+
+int hm_addhash(struct cli_matcher *root, const char *hash, uint32_t size, const char *virusname) {
+    const struct cli_htu32_element *item;
+    struct cli_sz_hash *szh;
+    struct cli_htu32 *ht;
+    enum CLI_HASH_TYPE type;
+    uint8_t binhash[32];
+    int hashlen, i;
+
+    if(!root || !hash) {
+	cli_errmsg("hm_addhash: NULL root or hash\n");
+	return CL_ENULLARG;
+    }
+
+    if(!size || size == (uint32_t)-1) {
+	cli_errmsg("hm_addhash: null or invalid size (%u)\n", size);
+	return CL_EARG;
+    }
+
+    hashlen = strlen(hash);
+    switch(hashlen) {
+    case 32:
+	type = CLI_HASH_MD5;
+	break;
+    case 40:
+	type = CLI_HASH_SHA1;
+	break;
+    case 64:
+	type = CLI_HASH_SHA256;
+	break;
+    default:
+	cli_errmsg("hm_addhash: invalid hash %s -- FIXME!\n", hash);
+	return CL_EARG;
+    }
+    if(cli_hex2str_to(hash, (char *)binhash, hashlen)) {
+	cli_errmsg("hm_addhash: invalid hash %s\n", hash);
+	return CL_EARG;
+    }
+
+    hashlen /= 2;
+    ht = &root->hm.sizehashes[type];
+    if(!root->hm.sizehashes[type].capacity) {
+	i = cli_htu32_init(ht, 64, root->mempool);
+	if(i) return i;
+    }
+
+    item = cli_htu32_find(ht, size);
+    if(!item) {
+	struct cli_htu32_element htitem;
+	szh = mpool_calloc(root->mempool, 1, sizeof(*szh));
+	if(!szh) {
+	    cli_errmsg("hm_addhash: failed to allocate size hash\n");
+	    return CL_EMEM;
+	}
+
+	htitem.key = size;
+	htitem.data.as_ptr = szh;
+	i = cli_htu32_insert(ht, &htitem, root->mempool);
+	if(i) {
+	    cli_errmsg("ht_addhash: failed to add item to hashtab");
+	    mpool_free(root->mempool, szh);
+	    return i;
+	}
+    } else
+	szh = (struct cli_sz_hash *)item->data.as_ptr;
+
+    szh->items++;
+
+    szh->hash_array = mpool_realloc2(root->mempool, szh->hash_array, hashlen * szh->items);
+    if(!szh->hash_array) {
+	cli_errmsg("ht_add: failed to grow hash array to %u entries\n", szh->items);
+	szh->items=0;
+	mpool_free(root->mempool, szh->virusnames);
+	szh->virusnames = NULL;
+	return CL_EMEM;
+    }
+
+    szh->virusnames = mpool_realloc2(root->mempool, szh->virusnames, sizeof(*szh->virusnames) * szh->items);
+    if(!szh->virusnames) {
+	cli_errmsg("ht_add: failed to grow virusname array to %u entries\n", szh->items);
+	szh->items=0;
+	mpool_free(root->mempool, szh->hash_array);
+	szh->hash_array = NULL;
+	return CL_EMEM;
+    }
+
+    memcpy(&szh->hash_array[(szh->items-1) * hashlen], binhash, hashlen);
+    szh->virusnames[(szh->items-1)] = virusname;
+    
+    return 0;
+}
+
+
+
+static const unsigned int hashlen[] = {
+    16, /* CLI_HASH_MD5 */
+    20, /* CLI_HASH_SHA1 */
+    32, /* CLI_HASH_SHA256 */
+};
+
+
+static inline int hm_cmp(const uint8_t *itm, const uint8_t *ref, unsigned int keylen) {
+    uint32_t i = *(uint32_t *)itm, r = *(uint32_t *)ref;
+    if(i!=r)
+	return (i<r) * 2 -1;
+    return memcmp(&itm[4], &ref[4], keylen - 4);
+}
+
+void hm_sort(struct cli_sz_hash *szh, size_t l, size_t r, unsigned int keylen) {
+    uint8_t piv[32], tmph[32];
+    size_t l1, r1;
+
+    const char *tmpv;
+
+    if(l + 1 >= r)
+	return;
+
+    l1 = l+1, r1 = r;
+
+    memcpy(piv, &szh->hash_array[keylen * l], keylen);
+    while(l1 < r1) {
+	if(hm_cmp(&szh->hash_array[keylen * l1], piv, keylen) > 0) {
+	    r1--;
+	    memcpy(tmph, &szh->hash_array[keylen * l1], keylen);
+	    tmpv = szh->virusnames[l1];
+	    memcpy(&szh->hash_array[keylen * l1], &szh->hash_array[keylen * r1], keylen);
+	    szh->virusnames[l1] = szh->virusnames[r1];
+	    memcpy(&szh->hash_array[keylen * r1], tmph, keylen);
+	    szh->virusnames[r1] = tmpv;
+	} else
+	    l1++;
+    }
+
+    l1--;
+    if(l1!=l) {
+	memcpy(tmph, &szh->hash_array[keylen * l1], keylen);
+	tmpv = szh->virusnames[l1];
+	memcpy(&szh->hash_array[keylen * l1], &szh->hash_array[keylen * l], keylen);
+	szh->virusnames[l1] = szh->virusnames[l];
+	memcpy(&szh->hash_array[keylen * l], tmph, keylen);
+	szh->virusnames[l] = tmpv;
+    }
+
+    hm_sort(szh, l, l1, keylen);
+    hm_sort(szh, r1, r, keylen);
+}
+
+
+void hm_flush(struct cli_matcher *root) {
+    enum CLI_HASH_TYPE type;
+
+    if(!root)
+	return;
+
+    for(type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
+	struct cli_htu32 *ht = &root->hm.sizehashes[type];
+	const struct cli_htu32_element *item = NULL;
+
+	if(!root->hm.sizehashes[type].capacity)
+	    continue;
+
+	while((item = cli_htu32_next(ht, item))) {
+	    struct cli_sz_hash *szh = (struct cli_sz_hash *)item->data.as_ptr;
+	    unsigned int keylen = hashlen[type];
+
+	    if(szh->items > 1)
+		hm_sort(szh, 0, szh->items, keylen);
+	}
+    }
+}
+
+
+int cli_hm_have_size(const struct cli_matcher *root, enum CLI_HASH_TYPE type, uint32_t size) {
+    return (size && size != 0xffffffff && root && root->hm.sizehashes[type].capacity && cli_htu32_find(&root->hm.sizehashes[type], size));
+}
+
+int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type) {
+    const struct cli_htu32_element *item;
+    unsigned int keylen;
+    struct cli_sz_hash *szh;
+    size_t l, r;
+
+    if(!digest || !size || size == 0xffffffff || !root || !root->hm.sizehashes[type].capacity)
+	return CL_CLEAN;
+
+    item = cli_htu32_find(&root->hm.sizehashes[type], size);
+    if(!item)
+	return CL_CLEAN;
+
+    szh = (struct cli_sz_hash *)item->data.as_ptr;
+    keylen = hashlen[type];
+
+    l = 0;
+    r = szh->items;
+    while(l <= r) {
+	size_t c = (l + r) / 2;
+	int res = hm_cmp(digest, &szh->hash_array[keylen * c], keylen);
+
+	if(res < 0) {
+	    if(!c)
+		break;
+	    r = c - 1;
+	} else if(res > 0)
+	    l = c + 1;
+	else {
+	    if(virname)
+		*virname = szh->virusnames[c];
+	    return CL_VIRUS;
+	}
+    }
+    return CL_CLEAN;
+}
+
+void hm_free(struct cli_matcher *root) {
+    enum CLI_HASH_TYPE type;
+
+    if(!root)
+	return;
+
+    for(type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
+	struct cli_htu32 *ht = &root->hm.sizehashes[type];
+	const struct cli_htu32_element *item = NULL;
+
+	if(!root->hm.sizehashes[type].capacity)
+	    continue;
+
+	while((item = cli_htu32_next(ht, item))) {
+	    struct cli_sz_hash *szh = (struct cli_sz_hash *)item->data.as_ptr;
+	    unsigned int keylen = hashlen[type];
+
+	    mpool_free(root->mempool, szh->hash_array);
+	    while(szh->items)
+		mpool_free(root->mempool, (void *)szh->virusnames[--szh->items]);
+	    mpool_free(root->mempool, szh->virusnames);
+	    mpool_free(root->mempool, szh);
+	}
+	cli_htu32_free(ht, root->mempool);
+    }
+}
+
diff --git a/libclamav/matcher-hash.h b/libclamav/matcher-hash.h
new file mode 100644
index 0000000..ea581d8
--- /dev/null
+++ b/libclamav/matcher-hash.h
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (C) 2010 Sourcefire, Inc.
+ *
+ *  Authors: aCaB
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA 02110-1301, USA.
+ */
+
+#ifndef __MATCHER_HASH_H
+#define __MATCHER_HASH_H
+
+#if HAVE_CONFIG_H
+#include "clamav-config.h"
+#endif
+
+#include "cltypes.h"
+#include "hashtab.h"
+
+enum CLI_HASH_TYPE {
+    CLI_HASH_MD5,
+    CLI_HASH_SHA1,
+    CLI_HASH_SHA256,
+
+    /* new hash types go above this line */
+    CLI_HASH_AVAIL_TYPES
+};
+
+struct cli_sz_hash {
+    uint8_t *hash_array;
+    const char **virusnames;
+    uint32_t items;
+};
+
+
+struct cli_hash_patt {
+    struct cli_htu32 sizehashes[CLI_HASH_AVAIL_TYPES];
+};
+
+int hm_addhash(struct cli_matcher *root, const char *hash, uint32_t size, const char *virusname);
+void hm_flush(struct cli_matcher *root);
+int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type);
+int cli_hm_have_size(const struct cli_matcher *root, enum CLI_HASH_TYPE type, uint32_t size);
+void hm_free(struct cli_matcher *root);
+
+#endif
diff --git a/libclamav/matcher-md5.c b/libclamav/matcher-md5.c
deleted file mode 100644
index 9bb2d63..0000000
--- a/libclamav/matcher-md5.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- *  Copyright (C) 2007-2010 Sourcefire, Inc.
- *
- *  Authors: Tomasz Kojm
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston,
- *  MA 02110-1301, USA.
- */
-
-#if HAVE_CONFIG_H
-#include "clamav-config.h"
-#endif
-
-#include <stdio.h>
-
-#include "clamav.h"
-#include "memory.h"
-#include "mpool.h"
-#include "others.h"
-#include "cltypes.h"
-#include "matcher.h"
-#include "matcher-md5.h"
-
-#define HASH(a,b,c) (211 * a + 37 * b + c)
-
-int cli_md5m_addpatt(struct cli_matcher *root, struct cli_md5m_patt *patt)
-{
-	unsigned int idx;
-	struct cli_md5m_patt *prev, *next = NULL;
-
-    idx = HASH(patt->md5[0], patt->md5[1], patt->md5[2]);
-    prev = next = root->md5tab[idx];
-    while(next) {
-	if(patt->md5[0] >= next->md5[0])
-	    break;
-	prev = next;
-	next = next->next;
-    }
-
-    if(next == root->md5tab[idx]) {
-	patt->next = root->md5tab[idx];
-	root->md5tab[idx] = patt;
-    } else {
-	patt->next = prev->next;
-	prev->next = patt;
-    }
-
-    root->md5_patterns++;
-    return CL_SUCCESS;
-}
-
-int cli_md5m_init(struct cli_matcher *root)
-{
-#ifdef USE_MPOOL
-    if(!root->mempool) {
-	cli_errmsg("cli_md5m_init: mempool must be initialized\n");
-	return CL_EMEM;
-    }
-#endif
-
-    if(!(root->md5tab = (struct cli_md5m_patt **) mpool_calloc(root->mempool, HASH(255, 255, 255) + 1, sizeof(struct cli_md5m_patt *)))) {
-	mpool_free(root->mempool, root->bm_shift);
-	return CL_EMEM;
-    }
-
-    return CL_SUCCESS;
-}
-
-void cli_md5m_free(struct cli_matcher *root)
-{
-	struct cli_md5m_patt *patt, *prev;
-	unsigned int i, size = HASH(255, 255, 255) + 1;
-
-    if(root->md5tab) {
-	for(i = 0; i < size; i++) {
-	    patt = root->md5tab[i];
-	    while(patt) {
-		prev = patt;
-		patt = patt->next;
-		if(prev->virname)
-		    mpool_free(root->mempool, prev->virname);
-		mpool_free(root->mempool, prev);
-	    }
-	}
-	mpool_free(root->mempool, root->md5tab);
-    }
-}
-
-int cli_md5m_scan(const unsigned char *md5, uint32_t filesize, const char **virname, const struct cli_matcher *root)
-{
-	unsigned int pchain = 0, idx;
-	struct cli_md5m_patt *p;
-
-    if(!root)
-	return CL_CLEAN;
-
-    idx = HASH(md5[0], md5[1], md5[2]);
-    p = root->md5tab[idx];
-    if(!p || (!p->next && p->filesize != filesize))
-	return CL_CLEAN;
-
-    while(p) {
-	if(p->md5[0] != md5[0]) {
-	    if(pchain)
-		break;
-	    p = p->next;
-	    continue;
-	} else pchain = 1;
-
-	if(p->filesize != filesize) {
-	    p = p->next;
-	    continue;
-	}
-
-	if(!memcmp(p->md5, md5, 16)) {
-	    if(virname)
-		*virname = p->virname;
-	    return CL_VIRUS;
-	}
-	p = p->next;
-    }
-
-    return CL_CLEAN;
-}
diff --git a/libclamav/matcher-md5.h b/libclamav/matcher-md5.h
deleted file mode 100644
index b63adf7..0000000
--- a/libclamav/matcher-md5.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Copyright (C) 2007-2010 Sourcefire, Inc.
- *
- *  Authors: Tomasz Kojm
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston,
- *  MA 02110-1301, USA.
- */
-
-#ifndef __MATCHER_MD5_H
-#define __MATCHER_MD5_H
-
-#include "matcher.h"
-#include "cltypes.h"
-
-struct cli_md5m_patt {
-    unsigned char md5[16];
-    uint32_t filesize;
-    char *virname;
-    struct cli_md5m_patt *next;
-};
-
-int cli_md5m_addpatt(struct cli_matcher *root, struct cli_md5m_patt *patt);
-int cli_md5m_init(struct cli_matcher *root);
-int cli_md5m_scan(const unsigned char *md5, uint32_t filesize, const char **virname, const struct cli_matcher *root);
-void cli_md5m_free(struct cli_matcher *root);
-
-#endif
diff --git a/libclamav/matcher.c b/libclamav/matcher.c
index 51f09c9..2df0e0e 100644
--- a/libclamav/matcher.c
+++ b/libclamav/matcher.c
@@ -34,8 +34,9 @@
 #include "others.h"
 #include "matcher-ac.h"
 #include "matcher-bm.h"
-#include "matcher-md5.h"
 #include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
 #include "filetypes.h"
 #include "matcher.h"
 #include "pe.h"
@@ -54,11 +55,8 @@
 #include "perflogging.h"
 #include "bytecode_priv.h"
 #include "bytecode_api_impl.h"
-
-#ifdef HAVE__INTERNAL__SHA_COLLECT
 #include "sha256.h"
 #include "sha1.h"
-#endif
 
 #ifdef CLI_PERF_LOGGING
 
@@ -382,35 +380,65 @@ int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx)
 	char md5[33];
 	unsigned int i;
 	const char *virname;
+        SHA1Context sha1;
+        SHA256_CTX sha256;
+        fmap_t *map;
+        char *ptr;
+        uint8_t shash1[SHA1_HASH_SIZE*2+1];
+        uint8_t shash256[SHA256_HASH_SIZE*2+1];
+	int have_sha1, have_sha256;
 
-    if(ctx->engine->md5_fp && cli_md5m_scan(digest, size, &virname, ctx->engine->md5_fp) == CL_VIRUS) {
-	cli_dbgmsg("cli_checkfp(): Found false positive detection (fp sig: %s)\n", virname);
+    if(cli_hm_scan(digest, size, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) {
+	cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s)\n", virname);
 	return CL_CLEAN;
     }
+
     for(i = 0; i < 16; i++)
 	sprintf(md5 + i * 2, "%02x", digest[i]);
     md5[32] = 0;
     cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) size, *ctx->virname ? *ctx->virname : "Name");
 
+    map = *ctx->fmap;
+    have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size);
+    have_sha256 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA256, size);
+    if(have_sha1 || have_sha256) {
+	if((ptr = fmap_need_off_once(map, 0, size))) {
+	    if(have_sha1) {
+		SHA1Init(&sha1);
+		SHA1Update(&sha1, ptr, size);
+		SHA1Final(&sha1, &shash1[SHA1_HASH_SIZE]);
+		if(cli_hm_scan(&shash1[SHA1_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS){
+		    cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname);
+		    return CL_CLEAN;
+		}
+	    }
+	    if(have_sha256) {
+		sha256_init(&sha256);
+		sha256_update(&sha256, ptr, size);
+		sha256_final(&sha256, &shash256[SHA256_HASH_SIZE]);
+		if(cli_hm_scan(&shash256[SHA256_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS){
+		    cli_dbgmsg("cli_checkfp(sha256): Found false positive detection (fp sig: %s)\n", virname);
+		    return CL_CLEAN;
+		}
+	    }
+	}
+    }
 #ifdef HAVE__INTERNAL__SHA_COLLECT
     if((ctx->options & CL_SCAN_INTERNAL_COLLECT_SHA) && ctx->sha_collect>0) {
-        SHA1Context sha1;
-        SHA256_CTX sha256;
-        fmap_t *map = *ctx->fmap;
-        char *ptr;
-        uint8_t shash1[SHA1_HASH_SIZE*2+1];
-        uint8_t shash256[SHA256_HASH_SIZE*2+1];
-
         if((ptr = fmap_need_off_once(map, 0, size))) {
-            sha256_init(&sha256);
-            sha256_update(&sha256, ptr, size);
-            sha256_final(&sha256, &shash256[SHA256_HASH_SIZE]);
+	    if(!have_sha256) {
+		sha256_init(&sha256);
+		sha256_update(&sha256, ptr, size);
+		sha256_final(&sha256, &shash256[SHA256_HASH_SIZE]);
+	    }
             for(i=0; i<SHA256_HASH_SIZE; i++)
                 sprintf((char *)shash256+i*2, "%02x", shash256[SHA256_HASH_SIZE+i]);
 
-            SHA1Init(&sha1);
-            SHA1Update(&sha1, ptr, size);
-            SHA1Final(&sha1, &shash1[SHA1_HASH_SIZE]);
+	    if(!have_sha1) {
+		SHA1Init(&sha1);
+		SHA1Update(&sha1, ptr, size);
+		SHA1Final(&sha1, &shash1[SHA1_HASH_SIZE]);
+	    }
             for(i=0; i<SHA1_HASH_SIZE; i++)
                 sprintf((char *)shash1+i*2, "%02x", shash1[SHA1_HASH_SIZE+i]);
 
@@ -502,7 +530,7 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
     return ret;
 }
 
-int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info)
+int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash)
 {
 	unsigned int i, evalcnt;
 	uint64_t evalids;
@@ -527,14 +555,17 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
 		    continue;
 	    }
 
-	    if(root->ac_lsigtable[i]->tdb.handlertype) {
-		ctx->recursion++;
-		if(cli_magic_scandesc_type(map->fd, ctx, root->ac_lsigtable[i]->tdb.handlertype[0]) == CL_VIRUS) {
+	    if(hash && root->ac_lsigtable[i]->tdb.handlertype) {
+		if(memcmp(ctx->handlertype_hash, hash, 16)) {
+		    ctx->recursion++;
+		    memcpy(ctx->handlertype_hash, hash, 16);
+		    if(cli_magic_scandesc_type(map->fd, ctx, root->ac_lsigtable[i]->tdb.handlertype[0]) == CL_VIRUS) {
+			ctx->recursion--;
+			return CL_VIRUS;
+		    }
 		    ctx->recursion--;
-		    return CL_VIRUS;
+		    continue;
 		}
-		ctx->recursion--;
-		continue;
 	    }
 
 	    if(root->ac_lsigtable[i]->tdb.icongrp1 || root->ac_lsigtable[i]->tdb.icongrp2) {
@@ -545,7 +576,7 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
 			if(ctx->virname)
 			    *ctx->virname = root->ac_lsigtable[i]->virname;
 			return CL_VIRUS;
-		    } else if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff[i], map) == CL_VIRUS) {
+		    } else if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
 			return CL_VIRUS;
 		    }
 		}
@@ -556,7 +587,7 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
 		    *ctx->virname = root->ac_lsigtable[i]->virname;
 		return CL_VIRUS;
 	    }
-	    if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff[i], map) == CL_VIRUS) {
+	    if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
 		return CL_VIRUS;
 	    }
 	}
@@ -567,16 +598,19 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
 int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash)
 {
  	unsigned char *buff;
-	int ret = CL_CLEAN, type = CL_CLEAN, bytes;
+	int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES];
 	unsigned int i = 0, bm_offmode = 0;
 	uint32_t maxpatlen, offset = 0;
 	struct cli_ac_data gdata, tdata;
 	struct cli_bm_off toff;
 	cli_md5_ctx md5ctx;
-	unsigned char digest[16];
+	SHA256_CTX sha256ctx;
+	SHA1Context sha1ctx;
+	unsigned char digest[CLI_HASH_AVAIL_TYPES][32];
 	struct cli_matcher *groot = NULL, *troot = NULL;
 	struct cli_target_info info;
 	fmap_t *map = *ctx->fmap;
+	struct cli_matcher *hdb, *fp;
 
     if(!ctx->engine) {
 	cli_errmsg("cli_scandesc: engine == NULL\n");
@@ -642,8 +676,33 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
 	}
     }
 
-    if(!refhash && !ftonly && ctx->engine->md5_hdb)
-	cli_md5_init(&md5ctx);
+    hdb = ctx->engine->hm_hdb;
+    fp = ctx->engine->hm_fp;
+
+    if(!ftonly && hdb) {
+	if(!refhash) {
+	    if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len)) {
+		cli_md5_init(&md5ctx);
+		compute_hash[CLI_HASH_MD5] = 1;
+	    } else
+		compute_hash[CLI_HASH_MD5] = 0;
+	} else {
+	    compute_hash[CLI_HASH_MD5] = 0;
+	    memcpy(digest[CLI_HASH_MD5], refhash, 16);
+	}
+
+	if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len)) {
+	    SHA1Init(&sha1ctx);
+	    compute_hash[CLI_HASH_SHA1] = 1;
+	} else
+	    compute_hash[CLI_HASH_SHA1] = 0;
+
+	if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len)) {
+	    sha256_init(&sha256ctx);
+	    compute_hash[CLI_HASH_SHA256] = 1;
+	} else
+	    compute_hash[CLI_HASH_SHA256] = 0;
+    }
 
     while(offset < map->len) {
 	bytes = MIN(map->len - offset, SCANBUFF);
@@ -687,16 +746,53 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
 		    type = ret;
 	    }
 
-	    if(!refhash && ctx->engine->md5_hdb)
-		cli_md5_update(&md5ctx, buff + maxpatlen * (offset!=0), bytes - maxpatlen * (offset!=0));
+	    if(hdb) {
+		void *data = buff + maxpatlen * (offset!=0);
+		uint32_t data_len = bytes - maxpatlen * (offset!=0);
+
+		if(compute_hash[CLI_HASH_MD5])
+		    cli_md5_update(&md5ctx, data, data_len);
+		if(compute_hash[CLI_HASH_SHA1])
+		    SHA1Update(&sha1ctx, data, data_len);
+		if(compute_hash[CLI_HASH_SHA256])
+		    sha256_update(&sha256ctx, data, data_len);
+	    }
 	}
 
 	if(bytes < SCANBUFF) break;
 	offset += bytes - maxpatlen;
     }
 
+    if(!ftonly && hdb) {
+	enum CLI_HASH_TYPE hashtype;
+
+	if(compute_hash[CLI_HASH_MD5])
+	    cli_md5_final(digest[CLI_HASH_MD5], &md5ctx);
+	if(refhash)
+	    compute_hash[CLI_HASH_MD5] = 1;
+	if(compute_hash[CLI_HASH_SHA1])
+	    SHA1Final(&sha1ctx, digest[CLI_HASH_SHA1]);
+	if(compute_hash[CLI_HASH_SHA256])
+	    sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]);
+
+	for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
+	    if(compute_hash[hashtype] && (ret = cli_hm_scan(digest[hashtype], map->len, ctx->virname, hdb, hashtype)) == CL_VIRUS)
+		break;
+	}
+
+	if(ret == CL_VIRUS && fp) {
+	    for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
+		if(compute_hash[hashtype] && cli_hm_scan(digest[hashtype], map->len, ctx->virname, fp, hashtype) == CL_VIRUS) {
+		    ret = CL_CLEAN;
+		    break;
+		}
+	    }
+	}
+    }
+
     if(troot) {
-	ret = cli_lsig_eval(ctx, troot, &tdata, &info);
+	if(ret != CL_VIRUS)
+	    ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash);
 	cli_ac_freedata(&tdata);
 	if(bm_offmode)
 	    cli_bm_freeoff(&toff);
@@ -704,7 +800,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
 
     if(groot) {
 	if(ret != CL_VIRUS)
-	    ret = cli_lsig_eval(ctx, groot, &gdata, &info);
+	    ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash);
 	cli_ac_freedata(&gdata);
     }
 
@@ -715,15 +811,6 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
     if(ret == CL_VIRUS)
 	return CL_VIRUS;
 
-    if(!ftonly && ctx->engine->md5_hdb) {
-	if(!refhash) {
-	    cli_md5_final(digest, &md5ctx);
-	    refhash = digest;
-	}
-	if(cli_md5m_scan(refhash, map->len, ctx->virname, ctx->engine->md5_hdb) == CL_VIRUS && cli_md5m_scan(refhash, map->len, NULL, ctx->engine->md5_fp) != CL_VIRUS)
-	    return CL_VIRUS;
-    }
-
     return (acmode & AC_SCAN_FT) ? type : CL_CLEAN;
 }
 
diff --git a/libclamav/matcher.h b/libclamav/matcher.h
index 6264f73..5079b7d 100644
--- a/libclamav/matcher.h
+++ b/libclamav/matcher.h
@@ -28,7 +28,6 @@
 #include "others.h"
 #include "execs.h"
 #include "cltypes.h"
-#include "md5.h"
 
 struct cli_target_info {
     off_t fsize;
@@ -38,7 +37,7 @@ struct cli_target_info {
 
 #include "matcher-ac.h"
 #include "matcher-bm.h"
-#include "hashtab.h"
+#include "matcher-hash.h"
 #include "fmap.h"
 #include "mpool.h"
 
@@ -89,13 +88,11 @@ struct cli_matcher {
     /* Extended Boyer-Moore */
     uint8_t *bm_shift;
     struct cli_bm_patt **bm_suffix, **bm_pattab;
-    struct cli_hashset md5_sizes_hs;
     uint32_t *soff, soff_len; /* for PE section sigs */
     uint32_t bm_offmode, bm_patterns, bm_reloff_num, bm_absoff_num;
 
-    /* MD5 */
-    struct cli_md5m_patt **md5tab;
-    uint32_t md5_patterns;
+    /* HASH */
+    struct cli_hash_patt hm;
 
     /* Extended Aho-Corasick */
     uint32_t ac_partsigs, ac_nodes, ac_patterns, ac_lsigs;
@@ -169,7 +166,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
 
 int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres);
 int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash);
-int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info);
+int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash);
 int cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max);
 
 int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx);
diff --git a/libclamav/mpool.c b/libclamav/mpool.c
index 3f3044f..839a802 100644
--- a/libclamav/mpool.c
+++ b/libclamav/mpool.c
@@ -68,81 +68,99 @@ static inline void spam(const char *fmt, ...) { fmt = fmt; } /* gcc STFU */
 
 #if SIZEOF_VOID_P==8
 static const unsigned int fragsz[] = {
-/* SIZE,      MAX   */
-      8, /*  783293 */
-     11, /*    4445 */
-     13, /*   10160 */
-     15, /*    8365 */
-     16, /*   24857 */
-     17, /*   24630 */
-     19, /*  136608 */
-     20, /*   90714 */
-     21, /*  176421 */
-     23, /*   52766 */
-     24, /*  161770 */
-     25, /*   38590 */
-     29, /*   18023 */
-     31, /*   15987 */
-     32, /*  104854 */
-     33, /*    9312 */
-     35, /*  371084 */
-     41, /*    5068 */
-     43, /*  142581 */
-     44, /*   99347 */
-     45, /*   74173 */
-     47, /*   31516 */
-     48, /*  320748 */
-     49, /*   45256 */
-     56, /*    1543 */
-     64, /*     963 */
-     74, /*   12397 */
-     76, /*   17846 */
-     79, /*   15721 */
-     80, /*  599771 */
-     81, /*    5618 */
-     93, /*    2101 */
-     97, /*    2425 */
-    104, /*    1495 */
-    113, /*    3107 */
-    116, /*    2403 */
-    123, /*    1415 */
-    128, /*    2368 */
-    131, /*    2697 */
-    143, /*   10539 */
-    150, /*   10982 */
-    151, /*    6869 */
-    152, /*   28254 */
-    153, /*   13670 */
-    229, /*     501 */
-    256, /*     830 */
-    304, /*     834 */
-    320, /*     377 */
-    512, /*      17 */
-   1024, /*       6 */
-   2048, /*       3 */
-   2056, /*   10116 */
-   4096, /*       3 */
-   8192, /*       2 */
-   9334, /*       3 */
-  12163, /*       4 */
-  16392, /*     257 */
-  18440, /*       2 */
-  21952, /*       1 */
-  32768, /*       1 */
-  35311, /*       1 */
-  43256, /*       2 */
-  48914, /*       1 */
-  63504, /*       8 */
-  65536, /*       0 */
-  92794, /*       1 */
- 107602, /*       1 */
- 131832, /*       7 */
- 156920, /*       1 */
- 262144, /*       1 */
- 374608, /*       1 */
- 507976, /*      10 */
- 524288, /*       0 */
-1048576,
+    8,
+    11,
+    13,
+    16,
+    17,
+    19,
+    20,
+    21,
+    22,
+    23,
+    24,
+    25,
+    26,
+    27,
+    28,
+    29,
+    30,
+    31,
+    32,
+    33,
+    37,
+    40,
+    41,
+    48,
+    56,
+    72,
+    74,
+    75,
+    76,
+    78,
+    79,
+    80,
+    81,
+    101,
+    104,
+    109,
+    113,
+    116,
+    120,
+    128,
+    131,
+    143,
+    151,
+    152,
+    153,
+    196,
+    256,
+    360,
+    403,
+    404,
+    432,
+    486,
+    514,
+    548,
+    578,
+    604,
+    633,
+    697,
+    743,
+    784,
+    839,
+   1176,
+   1536,
+   1666,
+   2056,
+   2168,
+   2392,
+   2985,
+   3221,
+   3433,
+   3753,
+   3832,
+   4104,
+   4280,
+   4696,
+   4952,
+   5256,
+   5826,
+   6264,
+   7176,
+   8440,
+   9096,
+  16392,
+  32780,
+  50961,
+  63504,
+  65558,
+ 101912,
+ 131088,
+ 262144,
+ 507976,
+ 524296,
+1048584,
 2097152,
 4194304,
 8388608,
@@ -152,108 +170,183 @@ static const unsigned int fragsz[] = {
 #else
 
 static const unsigned int fragsz[] = {
-/* SIZE,        MAX */
-      4, /*  576046 */
-      7, /*  205452 */
-      8, /*    2448 */
-      9, /*    1633 */
-     11, /*    2740 */
-     12, /*    6315 */
-     13, /*    8821 */
-     15, /*   15903 */
-     16, /*   31401 */
-     17, /*   28027 */
-     19, /*  136365 */
-     20, /*   95662 */
-     21, /*  176094 */
-     23, /*   54138 */
-     24, /*  156993 */
-     25, /*   39393 */
-     28, /*   16340 */
-     29, /*    9807 */
-     30, /*    5132 */
-     31, /*  176283 */
-     32, /*  484214 */
-     33, /*  129187 */
-     37, /*    1163 */
-     40, /*    3311 */
-     41, /*    1513 */
-     48, /*    3930 */
-     49, /*    1018 */
-     56, /*    2303 */
-     58, /*   13518 */
-     59, /*    4709 */
-     60, /*   13577 */
-     61, /*    3093 */
-     62, /*   10407 */
-     63, /*    3973 */
-     64, /*   26914 */
-     65, /*    2963 */
-     73, /*    2314 */
-     81, /*    2038 */
-     85, /*    1306 */
-     88, /*    1395 */
-     93, /*     885 */
-     96, /*     878 */
-    104, /*    3746 */
-    108, /*     685 */
-    113, /*    2940 */
-    115, /*    3625 */
-    116, /*    3827 */
-    117, /*    2517 */
-    119, /*    4260 */
-    120, /*   17485 */
-    121, /*    1945 */
-    128, /*    2653 */
-    131, /*    2304 */
-    136, /*    1090 */
-    143, /*   10787 */
-    148, /*    1292 */
-    152, /*    3494 */
-    153, /*   11788 */
-    168, /*     505 */
-    176, /*     652 */
-    200, /*     350 */
-    216, /*     312 */
-    232, /*     402 */
-    248, /*     495 */
-    256, /*     140 */
-    284, /*     439 */
-    309, /*     817 */
-    452, /*      57 */
-    512, /*       3 */
-    784, /*      14 */
-   1024, /*       2 */
-   1028, /*    3191 */
-   1032, /*    7777 */
-   2048, /*       5 */
-   4096, /*       3 */
-   5128, /*       5 */
-   8192, /*       3 */
-  11264, /*       5 */
-  11268, /*     238 */
-  11272, /*     243 */
-  16384, /*       1 */
-  17657, /*       1 */
-  21632, /*       2 */
-  23188, /*       2 */
-  24458, /*       1 */
-  32768, /*       1 */
-  46398, /*       1 */
-  53804, /*       1 */
-  63504, /*       7 */
-  65536, /*       1 */
- 131072, /*       0 */
- 131080, /*       7 */
- 131544, /*       2 */
- 156920, /*       1 */
- 187304, /*       1 */
- 253988, /*      10 */
- 262144, /*       0 */
-1048576,
-2097152,
-4194304,
-8388608,
+    4,
+    5,
+    8,
+    9,
+    11,
+    12,
+    13,
+    14,
+    15,
+    16,
+    17,
+    19,
+    20,
+    21,
+    22,
+    23,
+    24,
+    25,
+    26,
+    27,
+    28,
+    29,
+    30,
+    31,
+    32,
+    33,
+    35,
+    36,
+    37,
+    39,
+    40,
+    41,
+    44,
+    48,
+    49,
+    52,
+    53,
+    56,
+    58,
+    59,
+    60,
+    61,
+    62,
+    63,
+    64,
+    65,
+    68,
+    69,
+    72,
+    73,
+    77,
+    80,
+    81,
+    83,
+    85,
+    88,
+    89,
+    93,
+    96,
+    99,
+    101,
+    103,
+    104,
+    105,
+    108,
+    112,
+    113,
+    115,
+    116,
+    117,
+    119,
+    120,
+    121,
+    124,
+    128,
+    129,
+    131,
+    133,
+    136,
+    137,
+    141,
+    143,
+    145,
+    148,
+    151,
+    152,
+    153,
+    160,
+    168,
+    173,
+    176,
+    184,
+    194,
+    200,
+    208,
+    216,
+    224,
+    229,
+    232,
+    241,
+    244,
+    248,
+    256,
+    257,
+    264,
+    274,
+    280,
+    293,
+    296,
+    304,
+    307,
+    312,
+    326,
+    344,
+    354,
+    372,
+    396,
+    403,
+    418,
+    456,
+    485,
+    514,
+    546,
+    581,
+    608,
+    646,
+    693,
+    740,
+    776,
+    805,
+    828,
+    902,
+    964,
+    1028,
+    1032,
+    1136,
+    1238,
+    1314,
+    1420,
+    1501,
+    1668,
+    1720,
+    1832,
+    1940,
+    2048,
+    2119,
+    2264,
+    2584,
+    2724,
+    2994,
+    3336,
+    3428,
+    3828,
+    4104,
+    4471,
+    4836,
+    5044,
+    5176,
+    5912,
+    6227,
+    6792,
+    7732,
+    8192,
+    11272,
+    12500,
+    16384,
+    32768,
+    63500,
+    65536,
+    131080,
+    253988,
+    262148,
+    524292,
+    1048576,
+    2097152,
+    4194304,
+    8388608,
 };
 #endif
 #define FRAGSBITS (sizeof(fragsz)/sizeof(fragsz[0]))
diff --git a/libclamav/msexpand.c b/libclamav/msexpand.c
index 43e15b4..bcc33bc 100644
--- a/libclamav/msexpand.c
+++ b/libclamav/msexpand.c
@@ -68,11 +68,11 @@ struct msexp_hdr {
 #pragma pack
 #endif
 
-#define BSIZE 4096
-#define RWBUFF 2048
+#define B_SIZE 4096
+#define RW_SIZE 2048
 
 #define READBYTES				\
-    ret = cli_readn(fd, rbuff, RWBUFF);		\
+    ret = cli_readn(fd, rbuff, RW_SIZE);		\
     if(ret == -1)				\
 	return CL_EREAD;			\
     if(!ret)					\
@@ -94,8 +94,8 @@ int cli_msexpand(int fd, int ofd, cli_ctx *ctx)
 {
 	struct msexp_hdr hdr;
 	uint8_t i, mask, bits;
-	unsigned char buff[BSIZE], rbuff[RWBUFF], wbuff[RWBUFF];
-	unsigned int j = BSIZE - 16, k, l, r = 0, w = 0, rbytes = 0, wbytes = 0;
+	unsigned char buff[B_SIZE], rbuff[RW_SIZE], wbuff[RW_SIZE];
+	unsigned int j = B_SIZE - 16, k, l, r = 0, w = 0, rbytes = 0, wbytes = 0;
 	int ret;
 
 
@@ -112,7 +112,7 @@ int cli_msexpand(int fd, int ofd, cli_ctx *ctx)
     if(cli_checklimits("MSEXPAND", ctx, EC32(hdr.fsize), 0, 0)!=CL_CLEAN)
         return CL_SUCCESS;
 
-    memset(buff, 0, BSIZE);
+    memset(buff, 0, B_SIZE);
     while(1) {
 
 	if(!rbytes || (r == rbytes)) {
@@ -128,13 +128,13 @@ int cli_msexpand(int fd, int ofd, cli_ctx *ctx)
 		    READBYTES;
 		}
 
-		if(w == RWBUFF) {
+		if(w == RW_SIZE) {
 		    WRITEBYTES;
 		}
 
 		wbuff[w] = buff[j] = rbuff[r];
 		r++; w++;
-		j++; j %= BSIZE;
+		j++; j %= B_SIZE;
 	    } else {
 		if(r == rbytes) {
 		    READBYTES;
@@ -149,13 +149,13 @@ int cli_msexpand(int fd, int ofd, cli_ctx *ctx)
 		k += (l & 0xf0) << 4;
 		l = (l & 0x0f) + 3;
 		while(l--) {
-		    if(w == RWBUFF) {
+		    if(w == RW_SIZE) {
 			WRITEBYTES;
 		    }
 		    wbuff[w] = buff[j] = buff[k];
 		    w++;
-		    k++; k %= BSIZE;
-		    j++; j %= BSIZE;
+		    k++; k %= B_SIZE;
+		    j++; j %= B_SIZE;
 		}
 	    }
 	    mask *= 2;
diff --git a/libclamav/others.c b/libclamav/others.c
index 3272f53..533e382 100644
--- a/libclamav/others.c
+++ b/libclamav/others.c
@@ -60,6 +60,8 @@
 #include "clamav.h"
 #include "others.h"
 #include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
 #include "cltypes.h"
 #include "regex/regex.h"
 #include "ltdl.h"
@@ -251,6 +253,12 @@ const char *cl_strerror(int clerror)
 	    return "Error during bytecode execution";
 	case CL_EBYTECODE_TESTFAIL:
 	    return "Failure in bytecode testmode";
+	case CL_ELOCK:
+	    return "Mutex lock failed";
+	case CL_EBUSY:
+	    return "Scanner still active";
+	case CL_ESTATE:
+	    return "Bad state (engine not initialized, or already initialized)";
 	default:
 	    return "Unknown error code";
     }
@@ -562,8 +570,17 @@ struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine)
     settings->maxfiles = engine->maxfiles;
     settings->min_cc_count = engine->min_cc_count;
     settings->min_ssn_count = engine->min_ssn_count;
+    settings->bytecode_security = engine->bytecode_security;
+    settings->bytecode_timeout = engine->bytecode_timeout;
+    settings->bytecode_mode = engine->bytecode_mode;
     settings->pua_cats = engine->pua_cats ? strdup(engine->pua_cats) : NULL;
 
+    settings->cb_pre_scan = engine->cb_pre_scan;
+    settings->cb_post_scan = engine->cb_post_scan;
+    settings->cb_sigload = engine->cb_sigload;
+    settings->cb_sigload_ctx = engine->cb_sigload_ctx;
+    settings->cb_hash = engine->cb_hash;
+
     return settings;
 }
 
@@ -579,6 +596,9 @@ int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings
     engine->maxfiles = settings->maxfiles;
     engine->min_cc_count = settings->min_cc_count;
     engine->min_ssn_count = settings->min_ssn_count;
+    engine->bytecode_security = settings->bytecode_security;
+    engine->bytecode_timeout = settings->bytecode_timeout;
+    engine->bytecode_mode = settings->bytecode_mode;
 
     if(engine->tmpdir)
 	mpool_free(engine->mempool, engine->tmpdir);
@@ -600,6 +620,12 @@ int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings
 	engine->pua_cats = NULL;
     }
 
+    engine->cb_pre_scan = settings->cb_pre_scan;
+    engine->cb_post_scan = settings->cb_post_scan;
+    engine->cb_sigload = settings->cb_sigload;
+    engine->cb_sigload_ctx = settings->cb_sigload_ctx;
+    engine->cb_hash = settings->cb_hash;
+
     return CL_SUCCESS;
 }
 
@@ -659,73 +685,77 @@ int cli_updatelimits(cli_ctx *ctx, unsigned long needed) {
     return CL_CLEAN;
 }
 
-unsigned char *cli_md5digest(int desc)
-{
-	unsigned char *digest;
-	char buff[FILEBUFF];
-	cli_md5_ctx ctx;
-	int bytes;
-
-
-    if(!(digest = cli_malloc(16)))
-	return NULL;
-
-    cli_md5_init(&ctx);
-
-    while((bytes = cli_readn(desc, buff, FILEBUFF)))
-	cli_md5_update(&ctx, buff, bytes);
-
-    cli_md5_final(digest, &ctx);
-
-    return digest;
-}
-
-char *cli_md5stream(FILE *fs, unsigned char *digcpy)
+/*
+ * Type: 1 = MD5, 2 = SHA1, 3 = SHA256
+ */
+char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type)
 {
-	unsigned char digest[16];
+	unsigned char digest[32];
 	char buff[FILEBUFF];
-	cli_md5_ctx ctx;
-	char *md5str, *pt;
-	int i, bytes;
-
+	cli_md5_ctx md5;
+	SHA1Context sha1;
+	SHA256_CTX sha256;
+	char *hashstr, *pt;
+	int i, bytes, size;
 
-    cli_md5_init(&ctx);
 
-    while((bytes = fread(buff, 1, FILEBUFF, fs)))
-	cli_md5_update(&ctx, buff, bytes);
+    if(type == 1)
+	cli_md5_init(&md5);
+    else if(type == 2)
+	SHA1Init(&sha1);
+    else
+	sha256_init(&sha256);
+
+    while((bytes = fread(buff, 1, FILEBUFF, fs))) {
+	if(type == 1)
+	    cli_md5_update(&md5, buff, bytes);
+	else if(type == 2)
+	    SHA1Update(&sha1, buff, bytes);
+	else
+	    sha256_update(&sha256, buff, bytes);
+    }
 
-    cli_md5_final(digest, &ctx);
+    if(type == 1) {
+	cli_md5_final(digest, &md5);
+	size = 16;
+    } else if(type == 2) {
+	SHA1Final(&sha1, digest);
+	size = 20;
+    } else {
+	sha256_final(&sha256, digest);
+	size = 32;
+    }
 
-    if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
+    if(!(hashstr = (char *) cli_calloc(size*2 + 1, sizeof(char))))
 	return NULL;
 
-    pt = md5str;
-    for(i = 0; i < 16; i++) {
+    pt = hashstr;
+    for(i = 0; i < size; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
 
     if(digcpy)
-	memcpy(digcpy, digest, 16);
+	memcpy(digcpy, digest, size);
 
-    return md5str;
+    return hashstr;
 }
 
-char *cli_md5file(const char *filename)
+char *cli_hashfile(const char *filename, int type)
 {
 	FILE *fs;
-	char *md5str;
+	char *hashstr;
 
 
     if((fs = fopen(filename, "rb")) == NULL) {
-	cli_errmsg("cli_md5file(): Can't read file %s\n", filename);
+	cli_errmsg("cli_hashfile(): Can't open file %s\n", filename);
 	return NULL;
     }
 
-    md5str = cli_md5stream(fs, NULL);
-    fclose(fs);
+    hashstr = cli_hashstream(fs, NULL, type);
 
-    return md5str;
+    fclose(fs);
+    return hashstr;
 }
 
 /* Function: unlink
diff --git a/libclamav/others.h b/libclamav/others.h
index 308b8c9..b441bd5 100644
--- a/libclamav/others.h
+++ b/libclamav/others.h
@@ -53,7 +53,7 @@
  * in re-enabling affected modules.
  */
 
-#define CL_FLEVEL 58
+#define CL_FLEVEL 60
 #define CL_FLEVEL_DCONF	CL_FLEVEL
 #define CL_FLEVEL_SIGTOOL CL_FLEVEL
 
@@ -120,6 +120,7 @@ typedef struct cli_ctx_tag {
     unsigned int corrupted_input;
     cli_file_t container_type; /* FIXME: to be made into a stack or array - see bb#1579 & bb#1293 */
     size_t container_size;
+    unsigned char handlertype_hash[16];
     struct cli_dconf *dconf;
     fmap_t **fmap;
     bitset_t* hook_lsig_matches;
@@ -207,14 +208,13 @@ struct cl_engine {
     /* Roots table */
     struct cli_matcher **root;
 
-    /* B-M matcher for standard MD5 sigs */
-    struct cli_matcher *md5_hdb;
+    /* hash matcher for standard MD5 sigs */
+    struct cli_matcher *hm_hdb;
+    /* hash matcher for MD5 sigs for PE sections */
+    struct cli_matcher *hm_mdb;
+    /* hash matcher for whitelist db */
+    struct cli_matcher *hm_fp;
 
-    /* B-M matcher for MD5 sigs for PE sections */
-    struct cli_matcher *md5_mdb;
-
-    /* B-M matcher for whitelist db */
-    struct cli_matcher *md5_fp;
 
     /* Container metadata */
     struct cli_cdb *cdb;
@@ -253,9 +253,7 @@ struct cl_engine {
     clcb_post_scan cb_post_scan;
     clcb_sigload cb_sigload;
     void *cb_sigload_ctx;
-    clcb_msg cb_msg;
     clcb_hash cb_hash;
-    enum cl_msg cb_msg_minseverity;
 
     /* Used for bytecode */
     struct cli_all_bc bcs;
@@ -283,7 +281,18 @@ struct cl_settings {
     uint32_t maxfiles;
     uint32_t min_cc_count;
     uint32_t min_ssn_count;
+    enum bytecode_security bytecode_security;
+    uint32_t bytecode_timeout;
+    enum bytecode_mode bytecode_mode;
     char *pua_cats;
+
+    /* callbacks */
+    clcb_pre_scan cb_pre_scan;
+    clcb_post_scan cb_post_scan;
+    clcb_sigload cb_sigload;
+    void *cb_sigload_ctx;
+    clcb_msg cb_msg;
+    clcb_hash cb_hash;
 };
 
 extern int (*cli_unrar_open)(int fd, const char *dirname, unrar_state_t *state);
@@ -498,9 +507,8 @@ void *cli_realloc(void *ptr, size_t size);
 void *cli_realloc2(void *ptr, size_t size);
 char *cli_strdup(const char *s);
 int cli_rmdirs(const char *dirname);
-unsigned char *cli_md5digest(int desc);
-char *cli_md5stream(FILE *fs, unsigned char *digcpy);
-char *cli_md5file(const char *filename);
+char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type);
+char *cli_hashfile(const char *filename, int type);
 int cli_unlink(const char *pathname);
 int cli_readn(int fd, void *buff, unsigned int count);
 int cli_writen(int fd, const void *buff, unsigned int count);
diff --git a/libclamav/others_common.c b/libclamav/others_common.c
index 1cc2a93..b721d7f 100644
--- a/libclamav/others_common.c
+++ b/libclamav/others_common.c
@@ -111,6 +111,10 @@ static inline void *cli_getctx(void)
 {
     return current_ctx ? current_ctx->cb_ctx : NULL;
 }
+
+void cli_logg_unsetup(void)
+{
+}
 #endif
 uint8_t cli_debug_flag = 0;
 
diff --git a/libclamav/pdf.c b/libclamav/pdf.c
index ae9d03c..0b5e156 100644
--- a/libclamav/pdf.c
+++ b/libclamav/pdf.c
@@ -114,7 +114,7 @@ static int find_stream_bounds(const char *start, off_t bytesleft, off_t byteslef
     if ((q2 = cli_memstr(start, bytesleft, "stream", 6))) {
 	q2 += 6;
 	bytesleft -= q2 - start;
-	if (bytesleft < 1)
+	if (bytesleft < 0)
 	    return 0;
 	if (bytesleft >= 2 && q2[0] == '\xd' && q2[1] == '\xa')
 	    q2 += 2;
@@ -122,7 +122,7 @@ static int find_stream_bounds(const char *start, off_t bytesleft, off_t byteslef
 	    q2++;
 	*stream = q2 - start;
 	bytesleft2 -= q2 - start;
-	if (bytesleft2 < 0)
+	if (bytesleft2 <= 0)
 	    return 0;
 	q = q2;
 	q2 = cli_memstr(q, bytesleft2, "endstream", 9);
@@ -1761,11 +1761,12 @@ static int asciihexdecode(const char *buf, off_t len, char *output)
 	    continue;
 	if (buf[i] == '>')
 	    break;
-	if (cli_hex2str_to(buf+i, output+j++, 2) == -1) {
+	if (cli_hex2str_to(buf+i, output+j, 2) == -1) {
 	    if (len - i < 4)
 		continue;
 	    return -1;
 	}
+	j++;
 	i++;
     }
     return j;
@@ -1831,7 +1832,7 @@ ascii85decode(const char *buf, off_t len, unsigned char *output)
 
 				if(quintet > 1)
 					sum += (0xFFFFFF >> ((quintet - 2) * 8));
-				ret += quintet;
+				ret += quintet-1;
 				for(i = 0; i < quintet - 1; i++)
 					*output++ = (unsigned char)((sum >> (24 - 8 * i)) & 0xFF);
 			}
diff --git a/libclamav/pe.c b/libclamav/pe.c
index abcb2cd..76eb87a 100644
--- a/libclamav/pe.c
+++ b/libclamav/pe.c
@@ -54,7 +54,7 @@
 #include "mew.h"
 #include "upack.h"
 #include "matcher.h"
-#include "matcher-md5.h"
+#include "matcher-hash.h"
 #include "disasm.h"
 #include "special.h"
 #include "ishield.h"
@@ -1000,24 +1000,20 @@ int cli_scanpe(cli_ctx *ctx)
 	    if(SCAN_ALGO && (DCONF & PE_CONF_POLIPOS) && !*sname && exe_sections[i].vsz > 40000 && exe_sections[i].vsz < 70000 && exe_sections[i].chr == 0xe0000060) polipos = i;
 
 	    /* check MD5 section sigs */
-	    md5_sect = ctx->engine->md5_mdb;
+	    md5_sect = ctx->engine->hm_mdb;
 	    if((DCONF & PE_CONF_MD5SECT) && md5_sect) {
-		found = 0;
-		for(j = 0; j < md5_sect->soff_len && md5_sect->soff[j] <= exe_sections[i].rsz; j++) {
-		    if(md5_sect->soff[j] == exe_sections[i].rsz) {
-			unsigned char md5_dig[16];
-			const struct cli_md5m_patt *patt;
-			if(cli_md5sect(map, &exe_sections[i], md5_dig) && cli_md5m_scan(md5_dig, exe_sections[i].rsz, ctx->virname, ctx->engine->md5_mdb) == CL_VIRUS) {
-			    if(cli_md5m_scan(md5_dig, fsize, NULL, ctx->engine->md5_fp) != CL_VIRUS) {
-				free(section_hdr);
-				free(exe_sections);
-				return CL_VIRUS;
-			    }
-			}
-			break;
+		unsigned char md5_dig[16];
+		if(cli_hm_have_size(md5_sect, CLI_HASH_MD5, exe_sections[i].rsz) && 
+		   cli_md5sect(map, &exe_sections[i], md5_dig) &&
+		   cli_hm_scan(md5_dig, exe_sections[i].rsz, ctx->virname, md5_sect, CLI_HASH_MD5) == CL_VIRUS) {
+		    if(cli_hm_scan(md5_dig, fsize, NULL, ctx->engine->hm_fp, CLI_HASH_MD5) != CL_VIRUS) {
+			free(section_hdr);
+			free(exe_sections);
+			return CL_VIRUS;
 		    }
 		}
 	    }
+
 	}
 
 	if (exe_sections[i].urva>>31 || exe_sections[i].uvsz>>31 || (exe_sections[i].rsz && exe_sections[i].uraw>>31) || exe_sections[i].ursz>>31) {
diff --git a/libclamav/phishcheck.c b/libclamav/phishcheck.c
index 30e7865..71c0ea3 100644
--- a/libclamav/phishcheck.c
+++ b/libclamav/phishcheck.c
@@ -1205,6 +1205,7 @@ static int hash_match(const struct regex_matcher *rlist, const char *host, size_
 	    }
 	    h[64]='\0';
 	    cli_dbgmsg("Looking up hash %s for %s(%u)%s(%u)\n", h, host, (unsigned)hlen, path, (unsigned)plen);
+#if 0
 	    if (prefix_matched) {
 		if (cli_bm_scanbuff(sha256_dig, 4, &virname, NULL, &rlist->hostkey_prefix,0,NULL,NULL) == CL_VIRUS) {
 		    cli_dbgmsg("prefix matched\n");
@@ -1212,6 +1213,7 @@ static int hash_match(const struct regex_matcher *rlist, const char *host, size_
 		} else
 		    return CL_SUCCESS;
 	    }
+#endif
 	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, NULL, &rlist->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
 		cli_dbgmsg("This hash matched: %s\n", h);
 		switch(*virname) {
@@ -1408,6 +1410,7 @@ static int url_hash_match(const struct regex_matcher *rlist, const char *inurl,
 		    return rc;
 		}
 		count++;
+#if 0
 		if (count == 2 && !prefix_matched && rlist->hostkey_prefix.bm_patterns) {
 		    /* if hostkey is not matched, don't bother calculating
 		     * hashes for other parts of the URL, they are not in the DB
@@ -1415,6 +1418,7 @@ static int url_hash_match(const struct regex_matcher *rlist, const char *inurl,
 		    cli_dbgmsg("hostkey prefix not matched, short-circuiting lookups\n");
 		    return CL_SUCCESS;
 		}
+#endif
 	    }
 	}
 	return CL_SUCCESS;
diff --git a/libclamav/readdb.c b/libclamav/readdb.c
index 2dab42e..8b9ddd4 100644
--- a/libclamav/readdb.c
+++ b/libclamav/readdb.c
@@ -46,7 +46,7 @@
 #endif
 #include "matcher-ac.h"
 #include "matcher-bm.h"
-#include "matcher-md5.h"
+#include "matcher-hash.h"
 #include "matcher.h"
 #include "others.h"
 #include "str.h"
@@ -399,6 +399,11 @@ char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio)
 		dbio->bread += bread;
 		sha256_update(&dbio->sha256ctx, dbio->readpt, bread);
 	    }
+	    if(dbio->chkonly && dbio->bufpt) {
+		dbio->bufpt = NULL;
+		dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
+		continue;
+	    }
 	    nl = strchr(dbio->bufpt, '\n');
 	    if(nl) {
 		if(nl - dbio->bufpt >= size) {
@@ -1860,52 +1865,16 @@ static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options,
 #define MD5_MDB	    1
 #define MD5_FP	    2
 
-static int cli_md5db_init(struct cl_engine *engine, unsigned int mode)
-{
-	struct cli_matcher *bm = NULL;
-	int ret;
-
-
-    if(mode == MD5_HDB) {
-	bm = engine->md5_hdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
-    } else if(mode == MD5_MDB) {
-	bm = engine->md5_mdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
-    } else {
-	bm = engine->md5_fp = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
-    }
-
-    if(!bm)
-	return CL_EMEM;
-#ifdef USE_MPOOL
-    bm->mempool = engine->mempool;
-#endif
-    if((ret = cli_md5m_init(bm))) {
-	cli_errmsg("cli_md5db_init: Failed to initialize MD5 matcher\n");
-	return ret;
-    }
-
-    return CL_SUCCESS;
-}
-
-#define MD5_DB			    \
-    if(mode == MD5_HDB)		    \
-	db = engine->md5_hdb;    \
-    else if(mode == MD5_MDB)	    \
-	db = engine->md5_mdb;    \
-    else			    \
-	db = engine->md5_fp;
-
-#define MD5_TOKENS 3
-static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname)
+#define MD5_TOKENS 5
+static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname)
 {
 	const char *tokens[MD5_TOKENS + 1];
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
-	const char *pt;
-	unsigned char *md5;
+	const char *pt, *virname;
 	int ret = CL_SUCCESS;
 	unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
-	struct cli_md5m_patt *new;
 	struct cli_matcher *db = NULL;
+	unsigned long size;
 
 
     if(mode == MD5_MDB) {
@@ -1924,12 +1893,30 @@ static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo,
 	    strcpy(buffer_cpy, buffer);
 
 	tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens);
-	if(tokens_count != MD5_TOKENS) {
+	if(tokens_count < 3) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
-	if(!cli_isnumber(tokens[size_field])) {
-	    cli_errmsg("cli_loadmd5: Invalid value for the size field\n");
+	if(tokens_count > MD5_TOKENS - 2) {
+	    unsigned int req_fl = atoi(tokens[MD5_TOKENS - 2]);
+
+	    if(tokens_count > MD5_TOKENS) {
+		ret = CL_EMALFDB;
+		break;
+	    }
+
+	    if(cl_retflevel() < req_fl)
+		continue;
+	    if(tokens_count == MD5_TOKENS) {
+		req_fl = atoi(tokens[MD5_TOKENS - 1]);
+		if(cl_retflevel() > req_fl)
+		    continue;
+	    }
+	}
+
+	size = strtol(tokens[size_field], (char **)&pt, 10);
+	if(*pt || !size || size >= 0xffffffff) {
+	    cli_errmsg("cli_loadhash: Invalid value for the size field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
@@ -1949,71 +1936,58 @@ static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo,
 	    else
 		dot++;
 	    if(engine->cb_sigload(dot, pt, engine->cb_sigload_ctx)) {
-		cli_dbgmsg("cli_loadmd5: skipping %s due to callback\n", pt);
+		cli_dbgmsg("cli_loadhash: skipping %s (%s) due to callback\n", pt, dot);
 	        continue;
 	    }
 	}
 
-	new = (struct cli_md5m_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_md5m_patt));
-	if(!new) {
-	    ret = CL_EMEM;
-	    break;
-	}
-
-	pt = tokens[md5_field]; /* md5 */
-	if(strlen(pt) != 32 || !(md5 = (unsigned char *) cli_mpool_hex2str(engine->mempool, pt))) {
-	    cli_errmsg("cli_loadmd5: Malformed MD5 string at line %u\n", line);
-	    mpool_free(engine->mempool, new);
+	virname = cli_mpool_virname(engine->mempool, pt, options & CL_DB_OFFICIAL);
+	if(!virname) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
-	memcpy(new->md5, md5, 16);
-	mpool_free(engine->mempool, md5);
 
-	new->filesize = atoi(tokens[size_field]);
-
-	new->virname = cli_mpool_virname(engine->mempool, tokens[2], options & CL_DB_OFFICIAL);
-	if(!new->virname) {
-	    mpool_free(engine->mempool, new);
-	    ret = CL_EMALFDB;
-	    break;
-	}
+	if(mode == MD5_HDB)
+	    db = engine->hm_hdb;
+	else if(mode == MD5_MDB)
+	    db = engine->hm_mdb;
+	else
+	    db = engine->hm_fp;
 
-	MD5_DB;
-	if(!db && (ret = cli_md5db_init(engine, mode))) {
-	    mpool_free(engine->mempool, new->virname);
-	    mpool_free(engine->mempool, new);
-	    break;
-	} else {
-	    MD5_DB;
+	if(!db) {
+	    if(!(db = mpool_calloc(engine->mempool, 1, sizeof(*db)))) {
+		ret = CL_EMEM;
+		break;
+	    }
+#ifdef USE_MPOOL
+	    db->mempool = engine->mempool;
+#endif
+	    if(mode == MD5_HDB)
+		engine->hm_hdb = db;
+	    else if(mode == MD5_MDB)
+		engine->hm_mdb = db;
+	    else
+		engine->hm_fp = db;
 	}
 
-	if((ret = cli_md5m_addpatt(db, new))) {
-	    cli_errmsg("cli_loadmd5: Error adding BM pattern\n");
-	    mpool_free(engine->mempool, new->virname);
-	    mpool_free(engine->mempool, new);
+	if((ret = hm_addhash(db, tokens[md5_field], size, virname))) {
+	    cli_errmsg("cli_loadhash: Malformed MD5 string at line %u\n", line);
+	    mpool_free(engine->mempool, (void *)virname);
 	    break;
 	}
 
-	if(mode == MD5_MDB) { /* section MD5 */
-	    if(!db->md5_sizes_hs.capacity) {
-		cli_hashset_init_pool(&db->md5_sizes_hs, 65536, 80, engine->mempool);
-	    }
-	    cli_hashset_addkey(&db->md5_sizes_hs, new->filesize);
-	}
-
 	sigs++;
     }
     if(engine->ignored)
 	free(buffer_cpy);
 
     if(!line) {
-	cli_errmsg("cli_loadmd5: Empty database file\n");
+	cli_errmsg("cli_loadhash: Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
-	cli_errmsg("cli_loadmd5: Problem parsing database at line %u\n", line);
+	cli_errmsg("cli_loadhash: Problem parsing database at line %u\n", line);
 	return ret;
     }
 
@@ -2360,8 +2334,14 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
 	int ret = CL_SUCCESS;
 	uint8_t skipped = 0;
 	const char *dbname;
+	char buff[FILEBUFF];
 
 
+    if(dbio && dbio->chkonly) {
+	while(cli_dbgets(buff, FILEBUFF, NULL, dbio));
+	return CL_SUCCESS;
+    }
+
     if(!dbio && (fs = fopen(filename, "rb")) == NULL) {
 	if(options & CL_DB_DIRECTORY) { /* bb#1624 */
 	    if(access(filename, R_OK)) {
@@ -2384,29 +2364,27 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
 
     } else if(cli_strbcasestr(dbname, ".cvd")) {
-	ret = cli_cvdload(fs, engine, signo, options, 0, filename);
+	ret = cli_cvdload(fs, engine, signo, options, 0, filename, 0);
 
     } else if(cli_strbcasestr(dbname, ".cld")) {
-	ret = cli_cvdload(fs, engine, signo, options, 1, filename);
-
-    } else if(cli_strbcasestr(dbname, ".hdb")) {
-	ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options, dbio, dbname);
+	ret = cli_cvdload(fs, engine, signo, options, 1, filename, 0);
 
-    } else if(cli_strbcasestr(dbname, ".hdu")) {
+    } else if(cli_strbcasestr(dbname, ".hdb") || cli_strbcasestr(dbname, ".hsb")) {
+	ret = cli_loadhash(fs, engine, signo, MD5_HDB, options, dbio, dbname);
+    } else if(cli_strbcasestr(dbname, ".hdu") || cli_strbcasestr(dbname, ".hsu")) {
 	if(options & CL_DB_PUA)
-	    ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
+	    ret = cli_loadhash(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
 	else
 	    skipped = 1;
 
-    } else if(cli_strbcasestr(dbname, ".fp")) {
-	ret = cli_loadmd5(fs, engine, signo, MD5_FP, options, dbio, dbname);
+    } else if(cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".sfp")) {
+	ret = cli_loadhash(fs, engine, signo, MD5_FP, options, dbio, dbname);
+    } else if(cli_strbcasestr(dbname, ".mdb") || cli_strbcasestr(dbname, ".msb")) {
+	ret = cli_loadhash(fs, engine, signo, MD5_MDB, options, dbio, dbname);
 
-    } else if(cli_strbcasestr(dbname, ".mdb")) {
-	ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options, dbio, dbname);
-
-    } else if(cli_strbcasestr(dbname, ".mdu")) {
+    } else if(cli_strbcasestr(dbname, ".mdu") || cli_strbcasestr(dbname, ".msu")) {
 	if(options & CL_DB_PUA)
-	    ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
+	    ret = cli_loadhash(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
 	else
 	    skipped = 1;
 
@@ -2468,7 +2446,6 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
 
     } else if(cli_strbcasestr(dbname, ".cdb")) {
     	ret = cli_loadcdb(fs, engine, signo, options, dbio);
-
     } else {
 	cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
@@ -2945,22 +2922,18 @@ int cl_engine_free(struct cl_engine *engine)
 	mpool_free(engine->mempool, engine->root);
     }
 
-    if((root = engine->md5_hdb)) {
-	cli_md5m_free(root);
+    if((root = engine->hm_hdb)) {
+	hm_free(root);
 	mpool_free(engine->mempool, root);
     }
 
-    if((root = engine->md5_mdb)) {
-	cli_md5m_free(root);
-	mpool_free(engine->mempool, root->soff);
-	if(root->md5_sizes_hs.capacity) {
-		cli_hashset_destroy(&root->md5_sizes_hs);
-	}
+    if((root = engine->hm_mdb)) {
+	hm_free(root);
 	mpool_free(engine->mempool, root);
     }
 
-    if((root = engine->md5_fp)) {
-	cli_md5m_free(root);
+    if((root = engine->hm_fp)) {
+	hm_free(root);
 	mpool_free(engine->mempool, root);
     }
 
@@ -3045,29 +3018,6 @@ int cl_engine_free(struct cl_engine *engine)
     return CL_SUCCESS;
 }
 
-static void cli_md5db_build(struct cli_matcher* root)
-{
-	if(root && root->md5_sizes_hs.capacity) {
-		/* TODO: use hashset directly, instead of the array when matching*/
-		cli_dbgmsg("Converting hashset to array: %u entries\n", root->md5_sizes_hs.count);
-
-#ifdef USE_MPOOL
-		{
-		uint32_t *mpoolht;
-		unsigned int mpoolhtsz = root->md5_sizes_hs.count * sizeof(*mpoolht);
-		root->soff = mpool_malloc(root->mempool, mpoolhtsz);
-		root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &mpoolht);
-		memcpy(root->soff, mpoolht, mpoolhtsz);
-		free(mpoolht);
-		}
-#else
-		root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &root->soff);
-#endif
-		cli_hashset_destroy(&root->md5_sizes_hs);
-		cli_qsort(root->soff, root->soff_len, sizeof(uint32_t), NULL);
-	}
-}
-
 int cl_engine_compile(struct cl_engine *engine)
 {
 	unsigned int i;
@@ -3089,11 +3039,14 @@ int cl_engine_compile(struct cl_engine *engine)
 	    cli_dbgmsg("Matcher[%u]: %s: AC sigs: %u (reloff: %u, absoff: %u) BM sigs: %u (reloff: %u, absoff: %u) maxpatlen %u %s\n", i, cli_mtargets[i].name, root->ac_patterns, root->ac_reloff_num, root->ac_absoff_num, root->bm_patterns, root->bm_reloff_num, root->bm_absoff_num, root->maxpatlen, root->ac_only ? "(ac_only mode)" : "");
 	}
     }
-    if(engine->md5_hdb)
-	cli_dbgmsg("MD5 sigs (files): %u\n", engine->md5_hdb->md5_patterns);
+    if(engine->hm_hdb)
+	hm_flush(engine->hm_hdb);
+
+    if(engine->hm_mdb)
+	hm_flush(engine->hm_mdb);
 
-    if(engine->md5_mdb)
-	cli_dbgmsg("MD5 sigs (PE sections): %u\n", engine->md5_mdb->md5_patterns);
+    if(engine->hm_fp)
+	hm_flush(engine->hm_fp);
 
     if((ret = cli_build_regex_list(engine->whitelist_matcher))) {
 	    return ret;
@@ -3101,7 +3054,6 @@ int cl_engine_compile(struct cl_engine *engine)
     if((ret = cli_build_regex_list(engine->domainlist_matcher))) {
 	    return ret;
     }
-    cli_md5db_build(engine->md5_mdb);
     if(engine->ignored) {
 	cli_bm_free(engine->ignored);
 	mpool_free(engine->mempool, engine->ignored);
diff --git a/libclamav/readdb.h b/libclamav/readdb.h
index d8b48de..db9d081 100644
--- a/libclamav/readdb.h
+++ b/libclamav/readdb.h
@@ -37,6 +37,11 @@
 	cli_strbcasestr(ext, ".fp")    ||	\
 	cli_strbcasestr(ext, ".mdb")   ||	\
 	cli_strbcasestr(ext, ".mdu")   ||	\
+	cli_strbcasestr(ext, ".hsb")   ||	\
+	cli_strbcasestr(ext, ".hsu")   ||	\
+	cli_strbcasestr(ext, ".sfp")   ||	\
+	cli_strbcasestr(ext, ".msb")   ||	\
+	cli_strbcasestr(ext, ".msu")   ||	\
 	cli_strbcasestr(ext, ".ndb")   ||	\
 	cli_strbcasestr(ext, ".ndu")   ||	\
 	cli_strbcasestr(ext, ".ldb")   ||	\
diff --git a/libclamav/scanners.c b/libclamav/scanners.c
index 4aa5308..85c2dbb 100644
--- a/libclamav/scanners.c
+++ b/libclamav/scanners.c
@@ -1112,9 +1112,9 @@ static int cli_scanscript(cli_ctx *ctx)
 	}
 	free(normalized);
 	if(ret != CL_VIRUS) {
-	    ret = cli_lsig_eval(ctx, troot, &tmdata, NULL);
+	    ret = cli_lsig_eval(ctx, troot, &tmdata, NULL, NULL);
 	    if(ret != CL_VIRUS)
-		ret = cli_lsig_eval(ctx, groot, &gmdata, NULL);
+		ret = cli_lsig_eval(ctx, groot, &gmdata, NULL, NULL);
 	}
 	cli_ac_freedata(&tmdata);
 	cli_ac_freedata(&gmdata);
diff --git a/libclamav/sha1.h b/libclamav/sha1.h
index 7a6beb7..8ccb79c 100644
--- a/libclamav/sha1.h
+++ b/libclamav/sha1.h
@@ -29,13 +29,7 @@
 #ifndef _SHA1_H
 #define _SHA1_H
 
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#else
-# if HAVE_STDINT_H
-#  include <stdint.h>
-# endif
-#endif
+#include "cltypes.h"
 
 #define SHA1_HASH_SIZE 20
 
diff --git a/libclamav/sis.c b/libclamav/sis.c
index 99fd705..a3d219a 100644
--- a/libclamav/sis.c
+++ b/libclamav/sis.c
@@ -506,7 +506,6 @@ static int real_scansis(FILE *f, cli_ctx *ctx, const char *tmpd) {
 	    free(decomp);
 	    free(ptrs);
 	    free(alangs);
-	    close(fd);
 	    return CL_EWRITE;
 	  }
 	  free(decomp);
diff --git a/libclamav/vba_extract.c b/libclamav/vba_extract.c
index 062ec20..8de1058 100644
--- a/libclamav/vba_extract.c
+++ b/libclamav/vba_extract.c
@@ -221,8 +221,10 @@ vba_read_project_strings(int fd, int big_endian)
 		free(name);
 
 		if(!read_uint16(fd, &length, big_endian)) {
-			if(buf)
+			if(buf) {
 				free(buf);
+				buf = NULL;
+			}
 			break;
 		}
 
diff --git a/libltdl/Makefile.in b/libltdl/Makefile.in
index c079797..e09f467 100644
--- a/libltdl/Makefile.in
+++ b/libltdl/Makefile.in
@@ -209,6 +209,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/shared/cdiff.c b/shared/cdiff.c
index f6d29bd..927cf80 100644
--- a/shared/cdiff.c
+++ b/shared/cdiff.c
@@ -995,8 +995,16 @@ int cdiff_apply(int fd, unsigned short mode)
 			logg("!cdiff_apply: Can't resize line buffer to %d bytes\n", line_size);
 			cdiff_ctx_free(&ctx);
 			fclose(fh);
-			free(line);
-			free(lbuf);
+			if(!r1 && !r2) {
+			    free(line);
+			    free(lbuf);
+			} else if(!r1) {
+			    free(line);
+			    free(r2);
+			} else {
+			    free(r1);
+			    free(lbuf);
+			}
 			return -1;
 		    }
 		    line = r1;
diff --git a/shared/optparser.c b/shared/optparser.c
index a22b4c7..68a47b2 100644
--- a/shared/optparser.c
+++ b/shared/optparser.c
@@ -82,6 +82,8 @@ const struct clam_option __clam_options[] = {
     { NULL, "stream", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMDSCAN, "", "" },
     { NULL, "database", 'd', TYPE_STRING, NULL, -1, DATADIR, FLAG_REQUIRED | FLAG_MULTIPLE, OPT_CLAMSCAN, "", "" }, /* merge it with DatabaseDirectory (and fix conflict with --datadir */
     { NULL, "recursive", 'r', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", "" },
+    { NULL, "follow-dir-symlinks", 0, TYPE_NUMBER, MATCH_NUMBER, 1, NULL, 0, OPT_CLAMSCAN, "", "" },
+    { NULL, "follow-file-symlinks", 0, TYPE_NUMBER, MATCH_NUMBER, 1, NULL, 0, OPT_CLAMSCAN, "", "" },
     { NULL, "bell", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", "" },
     { NULL, "no-summary", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN | OPT_CLAMDSCAN, "", "" },
     { NULL, "file-list", 'f', TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMSCAN | OPT_CLAMDSCAN, "", "" },
@@ -96,6 +98,8 @@ const struct clam_option __clam_options[] = {
     { NULL, "structured-ssn-format", 0, TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMSCAN, "", "" },
     { NULL, "hex-dump", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", "" },
     { NULL, "md5", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", "" },
+    { NULL, "sha1", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", "" },
+    { NULL, "sha256", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", "" },
     { NULL, "mdb", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", "" },
     { NULL, "html-normalise", 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_SIGTOOL, "", "" },
     { NULL, "utf16-decode", 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_SIGTOOL, "", "" },
@@ -200,7 +204,7 @@ const struct clam_option __clam_options[] = {
     /* FIXME: add a regex for IP addr */
     { "TCPAddr", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD, "By default clamd binds to INADDR_ANY.\nThis option allows you to restrict the TCP address and provide\nsome degree of protection from the outside world.", "127.0.0.1" },
 
-    { "MaxConnectionQueueLength", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 15, NULL, 0, OPT_CLAMD, "Maximum length the queue of pending connections may grow to.", "30" },
+    { "MaxConnectionQueueLength", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 200, NULL, 0, OPT_CLAMD, "Maximum length the queue of pending connections may grow to.", "30" },
 
     { "StreamMaxLength", NULL, 0, TYPE_SIZE, MATCH_SIZE, CLI_DEFAULT_MAXFILESIZE, NULL, 0, OPT_CLAMD, "Close the STREAM session when the data size limit is exceeded.\nThe value should match your MTA's limit for the maximum attachment size.", "25M" },
 
@@ -252,8 +256,8 @@ const struct clam_option __clam_options[] = {
     { "Bytecode", "bytecode", 0, TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes" },
     { "BytecodeSecurity", NULL, 0, TYPE_STRING, "^(None|TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD, 
 	"Set bytecode security level.\nPossible values:\n\tNone - no security at all, meant for debugging. DO NOT USE THIS ON PRODUCTION SYSTEMS\n\tTrustSigned - trust bytecode loaded from signed .c[lv]d files,\n\t\t insert runtime safety checks for bytecode loaded from other sources\n\tParanoid - don't trust any bytecode, insert runtime checks for all\nRecommended: TrustSigned, because bytecode in .cvd files already has these checks\n","TrustSigned"},
-    { "BytecodeTimeout", "bytecode-timeout", 0, TYPE_NUMBER, MATCH_NUMBER, 60000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
-	"Set bytecode timeout in miliseconds.\n","60000"},
+    { "BytecodeTimeout", "bytecode-timeout", 0, TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
+	"Set bytecode timeout in miliseconds.\n","5000"},
     { "BytecodeMode", "bytecode-mode", 0, TYPE_STRING, "^(Auto|ForceJIT|ForceInterpreter|Test)$", -1, "Auto", FLAG_REQUIRED, OPT_CLAMD | OPT_CLAMSCAN,
 	"Set bytecode execution mode.\nPossible values:\n\tAuto - automatically choose JIT if possible, fallback to interpreter\nForceJIT - always choose JIT, fail if not possible\nForceIntepreter - always choose interpreter\nTest - run with both JIT and interpreter and compare results. Make all failures fatal\n","Auto"},
     { "DetectPUA", "detect-pua", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Detect Potentially Unwanted Applications.", "yes" },
@@ -445,6 +449,8 @@ const struct clam_option __clam_options[] = {
 
     { "LogInfected", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "This option allows to tune what is logged when a message is infected.\nPossible values are Off (the default - nothing is logged),\nBasic (minimal info logged), Full (verbose info logged)\nNote:\nFor this to work properly in sendmail, make sure the msg_id, mail_addr,\nrcpt_addr and i macroes are available in eom. In other words add a line like:\nMilter.macros.eom={msg_id}, {mail_addr}, {rcpt_addr}, i\nto your .cf file. Alternatively use the macro:\ndefine(`confMILTER_MACROS_EOM', `{msg_id}, {mail_addr}, {rcpt_addr}, i')\nPostfix should be working fine with the default settings.", "Basic" },
 
+    { "LogClean", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "This option allows to tune what is logged when no threat is found in a scanned message.\nSee LogInfected for possible values and caveats.\nUseful in debugging but drastically increases the log size.", "Basic" },
+
     /* Deprecated milter options */
 
     { "ArchiveBlockEncrypted", NULL, 0, TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", "" },
@@ -1003,7 +1009,7 @@ struct optstruct *optparse(const char *cfgfile, int argc, char **argv, int verbo
 		    lnumarg = UINT_MAX;
 		}
 
-		numarg = lnumarg;
+		numarg = lnumarg ? lnumarg : UINT_MAX;
 		break;
 
 	    case TYPE_BOOL:
diff --git a/sigtool/Makefile.in b/sigtool/Makefile.in
index be3bc52..24b626f 100644
--- a/sigtool/Makefile.in
+++ b/sigtool/Makefile.in
@@ -134,6 +134,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/sigtool/sigtool.c b/sigtool/sigtool.c
index 7d2db91..c42ad17 100644
--- a/sigtool/sigtool.c
+++ b/sigtool/sigtool.c
@@ -88,8 +88,12 @@ static const struct dblist_s {
     { "main.db",    1 },    { "daily.db",   1 },
     { "main.hdb",   1 },    { "daily.hdb",  1 },
     { "main.hdu",   1 },    { "daily.hdu",  1 },
+    { "main.hsb",   1 },    { "daily.hsb",  1 },
+    { "main.hsu",   1 },    { "daily.hsu",  1 },
     { "main.mdb",   1 },    { "daily.mdb",  1 },
     { "main.mdu",   1 },    { "daily.mdu",  1 },
+    { "main.msb",   1 },    { "daily.msb",  1 },
+    { "main.msu",   1 },    { "daily.msu",  1 },
     { "main.ndb",   1 },    { "daily.ndb",  1 },
     { "main.ndu",   1 },    { "daily.ndu",  1 },
     { "main.ldb",   1 },    { "daily.ldb",  1 },
@@ -99,6 +103,7 @@ static const struct dblist_s {
     { "main.rmd",   1 },    { "daily.rmd",  1 },
     { "main.idb",   0 },    { "daily.idb",  0 },
     { "main.fp",    0 },    { "daily.fp",   0 },
+    { "main.sfp",   0 },    { "daily.sfp",  0 },
     { "main.pdb",   1 },    { "daily.pdb",  1 },    { "safebrowsing.gdb", 1 },
     { "main.wdb",   0 },    { "daily.wdb",  0 },    { "safebrowsing.wdb", 0 },
 
@@ -149,9 +154,9 @@ static int hexdump(void)
     return 0;
 }
 
-static int md5sig(const struct optstruct *opts, unsigned int mdb)
+static int hashsig(const struct optstruct *opts, unsigned int mdb, int type)
 {
-	char *md5;
+	char *hash;
 	unsigned int i;
 	struct stat sb;
 
@@ -159,19 +164,19 @@ static int md5sig(const struct optstruct *opts, unsigned int mdb)
     if(opts->filename) {
 	for(i = 0; opts->filename[i]; i++) {
 	    if(stat(opts->filename[i], &sb) == -1) {
-		mprintf("!md5sig: Can't access file %s\n", opts->filename[i]);
-		perror("md5sig");
+		mprintf("!hashsig: Can't access file %s\n", opts->filename[i]);
+		perror("hashsig");
 		return -1;
 	    } else {
 		if((sb.st_mode & S_IFMT) == S_IFREG) {
-		    if((md5 = cli_md5file(opts->filename[i]))) {
+		    if((hash = cli_hashfile(opts->filename[i], type))) {
 			if(mdb)
-			    mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, md5, opts->filename[i]);
+			    mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, hash, opts->filename[i]);
 			else
-			    mprintf("%s:%u:%s\n", md5, (unsigned int) sb.st_size, opts->filename[i]);
-			free(md5);
+			    mprintf("%s:%u:%s\n", hash, (unsigned int) sb.st_size, opts->filename[i]);
+			free(hash);
 		    } else {
-			mprintf("!md5sig: Can't generate MD5 checksum for %s\n", opts->filename[i]);
+			mprintf("!hashsig: Can't generate hash for %s\n", opts->filename[i]);
 			return -1;
 		    }
 		}
@@ -179,13 +184,13 @@ static int md5sig(const struct optstruct *opts, unsigned int mdb)
 	}
 
     } else { /* stream */
-	md5 = cli_md5stream(stdin, NULL);
-	if(!md5) {
-	    mprintf("!md5sig: Can't generate MD5 checksum for input stream\n");
+	hash = cli_hashstream(stdin, NULL, type);
+	if(!hash) {
+	    mprintf("!hashsig: Can't generate hash for input stream\n");
 	    return -1;
 	}
-	mprintf("%s\n", md5);
-	free(md5);
+	mprintf("%s\n", hash);
+	free(hash);
     }
 
     return 0;
@@ -899,7 +904,7 @@ static int build(const struct optstruct *opts)
 	return -1;
     }
 
-    if(!(pt = cli_md5stream(fh, buffer))) {
+    if(!(pt = cli_hashstream(fh, buffer, 1))) {
 	mprintf("!build: Can't generate MD5 checksum for %s\n", tarfile);
 	fclose(fh);
 	unlink(tarfile);
@@ -1120,17 +1125,13 @@ static int cvdinfo(const struct optstruct *opts)
     if(cli_strbcasestr(pt, ".cvd")) {
 	mprintf("MD5: %s\n", cvd->md5);
 	mprintf("Digital signature: %s\n", cvd->dsig);
-	cl_cvdfree(cvd);
-	if((ret = cl_cvdverify(pt))) {
-	    mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
-	    return -1;
-	} else {
-	    mprintf("Verification OK.\n");
-	    return 0;
-	}
     }
-
     cl_cvdfree(cvd);
+    if((ret = cl_cvdverify(pt))) {
+	mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
+	return -1;
+    }
+    mprintf("Verification OK.\n");
     return 0;
 }
 
@@ -1564,12 +1565,12 @@ static int compare(const char *oldpath, const char *newpath, FILE *diff)
 {
 	FILE *old, *new;
 	char *obuff, *nbuff, *tbuff, *pt, *omd5, *nmd5;
-	unsigned int oline = 0, tline, found, i;
+	unsigned int oline = 0, tline, found, i, badxchg = 0;
 	int l1 = 0, l2;
 	long opos;
 
-    if(!access(oldpath, R_OK) && (omd5 = cli_md5file(oldpath))) {
-	if(!(nmd5 = cli_md5file(newpath))) {
+    if(!access(oldpath, R_OK) && (omd5 = cli_hashfile(oldpath, 1))) {
+	if(!(nmd5 = cli_hashfile(newpath, 1))) {
 	    mprintf("!compare: Can't get MD5 checksum of %s\n", newpath);
 	    free(omd5);
 	    return -1;
@@ -1675,6 +1676,10 @@ static int compare(const char *oldpath, const char *newpath, FILE *diff)
 			oline += tline;
 
 		    } else {
+			if(!*obuff || *obuff == ' ') {
+			    badxchg = 1;
+			    break;
+			}
 			obuff[MIN(16, l1-1)] = 0;
 			if((pt = strchr(obuff, ' ')))
 			    *pt = 0;
@@ -1688,23 +1693,34 @@ static int compare(const char *oldpath, const char *newpath, FILE *diff)
 	    }
 	}
     }
-    fclose(new);
 
     if(old) {
-	while(fgets(obuff, l1, old)) {
-	    oline++;
-	    obuff[MIN(16, l1-1)] = 0;
-	    if((pt = strchr(obuff, ' ')))
-		*pt = 0;
-	    fprintf(diff, "DEL %u %s\n", oline, obuff);
+	if(!badxchg) {
+	    while(fgets(obuff, l1, old)) {
+		oline++;
+		obuff[MIN(16, l1-1)] = 0;
+		if((pt = strchr(obuff, ' ')))
+		    *pt = 0;
+		fprintf(diff, "DEL %u %s\n", oline, obuff);
+	    }
 	}
 	fclose(old);
     }
-
     fprintf(diff, "CLOSE\n");
     free(obuff);
-    free(nbuff);
     free(tbuff);
+    if(badxchg) {
+	fprintf(diff, "UNLINK %s\n", newpath);
+	fprintf(diff, "OPEN %s\n", newpath);
+	rewind(new);
+	while(fgets(nbuff, l1, new)) {
+	    cli_chomp(nbuff);
+	    fprintf(diff, "ADD %s\n", nbuff);
+	}
+	fprintf(diff, "CLOSE\n");
+    }
+    free(nbuff);
+    fclose(new);
     return 0;
 }
 
@@ -2659,6 +2675,10 @@ static void help(void)
     mprintf("                                           string and print it on stdout\n");
     mprintf("    --md5 [FILES]                          generate MD5 checksum from stdin\n");
     mprintf("                                           or MD5 sigs for FILES\n");
+    mprintf("    --sha1 [FILES]                         generate SHA1 checksum from stdin\n");
+    mprintf("                                           or SHA1 sigs for FILES\n");
+    mprintf("    --sha256 [FILES]                       generate SHA256 checksum from stdin\n");
+    mprintf("                                           or SHA256 sigs for FILES\n");
     mprintf("    --mdb [FILES]                          generate .mdb sigs\n");
     mprintf("    --html-normalise=FILE                  create normalised parts of HTML file\n");
     mprintf("    --utf16-decode=FILE                    decode UTF16 encoded files\n");
@@ -2728,9 +2748,13 @@ int main(int argc, char **argv)
     if(optget(opts, "hex-dump")->enabled)
 	ret = hexdump();
     else if(optget(opts, "md5")->enabled)
-	ret = md5sig(opts, 0);
+	ret = hashsig(opts, 0, 1);
+    else if(optget(opts, "sha1")->enabled)
+	ret = hashsig(opts, 0, 2);
+    else if(optget(opts, "sha256")->enabled)
+	ret = hashsig(opts, 0, 3);
     else if(optget(opts, "mdb")->enabled)
-	ret = md5sig(opts, 1);
+	ret = hashsig(opts, 1, 1);
     else if(optget(opts, "html-normalise")->enabled)
 	ret = htmlnorm(opts);
     else if(optget(opts, "utf16-decode")->enabled)
diff --git a/test/Makefile.in b/test/Makefile.in
index 24aaba0..14d22a1 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -82,6 +82,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/unit_tests/Makefile.in b/unit_tests/Makefile.in
index da9881a..af2ea76 100644
--- a/unit_tests/Makefile.in
+++ b/unit_tests/Makefile.in
@@ -225,6 +225,7 @@ CHECK_LIBS = @CHECK_LIBS@
 CLAMAVGROUP = @CLAMAVGROUP@
 CLAMAVUSER = @CLAMAVUSER@
 CLAMAV_MILTER_LIBS = @CLAMAV_MILTER_LIBS@
+CLAMDSCAN_LIBS = @CLAMDSCAN_LIBS@
 CLAMD_LIBS = @CLAMD_LIBS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
diff --git a/unit_tests/check_bytecode.c b/unit_tests/check_bytecode.c
index e723385..70cc0f1 100644
--- a/unit_tests/check_bytecode.c
+++ b/unit_tests/check_bytecode.c
@@ -522,7 +522,7 @@ START_TEST (test_load_bytecode_int)
 }
 END_TEST
 
-#if defined(CL_THREAD_SAFE) && defined(C_LINUX) && ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= (2 << 16) + 3)
+#if defined(CL_THREAD_SAFE) && defined(C_LINUX) && ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= (2 << 16) + 4)
 #define DO_BARRIER
 #endif
 

-- 
Debian repository for ClamAV



More information about the Pkg-clamav-commits mailing list