libclamav/readdb.c
b151ef55
 /*
4bed6861
  *  Copyright (C) 2002 - 2005 Tomasz Kojm <tkojm@clamav.net>
b151ef55
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
8b242bb9
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b151ef55
 #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>
771e8818
 #include <sys/param.h>
b151ef55
 #include <fcntl.h>
 
 #include "clamav.h"
442d8407
 #include "cvd.h"
b151ef55
 #include "strings.h"
f91f55e0
 #include "matcher-ac.h"
 #include "matcher-bm.h"
b151ef55
 #include "others.h"
 #include "str.h"
 #include "defaults.h"
 
a45ec9cc
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
8c7e16b8
 #include <stddef.h>
a45ec9cc
 #endif
 
771e8818
 /* 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
8425b1a6
 #   else
 #     define	NAME_MAX	256
771e8818
 #   endif
 # endif
 #endif
 
f91f55e0
 /* TODO: clean up the code */
2fe19b26
 
df757556
 static int cli_ac_addsig(struct cl_node *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)
b151ef55
 {
f91f55e0
 	struct cli_ac_patt *new;
d898865b
 	char *pt, *hex;
a8b53539
 	int virlen, ret, error = 0;
 	unsigned int i;
b151ef55
 
 
f91f55e0
     if((new = (struct cli_ac_patt *) cli_calloc(1, sizeof(struct cli_ac_patt))) == NULL)
b151ef55
 	return CL_EMEM;
 
2fe19b26
     new->type = type;
b151ef55
     new->sigid = sigid;
     new->parts = parts;
     new->partno = partno;
d898865b
     new->mindist = mindist;
     new->maxdist = maxdist;
df757556
     new->target = target;
     new->offset = offset;
b151ef55
 
d898865b
     if(strchr(hexsig, '(')) {
ff8cb48b
 	    char *hexcpy, *hexnew, *start, *h, *c;
d898865b
 
 	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++;
c00d0af2
 	    new->altn = (unsigned short int *) realloc(new->altn, new->alt * sizeof(unsigned short int));
d898865b
 	    new->altn[new->alt - 1] = 0;
00c855d0
 	    new->altc = (char **) realloc(new->altc, new->alt * sizeof(char *));
d2f07436
 	    new->altc[new->alt - 1] = NULL;
d898865b
 
 	    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;
 		}
 
ff8cb48b
 		if((c = cli_hex2str(h)) == NULL) {
d898865b
 		    free(h);
 		    error = 1;
 		    break;
 		}
 
ff8cb48b
 		new->altc[new->alt - 1][i] = *c;
d898865b
 		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++)
d2f07436
 		    if(new->altc[i])
 			free(new->altc[i]);
d898865b
 		free(new->altc);
 	    }
 	    free(new);
 	    return CL_EMALFDB;
 	}
 
     } else
 	hex = (char *) hexsig;
 
 
     new->length = strlen(hex) / 2;
b151ef55
 
     if(new->length > root->maxpatlen)
 	root->maxpatlen = new->length;
 
ff8cb48b
     if((new->pattern = cli_hex2si(hex)) == NULL) {
d898865b
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
b151ef55
 	free(new);
 	return CL_EMALFDB;
     }
 
     if((pt = strstr(virname, "(Clam)")))
 	virlen = strlen(virname) - strlen(pt) - 1;
     else
 	virlen = strlen(virname);
 
e295ab3d
     if(virlen <= 0) {
8c853670
 	free(new->pattern);
d898865b
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
e295ab3d
 	free(new);
 	return CL_EMALFDB;
     }
 
50099661
     if((new->virname = cli_calloc(virlen + 1, sizeof(char))) == NULL) {
8c853670
 	free(new->pattern);
d898865b
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
50099661
 	free(new);
b151ef55
 	return CL_EMEM;
50099661
     }
 
7cc3891c
     strncpy(new->virname, virname, virlen);
b151ef55
 
f91f55e0
     if((ret = cli_ac_addpatt(root, new))) {
8c853670
 	free(new->pattern);
50099661
 	free(new->virname);
d898865b
 	if(new->alt) {
 	    free(new->altn);
 	    for(i = 0; i < new->alt; i++)
 		free(new->altc[i]);
 	    free(new->altc);
 	    free(hex);
 	}
50099661
 	free(new);
b151ef55
 	return ret;
50099661
     }
b151ef55
 
d898865b
     if(new->alt)
 	free(hex);
 
b151ef55
     return 0;
 }
 
df757556
 int cli_parse_add(struct cl_node *root, const char *virname, const char *hexsig, unsigned short type, char *offset, unsigned short target)
2fe19b26
 {
f91f55e0
 	struct cli_bm_patt *bm_new;
d898865b
 	char *pt, *hexcpy, *start, *n;
a8b53539
 	int ret, virlen, asterisk = 0;
 	unsigned int i, j, len, parts = 0;
d898865b
 	int mindist = 0, maxdist = 0, error = 0;
 
2fe19b26
 
d898865b
     if(strchr(hexsig, '{')) {
2fe19b26
 
f91f55e0
 	root->ac_partsigs++;
d898865b
 
 	if(!(hexcpy = strdup(hexsig)))
 	    return CL_EMEM;
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
06ce1480
 	    if(hexsig[i] == '{' || hexsig[i] == '*')
d898865b
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	start = pt = hexcpy;
 	for(i = 1; i <= parts; i++) {
 
 	    if(i != parts) {
06ce1480
 		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;
 		    }
 		}
d898865b
 		*pt++ = 0;
 	    }
 
df757556
 	    if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, type, mindist, maxdist, offset, target))) {
06ce1480
 		cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
d898865b
 		error = 1;
 		break;
 	    }
 
 	    if(i == parts)
 		break;
 
06ce1480
 	    mindist = maxdist = 0;
 
 	    if(asterisk) {
 		start = pt;
 		continue;
 	    }
 
d898865b
 	    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 {
df757556
 		if((n = cli_strtok(pt, 0, "-"))) {
d898865b
 		    if((mindist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
df757556
 		if((n = cli_strtok(pt, 1, "-"))) {
d898865b
 		    if((maxdist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 	    }
 	}
 
 	free(hexcpy);
 	if(error)
 	    return CL_EMALFDB;
 
     } else if(strchr(hexsig, '*')) {
f91f55e0
 	root->ac_partsigs++;
2fe19b26
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
 	    if(hexsig[i] == '*')
 		parts++;
 
f91f55e0
 	if(parts)
2fe19b26
 	    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;
 	    }
 
df757556
 	    if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, type, 0, 0, offset, target))) {
06ce1480
 		cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
2fe19b26
 		free(pt);
 		return ret;
 	    }
 
 	    free(pt);
 	}
 
f91f55e0
     } else if(strpbrk(hexsig, "?(") || type) {
df757556
 	if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, type, 0, 0, offset, target))) {
06ce1480
 	    cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
f91f55e0
 	    return ret;
 	}
 
     } else {
 	bm_new = (struct cli_bm_patt *) calloc(1, sizeof(struct cli_bm_patt));
 	if(!bm_new)
 	    return CL_EMEM;
 
85ce7ea9
 	if(!(bm_new->pattern = cli_hex2str(hexsig))) {
 	    free(bm_new);
f91f55e0
 	    return CL_EMALFDB;
85ce7ea9
 	}
f91f55e0
 
 	bm_new->length = strlen(hexsig) / 2;
 
 	if((pt = strstr(virname, "(Clam)")))
 	    virlen = strlen(virname) - strlen(pt) - 1;
 	else
 	    virlen = strlen(virname);
 
 	if(virlen <= 0) {
85ce7ea9
 	    free(bm_new->pattern);
f91f55e0
 	    free(bm_new);
 	    return CL_EMALFDB;
 	}
 
 	if((bm_new->virname = cli_calloc(virlen + 1, sizeof(char))) == NULL) {
85ce7ea9
 	    free(bm_new->pattern);
f91f55e0
 	    free(bm_new);
 	    return CL_EMEM;
 	}
 
 	strncpy(bm_new->virname, virname, virlen);
 
df757556
 	bm_new->offset = offset;
 	bm_new->target = target;
 
f91f55e0
 	if(bm_new->length > root->maxpatlen)
 	    root->maxpatlen = bm_new->length;
 
 	if((ret = cli_bm_addpatt(root, bm_new))) {
06ce1480
 	    cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
85ce7ea9
 	    free(bm_new->pattern);
 	    free(bm_new->virname);
 	    free(bm_new);
2fe19b26
 	    return ret;
 	}
     }
 
     return 0;
 }
b151ef55
 
06d4e856
 static int cli_loaddb(FILE *fd, struct cl_node **root, unsigned int *signo)
b151ef55
 {
ff8cb48b
 	char buffer[FILEBUFF], *pt, *start;
 	int line = 0, ret = 0;
b151ef55
 
 
ff8cb48b
     if(!*root) {
 	cli_dbgmsg("Initializing main node\n");
 	*root = (struct cl_node *) cli_calloc(1, sizeof(struct cl_node));
 	if(!*root)
 	    return CL_EMEM;
538a6756
 	(*root)->refcount = 1;
b151ef55
     }
 
ff8cb48b
     if(!(*root)->ac_root) {
 	cli_dbgmsg("Initializing trie\n");
 	(*root)->ac_root =  (struct cli_ac_node *) cli_calloc(1, sizeof(struct cli_ac_node));
 	if(!(*root)->ac_root) {
 	    free(*root);
f91f55e0
 	    cli_errmsg("Can't initialise AC pattern matcher\n");
ff8cb48b
 	    return CL_EMEM;
 	}
50099661
     }
7b7b3ca5
 
f91f55e0
     if(!(*root)->bm_shift) {
 	cli_dbgmsg("Initializing BM tables\n");
 	if((ret = cli_bm_init(*root))) {
 	    cli_errmsg("Can't initialise BM pattern matcher\n");
 	    return ret;
 	}
     }
 
ff8cb48b
     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;
50099661
 
ff8cb48b
 	if(*pt == '=') continue;
50099661
 
df757556
 	if((ret = cli_parse_add(*root, start, pt, 0, NULL, 0))) {
 	    cli_errmsg("Problem parsing signature at line %d\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	cl_free(*root);
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	cl_free(*root);
 	return ret;
     }
 
     if(signo)
 	*signo += line;
 
     return 0;
 }
 
d99b1840
 static int cli_loadndb(FILE *fd, struct cl_node **root, unsigned int *signo, unsigned short sdb)
df757556
 {
 	char buffer[FILEBUFF], *sig, *virname, *offset, *pt;
b2065eec
 	int line = 0, sigs = 0, ret = 0;
df757556
 	unsigned short target;
 
 
     if(!*root) {
 	cli_dbgmsg("Initializing main node\n");
 	*root = (struct cl_node *) cli_calloc(1, sizeof(struct cl_node));
 	if(!*root)
 	    return CL_EMEM;
538a6756
 	(*root)->refcount = 1;
df757556
     }
 
     if(!(*root)->ac_root) {
 	cli_dbgmsg("Initializing trie\n");
 	(*root)->ac_root =  (struct cli_ac_node *) cli_calloc(1, sizeof(struct cli_ac_node));
 	if(!(*root)->ac_root) {
 	    free(*root);
 	    cli_errmsg("Can't initialise AC pattern matcher\n");
 	    return CL_EMEM;
 	}
     }
 
     if(!(*root)->bm_shift) {
 	cli_dbgmsg("Initializing BM tables\n");
 	if((ret = cli_bm_init(*root))) {
 	    cli_errmsg("Can't initialise BM pattern matcher\n");
 	    return ret;
 	}
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
 	line++;
14ae9b9f
 
 	if(!strncmp(buffer, "Exploit.JPEG.Comment", 20)) /* temporary */
 	    continue;
 
b2065eec
 	sigs++;
df757556
 	cli_chomp(buffer);
 
 	if(!(virname = cli_strtok(buffer, 0, ":"))) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
d99b1840
 	if((pt = cli_strtok(buffer, 4, ":"))) { /* min version */
b2065eec
 	    if(!isdigit(*pt)) {
 		free(virname);
 		free(pt);
 		ret = CL_EMALFDB;
 		break;
 	    }
 
 	    if(atoi(pt) > cl_retflevel()) {
 		cli_warnmsg("Signature for %s requires new ClamAV version. Please update!\n", virname);
 		sigs--;
 		free(virname);
 		free(pt);
 		continue;
 	    }
 
 	    free(pt);
d99b1840
 
 	    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);
 	    }
 
b2065eec
 	}
 
df757556
 	if(!(pt = cli_strtok(buffer, 1, ":")) || !isdigit(*pt)) {
 	    free(virname);
b2065eec
 	    if(pt)
 		free(pt);
df757556
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	target = (unsigned short) atoi(pt);
 	free(pt);
 
 	if(!(offset = cli_strtok(buffer, 2, ":"))) {
 	    free(virname);
 	    ret = CL_EMALFDB;
 	    break;
856d9c84
 	} else if(!strcmp(offset, "*")) {
 	    free(offset);
 	    offset = NULL;
df757556
 	}
 
 	if(!(sig = cli_strtok(buffer, 3, ":"))) {
 	    free(virname);
 	    free(offset);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	if((ret = cli_parse_add(*root, virname, sig, 0, offset, target))) {
ff8cb48b
 	    cli_errmsg("Problem parsing signature at line %d\n", line);
df757556
 	    free(virname);
 	    free(offset);
 	    free(sig);
ff8cb48b
 	    ret = CL_EMALFDB;
 	    break;
 	}
df757556
 
 	free(virname);
 	free(sig);
50099661
     }
 
ff8cb48b
     if(!line) {
 	cli_errmsg("Empty database file\n");
f91f55e0
 	cl_free(*root);
ff8cb48b
 	return CL_EMALFDB;
     }
4cd4319e
 
ff8cb48b
     if(ret) {
df757556
 	cli_errmsg("Problem parsing database at line %d\n", line);
f91f55e0
 	cl_free(*root);
4cd4319e
 	return ret;
     }
 
06d4e856
     if(signo)
b2065eec
 	*signo += sigs;
ff8cb48b
 
d99b1840
     if(sdb && sigs && !(*root)->sdb) {
 	(*root)->sdb = 1;
 	cli_dbgmsg("*** Self protection mechanism activated.\n");
     }
 
ff8cb48b
     return 0;
 }
 
3f66a5af
 static int cli_loadhdb(FILE *fd, struct cl_node **root, unsigned int *signo, unsigned short fp)
ff8cb48b
 {
f91f55e0
 	char buffer[FILEBUFF], *pt;
ff8cb48b
 	int line = 0, ret = 0;
 	struct cli_md5_node *new;
b151ef55
 
ff8cb48b
 
     if(!*root) {
 	cli_dbgmsg("Initializing main node\n");
 	*root = (struct cl_node *) cli_calloc(1, sizeof(struct cl_node));
 	if(!*root)
 	    return CL_EMEM;
538a6756
 	(*root)->refcount = 1;
ff8cb48b
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
b151ef55
 	line++;
 	cli_chomp(buffer);
 
ff8cb48b
 	new = (struct cli_md5_node *) cli_calloc(1, sizeof(struct cli_md5_node));
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
b151ef55
 	}
 
3f66a5af
 	new->fp = fp;
 
ff8cb48b
 	if(!(pt = cli_strtok(buffer, 0, ":"))) {
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
b151ef55
 
7c0991e0
 	if(!(new->md5 = (unsigned char *) cli_hex2str(pt))) {
ff8cb48b
 	    cli_errmsg("Malformed MD5 string at line %d\n", line);
 	    free(pt);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
b151ef55
 	}
ff8cb48b
 	free(pt);
b151ef55
 
61f31052
 	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, ":"))) {
ff8cb48b
 	    free(new->md5);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
b151ef55
 	}
ff8cb48b
 
61f31052
 	new->viralias = cli_strtok(buffer, 3, ":"); /* aliases are optional */
ff8cb48b
 
f91f55e0
 	if(!(*root)->md5_hlist) {
 	    cli_dbgmsg("Initializing md5 list structure\n");
 	    (*root)->md5_hlist = (struct cli_md5_node **) cli_calloc(256, sizeof(struct cli_md5_node *));
 	    if(!(*root)->md5_hlist) {
 		ret = CL_EMEM;
 		break;
 	    }
 	}
 
 	new->next = (*root)->md5_hlist[new->md5[0] & 0xff];
 	(*root)->md5_hlist[new->md5[0] & 0xff] = new;
b151ef55
     }
 
ff8cb48b
     if(!line) {
 	cli_errmsg("Empty database file\n");
f91f55e0
 	cl_free(*root);
ff8cb48b
 	return CL_EMALFDB;
     }
b151ef55
 
ff8cb48b
     if(ret) {
df757556
 	cli_errmsg("Problem parsing database at line %d\n", line);
f91f55e0
 	cl_free(*root);
ff8cb48b
 	return ret;
     }
50099661
 
06d4e856
     if(signo)
 	*signo += line;
372063ec
 
b151ef55
     return 0;
 }
 
92152740
 static int cli_loadmd(FILE *fd, struct cl_node **root, unsigned int *signo, int type)
34e96745
 {
 	char buffer[FILEBUFF], *pt;
a6d49269
 	int line = 0, comments = 0, ret = 0, crc32;
92152740
 	struct cli_meta_node *new;
34e96745
 
 
     if(!*root) {
 	cli_dbgmsg("Initializing main node\n");
 	*root = (struct cl_node *) cli_calloc(1, sizeof(struct cl_node));
 	if(!*root)
 	    return CL_EMEM;
9c8806fb
 	(*root)->refcount = 1;
34e96745
     }
 
     while(fgets(buffer, FILEBUFF, fd)) {
 	line++;
 	if(buffer[0] == '#') {
 	    comments++;
 	    continue;
 	}
 
 	cli_chomp(buffer);
 
92152740
 	new = (struct cli_meta_node *) cli_calloc(1, sizeof(struct cli_meta_node));
34e96745
 	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 {
a6d49269
 		crc32 = cli_hex2num(pt);
 		if(crc32 == -1) {
34e96745
 		    ret = CL_EMALFDB;
 		    break;
 		}
a6d49269
 		new->crc32 = (unsigned int) crc32;
34e96745
 	    }
 	    free(pt);
 	}
 
 	if(!(pt = cli_strtok(buffer, 6, ":"))) {
 	    free(new->filename);
 	    free(new->virname);
 	    free(new);
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(pt, "*"))
92152740
 		new->method = -1;
34e96745
 	    else
92152740
 		new->method = atoi(pt);
34e96745
 	    free(pt);
 	}
 
eceef468
 	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);
 	}
 
92152740
 	if(type == 1) {
 	    new->next = (*root)->zip_mlist;
 	    (*root)->zip_mlist = new;
 	} else {
 	    new->next = (*root)->rar_mlist;
 	    (*root)->rar_mlist = new;
 	}
34e96745
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	cl_free(*root);
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	cl_free(*root);
 	return ret;
     }
 
     if(signo)
 	*signo += (line - comments);
 
     return 0;
 }
 
06d4e856
 int cl_loaddb(const char *filename, struct cl_node **root, unsigned int *signo)
b151ef55
 {
ff8cb48b
 	FILE *fd;
 	int ret;
 
 
     if((fd = fopen(filename, "rb")) == NULL) {
 	cli_errmsg("cl_loaddb(): Can't open file %s\n", filename);
 	return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading %s\n", filename);
 
     if(cli_strbcasestr(filename, ".db")  || cli_strbcasestr(filename, ".db2") || cli_strbcasestr(filename, ".db3")) {
06d4e856
 	ret = cli_loaddb(fd, root, signo);
ff8cb48b
 
     } else if(cli_strbcasestr(filename, ".cvd")) {
4e91e4c7
 	    int warn = 0;
 
f0635204
 	if(strstr(filename, "daily.cvd"))
4e91e4c7
 	    warn = 1;
 
 	ret = cli_cvdload(fd, root, signo, warn);
ff8cb48b
 
ab62edc8
     } else if(cli_strbcasestr(filename, ".hdb")) {
3f66a5af
 	ret = cli_loadhdb(fd, root, signo, 0);
 
     } else if(cli_strbcasestr(filename, ".fp")) {
 	ret = cli_loadhdb(fd, root, signo, 1);
ff8cb48b
 
df757556
     } else if(cli_strbcasestr(filename, ".ndb")) {
d99b1840
 	ret = cli_loadndb(fd, root, signo, 0);
 
     } else if(cli_strbcasestr(filename, ".sdb")) {
 	ret = cli_loadndb(fd, root, signo, 1);
df757556
 
34e96745
     } else if(cli_strbcasestr(filename, ".zmd")) {
92152740
 	ret = cli_loadmd(fd, root, signo, 1);
 
     } else if(cli_strbcasestr(filename, ".rmd")) {
 	ret = cli_loadmd(fd, root, signo, 2);
34e96745
 
ff8cb48b
     } else {
 	cli_dbgmsg("cl_loaddb: unknown extension - assuming old database format\n");
06d4e856
 	ret = cli_loaddb(fd, root, signo);
ff8cb48b
     }
 
     if(ret)
07f0d4bf
 	cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
ff8cb48b
 
     fclose(fd);
     return ret;
b151ef55
 }
 
06d4e856
 int cl_loaddbdir(const char *dirname, struct cl_node **root, unsigned int *signo)
b151ef55
 {
 	DIR *dd;
 	struct dirent *dent;
ec748835
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
8c7e16b8
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
a45ec9cc
 #endif
b151ef55
 	char *dbfile;
 	int ret;
 
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_loaddbdir(): Can't open directory %s\n", dirname);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading databases from %s\n", dirname);
 
ec748835
 #ifdef HAVE_READDIR_R_3
8c7e16b8
     while(!readdir_r(dd, &result.d, &dent) && dent) {
ec748835
 #elif defined(HAVE_READDIR_R_2)
8c7e16b8
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
ec748835
 #else
b151ef55
     while((dent = readdir(dd))) {
ec748835
 #endif
a6d49269
 #if ((!defined(C_CYGWIN)) && (!defined(C_INTERIX)))
618a038b
 	if(dent->d_ino)
 #endif
 	{
4cd4319e
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
9ce8ad74
 	    (cli_strbcasestr(dent->d_name, ".db")   ||
 	     cli_strbcasestr(dent->d_name, ".db2")  ||
 	     cli_strbcasestr(dent->d_name, ".db3")  ||
 	     cli_strbcasestr(dent->d_name, ".hdb")  ||
92152740
 	     cli_strbcasestr(dent->d_name, ".fp")   ||
9ce8ad74
 	     cli_strbcasestr(dent->d_name, ".ndb")  ||
d99b1840
 	     cli_strbcasestr(dent->d_name, ".sdb")  ||
eceef468
 	     cli_strbcasestr(dent->d_name, ".zmd")  ||
92152740
 	     cli_strbcasestr(dent->d_name, ".rmd")  ||
4cd4319e
 	     cli_strbcasestr(dent->d_name, ".cvd"))) {
 
b151ef55
 		dbfile = (char *) cli_calloc(strlen(dent->d_name) + strlen(dirname) + 2, sizeof(char));
 
 		if(!dbfile) {
4cd4319e
 		    cli_dbgmsg("cl_loaddbdir(): dbfile == NULL\n");
b151ef55
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 		sprintf(dbfile, "%s/%s", dirname, dent->d_name);
06d4e856
 		if((ret = cl_loaddb(dbfile, root, signo))) {
c0eb3ceb
 		    cli_dbgmsg("cl_loaddbdir(): error loading database %s\n", dbfile);
b151ef55
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
ff8cb48b
 const char *cl_retdbdir(void)
 {
     return DATADIR;
 }
 
b151ef55
 int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
 {
 	DIR *dd;
a45ec9cc
 	const struct dirent *dent;
ec748835
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
8c7e16b8
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
a45ec9cc
 #endif
b151ef55
         char *fname;
 
 
     if(dbstat) {
 	dbstat->no = 0;
 	dbstat->stattab = NULL;
193c72c5
 	dbstat->statdname = NULL;
b151ef55
 	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);
 
ec748835
 #ifdef HAVE_READDIR_R_3
8c7e16b8
     while(!readdir_r(dd, &result.d, &dent) && dent) {
ec748835
 #elif defined(HAVE_READDIR_R_2)
8c7e16b8
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
ec748835
 #else
b151ef55
     while((dent = readdir(dd))) {
ec748835
 #endif
a6d49269
 #if ((!defined(C_CYGWIN)) && (!defined(C_INTERIX)))
618a038b
 	if(dent->d_ino)
 #endif
 	{
f91f55e0
 	    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")  || 
3f66a5af
 	    cli_strbcasestr(dent->d_name, ".fp")  || 
df757556
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
d99b1840
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
eceef468
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
92152740
 	    cli_strbcasestr(dent->d_name, ".rmd")  || 
f91f55e0
 	    cli_strbcasestr(dent->d_name, ".cvd"))) {
b151ef55
 
 		dbstat->no++;
 		dbstat->stattab = (struct stat *) realloc(dbstat->stattab, dbstat->no * sizeof(struct stat));
4bed6861
 #if defined(C_INTERIX) || defined(C_OS2)
193c72c5
 		dbstat->statdname = (char **) realloc(dbstat->statdname, dbstat->no * sizeof(char *));
 #endif
 
b151ef55
                 fname = cli_calloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char));
 		sprintf(fname, "%s/%s", dirname, dent->d_name);
4bed6861
 #if defined(C_INTERIX) || defined(C_OS2)
193c72c5
 		dbstat->statdname[dbstat->no - 1] = (char *) calloc(strlen(dent->d_name) + 1, sizeof(char));
 		strcpy(dbstat->statdname[dbstat->no - 1], dent->d_name);
 #endif
b151ef55
 		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;
ec748835
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
8c7e16b8
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
a45ec9cc
 #endif
b151ef55
 	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);
 
ec748835
 #ifdef HAVE_READDIR_R_3
8c7e16b8
     while(!readdir_r(dd, &result.d, &dent) && dent) {
ec748835
 #elif defined(HAVE_READDIR_R_2)
8c7e16b8
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
ec748835
 #else
b151ef55
     while((dent = readdir(dd))) {
ec748835
 #endif
a6d49269
 #if ((!defined(C_CYGWIN)) && (!defined(C_INTERIX)))
618a038b
 	if(dent->d_ino)
 #endif
 	{
f91f55e0
 	    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")  || 
3f66a5af
 	    cli_strbcasestr(dent->d_name, ".fp")  || 
df757556
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
d99b1840
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
eceef468
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
92152740
 	    cli_strbcasestr(dent->d_name, ".rmd")  || 
f91f55e0
 	    cli_strbcasestr(dent->d_name, ".cvd"))) {
b151ef55
 
                 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++)
4bed6861
 #if defined(C_INTERIX) || defined(C_OS2)
193c72c5
 		    if(!strcmp(dbstat->statdname[i], dent->d_name)) {
 #else
b151ef55
 		    if(dbstat->stattab[i].st_ino == sb.st_ino) {
193c72c5
 #endif
b151ef55
 			found = 1;
82b133ea
 			if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
 			    closedir(dd);
b151ef55
 			    return 1;
82b133ea
 			}
b151ef55
 		    }
 
82b133ea
 		if(!found) {
 		    closedir(dd);
b151ef55
 		    return 1;
82b133ea
 		}
b151ef55
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statfree(struct cl_stat *dbstat)
 {
 
     if(dbstat) {
193c72c5
 
4bed6861
 #if defined(C_INTERIX) || defined(C_OS2)
193c72c5
 	    int i;
 
 	for(i = 0;i < dbstat->no; i++) {
 	    free(dbstat->statdname[i]);
 	    dbstat->statdname[i] = NULL;
 	}
 	free(dbstat->statdname);
 	dbstat->statdname = NULL;
 #endif
 
b151ef55
 	free(dbstat->stattab);
 	dbstat->stattab = NULL;
 	dbstat->no = 0;
9c1c9007
 	if(dbstat->dir) {
b151ef55
 	    free(dbstat->dir);
9c1c9007
 	    dbstat->dir = NULL;
 	}
b151ef55
     } else {
f91f55e0
         cli_errmsg("cl_statfree(): Null argument passed\n");
b151ef55
 	return CL_ENULLARG;
     }
 
     return 0;
 }