clamav-devel/libclamav/readdb.c
e3aaff8e
 /*
f51e962f
  *  Copyright (C) 2002 - 2006 Tomasz Kojm <tkojm@clamav.net>
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
15edd45f
 #include <sys/param.h>
e3aaff8e
 #include <fcntl.h>
 
 #include "clamav.h"
e4ae7726
 #include "cvd.h"
e3aaff8e
 #include "strings.h"
8000d078
 #include "matcher-ac.h"
 #include "matcher-bm.h"
e3aaff8e
 #include "others.h"
 #include "str.h"
 #include "defaults.h"
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
15edd45f
 /* Maximum filenames under various systems - njh */
 #ifndef	NAME_MAX	/* e.g. Linux */
 # ifdef	MAXNAMELEN	/* e.g. Solaris */
 #   define	NAME_MAX	MAXNAMELEN
 # else
 #   ifdef	FILENAME_MAX	/* e.g. SCO */
 #     define	NAME_MAX	FILENAME_MAX
3d42cc7a
 #   else
 #     define	NAME_MAX	256
15edd45f
 #   endif
 # endif
 #endif
 
f51e962f
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
 static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
 #ifdef HAVE_HWACCEL
 #include <sn_sigscan/sn_sigscan.h>
 #endif
 
 
8000d078
 /* TODO: clean up the code */
888f5794
 
5612732c
 static int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, int sigid, int parts, int partno, unsigned short type, unsigned int mindist, unsigned int maxdist, char *offset, unsigned short target)
e3aaff8e
 {
8000d078
 	struct cli_ac_patt *new;
084ee140
 	char *pt, *hex;
33f89aa5
 	int virlen, ret, error = 0;
 	unsigned int i;
e3aaff8e
 
 
8000d078
     if((new = (struct cli_ac_patt *) cli_calloc(1, sizeof(struct cli_ac_patt))) == NULL)
e3aaff8e
 	return CL_EMEM;
 
888f5794
     new->type = type;
e3aaff8e
     new->sigid = sigid;
     new->parts = parts;
     new->partno = partno;
084ee140
     new->mindist = mindist;
     new->maxdist = maxdist;
b68d11d2
     new->target = target;
     new->offset = offset;
e3aaff8e
 
084ee140
     if(strchr(hexsig, '(')) {
4048c4f6
 	    char *hexcpy, *hexnew, *start, *h, *c;
084ee140
 
 	if(!(hexcpy = strdup(hexsig))) {
 	    free(new);
 	    return CL_EMEM;
 	}
 
 	if(!(hexnew = (char *) cli_calloc(strlen(hexsig) + 1, 1))) {
 	    free(hexcpy);
 	    free(new);
 	    return CL_EMEM;
 	}
 
 	start = pt = hexcpy;
 	while((pt = strchr(start, '('))) {
 	    *pt++ = 0;
 
 	    if(!start) {
 		error = 1;
 		break;
 	    }
 
 	    strcat(hexnew, start);
 	    strcat(hexnew, "@@");
 
 	    if(!(start = strchr(pt, ')'))) {
 		error = 1;
 		break;
 	    }
 	    *start++ = 0;
 
 	    new->alt++;
87fa90f0
 	    new->altn = (unsigned short int *) realloc(new->altn, new->alt * sizeof(unsigned short int));
084ee140
 	    new->altn[new->alt - 1] = 0;
d0cb1e9c
 	    new->altc = (char **) realloc(new->altc, new->alt * sizeof(char *));
58744aab
 	    new->altc[new->alt - 1] = NULL;
084ee140
 
 	    for(i = 0; i < strlen(pt); i++)
 		if(pt[i] == '|')
 		    new->altn[new->alt - 1]++;
 
 	    if(!new->altn[new->alt - 1]) {
 		error = 1;
 		break;
 	    } else
 		new->altn[new->alt - 1]++;
 
 	    if(!(new->altc[new->alt - 1] = (char *) cli_calloc(new->altn[new->alt - 1], 1))) {
 		error = 1;
 		break;
 	    }
 
 	    for(i = 0; i < new->altn[new->alt - 1]; i++) {
 		if((h = cli_strtok(pt, i, "|")) == NULL) {
 		    error = 1;
 		    break;
 		}
 
4048c4f6
 		if((c = cli_hex2str(h)) == NULL) {
084ee140
 		    free(h);
 		    error = 1;
 		    break;
 		}
 
4048c4f6
 		new->altc[new->alt - 1][i] = *c;
084ee140
 		free(c);
 		free(h);
 	    }
 
 	    if(error)
 		break;
 	}
 
 	if(start)
 	    strcat(hexnew, start);
 
 	hex = hexnew;
 	free(hexcpy);
 
 	if(error) {
 	    free(hexnew);
 	    if(new->alt) {
 		free(new->altn);
 		for(i = 0; i < new->alt; i++)
58744aab
 		    if(new->altc[i])
 			free(new->altc[i]);
084ee140
 		free(new->altc);
 	    }
 	    free(new);
 	    return CL_EMALFDB;
 	}
 
     } else
 	hex = (char *) hexsig;
 
 
     new->length = strlen(hex) / 2;
e3aaff8e
 
     if(new->length > root->maxpatlen)
 	root->maxpatlen = new->length;
 
4048c4f6
     if((new->pattern = cli_hex2si(hex)) == NULL) {
084ee140
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
e3aaff8e
 	free(new);
 	return CL_EMALFDB;
     }
 
     if((pt = strstr(virname, "(Clam)")))
 	virlen = strlen(virname) - strlen(pt) - 1;
     else
 	virlen = strlen(virname);
 
831b61bf
     if(virlen <= 0) {
4332ab2b
 	free(new->pattern);
084ee140
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
831b61bf
 	free(new);
 	return CL_EMALFDB;
     }
 
ee039e40
     if((new->virname = cli_calloc(virlen + 1, sizeof(char))) == NULL) {
4332ab2b
 	free(new->pattern);
084ee140
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
ee039e40
 	free(new);
e3aaff8e
 	return CL_EMEM;
ee039e40
     }
 
658f19f8
     strncpy(new->virname, virname, virlen);
e3aaff8e
 
8000d078
     if((ret = cli_ac_addpatt(root, new))) {
4332ab2b
 	free(new->pattern);
ee039e40
 	free(new->virname);
084ee140
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
ee039e40
 	free(new);
e3aaff8e
 	return ret;
ee039e40
     }
e3aaff8e
 
084ee140
     if(new->alt)
 	free(hex);
 
e3aaff8e
     return 0;
 }
 
5612732c
 int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hexsig, unsigned short type, char *offset, unsigned short target)
888f5794
 {
8000d078
 	struct cli_bm_patt *bm_new;
084ee140
 	char *pt, *hexcpy, *start, *n;
33f89aa5
 	int ret, virlen, asterisk = 0;
 	unsigned int i, j, len, parts = 0;
084ee140
 	int mindist = 0, maxdist = 0, error = 0;
 
888f5794
 
084ee140
     if(strchr(hexsig, '{')) {
888f5794
 
8000d078
 	root->ac_partsigs++;
084ee140
 
 	if(!(hexcpy = strdup(hexsig)))
 	    return CL_EMEM;
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
a8f79297
 	    if(hexsig[i] == '{' || hexsig[i] == '*')
084ee140
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	start = pt = hexcpy;
 	for(i = 1; i <= parts; i++) {
 
 	    if(i != parts) {
a8f79297
 		for(j = 0; j < strlen(start); j++) {
 		    if(start[j] == '{') {
 			asterisk = 0;
 			pt = start + j;
 			break;
 		    }
 		    if(start[j] == '*') {
 			asterisk = 1;
 			pt = start + j;
 			break;
 		    }
 		}
084ee140
 		*pt++ = 0;
 	    }
 
b68d11d2
 	    if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, type, mindist, maxdist, offset, target))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
084ee140
 		error = 1;
 		break;
 	    }
 
 	    if(i == parts)
 		break;
 
a8f79297
 	    mindist = maxdist = 0;
 
 	    if(asterisk) {
 		start = pt;
 		continue;
 	    }
 
084ee140
 	    if(!(start = strchr(pt, '}'))) {
 		error = 1;
 		break;
 	    }
 	    *start++ = 0;
 
 	    if(!pt) {
 		error = 1;
 		break;
 	    }
 
 	    if(!strchr(pt, '-')) {
 		if((mindist = maxdist = atoi(pt)) < 0) {
 		    error = 1;
 		    break;
 		}
 	    } else {
b68d11d2
 		if((n = cli_strtok(pt, 0, "-"))) {
084ee140
 		    if((mindist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
b68d11d2
 		if((n = cli_strtok(pt, 1, "-"))) {
084ee140
 		    if((maxdist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 	    }
 	}
 
 	free(hexcpy);
 	if(error)
 	    return CL_EMALFDB;
 
     } else if(strchr(hexsig, '*')) {
8000d078
 	root->ac_partsigs++;
888f5794
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
 	    if(hexsig[i] == '*')
 		parts++;
 
8000d078
 	if(parts)
888f5794
 	    parts++;
 
 	for(i = 1; i <= parts; i++) {
 	    if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
 		cli_errmsg("Can't extract part %d of partial signature.\n", i + 1);
 		return CL_EMALFDB;
 	    }
 
b68d11d2
 	    if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, type, 0, 0, offset, target))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
888f5794
 		free(pt);
 		return ret;
 	    }
 
 	    free(pt);
 	}
 
83fa5305
     } else if(root->ac_only || strpbrk(hexsig, "?(") || type) {
b68d11d2
 	if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, type, 0, 0, offset, target))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
8000d078
 	    return ret;
 	}
 
     } else {
 	bm_new = (struct cli_bm_patt *) calloc(1, sizeof(struct cli_bm_patt));
 	if(!bm_new)
 	    return CL_EMEM;
 
091df0e3
 	if(!(bm_new->pattern = cli_hex2str(hexsig))) {
 	    free(bm_new);
8000d078
 	    return CL_EMALFDB;
091df0e3
 	}
8000d078
 
 	bm_new->length = strlen(hexsig) / 2;
 
 	if((pt = strstr(virname, "(Clam)")))
 	    virlen = strlen(virname) - strlen(pt) - 1;
 	else
 	    virlen = strlen(virname);
 
 	if(virlen <= 0) {
091df0e3
 	    free(bm_new->pattern);
8000d078
 	    free(bm_new);
 	    return CL_EMALFDB;
 	}
 
 	if((bm_new->virname = cli_calloc(virlen + 1, sizeof(char))) == NULL) {
091df0e3
 	    free(bm_new->pattern);
8000d078
 	    free(bm_new);
 	    return CL_EMEM;
 	}
 
 	strncpy(bm_new->virname, virname, virlen);
 
b68d11d2
 	bm_new->offset = offset;
 	bm_new->target = target;
 
8000d078
 	if(bm_new->length > root->maxpatlen)
 	    root->maxpatlen = bm_new->length;
 
 	if((ret = cli_bm_addpatt(root, bm_new))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
091df0e3
 	    free(bm_new->pattern);
 	    free(bm_new->virname);
 	    free(bm_new);
888f5794
 	    return ret;
 	}
     }
 
     return 0;
 }
e3aaff8e
 
83fa5305
 static int cli_initengine(struct cl_engine **engine, unsigned int options)
e3aaff8e
 {
5612732c
 	struct cli_matcher *root;
 	int i, ret;
e3aaff8e
 
 
5612732c
     if(!*engine) {
 	cli_dbgmsg("Initializing the engine structure\n");
 
 	*engine = (struct cl_engine *) cli_calloc(1, sizeof(struct cl_engine));
 	if(!*engine) {
 	    cli_errmsg("Can't allocate memory for the engine structure!\n");
4048c4f6
 	    return CL_EMEM;
5612732c
 	}
 
 	(*engine)->refcount = 1;
e3aaff8e
 
5612732c
 	(*engine)->root = (struct cli_matcher **) cli_calloc(CL_TARGET_TABLE_SIZE, sizeof(struct cli_matcher *));
 	if(!(*engine)->root) {
 	    /* no need to free previously allocated memory here */
 	    cli_errmsg("Can't allocate memory for roots!\n");
4048c4f6
 	    return CL_EMEM;
 	}
ee039e40
     }
f7148839
 
5612732c
     return 0;
 }
 
83fa5305
 static int cli_initroots(struct cl_engine *engine, unsigned int options)
5612732c
 {
 	int i, ret;
 	struct cli_matcher *root;
 
 
     for(i = 0; i < CL_TARGET_TABLE_SIZE; i++) {
 	if(!engine->root[i]) {
 	    cli_dbgmsg("Initializing engine->root[%d]\n", i);
 	    root = engine->root[i] = (struct cli_matcher *) cli_calloc(1, sizeof(struct cli_matcher));
 	    if(!root) {
 		cli_errmsg("Can't initialise AC pattern matcher\n");
 		return CL_EMEM;
 	    }
 
83fa5305
 	    if(options & CL_DB_ACONLY) {
 		cli_dbgmsg("Only using AC pattern matcher.\n");
 		root->ac_only = 1;
 	    }
 
5612732c
 	    cli_dbgmsg("Initialising AC pattern matcher of root[%d]\n", i);
 	    root->ac_root =  (struct cli_ac_node *) cli_calloc(1, sizeof(struct cli_ac_node));
 	    if(!root->ac_root) {
 		/* no need to free previously allocated memory here */
 		cli_errmsg("Can't initialise AC pattern matcher\n");
 		return CL_EMEM;
 	    }
 
83fa5305
 	    if(!root->ac_only) {
 		cli_dbgmsg("Initializing BM tables of root[%d]\n", i);
 		if((ret = cli_bm_init(root))) {
 		    cli_errmsg("Can't initialise BM pattern matcher\n");
 		    return ret;
 		}
5612732c
 	    }
8000d078
 	}
     }
 
5612732c
     return 0;
 }
 
15e1a7cd
 static int cli_loaddb(FILE *fd, struct cl_engine **engine, unsigned int *signo, unsigned int options)
5612732c
 {
 	char buffer[FILEBUFF], *pt, *start;
 	int line = 0, ret = 0;
 	struct cli_matcher *root;
 
 
83fa5305
     if((ret = cli_initengine(engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
     }
 
83fa5305
     if((ret = cli_initroots(*engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
     }
 
     root = (*engine)->root[0];
 
4048c4f6
     while(fgets(buffer, FILEBUFF, fd)) {
 	line++;
 	cli_chomp(buffer);
 
 	pt = strchr(buffer, '=');
 	if(!pt) {
 	    cli_errmsg("Malformed pattern line %d\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	start = buffer;
 	*pt++ = 0;
ee039e40
 
4048c4f6
 	if(*pt == '=') continue;
ee039e40
 
5612732c
 	if((ret = cli_parse_add(root, start, pt, 0, NULL, 0))) {
b68d11d2
 	    cli_errmsg("Problem parsing signature at line %d\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
5612732c
 	cl_free(*engine);
b68d11d2
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
5612732c
 	cl_free(*engine);
b68d11d2
 	return ret;
     }
 
     if(signo)
 	*signo += line;
 
     return 0;
 }
 
555c5390
 static int cli_loadndb(FILE *fd, struct cl_engine **engine, unsigned int *signo, unsigned short sdb, unsigned int options)
b68d11d2
 {
 	char buffer[FILEBUFF], *sig, *virname, *offset, *pt;
5612732c
 	struct cli_matcher *root;
95083a4f
 	int line = 0, sigs = 0, ret = 0;
b68d11d2
 	unsigned short target;
d6449522
 	unsigned int nophish = options & CL_DB_NOPHISHING;
b68d11d2
 
 
83fa5305
     if((ret = cli_initengine(engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
b68d11d2
     }
 
83fa5305
     if((ret = cli_initroots(*engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
b68d11d2
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
 	line++;
7afdc309
 
 	if(!strncmp(buffer, "Exploit.JPEG.Comment", 20)) /* temporary */
 	    continue;
 
d6449522
 	if(nophish)
 	    if(!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14))
 		continue;
 
95083a4f
 	sigs++;
b68d11d2
 	cli_chomp(buffer);
 
 	if(!(virname = cli_strtok(buffer, 0, ":"))) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
555c5390
 	if((pt = cli_strtok(buffer, 4, ":"))) { /* min version */
95083a4f
 	    if(!isdigit(*pt)) {
 		free(virname);
 		free(pt);
 		ret = CL_EMALFDB;
 		break;
 	    }
 
 	    if(atoi(pt) > cl_retflevel()) {
5612732c
 		cli_dbgmsg("Signature for %s requires new ClamAV version. Please update!\n", virname);
95083a4f
 		sigs--;
 		free(virname);
 		free(pt);
 		continue;
 	    }
 
 	    free(pt);
555c5390
 
 	    if((pt = cli_strtok(buffer, 5, ":"))) { /* max version */
 		if(!isdigit(*pt)) {
 		    free(virname);
 		    free(pt);
 		    ret = CL_EMALFDB;
 		    break;
 		}
 
 		if(atoi(pt) < cl_retflevel()) {
 		    sigs--;
 		    free(virname);
 		    free(pt);
 		    continue;
 		}
 
 		free(pt);
 	    }
95083a4f
 	}
 
b68d11d2
 	if(!(pt = cli_strtok(buffer, 1, ":")) || !isdigit(*pt)) {
 	    free(virname);
95083a4f
 	    if(pt)
 		free(pt);
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	target = (unsigned short) atoi(pt);
 	free(pt);
 
5612732c
 	if(target >= CL_TARGET_TABLE_SIZE) {
 	    cli_dbgmsg("Not supported target type in signature for %s\n", virname);
 	    sigs--;
 	    free(virname);
 	    free(pt);
 	    continue;
 	}
 
 	root = (*engine)->root[target];
 
b68d11d2
 	if(!(offset = cli_strtok(buffer, 2, ":"))) {
 	    free(virname);
 	    ret = CL_EMALFDB;
 	    break;
7ec67e94
 	} else if(!strcmp(offset, "*")) {
 	    free(offset);
 	    offset = NULL;
b68d11d2
 	}
 
 	if(!(sig = cli_strtok(buffer, 3, ":"))) {
 	    free(virname);
 	    free(offset);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
5612732c
 	if((ret = cli_parse_add(root, virname, sig, 0, offset, target))) {
4048c4f6
 	    cli_errmsg("Problem parsing signature at line %d\n", line);
b68d11d2
 	    free(virname);
 	    free(offset);
 	    free(sig);
4048c4f6
 	    ret = CL_EMALFDB;
 	    break;
 	}
b68d11d2
 
 	free(virname);
 	free(sig);
ee039e40
     }
 
4048c4f6
     if(!line) {
 	cli_errmsg("Empty database file\n");
5612732c
 	cl_free(*engine);
4048c4f6
 	return CL_EMALFDB;
     }
8139fd99
 
4048c4f6
     if(ret) {
b68d11d2
 	cli_errmsg("Problem parsing database at line %d\n", line);
5612732c
 	cl_free(*engine);
8139fd99
 	return ret;
     }
 
3805ebcb
     if(signo)
95083a4f
 	*signo += sigs;
4048c4f6
 
555c5390
     if(sdb && sigs && !(*engine)->sdb) {
 	(*engine)->sdb = 1;
 	cli_dbgmsg("*** Self protection mechanism activated.\n");
     }
 
4048c4f6
     return 0;
 }
 
15e1a7cd
 static int cli_loadhdb(FILE *fd, struct cl_engine **engine, unsigned int *signo, unsigned short fp, unsigned int options)
4048c4f6
 {
8000d078
 	char buffer[FILEBUFF], *pt;
4048c4f6
 	int line = 0, ret = 0;
 	struct cli_md5_node *new;
e3aaff8e
 
4048c4f6
 
83fa5305
     if((ret = cli_initengine(engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
4048c4f6
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
e3aaff8e
 	line++;
 	cli_chomp(buffer);
 
4048c4f6
 	new = (struct cli_md5_node *) cli_calloc(1, sizeof(struct cli_md5_node));
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
e3aaff8e
 	}
 
db65451b
 	new->fp = fp;
 
4048c4f6
 	if(!(pt = cli_strtok(buffer, 0, ":"))) {
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
e3aaff8e
 
058ccefe
 	if(!(new->md5 = (unsigned char *) cli_hex2str(pt))) {
4048c4f6
 	    cli_errmsg("Malformed MD5 string at line %d\n", line);
 	    free(pt);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
e3aaff8e
 	}
4048c4f6
 	free(pt);
e3aaff8e
 
022a21cf
 	if(!(pt = cli_strtok(buffer, 1, ":"))) {
 	    free(new->md5);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	new->size = atoi(pt);
 	free(pt);
 
 	if(!(new->virname = cli_strtok(buffer, 2, ":"))) {
4048c4f6
 	    free(new->md5);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
e3aaff8e
 	}
4048c4f6
 
022a21cf
 	new->viralias = cli_strtok(buffer, 3, ":"); /* aliases are optional */
4048c4f6
 
5612732c
 	if(!(*engine)->md5_hlist) {
8000d078
 	    cli_dbgmsg("Initializing md5 list structure\n");
5612732c
 	    (*engine)->md5_hlist = (struct cli_md5_node **) cli_calloc(256, sizeof(struct cli_md5_node *));
 	    if(!(*engine)->md5_hlist) {
8000d078
 		ret = CL_EMEM;
 		break;
 	    }
 	}
 
5612732c
 	new->next = (*engine)->md5_hlist[new->md5[0] & 0xff];
 	(*engine)->md5_hlist[new->md5[0] & 0xff] = new;
e3aaff8e
     }
 
4048c4f6
     if(!line) {
 	cli_errmsg("Empty database file\n");
5612732c
 	cl_free(*engine);
4048c4f6
 	return CL_EMALFDB;
     }
e3aaff8e
 
4048c4f6
     if(ret) {
b68d11d2
 	cli_errmsg("Problem parsing database at line %d\n", line);
5612732c
 	cl_free(*engine);
4048c4f6
 	return ret;
     }
ee039e40
 
3805ebcb
     if(signo)
 	*signo += line;
3365db23
 
e3aaff8e
     return 0;
 }
 
15e1a7cd
 static int cli_loadmd(FILE *fd, struct cl_engine **engine, unsigned int *signo, int type, unsigned int options)
e5916a51
 {
 	char buffer[FILEBUFF], *pt;
 	int line = 0, comments = 0, ret = 0;
a62ae54f
 	struct cli_meta_node *new;
e5916a51
 
 
83fa5305
     if((ret = cli_initengine(engine, options))) {
5612732c
 	cl_free(*engine);
 	return ret;
e5916a51
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
 	line++;
 	if(buffer[0] == '#') {
 	    comments++;
 	    continue;
 	}
 
 	cli_chomp(buffer);
 
a62ae54f
 	new = (struct cli_meta_node *) cli_calloc(1, sizeof(struct cli_meta_node));
e5916a51
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
 	if(!(new->virname = cli_strtok(buffer, 0, ":"))) {
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	if(!(pt = cli_strtok(buffer, 1, ":"))) {
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    new->encrypted = atoi(pt);
 	    free(pt);
 	}
 
 	if(!(new->filename = cli_strtok(buffer, 2, ":"))) {
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(new->filename, "*")) {
 		free(new->filename);
 		new->filename = NULL;
 	    }
 	}
 
 	if(!(pt = cli_strtok(buffer, 3, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
 		new->size = -1;
 	    else
 		new->size = atoi(pt);
 	    free(pt);
 	}
 
 	if(!(pt = cli_strtok(buffer, 4, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
 		new->csize = -1;
 	    else
 		new->csize = atoi(pt);
 	    free(pt);
 	}
 
 	if(!(pt = cli_strtok(buffer, 5, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*")) {
 		new->crc32 = 0;
 	    } else {
 		new->crc32 = cli_hex2num(pt);
 		if(new->crc32 == -1) {
 		    ret = CL_EMALFDB;
 		    break;
 		}
 	    }
 	    free(pt);
 	}
 
 	if(!(pt = cli_strtok(buffer, 6, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
a62ae54f
 		new->method = -1;
e5916a51
 	    else
a62ae54f
 		new->method = atoi(pt);
e5916a51
 	    free(pt);
 	}
 
19e2ac07
 	if(!(pt = cli_strtok(buffer, 7, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
 		new->fileno = 0;
 	    else
 		new->fileno = atoi(pt);
 	    free(pt);
 	}
 
 	if(!(pt = cli_strtok(buffer, 8, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
 		new->maxdepth = 0;
 	    else
 		new->maxdepth = atoi(pt);
 	    free(pt);
 	}
 
a62ae54f
 	if(type == 1) {
5612732c
 	    new->next = (*engine)->zip_mlist;
 	    (*engine)->zip_mlist = new;
a62ae54f
 	} else {
5612732c
 	    new->next = (*engine)->rar_mlist;
 	    (*engine)->rar_mlist = new;
a62ae54f
 	}
e5916a51
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
5612732c
 	cl_free(*engine);
e5916a51
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
5612732c
 	cl_free(*engine);
e5916a51
 	return ret;
     }
 
     if(signo)
 	*signo += (line - comments);
 
     return 0;
 }
 
f51e962f
 #ifdef HAVE_HWACCEL
 static int cli_loadhw(const char *filename, struct cl_engine **engine, unsigned int *signo, unsigned int options)
 {
 	int ret = 0;
 
 
     if((ret = cli_initengine(engine, options))) {
 	cl_free(*engine);
 	return ret;
     }
 
     if((ret = sn_sigscan_initdb(&(*engine)->hwdb)) < 0) {
 	cli_errmsg("hwaccel: error initializing the matcher: %d\n", ret);
 	cl_free(*engine);
 	return CL_EHWINIT;
     }
 
     (*engine)->hwaccel = 1;
 
     if((ret = sn_sigscan_loaddb((*engine)->hwdb, filename, 0, signo)) < 0) {
 	cli_errmsg("hwaccel: can't load hardware database: %d\n", ret);
 	cl_free(*engine);
 	return CL_EHWLOAD;
     }
 
     return CL_SUCCESS;
 }
 #endif /* HAVE_HWACCEL */
 
04933acd
 static int cli_load(const char *filename, struct cl_engine **engine, unsigned int *signo, unsigned int options)
e3aaff8e
 {
4048c4f6
 	FILE *fd;
f51e962f
 	int ret = CL_SUCCESS;
 
4048c4f6
 
f51e962f
 #ifdef HAVE_HWACCEL
     if(options & CL_DB_HWACCEL) {
 	if(cli_strbcasestr(filename, ".hw")) {
 	    cli_dbgmsg("Loading %s\n", filename);
 	    ret = cli_loadhw(filename, engine, signo, options);
 	} else {
 	    cli_dbgmsg("Ignoring %s\n", filename);
 	}
 
 	return ret;
     }
 #endif /* HAVE_HWACCEL */
4048c4f6
 
     if((fd = fopen(filename, "rb")) == NULL) {
f51e962f
 	cli_errmsg("cli_load(): Can't open file %s\n", filename);
4048c4f6
 	return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading %s\n", filename);
 
     if(cli_strbcasestr(filename, ".db")  || cli_strbcasestr(filename, ".db2") || cli_strbcasestr(filename, ".db3")) {
15e1a7cd
 	ret = cli_loaddb(fd, engine, signo, options);
4048c4f6
 
     } else if(cli_strbcasestr(filename, ".cvd")) {
37743d67
 	    int warn = 0;
 
 	if(!strcmp(filename, "daily.cvd"))
 	    warn = 1;
 
15e1a7cd
 	ret = cli_cvdload(fd, engine, signo, warn, options);
4048c4f6
 
26720e3f
     } else if(cli_strbcasestr(filename, ".hdb")) {
15e1a7cd
 	ret = cli_loadhdb(fd, engine, signo, 0, options);
db65451b
 
     } else if(cli_strbcasestr(filename, ".fp")) {
15e1a7cd
 	ret = cli_loadhdb(fd, engine, signo, 1, options);
4048c4f6
 
b68d11d2
     } else if(cli_strbcasestr(filename, ".ndb")) {
555c5390
 	ret = cli_loadndb(fd, engine, signo, 0, options);
 
     } else if(cli_strbcasestr(filename, ".sdb")) {
 	ret = cli_loadndb(fd, engine, signo, 1, options);
b68d11d2
 
e5916a51
     } else if(cli_strbcasestr(filename, ".zmd")) {
15e1a7cd
 	ret = cli_loadmd(fd, engine, signo, 1, options);
a62ae54f
 
     } else if(cli_strbcasestr(filename, ".rmd")) {
15e1a7cd
 	ret = cli_loadmd(fd, engine, signo, 2, options);
e5916a51
 
8df99ec7
     } else if(cli_strbcasestr(filename, ".hw")) {
 	/* ignore */
 
4048c4f6
     } else {
f51e962f
 	cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
15e1a7cd
 	ret = cli_loaddb(fd, engine, signo, options);
4048c4f6
     }
 
     if(ret)
975ef8ce
 	cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
4048c4f6
 
     fclose(fd);
     return ret;
e3aaff8e
 }
 
04933acd
 int cl_loaddb(const char *filename, struct cl_engine **engine, unsigned int *signo) {
     return cli_load(filename, engine, signo, 0);
 }
 
 static int cli_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo, unsigned int options)
e3aaff8e
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	char *dbfile;
 	int ret;
 
 
     if((dd = opendir(dirname)) == NULL) {
15e1a7cd
         cli_errmsg("cli_loaddbdir(): Can't open directory %s\n", dirname);
e3aaff8e
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading databases from %s\n", dirname);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
8139fd99
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
cfeb200c
 	    (cli_strbcasestr(dent->d_name, ".db")   ||
 	     cli_strbcasestr(dent->d_name, ".db2")  ||
 	     cli_strbcasestr(dent->d_name, ".db3")  ||
 	     cli_strbcasestr(dent->d_name, ".hdb")  ||
a62ae54f
 	     cli_strbcasestr(dent->d_name, ".fp")   ||
cfeb200c
 	     cli_strbcasestr(dent->d_name, ".ndb")  ||
555c5390
 	     cli_strbcasestr(dent->d_name, ".sdb")  ||
19e2ac07
 	     cli_strbcasestr(dent->d_name, ".zmd")  ||
a62ae54f
 	     cli_strbcasestr(dent->d_name, ".rmd")  ||
f51e962f
 	     cli_strbcasestr(dent->d_name, ".hw")  ||
8139fd99
 	     cli_strbcasestr(dent->d_name, ".cvd"))) {
 
e3aaff8e
 		dbfile = (char *) cli_calloc(strlen(dent->d_name) + strlen(dirname) + 2, sizeof(char));
 
 		if(!dbfile) {
15e1a7cd
 		    cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
e3aaff8e
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 		sprintf(dbfile, "%s/%s", dirname, dent->d_name);
15e1a7cd
 		if((ret = cli_load(dbfile, engine, signo, options))) {
 		    cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
e3aaff8e
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
04933acd
 int cl_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo) {
     return cli_loaddbdir(dirname, engine, signo, 0);
 }
 
 int cl_load(const char *path, struct cl_engine **engine, unsigned int *signo, unsigned int options)
 {
 	struct stat sb;
f7470773
 	int ret;
04933acd
 
 
     if(stat(path, &sb) == -1) {
         cli_errmsg("cl_loaddbdir(): Can't get status of %s\n", path);
         return CL_EIO;
     }
 
83fa5305
     if((ret = cli_initengine(engine, options))) {
f7470773
 	cl_free(*engine);
 	return ret;
     }
 
f51e962f
     switch(sb.st_mode & S_IFMT) {
04933acd
 	case S_IFREG: 
 	    return cli_load(path, engine, signo, options);
 
 	case S_IFDIR:
 	    return cli_loaddbdir(path, engine, signo, options);
 
 	default:
 	    cli_errmsg("cl_load(): Not supported database file type\n");
 	    return CL_EOPEN;
     }
 }
 
4048c4f6
 const char *cl_retdbdir(void)
 {
     return DATADIR;
 }
 
e3aaff8e
 int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
 {
 	DIR *dd;
2bb229f6
 	const struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
         char *fname;
 
 
     if(dbstat) {
 	dbstat->no = 0;
 	dbstat->stattab = NULL;
75ccac9f
 	dbstat->statdname = NULL;
e3aaff8e
 	dbstat->dir = strdup(dirname);
     } else {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dirname);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
8000d078
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
 	    cli_strbcasestr(dent->d_name, ".db2")  || 
 	    cli_strbcasestr(dent->d_name, ".db3")  || 
 	    cli_strbcasestr(dent->d_name, ".hdb")  || 
f51e962f
 	    cli_strbcasestr(dent->d_name, ".fp")   || 
b68d11d2
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
555c5390
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
19e2ac07
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
a62ae54f
 	    cli_strbcasestr(dent->d_name, ".rmd")  || 
f51e962f
 	    cli_strbcasestr(dent->d_name, ".hw")   ||
8000d078
 	    cli_strbcasestr(dent->d_name, ".cvd"))) {
e3aaff8e
 
 		dbstat->no++;
 		dbstat->stattab = (struct stat *) realloc(dbstat->stattab, dbstat->no * sizeof(struct stat));
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 		dbstat->statdname = (char **) realloc(dbstat->statdname, dbstat->no * sizeof(char *));
 #endif
 
e3aaff8e
                 fname = cli_calloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char));
 		sprintf(fname, "%s/%s", dirname, dent->d_name);
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 		dbstat->statdname[dbstat->no - 1] = (char *) calloc(strlen(dent->d_name) + 1, sizeof(char));
 		strcpy(dbstat->statdname[dbstat->no - 1], dent->d_name);
 #endif
e3aaff8e
 		stat(fname, &dbstat->stattab[dbstat->no - 1]);
 		free(fname);
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statchkdir(const struct cl_stat *dbstat)
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	struct stat sb;
 	int i, found;
 	char *fname;
 
 
     if(!dbstat || !dbstat->dir) {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dbstat->dir)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
8000d078
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
 	    cli_strbcasestr(dent->d_name, ".db2")  || 
 	    cli_strbcasestr(dent->d_name, ".db3")  || 
 	    cli_strbcasestr(dent->d_name, ".hdb")  || 
f51e962f
 	    cli_strbcasestr(dent->d_name, ".fp")   || 
b68d11d2
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
555c5390
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
19e2ac07
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
a62ae54f
 	    cli_strbcasestr(dent->d_name, ".rmd")  || 
f51e962f
 	    cli_strbcasestr(dent->d_name, ".hw")   ||
8000d078
 	    cli_strbcasestr(dent->d_name, ".cvd"))) {
e3aaff8e
 
                 fname = cli_calloc(strlen(dbstat->dir) + strlen(dent->d_name) + 2, sizeof(char));
 		sprintf(fname, "%s/%s", dbstat->dir, dent->d_name);
 		stat(fname, &sb);
 		free(fname);
 
 		found = 0;
 		for(i = 0; i < dbstat->no; i++)
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 		    if(!strcmp(dbstat->statdname[i], dent->d_name)) {
 #else
e3aaff8e
 		    if(dbstat->stattab[i].st_ino == sb.st_ino) {
75ccac9f
 #endif
e3aaff8e
 			found = 1;
e3f0de57
 			if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
 			    closedir(dd);
e3aaff8e
 			    return 1;
e3f0de57
 			}
e3aaff8e
 		    }
 
e3f0de57
 		if(!found) {
 		    closedir(dd);
e3aaff8e
 		    return 1;
e3f0de57
 		}
e3aaff8e
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statfree(struct cl_stat *dbstat)
 {
 
     if(dbstat) {
75ccac9f
 
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 	    int i;
 
 	for(i = 0;i < dbstat->no; i++) {
 	    free(dbstat->statdname[i]);
 	    dbstat->statdname[i] = NULL;
 	}
 	free(dbstat->statdname);
 	dbstat->statdname = NULL;
 #endif
 
e3aaff8e
 	free(dbstat->stattab);
 	dbstat->stattab = NULL;
 	dbstat->no = 0;
9e431a95
 	if(dbstat->dir) {
e3aaff8e
 	    free(dbstat->dir);
9e431a95
 	    dbstat->dir = NULL;
 	}
e3aaff8e
     } else {
8000d078
         cli_errmsg("cl_statfree(): Null argument passed\n");
e3aaff8e
 	return CL_ENULLARG;
     }
 
     return 0;
 }
f51e962f
 
 void cl_free(struct cl_engine *engine)
 {
 	int i, ret;
 	struct cli_md5_node *md5pt, *md5h;
 	struct cli_meta_node *metapt, *metah;
 	struct cli_matcher *root;
 
     if(!engine) {
 	cli_errmsg("cl_free: engine == NULL\n");
 	return;
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     engine->refcount--;
     if(engine->refcount) {
 #ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 	return;
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
 #ifdef HAVE_HWACCEL
     if(engine->hwaccel) {
 	if((ret = sn_sigscan_closedb(engine->hwdb)) < 0) {
 	    cli_errmsg("cl_free: can't close hardware database: %d\n", ret);
 	}
     }
 #endif
 
     for(i = 0; i < CL_TARGET_TABLE_SIZE; i++) {
 	if((root = engine->root[i])) {
 	    cli_ac_free(root);
 	    if(!engine->root[i]->ac_only)
 		cli_bm_free(root);
 	}
     }
 
     if(engine->md5_hlist) {
 	for(i = 0; i < 256; i++) {
 	    md5pt = engine->md5_hlist[i];
 	    while(md5pt) {
 		md5h = md5pt;
 		md5pt = md5pt->next;
 		free(md5h->md5);
 		free(md5h->virname);
 		if(md5h->viralias)
 		    free(md5h->viralias);
 		free(md5h);
 	    }
 	}
 	free(engine->md5_hlist);
     }
 
     metapt = engine->zip_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
 	free(metah->virname);
 	if(metah->filename)
 	    free(metah->filename);
 	free(metah);
     }
 
     metapt = engine->rar_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
 	free(metah->virname);
 	if(metah->filename)
 	    free(metah->filename);
 	free(metah);
     }
 
     free(engine);
 }
 
 int cl_build(struct cl_engine *engine)
 {
 	int i, ret;
 	struct cli_matcher *root;
 
 
     if((ret = cli_addtypesigs(engine)))
 	return ret;
 
     for(i = 0; i < CL_TARGET_TABLE_SIZE; i++)
 	if((root = engine->root[i]))
 	    cli_ac_buildtrie(root);
     /* FIXME: check return values of cli_ac_buildtree */
 
     return 0;
 }
 
 struct cl_engine *cl_dup(struct cl_engine *engine)
 {
     if(!engine) {
 	cli_errmsg("cl_dup: engine == NULL\n");
 	return NULL;
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     engine->refcount++;
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
     return engine;
 }