sigtool/sigtool.c
e3aaff8e
 /*
e18f93ee
  *  Copyright (C) 2007 - 2008 Sourcefire, Inc.
8ca8a18e
  *  Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net>
bb34cb31
  *  CDIFF code (C) 2006 Sensory Networks, Inc.
e18f93ee
  *  Author: Tomasz Kojm <tkojm@clamav.net>
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  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 <unistd.h>
8139fd99
 #include <zlib.h>
 #include <time.h>
 #include <locale.h>
ae307914
 #include <sys/types.h>
08d6b1e3
 #include <sys/stat.h>
 #include <fcntl.h>
081f6473
 #ifndef _WIN32
ae307914
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
2d70a403
 #include <sys/wait.h>
4cd80898
 #endif
0a2ad257
 #include <dirent.h>
e3aaff8e
 
f0d0a4c0
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
6a31c2b4
 #include "vba.h"
e3aaff8e
 
3d167464
 #include "shared/output.h"
d6af38e7
 #include "shared/optparser.h"
3d167464
 #include "shared/misc.h"
bb990ecd
 #include "shared/cdiff.h"
4e46d65d
 #include "libclamav/sha256.h"
e18f93ee
 #include "shared/tar.h"
e3aaff8e
 
e48dcea9
 #include "libclamav/clamav.h"
93173872
 #include "libclamav/matcher.h"
3d167464
 #include "libclamav/cvd.h"
 #include "libclamav/others.h"
 #include "libclamav/str.h"
 #include "libclamav/ole2_extract.h"
 #include "libclamav/htmlnorm.h"
4367454d
 #include "libclamav/default.h"
084d19aa
 #include "libclamav/fmap.h"
a96eead4
 #include "libclamav/readdb.h"
e3aaff8e
 
6b5d21a3
 #define MAX_DEL_LOOKAHEAD   200
e3aaff8e
 
4367454d
 /*
  * Force backward compatibility with the cdiff interpreter of clamav < 0.95
  */
c65b5907
 #define COMPATIBILITY_LIMIT 980
4367454d
 
e18f93ee
 static const struct dblist_s {
     const char *name;
     unsigned int count;
 } dblist[] = {
 
     /* special files */
     { "COPYING",    0 },
     { "daily.cfg",  0 },
     { "daily.ign",  0 },
ba238db6
     { "daily.ign2",  0 },
6038397e
     { "daily.ftm",  0 },
061029a6
     { "main.info",  0 },    { "daily.info", 0 },    { "safebrowsing.info", 0 },
e18f93ee
 
     /* databases */
     { "main.db",    1 },    { "daily.db",   1 },
     { "main.hdb",   1 },    { "daily.hdb",  1 },
     { "main.hdu",   1 },    { "daily.hdu",  1 },
     { "main.mdb",   1 },    { "daily.mdb",  1 },
     { "main.mdu",   1 },    { "daily.mdu",  1 },
     { "main.ndb",   1 },    { "daily.ndb",  1 },
     { "main.ndu",   1 },    { "daily.ndu",  1 },
1437615c
     { "main.ldb",   1 },    { "daily.ldb",  1 },
     { "main.ldu",   1 },    { "daily.ldu",  1 },
e18f93ee
     { "main.sdb",   1 },    { "daily.sdb",  1 },
     { "main.zmd",   1 },    { "daily.zmd",  1 },
     { "main.rmd",   1 },    { "daily.rmd",  1 },
     { "main.fp",    0 },    { "daily.fp",   0 },
0810d861
     { "main.pdb",   1 },    { "daily.pdb",  1 },    { "safebrowsing.gdb", 1 },
03527bee
     { "main.wdb",   0 },    { "daily.wdb",  0 },    { "safebrowsing.wdb", 0 },
e18f93ee
 
     { NULL,	    0 }
 };
 
03527bee
 static const char *getdbname(const char *str)
 {
     if(strstr(str, "main"))
 	return "main";
     else if(strstr(str, "daily"))
 	return "daily";
     else if(strstr(str, "safebrowsing"))
 	return "safebrowsing";
     else {
 	mprintf("!getdbname: Can't extract db name\n");
 	return "UNKNOWN";
     }
 }
 
3d167464
 static int hexdump(void)
 {
 	char buffer[FILEBUFF], *pt;
 	int bytes;
6a2532ca
 
e3aaff8e
 
3d167464
     while((bytes = read(0, buffer, FILEBUFF)) > 0) {
 	pt = cli_str2hex(buffer, bytes);
 	if(write(1, pt, 2 * bytes) == -1) {
efa239e2
 	    mprintf("!hexdump: Can't write to stdout\n");
3d167464
 	    free(pt);
 	    return -1;
 	}
 	free(pt);
e3aaff8e
     }
 
3d167464
     if(bytes == -1)
 	return -1;
e3aaff8e
 
3d167464
     return 0;
 }
e3aaff8e
 
d6af38e7
 static int md5sig(const struct optstruct *opts, unsigned int mdb)
3d167464
 {
19c17946
 	char *md5;
e18f93ee
 	unsigned int i;
3d167464
 	struct stat sb;
a36e6e5c
 
bcf3dc79
 
d6af38e7
     if(opts->filename) {
19c17946
 	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]);
3d167464
 		perror("md5sig");
 		return -1;
 	    } else {
 		if((sb.st_mode & S_IFMT) == S_IFREG) {
19c17946
 		    if((md5 = cli_md5file(opts->filename[i]))) {
60892bc1
 			if(mdb)
19c17946
 			    mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, md5, opts->filename[i]);
60892bc1
 			else
19c17946
 			    mprintf("%s:%u:%s\n", md5, (unsigned int) sb.st_size, opts->filename[i]);
3d167464
 			free(md5);
 		    } else {
19c17946
 			mprintf("!md5sig: Can't generate MD5 checksum for %s\n", opts->filename[i]);
3d167464
 			return -1;
bcf3dc79
 		    }
 		}
 	    }
08d6b1e3
 	}
 
3d167464
     } else { /* stream */
 	md5 = cli_md5stream(stdin, NULL);
 	if(!md5) {
efa239e2
 	    mprintf("!md5sig: Can't generate MD5 checksum for input stream\n");
3d167464
 	    return -1;
ae307914
 	}
efa239e2
 	mprintf("%s\n", md5);
3d167464
 	free(md5);
     }
8139fd99
 
3d167464
     return 0;
 }
8139fd99
 
d6af38e7
 static int htmlnorm(const struct optstruct *opts)
3d167464
 {
 	int fd;
49cc1e3c
 	fmap_t *map;
0f387b1b
 
d6af38e7
     if((fd = open(optget(opts, "html-normalise")->strarg, O_RDONLY)) == -1) {
 	mprintf("!htmlnorm: Can't open file %s\n", optget(opts, "html-normalise")->strarg);
3d167464
 	return -1;
     }
55216b6e
 
084d19aa
     if((map = fmap(fd, 0, 0))) {
 	html_normalise_map(map, ".", NULL, NULL);
49cc1e3c
 	funmap(map);
084d19aa
     } else
 	mprintf("!fmap failed\n");
 	
3d167464
     close(fd);
55216b6e
 
3d167464
     return 0;
 }
8139fd99
 
d6af38e7
 static int utf16decode(const struct optstruct *opts)
ec5e029e
 {
 	const char *fname;
 	char *newname, buff[512], *decoded;
4f411f34
 	int fd1, fd2, bytes;
ec5e029e
 
 
d6af38e7
     fname = optget(opts, "utf16-decode")->strarg;
ec5e029e
     if((fd1 = open(fname, O_RDONLY)) == -1) {
 	mprintf("!utf16decode: Can't open file %s\n", fname);
 	return -1;
     }
 
     newname = malloc(strlen(fname) + 7);
e18f93ee
     if(!newname) {
 	mprintf("!utf16decode: Can't allocate memory\n");
9b3e1e85
 	close(fd1);
e18f93ee
 	return -1;
     }
ec5e029e
     sprintf(newname, "%s.ascii", fname);
 
     if((fd2 = open(newname, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
 	mprintf("!utf16decode: Can't create file %s\n", newname);
 	free(newname);
 	close(fd1);
 	return -1;
     }
 
     while((bytes = read(fd1, buff, sizeof(buff))) > 0) {
 	decoded = cli_utf16toascii(buff, bytes);
 	if(decoded) {
 	    if(write(fd2, decoded, strlen(decoded)) == -1) {
 		mprintf("!utf16decode: Can't write to file %s\n", newname);
 		free(decoded);
 		unlink(newname);
 		free(newname);
 		close(fd1);
 		close(fd2);
 		return -1;
 	    }
 	    free(decoded);
 	}
     }
 
     free(newname);
     close(fd1);
     close(fd2);
 
     return 0;
 }
 
3d167464
 static unsigned int countlines(const char *filename)
 {
e18f93ee
 	FILE *fh;
3d167464
 	char buff[1024];
 	unsigned int lines = 0;
8139fd99
 
0a2ad257
 
e18f93ee
     if((fh = fopen(filename, "r")) == NULL)
3d167464
 	return 0;
0a2ad257
 
e18f93ee
     while(fgets(buff, sizeof(buff), fh)) {
3d167464
 	if(buff[0] == '#') continue;
 	lines++;
     }
9d0b7ebd
 
e18f93ee
     fclose(fh);
3d167464
     return lines;
 }
9d0b7ebd
 
e18f93ee
 static char *getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode)
3d167464
 {
5b68299b
 	char buff[512], cmd[128], pass[30], *pt;
3d167464
         struct sockaddr_in server;
 	int sockd, bread, len;
f0d0a4c0
 #ifdef HAVE_TERMIOS_H
 	struct termios old, new;
 #endif
9d0b7ebd
 
 
4f411f34
     if((pt = getenv("SIGNDPASS"))) {
 	strncpy(pass, pt, sizeof(pass));
72ce4b70
 	pass[sizeof(pass)-1]='\0';
4f411f34
     } else {
 	mprintf("Password: ");
 
 #ifdef HAVE_TERMIOS_H
 	if(tcgetattr(0, &old)) {
 	    mprintf("!getdsig: tcgetattr() failed\n");
 	    return NULL;
 	}
 	new = old;
 	new.c_lflag &= ~ECHO;
 	if(tcsetattr(0, TCSAFLUSH, &new)) {
 	    mprintf("!getdsig: tcsetattr() failed\n");
 	    return NULL;
 	}
 #endif
b2742f88
 	if(scanf("%as", &pt) == EOF || !pt) {
4f411f34
 	    mprintf("!getdsig: Can't get password\n");
e18f93ee
 #ifdef HAVE_TERMIOS_H
 	    tcsetattr(0, TCSAFLUSH, &old);
 #endif
4f411f34
 	    return NULL;
 	}
e18f93ee
 	strncpy(pass, pt, sizeof(pass));
72ce4b70
 	pass[sizeof(pass)-1]='\0';
e18f93ee
 	free(pt);
4f411f34
 
 #ifdef HAVE_TERMIOS_H
 	if(tcsetattr(0, TCSAFLUSH, &old)) {
797c5b1e
 	    mprintf("!getdsig: tcsetattr() failed\n");
72ce4b70
 	    memset(pass, 0, sizeof(pass));
4f411f34
 	    return NULL;
 	}
 #endif
 	mprintf("\n");
     }
 
3d167464
     if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 	perror("socket()");
efa239e2
 	mprintf("!getdsig: Can't create socket\n");
72ce4b70
 	memset(pass, 0, sizeof(pass));
3d167464
 	return NULL;
     }
9d0b7ebd
 
3d167464
     server.sin_family = AF_INET;
     server.sin_addr.s_addr = inet_addr(host);
     server.sin_port = htons(33101);
e3aaff8e
 
3d167464
     if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0) {
081f6473
         closesocket(sockd);
3d167464
 	perror("connect()");
efa239e2
 	mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host);
72ce4b70
 	memset(pass, 0, sizeof(pass));
3d167464
 	return NULL;
e3aaff8e
     }
3d167464
     memset(cmd, 0, sizeof(cmd));
f0d0a4c0
 
5b68299b
     if(mode == 1)
b2742f88
 	snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
     else if(mode == 2)
5b68299b
 	snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass);
     else
b2742f88
 	snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS2:%s:%s:", user, pass);
5b68299b
 
3d167464
     len = strlen(cmd);
     pt = cmd + len;
5b68299b
     memcpy(pt, data, datalen);
     len += datalen;
e4ae7726
 
081f6473
     if(send(sockd, cmd, len, 0) < 0) {
efa239e2
 	mprintf("!getdsig: Can't write to socket\n");
081f6473
 	closesocket(sockd);
72ce4b70
 	memset(cmd, 0, sizeof(cmd));
 	memset(pass, 0, sizeof(pass));
3d167464
 	return NULL;
     }
19e2ac07
 
72ce4b70
     memset(cmd, 0, sizeof(cmd));
     memset(pass, 0, sizeof(pass));
3d167464
     memset(buff, 0, sizeof(buff));
e4ae7726
 
081f6473
     if((bread = recv(sockd, buff, sizeof(buff), 0)) > 0) {
3d167464
 	if(!strstr(buff, "Signature:")) {
efa239e2
 	    mprintf("!getdsig: Error generating digital signature\n");
 	    mprintf("!getdsig: Answer from remote server: %s\n", buff);
081f6473
 	    closesocket(sockd);
3d167464
 	    return NULL;
 	} else {
5cd3f734
 	    mprintf("Signature received (length = %lu)\n", strlen(buff) - 10);
3d167464
 	}
827bb6ca
     } else {
 	mprintf("!getdsig: Communication error with remote server\n");
081f6473
 	closesocket(sockd);
827bb6ca
 	return NULL;
19e2ac07
     }
e4ae7726
 
081f6473
     closesocket(sockd);
827bb6ca
 
3d167464
     pt = buff;
     pt += 10;
     return strdup(pt);
e4ae7726
 }
 
b2742f88
 static char *sha256file(const char *file, unsigned int *size)
0a4afbbd
 {
 	FILE *fh;
b2742f88
 	unsigned int i, bytes;
 	unsigned char digest[32], buffer[FILEBUFF];
 	char *sha;
 	SHA256_CTX ctx;
 
 
     sha256_init(&ctx);
     if(!(fh = fopen(file, "r"))) {
 	mprintf("!sha256file: Can't open file %s\n", file);
 	return NULL;
     }
     if(size)
 	*size = 0;
     while((bytes = fread(buffer, 1, sizeof(buffer), fh))) {
 	sha256_update(&ctx, buffer, bytes);
 	if(size)
 	    *size += bytes;
     }
     sha256_final(&ctx, digest);
     sha = (char *) malloc(65);
     if(!sha)
 	return NULL;
     for(i = 0; i < 32; i++)
 	sprintf(sha + i * 2, "%02x", digest[i]);
     return sha;
 }
0a4afbbd
 
b2742f88
 static int writeinfo(const char *dbname, const char *builder, const char *header, const struct optstruct *opts)
 {
 	FILE *fh;
 	unsigned int i, bytes;
 	char file[32], *pt;
 	unsigned char digest[32], buffer[FILEBUFF];
 	SHA256_CTX ctx;
0a4afbbd
 
e18f93ee
     snprintf(file, sizeof(file), "%s.info", dbname);
     if(!access(file, R_OK)) {
0a4afbbd
 	if(unlink(file) == -1) {
efa239e2
 	    mprintf("!writeinfo: Can't unlink %s\n", file);
0a4afbbd
 	    return -1;
 	}
     }
 
b2742f88
     if(!(fh = fopen(file, "w+"))) {
efa239e2
 	mprintf("!writeinfo: Can't create file %s\n", file);
0a4afbbd
 	return -1;
     }
 
e3bcc33a
     if(fprintf(fh, "%s\n", header) < 0) {
 	mprintf("!writeinfo: Can't write to %s\n", file);
0a4afbbd
 	fclose(fh);
 	return -1;
     }
 
e18f93ee
     for(i = 0; dblist[i].name; i++) {
 	if(!cli_strbcasestr(dblist[i].name, ".info") && strstr(dblist[i].name, dbname) && !access(dblist[i].name, R_OK)) {
b2742f88
 	    if(!(pt = sha256file(dblist[i].name, &bytes))) {
 		mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
0a4afbbd
 		fclose(fh);
 		return -1;
 	    }
b2742f88
 	    if(fprintf(fh, "%s:%u:%s\n", dblist[i].name, bytes, pt) < 0) {
efa239e2
 		mprintf("!writeinfo: Can't write to info file\n");
0a4afbbd
 		fclose(fh);
b2742f88
 		free(pt);
0a4afbbd
 		return -1;
 	    }
b2742f88
 	    free(pt);
0a4afbbd
 	}
     }
 
b2742f88
     rewind(fh);
     sha256_init(&ctx);
     while((bytes = fread(buffer, 1, sizeof(buffer), fh)))
 	sha256_update(&ctx, buffer, bytes);
     sha256_final(&ctx, digest);
     if(!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 3))) {
 	mprintf("!writeinfo: Can't get digital signature from remote server\n");
 	fclose(fh);
 	return -1;
     }
26a59f82
     fprintf(fh, "DSIG:%s\n", pt);
b2742f88
     free(pt);
0a4afbbd
     fclose(fh);
     return 0;
 }
 
1aa405c3
 static int diffdirs(const char *old, const char *new, const char *patch);
5b68299b
 static int verifydiff(const char *diff, const char *cvd, const char *incdir);
 
d6af38e7
 static int script2cdiff(const char *script, const char *builder, const struct optstruct *opts)
5b68299b
 {
 	char *cdiff, *pt, buffer[FILEBUFF];
 	unsigned char digest[32];
 	SHA256_CTX ctx;
 	struct stat sb;
 	FILE *scripth, *cdiffh;
 	gzFile *gzh;
 	unsigned int ver, osize;
 	int bytes;
 
 
     if(stat(script, &sb) == -1) {
 	mprintf("!script2diff: Can't stat file %s\n", script);
 	return -1;
     }
     osize = (unsigned int) sb.st_size;
 
     cdiff = strdup(script);
     pt = strstr(cdiff, ".script");
     if(!pt) {
 	mprintf("!script2cdiff: Incorrect file name (no .script extension)\n");
 	free(cdiff);
 	return -1;
     }
     strcpy(pt, ".cdiff");
 
     if(!(pt = strchr(script, '-'))) {
 	mprintf("!script2cdiff: Incorrect file name syntax\n");
 	free(cdiff);
 	return -1;
     }
e18f93ee
 
     if(sscanf(++pt, "%u.script", &ver) == EOF) {
 	mprintf("!script2cdiff: Incorrect file name syntax\n");
 	free(cdiff);
 	return -1;
     }
5b68299b
 
     if(!(cdiffh = fopen(cdiff, "wb"))) {
 	mprintf("!script2cdiff: Can't open %s for writing\n", cdiff);
 	free(cdiff);
 	return -1;
     }
 
     if(fprintf(cdiffh, "ClamAV-Diff:%u:%u:", ver, osize) < 0) {
 	mprintf("!script2cdiff: Can't write to %s\n", cdiff);
 	fclose(cdiffh);
 	free(cdiff);
 	return -1;
     }
     fclose(cdiffh);
 
     if(!(scripth = fopen(script, "rb"))) {
 	mprintf("!script2cdiff: Can't open file %s for reading\n", script);
 	unlink(cdiff);
 	free(cdiff);
 	return -1;
     }
 
     if(!(gzh = gzopen(cdiff, "ab"))) {
 	mprintf("!script2cdiff: Can't open file %s for appending\n", cdiff);
 	unlink(cdiff);
 	free(cdiff);
 	fclose(scripth);
 	return -1;
     }
 
     while((bytes = fread(buffer, 1, sizeof(buffer), scripth)) > 0) {
 	if(!gzwrite(gzh, buffer, bytes)) {
 	    mprintf("!script2cdiff: Can't gzwrite to %s\n", cdiff);
 	    unlink(cdiff);
 	    free(cdiff);
 	    fclose(scripth);
 	    gzclose(gzh);
 	    return -1;
 	}
     }
     fclose(scripth);
     gzclose(gzh);
 
     if(!(cdiffh = fopen(cdiff, "rb"))) {
 	mprintf("!script2cdiff: Can't open %s for reading/writing\n", cdiff);
 	unlink(cdiff);
 	free(cdiff);
 	return -1;
     }
 
     sha256_init(&ctx);
 
     while((bytes = fread(buffer, 1, sizeof(buffer), cdiffh)))
 	sha256_update(&ctx, (unsigned char *) buffer, bytes);
 
     fclose(cdiffh);
4e46d65d
     sha256_final(&ctx, digest);
5b68299b
 
b2742f88
     if(!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 2))) {
5b68299b
 	mprintf("!script2cdiff: Can't get digital signature from remote server\n");
 	unlink(cdiff);
 	free(cdiff);
 	return -1;
     }
 
     if(!(cdiffh = fopen(cdiff, "ab"))) {
 	mprintf("!script2cdiff: Can't open %s for appending\n", cdiff);
 	unlink(cdiff);
 	free(cdiff);
 	return -1;
     }
     fprintf(cdiffh, ":%s", pt);
     free(pt);
     fclose(cdiffh);
 
     mprintf("Created %s\n", cdiff);
     free(cdiff);
 
     return 0;
 }
1aa405c3
 
d6af38e7
 static int build(const struct optstruct *opts)
8139fd99
 {
e18f93ee
 	int ret;
3d167464
 	size_t bytes;
e18f93ee
 	unsigned int i, sigs = 0, oldsigs = 0, lines = 0, version, real_header, fl;
8139fd99
 	struct stat foo;
e18f93ee
 	unsigned char buffer[FILEBUFF];
 	char *tarfile, header[513], smbuff[32], builder[32], *pt, olddb[512], patch[32], broken[32];
 	const char *dbname, *newcvd;
370892d0
         struct cl_engine *engine;
e18f93ee
 	FILE *cvd, *fh;
 	gzFile *tar;
8139fd99
 	time_t timet;
 	struct tm *brokent;
0a4afbbd
 	struct cl_cvd *oldcvd;
8139fd99
 
 
d6af38e7
     if(!optget(opts, "server")->enabled) {
efa239e2
 	mprintf("!build: --server is required for --build\n");
3d167464
 	return -1;
8139fd99
     }
 
3d167464
     if(stat("COPYING", &foo) == -1) {
efa239e2
 	mprintf("!build: COPYING file not found in current working directory.\n");
3d167464
 	return -1;
8139fd99
     }
 
03527bee
     dbname = getdbname(optget(opts, "build")->strarg);
8139fd99
 
b8fe70b3
     if(!(engine = cl_engine_new())) {
370892d0
 	mprintf("!build: Can't initialize antivirus engine\n");
 	return 50;
     }
 
     if((ret = cl_load(".", engine, &sigs, CL_DB_STDOPT | CL_DB_PUA))) {
efa239e2
 	mprintf("!build: Can't load database: %s\n", cl_strerror(ret));
370892d0
 	cl_engine_free(engine);
3d167464
 	return -1;
     }
370892d0
     cl_engine_free(engine);
8139fd99
 
3d167464
     if(!sigs) {
efa239e2
 	mprintf("!build: There are no signatures in database files\n");
0f387b1b
     } else {
e18f93ee
 	for(i = 0; dblist[i].name; i++)
 	    if(dblist[i].count && strstr(dblist[i].name, dbname) && !access(dblist[i].name, R_OK))
 		lines += countlines(dblist[i].name);
 
 	if(lines != sigs)
 	    mprintf("^build: Signatures in %s db files: %u, loaded by libclamav: %u\n", dbname, lines, sigs);
 
 	if(!lines || (sigs > lines && sigs - lines >= 1000)) {
 	    mprintf("!Bad number of signatures in database files\n");
 	    return -1;
0f387b1b
 	}
     }
8139fd99
 
0a4afbbd
     /* try to read cvd header of current database */
d6af38e7
     if(opts->filename) {
19c17946
 	if(cli_strbcasestr(opts->filename[0], ".cvd") || cli_strbcasestr(opts->filename[0], ".cld")) {
 	    strncpy(olddb, opts->filename[0], sizeof(olddb));
72ce4b70
 	    olddb[sizeof(olddb)-1]='\0';
c4cbb847
 	} else {
e18f93ee
 	    mprintf("!build: Not a CVD/CLD file\n");
c4cbb847
 	    return -1;
 	}
 
     } else {
 	pt = freshdbdir();
58481352
 	snprintf(olddb, sizeof(olddb), "%s"PATHSEP"%s.cvd", pt, dbname);
e18f93ee
 	if(access(olddb, R_OK))
58481352
 	    snprintf(olddb, sizeof(olddb), "%s"PATHSEP"%s.cld", pt, dbname);
c4cbb847
 	free(pt);
     }
1aa405c3
 
     if(!(oldcvd = cl_cvdhead(olddb))) {
e18f93ee
 	mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s (wait 3 s)\n", olddb);
0a4afbbd
 	sleep(3);
     }
 
     if(oldcvd) {
 	version = oldcvd->version + 1;
b336436b
 	oldsigs = oldcvd->sigs;
0a4afbbd
 	cl_cvdfree(oldcvd);
     } else {
efa239e2
 	mprintf("Version number: ");
e18f93ee
 	if(scanf("%u", &version) == EOF) {
 	    mprintf("!build: scanf() failed\n");
 	    return -1;
 	}
0a4afbbd
     }
 
b336436b
     mprintf("Total sigs: %u\n", sigs);
     if(sigs > oldsigs)
 	mprintf("New sigs: %u\n", sigs - oldsigs);
 
e3bcc33a
     strcpy(header, "ClamAV-VDB:");
 
     /* time */
     time(&timet);
     brokent = localtime(&timet);
     setlocale(LC_TIME, "C");
     strftime(smbuff, sizeof(smbuff), "%d %b %Y %H-%M %z", brokent);
     strcat(header, smbuff);
 
     /* version */
e18f93ee
     sprintf(header + strlen(header), ":%u:", version);
e3bcc33a
 
     /* number of signatures */
e18f93ee
     sprintf(header + strlen(header), "%u:", sigs);
e3bcc33a
 
     /* functionality level */
d08d2bd0
     if(!strcmp(dbname, "main")) {
 	mprintf("Functionality level: ");
e18f93ee
 	if(scanf("%u", &fl) == EOF || !fl || fl > 99) {
a47ed53d
 	    mprintf("!build: Incorrect functionality level\n");
 	    return -1;
 	}
d08d2bd0
     } else {
 	fl = cl_retflevel();
     }
e18f93ee
     sprintf(header + strlen(header), "%u:", fl);
e3bcc33a
 
     real_header = strlen(header);
 
     /* add fake MD5 and dsig (for writeinfo) */
     strcat(header, "X:X:");
 
4f411f34
     if((pt = getenv("SIGNDUSER"))) {
 	strncpy(builder, pt, sizeof(builder));
72ce4b70
 	builder[sizeof(builder)-1]='\0';
e3bcc33a
     } else {
4f411f34
 	mprintf("Builder name: ");
b2742f88
 	if(scanf("%as", &pt) == EOF || !pt) {
4f411f34
 	    mprintf("!build: Can't get builder name\n");
 	    return -1;
 	}
e18f93ee
 	strncpy(builder, pt, sizeof(builder));
72ce4b70
 	builder[sizeof(builder)-1]='\0';
e18f93ee
 	free(pt);
e3bcc33a
     }
 
     /* add builder */
     strcat(header, builder);
 
     /* add current time */
e18f93ee
     sprintf(header + strlen(header), ":%u", (unsigned int) timet);
e3bcc33a
 
b2742f88
     if(writeinfo(dbname, builder, header, opts) == -1) {
efa239e2
 	mprintf("!build: Can't generate info file\n");
0a4afbbd
 	return -1;
     }
 
e3bcc33a
     header[real_header] = 0;
 
3d167464
     if(!(tarfile = cli_gentemp("."))) {
efa239e2
 	mprintf("!build: Can't generate temporary name for tarfile\n");
3d167464
 	return -1;
     }
8139fd99
 
e18f93ee
     if((tar = gzopen(tarfile, "wb")) == NULL) {
 	mprintf("!build: Can't open file %s for writing\n", tarfile);
3d167464
 	free(tarfile);
 	return -1;
8139fd99
     }
 
e18f93ee
     if(tar_addfile(-1, tar, "COPYING") == -1) {
 	mprintf("!build: Can't add COPYING to tar archive\n");
 	gzclose(tar);
 	unlink(tarfile);
3d167464
 	free(tarfile);
 	return -1;
     }
 
e18f93ee
     for(i = 0; dblist[i].name; i++) {
 	if(strstr(dblist[i].name, dbname) && !access(dblist[i].name, R_OK)) {
 	    if(tar_addfile(-1, tar, dblist[i].name) == -1) {
 		gzclose(tar);
 		unlink(tarfile);
 		free(tarfile);
 		return -1;
 	    }
3d167464
 	}
     }
e18f93ee
     gzclose(tar);
8139fd99
 
e18f93ee
     /* MD5 + dsig */
     if(!(fh = fopen(tarfile, "rb"))) {
 	mprintf("!build: Can't open file %s for reading\n", tarfile);
 	unlink(tarfile);
 	free(tarfile);
3d167464
 	return -1;
     }
 
e18f93ee
     if(!(pt = cli_md5stream(fh, buffer))) {
 	mprintf("!build: Can't generate MD5 checksum for %s\n", tarfile);
 	fclose(fh);
 	unlink(tarfile);
 	free(tarfile);
3d167464
 	return -1;
     }
e18f93ee
     rewind(fh);
     sprintf(header + strlen(header), "%s:", pt);
335d1663
     free(pt);
3d167464
 
b2742f88
     if(!(pt = getdsig(optget(opts, "server")->strarg, builder, buffer, 16, 1))) {
efa239e2
 	mprintf("!build: Can't get digital signature from remote server\n");
e18f93ee
 	fclose(fh);
 	unlink(tarfile);
 	free(tarfile);
3d167464
 	return -1;
ae307914
     }
e18f93ee
     sprintf(header + strlen(header), "%s:", pt);
ae307914
     free(pt);
8139fd99
 
3365db23
     /* add builder */
e3bcc33a
     strcat(header, builder);
8139fd99
 
3365db23
     /* add current time */
e18f93ee
     sprintf(header + strlen(header), ":%u", (unsigned int) timet);
3365db23
 
8139fd99
     /* fill up with spaces */
3d167464
     while(strlen(header) < sizeof(header) - 1)
658f19f8
 	strcat(header, " ");
8139fd99
 
     /* build the final database */
d6af38e7
     newcvd = optget(opts, "build")->strarg;
e18f93ee
     if(!(cvd = fopen(newcvd, "wb"))) {
 	mprintf("!build: Can't create final database %s\n", newcvd);
 	fclose(fh);
 	unlink(tarfile);
 	free(tarfile);
3d167464
 	return -1;
8139fd99
     }
 
3d167464
     if(fwrite(header, 1, 512, cvd) != 512) {
e18f93ee
 	mprintf("!build: Can't write to %s\n", newcvd);
 	fclose(fh);
 	unlink(tarfile);
 	free(tarfile);
3d167464
 	fclose(cvd);
e18f93ee
 	unlink(newcvd);
3d167464
 	return -1;
8139fd99
     }
 
e18f93ee
     while((bytes = fread(buffer, 1, FILEBUFF, fh)) > 0) {
3d167464
 	if(fwrite(buffer, 1, bytes, cvd) != bytes) {
e18f93ee
 	    mprintf("!build: Can't write to %s\n", newcvd);
 	    fclose(fh);
 	    unlink(tarfile);
 	    free(tarfile);
3d167464
 	    fclose(cvd);
e18f93ee
 	    unlink(newcvd);
3d167464
 	    return -1;
 	}
     }
8139fd99
 
e18f93ee
     fclose(fh);
8139fd99
     fclose(cvd);
e18f93ee
 
     if(unlink(tarfile) == -1) {
 	mprintf("^build: Can't unlink %s\n", tarfile);
 	unlink(tarfile);
 	free(tarfile);
 	unlink(newcvd);
3d167464
 	return -1;
     }
e18f93ee
     free(tarfile);
8139fd99
 
e18f93ee
     mprintf("Created %s\n", newcvd);
3d167464
 
03527bee
     if(optget(opts, "no-cdiff")->enabled) {
 	mprintf("Skipping .cdiff creation\n");
 	return 0;
     }
 
1aa405c3
     /* generate patch */
e18f93ee
     if(!(pt = cli_gentemp(NULL))) {
 	mprintf("!build: Can't generate temporary name\n");
 	unlink(newcvd);
 	return -1;
c4cbb847
     }
1aa405c3
 
e18f93ee
     if(mkdir(pt, 0700)) {
 	mprintf("!build: Can't create temporary directory %s\n", pt);
 	free(pt);
 	unlink(newcvd);
 	return -1;
     }
 
afff80ef
     if(cli_cvdunpack(olddb, pt) == -1) {
e18f93ee
 	mprintf("!build: Can't unpack CVD file %s\n", olddb);
 	cli_rmdirs(pt);
 	free(pt);
 	unlink(newcvd);
 	return -1;
     }
     strncpy(olddb, pt, sizeof(olddb));
72ce4b70
     olddb[sizeof(olddb)-1]='\0';
e18f93ee
     free(pt);
 
     if(!(pt = cli_gentemp(NULL))) {
 	mprintf("!build: Can't generate temporary name\n");
 	cli_rmdirs(olddb);
 	unlink(newcvd);
 	return -1;
1aa405c3
     }
 
     if(mkdir(pt, 0700)) {
 	mprintf("!build: Can't create temporary directory %s\n", pt);
 	free(pt);
e18f93ee
 	cli_rmdirs(olddb);
 	unlink(newcvd);
1aa405c3
 	return -1;
     }
e18f93ee
 
afff80ef
     if(cli_cvdunpack(newcvd, pt) == -1) {
e18f93ee
 	mprintf("!build: Can't unpack CVD file %s\n", newcvd);
66ba1785
 	cli_rmdirs(pt);
1aa405c3
 	free(pt);
e18f93ee
 	cli_rmdirs(olddb);
 	unlink(newcvd);
1aa405c3
 	return -1;
     }
 
03527bee
     snprintf(patch, sizeof(patch), "%s-%u.script", dbname, version);
1aa405c3
     ret = diffdirs(olddb, pt, patch);
 
66ba1785
     cli_rmdirs(pt);
1aa405c3
     free(pt);
 
     if(ret == -1) {
e18f93ee
 	cli_rmdirs(olddb);
 	unlink(newcvd);
1aa405c3
 	return -1;
     }
 
5b68299b
     ret = verifydiff(patch, NULL, olddb);
e18f93ee
     cli_rmdirs(olddb);
1aa405c3
 
     if(ret == -1) {
 	snprintf(broken, sizeof(broken), "%s.broken", patch);
 	if(rename(patch, broken)) {
 	    unlink(patch);
 	    mprintf("!Generated file is incorrect, removed");
 	} else {
 	    mprintf("!Generated file is incorrect, renamed to %s\n", broken);
 	}
5b68299b
     } else {
d6af38e7
 	ret = script2cdiff(patch, builder, opts);
1aa405c3
     }
 
     return ret;
3d167464
 }
 
d6af38e7
 static int unpack(const struct optstruct *opts)
3d167464
 {
e18f93ee
 	char name[512], *dbdir;
3d167464
 
 
d6af38e7
     if(optget(opts, "unpack-current")->enabled) {
3d167464
 	dbdir = freshdbdir();
58481352
 	snprintf(name, sizeof(name), "%s"PATHSEP"%s.cvd", dbdir, optget(opts, "unpack-current")->strarg);
e18f93ee
 	if(access(name, R_OK)) {
58481352
 	    snprintf(name, sizeof(name), "%s"PATHSEP"%s.cld", dbdir, optget(opts, "unpack-current")->strarg);
e18f93ee
 	    if(access(name, R_OK)) {
d6af38e7
 		mprintf("!unpack: Couldn't find %s CLD/CVD database\n", optget(opts, "unpack-current")->strarg);
1aa405c3
 		free(dbdir);
 		return -1;
 	    }
 	}
3d167464
 	free(dbdir);
1aa405c3
 
e18f93ee
     } else {
d6af38e7
 	strncpy(name, optget(opts, "unpack")->strarg, sizeof(name));
72ce4b70
 	name[sizeof(name)-1]='\0';
e18f93ee
     }
8139fd99
 
afff80ef
     if(cli_cvdunpack(name, ".") == -1) {
e18f93ee
 	mprintf("!unpack: Can't unpack file %s\n", name);
3d167464
 	return -1;
     }
 
2d70a403
     return 0;
8139fd99
 }
 
d6af38e7
 static int cvdinfo(const struct optstruct *opts)
8139fd99
 {
 	struct cl_cvd *cvd;
 	char *pt;
6a2532ca
 	int ret;
8139fd99
 
3d167464
 
d6af38e7
     pt = optget(opts, "info")->strarg;
6a2532ca
     if((cvd = cl_cvdhead(pt)) == NULL) {
efa239e2
 	mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt);
3d167464
 	return -1;
8139fd99
     }
d22970ec
     mprintf("File: %s\n", pt);
8139fd99
 
3d167464
     pt = strchr(cvd->time, '-');
     *pt = ':';
efa239e2
     mprintf("Build time: %s\n", cvd->time);
e18f93ee
     mprintf("Version: %u\n", cvd->version);
     mprintf("Signatures: %u\n", cvd->sigs);
     mprintf("Functionality level: %u\n", cvd->fl);
efa239e2
     mprintf("Builder: %s\n", cvd->builder);
e18f93ee
 
d6af38e7
     pt = optget(opts, "info")->strarg;
e18f93ee
     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;
 	}
     }
6a2532ca
 
3d167464
     cl_cvdfree(cvd);
     return 0;
8139fd99
 }
 
b4561aa2
 static int listdb(const char *filename, const regex_t *regex);
ae307914
 
b4561aa2
 static int listdir(const char *dirname, const regex_t *regex)
3d167464
 {
 	DIR *dd;
 	struct dirent *dent;
 	char *dbfile;
ae307914
 
3d167464
     if((dd = opendir(dirname)) == NULL) {
efa239e2
         mprintf("!listdir: Can't open directory %s\n", dirname);
3d167464
         return -1;
ae307914
     }
 
3d167464
     while((dent = readdir(dd))) {
 	if(dent->d_ino)
 	{
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
 	     cli_strbcasestr(dent->d_name, ".hdb") ||
70edb085
 	     cli_strbcasestr(dent->d_name, ".hdu") ||
c09d6c19
 	     cli_strbcasestr(dent->d_name, ".mdb") ||
70edb085
 	     cli_strbcasestr(dent->d_name, ".mdu") ||
3d167464
 	     cli_strbcasestr(dent->d_name, ".ndb") ||
70edb085
 	     cli_strbcasestr(dent->d_name, ".ndu") ||
1437615c
 	     cli_strbcasestr(dent->d_name, ".ldb") ||
 	     cli_strbcasestr(dent->d_name, ".ldu") ||
0a4afbbd
 	     cli_strbcasestr(dent->d_name, ".sdb") ||
3d167464
 	     cli_strbcasestr(dent->d_name, ".zmd") ||
 	     cli_strbcasestr(dent->d_name, ".rmd") ||
af0c76f1
 	     cli_strbcasestr(dent->d_name, ".cld") ||
3d167464
 	     cli_strbcasestr(dent->d_name, ".cvd"))) {
ae307914
 
8ca8a18e
 		dbfile = (char *) malloc(strlen(dent->d_name) + strlen(dirname) + 2);
3d167464
 		if(!dbfile) {
efa239e2
 		    mprintf("!listdir: Can't allocate memory for dbfile\n");
3d167464
 		    closedir(dd);
 		    return -1;
 		}
58481352
 		sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
ae307914
 
b4561aa2
 		if(listdb(dbfile, regex) == -1) {
efa239e2
 		    mprintf("!listdb: Error listing database %s\n", dbfile);
3d167464
 		    free(dbfile);
 		    closedir(dd);
 		    return -1;
 		}
 		free(dbfile);
 	    }
2d70a403
 	}
     }
ae307914
 
3d167464
     closedir(dd);
     return 0;
0f387b1b
 }
0a2ad257
 
b4561aa2
 static int listdb(const char *filename, const regex_t *regex)
0a2ad257
 {
e18f93ee
 	FILE *fh;
3d167464
 	char *buffer, *pt, *start, *dir;
e18f93ee
 	unsigned int line = 0;
0a2ad257
 
 
e18f93ee
     if((fh = fopen(filename, "rb")) == NULL) {
efa239e2
 	mprintf("!listdb: Can't open file %s\n", filename);
0a2ad257
 	return -1;
     }
 
8ca8a18e
     if(!(buffer = (char *) malloc(FILEBUFF))) {
efa239e2
 	mprintf("!listdb: Can't allocate memory for buffer\n");
e18f93ee
 	fclose(fh);
0a2ad257
 	return -1;
     }
 
     /* check for CVD file */
e18f93ee
     if(!fgets(buffer, 12, fh)) {
 	mprintf("!listdb: fgets failed\n");
9b3e1e85
 	free(buffer);
e18f93ee
 	fclose(fh);
 	return -1;
     }
     rewind(fh);
0a2ad257
 
     if(!strncmp(buffer, "ClamAV-VDB:", 11)) {
3d167464
 	free(buffer);
e18f93ee
 	fclose(fh);
0a2ad257
 
081f6473
 	if(!(dir = cli_gentemp(NULL))) {
efa239e2
 	    mprintf("!listdb: Can't generate temporary name\n");
0a2ad257
 	    return -1;
 	}
 
3d167464
 	if(mkdir(dir, 0700)) {
efa239e2
 	    mprintf("!listdb: Can't create temporary directory %s\n", dir);
0a2ad257
 	    free(dir);
 	    return -1;
 	}
 
afff80ef
 	if(cli_cvdunpack(filename, dir) == -1) {
efa239e2
 	    mprintf("!listdb: Can't unpack CVD file %s\n", filename);
66ba1785
 	    cli_rmdirs(dir);
0a2ad257
 	    free(dir);
 	    return -1;
 	}
 
 	/* list extracted directory */
b4561aa2
 	if(listdir(dir, regex) == -1) {
89f4fd0f
 	    mprintf("!listdb: Can't list directory %s\n", filename);
66ba1785
 	    cli_rmdirs(dir);
3d167464
 	    free(dir);
 	    return -1;
 	}
0a2ad257
 
66ba1785
 	cli_rmdirs(dir);
0a2ad257
 	free(dir);
 
 	return 0;
     }
 
3d167464
     if(cli_strbcasestr(filename, ".db")) { /* old style database */
3365db23
 
e18f93ee
 	while(fgets(buffer, FILEBUFF, fh)) {
b4561aa2
 	    if(regex) {
 		if(!cli_regexec(regex, buffer, 0, NULL, 0))
 		    mprintf("%s", buffer);
 		continue;
 	    }
3365db23
 	    line++;
 	    pt = strchr(buffer, '=');
 	    if(!pt) {
e18f93ee
 		mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
 		fclose(fh);
3365db23
 		free(buffer);
 		return -1;
 	    }
0a2ad257
 
3365db23
 	    start = buffer;
 	    *pt = 0;
0a2ad257
 
3365db23
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
efa239e2
 	    mprintf("%s\n", start);
0a2ad257
 	}
 
70edb085
     } else if(cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".hdu") || cli_strbcasestr(filename, ".mdb") || cli_strbcasestr(filename, ".mdu")) { /* hash database */
0a2ad257
 
e18f93ee
 	while(fgets(buffer, FILEBUFF, fh)) {
b4561aa2
 	    if(regex) {
 		if(!cli_regexec(regex, buffer, 0, NULL, 0))
 		    mprintf("%s", buffer);
 		continue;
 	    }
3365db23
 	    line++;
 	    cli_chomp(buffer);
 	    start = cli_strtok(buffer, 2, ":");
 
 	    if(!start) {
e18f93ee
 		mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
 		fclose(fh);
3365db23
 		free(buffer);
 		return -1;
 	    }
 
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
efa239e2
 	    mprintf("%s\n", start);
3365db23
 	    free(start);
 	}
0a2ad257
 
1437615c
     } else if(cli_strbcasestr(filename, ".ndb") || cli_strbcasestr(filename, ".ndu") || cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu") || cli_strbcasestr(filename, ".sdb") || cli_strbcasestr(filename, ".zmd") || cli_strbcasestr(filename, ".rmd")) {
7ec67e94
 
e18f93ee
 	while(fgets(buffer, FILEBUFF, fh)) {
b4561aa2
 	    if(regex) {
 		if(!cli_regexec(regex, buffer, 0, NULL, 0))
 		    mprintf("%s", buffer);
 		continue;
 	    }
7ec67e94
 	    line++;
 	    cli_chomp(buffer);
1437615c
 
 	    if(cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu"))
 		start = cli_strtok(buffer, 0, ";");
 	    else
 		start = cli_strtok(buffer, 0, ":");
7ec67e94
 
 	    if(!start) {
e18f93ee
 		mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
 		fclose(fh);
7ec67e94
 		free(buffer);
 		return -1;
 	    }
 
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
efa239e2
 	    mprintf("%s\n", start);
7ec67e94
 	    free(start);
 	}
0a2ad257
     }
e18f93ee
     fclose(fh);
0a2ad257
     free(buffer);
     return 0;
 }
 
b4561aa2
 static int listsigs(const struct optstruct *opts, int mode)
0a2ad257
 {
 	int ret;
 	const char *name;
98ce643b
 	char *dbdir;
d6af38e7
 	struct stat sb;
b4561aa2
 	regex_t reg;
0a2ad257
 
3d167464
 
b4561aa2
     if(mode == 0) {
 	name = optget(opts, "list-sigs")->strarg;
 	if(stat(name, &sb) == -1) {
 	    mprintf("--list-sigs: Can't get status of %s\n", name);
 	    return -1;
 	}
0a2ad257
 
b4561aa2
 	mprintf_stdout = 1;
 	if(S_ISDIR(sb.st_mode)) {
 	    if(!strcmp(name, DATADIR)) {
 		dbdir = freshdbdir();
 		ret = listdir(dbdir, NULL);
 		free(dbdir);
 	    } else {
 		ret = listdir(name, NULL);
 	    }
d6af38e7
 	} else {
b4561aa2
 	    ret = listdb(name, NULL);
d6af38e7
 	}
b4561aa2
 
98ce643b
     } else {
b4561aa2
 	if(cli_regcomp(&reg, optget(opts, "find-sigs")->strarg, REG_EXTENDED | REG_NOSUB) != 0) {
 	    mprintf("--find-sigs: Can't compile regex\n");
 	    return -1;
 	}
 	mprintf_stdout = 1;
 	dbdir = freshdbdir();
 	ret = listdir(dbdir, &reg);
 	free(dbdir);
 	cli_regfree(&reg);
98ce643b
     }
0a2ad257
 
3d167464
     return ret;
 }
 
d6af38e7
 static int vbadump(const struct optstruct *opts)
3d167464
 {
05cb44b2
 	int fd, hex_output;
3d167464
 	char *dir;
05cb44b2
 	const char *pt;
b0c7e984
 	struct uniq *vba = NULL;
034c02fd
 	cli_ctx ctx;
3d167464
 
 
d6af38e7
     if(optget(opts, "vba-hex")->enabled) {
3d167464
 	hex_output = 1;
d6af38e7
 	pt = optget(opts, "vba-hex")->strarg;
05cb44b2
     } else {
 	hex_output = 0;
d6af38e7
 	pt = optget(opts, "vba")->strarg;
05cb44b2
     }
3d167464
  
081f6473
     if((fd = open(pt, O_RDONLY|O_BINARY)) == -1) {
05cb44b2
 	mprintf("!vbadump: Can't open file %s\n", pt);
 	return -1;
3d167464
     }
 
     /* generate the temporary directory */
e18f93ee
     if(!(dir = cli_gentemp(NULL))) {
 	mprintf("!vbadump: Can't generate temporary name\n");
 	close(fd);
 	return -1;
     }
 
3d167464
     if(mkdir(dir, 0700)) {
efa239e2
 	mprintf("!vbadump: Can't create temporary directory %s\n", dir);
3d167464
 	free(dir);
 	close(fd);
         return -1;
     }
 
034c02fd
     ctx.fmap = cli_malloc(sizeof(struct F_MAP *));
     if(!ctx.fmap) {
 	printf("malloc failed\n");
 	return 1;
     }
     *ctx.fmap = fmap(fd, 0, 0);
     if(*ctx.fmap) {
 	printf("fmap failed\n");
 	return 1;
     }
     
     if(cli_ole2_extract(dir, NULL, &vba)) {
3d167464
 	cli_rmdirs(dir);
         free(dir);
 	close(fd);
         return -1;
     }
     close(fd);
72ce4b70
     if (vba) 
       sigtool_vba_scandir(dir, hex_output, vba);
66ba1785
     cli_rmdirs(dir);
3d167464
     free(dir);
     return 0;
ee039e40
 }
 
b2742f88
 static int comparesha(const char *dbname)
e669f71e
 {
b2742f88
 	char info[32], buff[FILEBUFF], *sha;
 	const char *tokens[3];
e669f71e
 	FILE *fh;
b2742f88
 	int ret = 0, tokens_count;
e669f71e
 
 
03527bee
     snprintf(info, sizeof(info), "%s.info", getdbname(dbname));
e669f71e
 
     if(!(fh = fopen(info, "r"))) {
 	mprintf("!verifydiff: Can't open %s\n", info);
 	return -1;
     }
 
     if(!fgets(buff, sizeof(buff), fh) || strncmp(buff, "ClamAV-VDB", 10)) {
 	mprintf("!verifydiff: Incorrect info file %s\n", info);
 	fclose(fh);
 	return -1;
     }
 
     while(fgets(buff, sizeof(buff), fh)) {
 	cli_chomp(buff);
b2742f88
 	tokens_count = cli_strtokenize(buff, ':', 3, tokens);
 	if(tokens_count != 3) {
 	    if(!strcmp(tokens[0], "DSIG"))
 		continue;
e669f71e
 	    mprintf("!verifydiff: Incorrect format of %s\n", info);
 	    ret = -1;
 	    break;
 	}
b2742f88
 	if(!(sha = sha256file(tokens[0], NULL))) {
e669f71e
 	    mprintf("!verifydiff: Can't generate MD5 for %s\n", buff);
 	    ret = -1;
 	    break;
 	}
b2742f88
 	if(strcmp(sha, tokens[2])) {
e669f71e
 	    mprintf("!verifydiff: %s has incorrect checksum\n", buff);
 	    ret = -1;
b2742f88
 	    free(sha);
e669f71e
 	    break;
 	}
b2742f88
 	free(sha);
e669f71e
     }
 
     fclose(fh);
     return ret;
 }
 
 
d6af38e7
 static int rundiff(const struct optstruct *opts)
bb990ecd
 {
 	int fd, ret;
5b68299b
 	unsigned short mode;
 	const char *diff;
 
bb990ecd
 
d6af38e7
     diff = optget(opts, "run-cdiff")->strarg;
5b68299b
     if(strstr(diff, ".cdiff")) {
 	mode = 1;
     } else if(strstr(diff, ".script")) {
 	mode = 0;
     } else {
 	mprintf("!rundiff: Incorrect file name (no .cdiff/.script extension)\n");
 	return -1;
     }
bb990ecd
 
5b68299b
     if((fd = open(diff, O_RDONLY)) == -1) {
 	mprintf("!rundiff: Can't open file %s\n", diff);
bb990ecd
 	return -1;
6edef583
     }
bb990ecd
 
5b68299b
     ret = cdiff_apply(fd, mode);
bb990ecd
     close(fd);
 
e669f71e
     if(!ret)
b2742f88
 	ret = comparesha(diff);
e669f71e
 
bb990ecd
     return ret;
 }
 
37eca903
 static int compare(const char *oldpath, const char *newpath, FILE *diff)
 {
 	FILE *old, *new;
4367454d
 	char obuff[CLI_DEFAULT_LSIG_BUFSIZE + 1], nbuff[CLI_DEFAULT_LSIG_BUFSIZE + 1], tbuff[CLI_DEFAULT_LSIG_BUFSIZE + 1], *pt, *omd5, *nmd5;
fb30f402
 	unsigned int oline = 0, tline, found, i;
 	long opos;
37eca903
 
 
     if((omd5 = cli_md5file(oldpath))) {
 	if(!(nmd5 = cli_md5file(newpath))) {
 	    mprintf("!compare: Can't get MD5 checksum of %s\n", newpath);
 	    free(omd5);
 	    return -1;
 	}
 	if(!strcmp(omd5, nmd5)) {
 	    free(omd5);
 	    free(nmd5);
 	    return 0;
 	}
 	free(omd5);
 	free(nmd5);
     }
 
     fprintf(diff, "OPEN %s\n", newpath);
 
9b3e1e85
     if(!(new = fopen(newpath, "r"))) {
 	mprintf("!compare: Can't open file %s for reading\n", newpath);
 	return -1;
     }
37eca903
     old = fopen(oldpath, "r");
 
     while(fgets(nbuff, sizeof(nbuff), new)) {
37b12377
 	i = strlen(nbuff);
 	if(i >= 2 && (nbuff[i - 1] == '\r' || (nbuff[i - 1] == '\n' && nbuff[i - 2] == '\r'))) {
 	    mprintf("!compare: New %s file contains lines terminated with CRLF or CR\n", newpath);
 	    if(old)
 		fclose(old);
9b3e1e85
 	    fclose(new);
37b12377
 	    return -1;
 	}
37eca903
 	cli_chomp(nbuff);
 	if(!old) {
 	    fprintf(diff, "ADD %s\n", nbuff);
 	} else {
 	    if(fgets(obuff, sizeof(obuff), old)) {
fb30f402
 		oline++;
37eca903
 		cli_chomp(obuff);
 		if(!strcmp(nbuff, obuff)) {
 		    continue;
 		} else {
fb30f402
 		    tline = 0;
 		    found = 0;
 		    opos = ftell(old);
 		    while(fgets(tbuff, sizeof(tbuff), old)) {
 			tline++;
 			cli_chomp(tbuff);
 
 			if(tline > MAX_DEL_LOOKAHEAD)
 			    break;
 
 			if(!strcmp(tbuff, nbuff)) {
 			    found = 1;
 			    break;
 			}
 		    }
 		    fseek(old, opos, SEEK_SET);
 
 		    if(found) {
 			strncpy(tbuff, obuff, sizeof(tbuff));
72ce4b70
 			tbuff[sizeof(tbuff)-1]='\0';
fb30f402
 			for(i = 0; i < tline; i++) {
 			    tbuff[16] = 0;
 			    if((pt = strchr(tbuff, ' ')))
 				*pt = 0;
 			    fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
e18f93ee
 			    if(!fgets(tbuff, sizeof(tbuff), old))
 				break;
fb30f402
 			}
 			oline += tline;
 
 		    } else {
 			obuff[16] = 0;
37eca903
 			if((pt = strchr(obuff, ' ')))
 			    *pt = 0;
fb30f402
 			fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff);
37eca903
 		    }
 		}
 	    } else {
 		fclose(old);
 		old = NULL;
 		fprintf(diff, "ADD %s\n", nbuff);
 	    }
 	}
c65b5907
 #ifdef COMPATIBILITY_LIMIT
        if(strlen(nbuff) > COMPATIBILITY_LIMIT) {
            mprintf("!compare: COMPATIBILITY_LIMIT: Found too long line in new %s\n", newpath);
            if(old)
                fclose(old);
9b3e1e85
 	   fclose(new);
c65b5907
            return -1;
        }
 #endif
37eca903
     }
9b3e1e85
     fclose(new);
37eca903
 
fb30f402
     if(old) {
 	while(fgets(obuff, sizeof(obuff), old)) {
 	    oline++;
 	    obuff[16] = 0;
 	    if((pt = strchr(obuff, ' ')))
 		*pt = 0;
 	    fprintf(diff, "DEL %u %s\n", oline, obuff);
 	}
37eca903
 	fclose(old);
fb30f402
     }
 
37eca903
     fprintf(diff, "CLOSE\n");
     return 0;
 }
 
afff80ef
 static int dircopy(const char *src, const char *dest)
 {
 	DIR *dd;
 	struct dirent *dent;
 	struct stat sb;
 	char spath[512], dpath[512];
 
 
     if(stat(dest, &sb) == -1) {
 	if(mkdir(dest, 0755)) {
 	    /* mprintf("!dircopy: Can't create temporary directory %s\n", dest); */
 	    return -1;
 	}
     }
 
     if((dd = opendir(src)) == NULL) {
         /* mprintf("!dircopy: Can't open directory %s\n", src); */
         return -1;
     }
 
     while((dent = readdir(dd))) {
 	if(dent->d_ino)
 	{
 	    if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 		continue;
 
58481352
 	    snprintf(spath, sizeof(spath), "%s"PATHSEP"%s", src, dent->d_name);
 	    snprintf(dpath, sizeof(dpath), "%s"PATHSEP"%s", dest, dent->d_name);
afff80ef
 
 	    if(filecopy(spath, dpath) == -1) {
 		/* mprintf("!dircopy: Can't copy %s to %s\n", spath, dpath); */
 		cli_rmdirs(dest);
 		closedir(dd);
 		return -1;
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
5b68299b
 static int verifydiff(const char *diff, const char *cvd, const char *incdir)
6edef583
 {
e669f71e
 	char *tempdir, cwd[512];
6edef583
 	int ret = 0, fd;
5b68299b
 	unsigned short mode;
 
6edef583
 
5b68299b
     if(strstr(diff, ".cdiff")) {
 	mode = 1;
     } else if(strstr(diff, ".script")) {
 	mode = 0;
     } else {
 	mprintf("!verifydiff: Incorrect file name (no .cdiff/.script extension)\n");
 	return -1;
     }
6edef583
 
     tempdir = cli_gentemp(NULL);
     if(!tempdir) {
5b68299b
 	mprintf("!verifydiff: Can't generate temporary name for tempdir\n");
6edef583
 	return -1;
     }
 
     if(mkdir(tempdir, 0700) == -1) {
5b68299b
 	mprintf("!verifydiff: Can't create directory %s\n", tempdir);
6edef583
 	free(tempdir);
 	return -1;
     }
 
1aa405c3
     if(cvd) {
afff80ef
 	if(cli_cvdunpack(cvd, tempdir) == -1) {
5b68299b
 	    mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd);
66ba1785
 	    cli_rmdirs(tempdir);
1aa405c3
 	    free(tempdir);
 	    return -1;
 	}
     } else {
 	if(dircopy(incdir, tempdir) == -1) {
5b68299b
 	    mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir);
66ba1785
 	    cli_rmdirs(tempdir);
1aa405c3
 	    free(tempdir);
 	    return -1;
 	}
6edef583
     }
 
9b3e1e85
     if(!getcwd(cwd, sizeof(cwd))) {
 	mprintf("!verifydiff: getcwd() failed\n");
66ba1785
 	cli_rmdirs(tempdir);
6edef583
 	free(tempdir);
 	return -1;
     }
 
9b3e1e85
     if((fd = open(diff, O_RDONLY)) == -1) {
 	mprintf("!verifydiff: Can't open diff file %s\n", diff);
e18f93ee
 	cli_rmdirs(tempdir);
 	free(tempdir);
 	return -1;
     }
6edef583
 
     if(chdir(tempdir) == -1) {
5b68299b
 	mprintf("!verifydiff: Can't chdir to %s\n", tempdir);
66ba1785
 	cli_rmdirs(tempdir);
6edef583
 	free(tempdir);
 	close(fd);
 	return -1;
     }
 
5b68299b
     if(cdiff_apply(fd, mode) == -1) {
 	mprintf("!verifydiff: Can't apply %s\n", diff);
e18f93ee
 	if(chdir(cwd) == -1)
 	    mprintf("^verifydiff: Can't chdir to %s\n", cwd);
66ba1785
 	cli_rmdirs(tempdir);
6edef583
 	free(tempdir);
 	close(fd);
 	return -1;
     }
     close(fd);
 
b2742f88
     ret = comparesha(diff);
6edef583
 
e18f93ee
     if(chdir(cwd) == -1)
 	mprintf("^verifydiff: Can't chdir to %s\n", cwd);
66ba1785
     cli_rmdirs(tempdir);
6edef583
     free(tempdir);
 
1aa405c3
     if(!ret) {
 	if(cvd)
 	    mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
 	else
b336436b
 	    mprintf("Verification: %s correctly applies to the previous version\n", diff);
1aa405c3
     }
6edef583
 
     return ret;
 }
 
a96eead4
 static int matchsig(const char *sig, int fd)
 {
 	struct cl_engine *engine;
 	int ret;
 
     if(!(engine = cl_engine_new())) {
 	mprintf("!matchsig: Can't create new engine\n");
 	return 0;
     }
 
     if(cli_initroots(engine, 0) != CL_SUCCESS) {
 	mprintf("!matchsig: cli_initroots() failed\n");
 	cl_engine_free(engine);
 	return 0;
     }
 
     if(cli_parse_add(engine->root[0], "test", sig, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
 	mprintf("!matchsig: Can't parse signature\n");
 	cl_engine_free(engine);
 	return 0;
     }
 
     if(cl_engine_compile(engine) != CL_SUCCESS) {
 	mprintf("!matchsig: Can't compile engine\n");
 	cl_engine_free(engine);
 	return 0;
     }
 
     lseek(fd, 0, SEEK_SET);
     ret = cl_scandesc(fd, NULL, NULL, engine, CL_SCAN_STDOPT);
     cl_engine_free(engine);
 
     return (ret == CL_VIRUS) ? 1 : 0;
 }
 
252a354b
 static char *decodehexstr(const char *hex, unsigned int *dlen)
93173872
 {
 	uint16_t *str16;
 	char *decoded;
 	unsigned int i, p = 0, wildcard = 0, len = strlen(hex)/2;
 
     str16 = cli_hex2ui(hex);
     if(!str16)
 	return NULL;
 
     for(i = 0; i < len; i++)
 	if(str16[i] & CLI_MATCH_WILDCARD)
 	    wildcard++;
 
7e8402ac
     decoded = calloc(len + 1 + wildcard * 32, sizeof(char));
93173872
 
     for(i = 0; i < len; i++) {
 	if(str16[i] & CLI_MATCH_WILDCARD) {
 	    switch(str16[i] & CLI_MATCH_WILDCARD) {
 		case CLI_MATCH_IGNORE:
6f003ed6
 		    p += sprintf(decoded + p, "{WILDCARD_IGNORE}");
7e8402ac
 		    break;
 
93173872
 		case CLI_MATCH_NIBBLE_HIGH:
6f003ed6
 		    p += sprintf(decoded + p, "{WILDCARD_NIBBLE_HIGH:0x%x}", str16[i] & 0x00f0);
7e8402ac
 		    break;
 
93173872
 		case CLI_MATCH_NIBBLE_LOW:
6f003ed6
 		    p += sprintf(decoded + p, "{WILDCARD_NIBBLE_LOW:0x%x}", str16[i] & 0x000f);
7e8402ac
 		    break;
 
93173872
 		default:
6f003ed6
 		    mprintf("!decodehexstr: Unknown wildcard (0x%x@%u)\n", str16[i] & CLI_MATCH_WILDCARD, i);
93173872
 		    free(decoded);
 		    return NULL;
 	    }
 	} else {
 	    decoded[p] = str16[i];
7e8402ac
 	    p++;
93173872
 	}
     }
 
252a354b
     if(dlen)
 	*dlen = p;
 
93173872
     return decoded;
 }
 
6f003ed6
 static char *decodehexspecial(const char *hex, unsigned int *dlen)
 {
 	char *pt, *start, *hexcpy, *decoded, *h, *c;
 	unsigned int i, len = 0, hlen, negative, altnum, alttype;
 	char *buff;
 
 
     hexcpy = strdup(hex);
     if(!hexcpy) {
 	mprintf("!decodehexspecial: strdup(hex) failed\n");
 	return NULL;
     }
     pt = strchr(hexcpy, '(');
     if(!pt) {
 	free(hexcpy);
 	return decodehexstr(hex, dlen);
     } else {
 	buff = calloc(strlen(hex) + 512, sizeof(char));
 	if(!buff) {
 	    mprintf("!decodehexspecial: Can't allocate memory for buff\n");
 	    return NULL;
 	}
 	start = hexcpy;
 	do {
 	    negative = 0;
 	    *pt++ = 0;
 	    if(!start) {
 		mprintf("!decodehexspecial: Unexpected EOL\n");
 		return NULL;
 	    }
 	    if(pt >= hexcpy + 2) {
 		if(pt[-2] == '!') {
 		    negative = 1;
 		    pt[-2] = 0;
 		}
 	    }
 	    if(!(decoded = decodehexstr(start, &hlen))) {
 		mprintf("!Decoding failed (1): %s\n", pt);
 		free(hexcpy);
 		return NULL;
 	    }
 	    memcpy(&buff[len], decoded, hlen);
 	    len += hlen;
 	    free(decoded);
 
 	    if(!(start = strchr(pt, ')'))) {
 		mprintf("!decodehexspecial: Missing closing parethesis\n");
 		free(hexcpy);
 		return NULL;
 	    }
 
 	    *start++ = 0;
 	    if(!strlen(pt)) {
3df571b3
 		mprintf("!decodehexspecial: Empty block\n");
6f003ed6
 		free(hexcpy);
 		return NULL;
 	    }
 
 	    if(!strcmp(pt, "B")) {
 		if(!*start) {
 		    if(negative)
 			len += sprintf(buff + len, "{NOT_BOUNDARY_RIGHT}");
 		    else
 			len += sprintf(buff + len, "{BOUNDARY_RIGHT}");
 		    continue;
 		} else if(pt - 1 == hexcpy) {
 		    if(negative)
 			len += sprintf(buff + len, "{NOT_BOUNDARY_LEFT}");
 		    else
 			len += sprintf(buff + len, "{BOUNDARY_LEFT}");
 		    continue;
 		}
 	    } else if(!strcmp(pt, "L")) {
 		if(!*start) {
 		    if(negative)
 			len += sprintf(buff + len, "{NOT_LINE_MARKER_RIGHT}");
 		    else
 			len += sprintf(buff + len, "{LINE_MARKER_RIGHT}");
 		    continue;
 		} else if(pt - 1 == hexcpy) {
 		    if(negative)
 			len += sprintf(buff + len, "{NOT_LINE_MARKER_LEFT}");
 		    else
 			len += sprintf(buff + len, "{LINE_MARKER_LEFT}");
 		    continue;
 		}
 	    } else {
 		altnum = 0;
 		for(i = 0; i < strlen(pt); i++)
 		    if(pt[i] == '|')
 			altnum++;
 
 		if(!altnum) {
3df571b3
 		    mprintf("!decodehexspecial: Empty block\n");
6f003ed6
 		    free(hexcpy);
 		    return NULL;
 		}
 		altnum++;
 
 		if(3 * altnum - 1 == (uint16_t) strlen(pt)) {
 		    alttype = 1; /* char */
 		    if(negative)
 			len += sprintf(buff + len, "{EXCLUDING_CHAR_ALTERNATIVE:");
 		    else
 			len += sprintf(buff + len, "{CHAR_ALTERNATIVE:");
 		} else {
 		    alttype = 2; /* str */
 		    if(negative)
 			len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
 		    else
 			len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
 		}
 
 		for(i = 0; i < altnum; i++) {
 		    if(!(h = cli_strtok(pt, i, "|"))) {
 			free(hexcpy);
 			return NULL;
 		    }
 
 		    if(!(c = cli_hex2str(h))) {
 			free(h);
 			free(hexcpy);
 			return NULL;
 		    }
 
 		    if(alttype == 1) {
 			buff[len++] = *c;
 		    } else {
 			memcpy(&buff[len], c, strlen(h) / 2);
 			len += strlen(h) / 2;
 		    }
 		    if(i + 1 != altnum)
 			buff[len++] = '|';
 		}
 		buff[len++] = '}';
 	    }
 	} while((pt = strchr(start, '(')));
 
 	if(start) {
 	    if(!(decoded = decodehexstr(start, &hlen))) {
 		mprintf("!Decoding failed (2)\n");
 		free(hexcpy);
 		return NULL;
 	    }
 	    memcpy(&buff[len], decoded, hlen);
 	    len += hlen;
 	}
     }
     free(hexcpy);
     if(dlen)
 	*dlen = len;
     return buff;
 }
 
7e8402ac
 static int decodehex(const char *hexsig)
93173872
 {
55d28283
 	char *pt, *hexcpy, *start, *n, *decoded;
7e8402ac
 	int asterisk = 0;
252a354b
 	unsigned int i, j, hexlen, dlen, parts = 0, bw;
93173872
 	int mindist = 0, maxdist = 0, error = 0;
 
 
     hexlen = strlen(hexsig);
ef1152a6
     if(strchr(hexsig, '{') || strchr(hexsig, '[')) {
6f003ed6
 	if(!(hexcpy = strdup(hexsig)))
7e8402ac
 	    return -1;
93173872
 
 	for(i = 0; i < hexlen; i++)
ef1152a6
 	    if(hexsig[i] == '{' || hexsig[i] == '[' || hexsig[i] == '*')
93173872
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	start = pt = hexcpy;
 	for(i = 1; i <= parts; i++) {
 	    if(i != parts) {
 		for(j = 0; j < strlen(start); j++) {
ef1152a6
 		    if(start[j] == '{' || start[j] == '[') {
93173872
 			asterisk = 0;
 			pt = start + j;
 			break;
 		    }
 		    if(start[j] == '*') {
 			asterisk = 1;
 			pt = start + j;
 			break;
 		    }
 		}
 		*pt++ = 0;
 	    }
 
7e8402ac
 	    if(mindist && maxdist) {
 		if(mindist == maxdist)
 		    mprintf("{WILDCARD_ANY_STRING(LENGTH==%u)}", mindist);
 		else
 		    mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u&&<=%u)}", mindist, maxdist);
 	    } else if(mindist)
 		mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u)}", mindist);
 	    else if(maxdist)
 		mprintf("{WILDCARD_ANY_STRING(LENGTH<=%u)}", maxdist);
 
6f003ed6
 	    if(!(decoded = decodehexspecial(start, &dlen))) {
55d28283
 		mprintf("!Decoding failed\n");
 		free(hexcpy);
 		return -1;
 	    }
252a354b
 	    bw = write(1, decoded, dlen);
55d28283
 	    free(decoded);
93173872
 
 	    if(i == parts)
 		break;
 
7e8402ac
 	    if(asterisk)
 		mprintf("{WILDCARD_ANY_STRING}");
 
93173872
 	    mindist = maxdist = 0;
 
 	    if(asterisk) {
 		start = pt;
 		continue;
 	    }
 
ef1152a6
 	    if(!(start = strchr(pt, '}')) && !(start = strchr(pt, ']'))) {
93173872
 		error = 1;
 		break;
 	    }
 	    *start++ = 0;
 
 	    if(!pt) {
 		error = 1;
 		break;
 	    }
 
 	    if(!strchr(pt, '-')) {
 		if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
 		    error = 1;
 		    break;
 		}
 	    } else {
 		if((n = cli_strtok(pt, 0, "-"))) {
 		    if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
 		if((n = cli_strtok(pt, 1, "-"))) {
 		    if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
 		if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
 		    error = 1;
 		    free(n);
 		    break;
 		}
 	    }
 	}
 
 	free(hexcpy);
 	if(error)
7e8402ac
 	    return -1;
93173872
 
     } else if(strchr(hexsig, '*')) {
 	for(i = 0; i < hexlen; i++)
 	    if(hexsig[i] == '*')
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	for(i = 1; i <= parts; i++) {
 	    if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
 		mprintf("!Can't extract part %u of partial signature\n", i);
7e8402ac
 		return -1;
93173872
 	    }
6f003ed6
 	    if(!(decoded = decodehexspecial(pt, &dlen))) {
55d28283
 		mprintf("!Decoding failed\n");
 		return -1;
 	    }
252a354b
 	    bw = write(1, decoded, dlen);
55d28283
 	    free(decoded);
7e8402ac
 	    if(i < parts)
 		mprintf("{WILDCARD_ANY_STRING}");
93173872
 	    free(pt);
 	}
 
     } else {
6f003ed6
 	if(!(decoded = decodehexspecial(hexsig, &dlen))) {
55d28283
 	    mprintf("!Decoding failed\n");
 	    return -1;
 	}
252a354b
 	bw = write(1, decoded, dlen);
55d28283
 	free(decoded);
93173872
     }
 
7e8402ac
     mprintf("\n");
     return 0;
93173872
 }
 
a96eead4
 static int decodesig(char *sig, int fd)
93173872
 {
7e8402ac
 	char *pt;
9c3bcd84
 	const char *tokens[68];
4cf78d9e
 	int tokens_count, subsigs, i, bc = 0;
93173872
 
     if(strchr(sig, ';')) { /* lsig */
9c3bcd84
         tokens_count = cli_strtokenize(sig, ';', 67 + 1, (const char **) tokens);
 	if(tokens_count < 4) {
 	    mprintf("!decodesig: Invalid or not supported signature format\n");
 	    return -1;
 	}
 	mprintf("VIRUS NAME: %s\n", tokens[0]);
4cf78d9e
 	if(strlen(tokens[0]) && strstr(tokens[0], ".{") && tokens[0][strlen(tokens[0]) - 1] == '}')
 	    bc = 1;
9c3bcd84
 	mprintf("TDB: %s\n", tokens[1]);
 	mprintf("LOGICAL EXPRESSION: %s\n", tokens[2]);
 	subsigs = cli_ac_chklsig(tokens[2], tokens[2] + strlen(tokens[2]), NULL, NULL, NULL, 1);
 	if(subsigs == -1) {
 	    mprintf("!decodesig: Broken logical expression\n");
 	    return -1;
 	}
 	subsigs++;
 	if(subsigs > 64) {
 	    mprintf("!decodesig: Too many subsignatures\n");
 	    return -1;
 	}
4cf78d9e
 	if(!bc && subsigs != tokens_count - 3) {
9c3bcd84
 	    mprintf("!decodesig: The number of subsignatures (==%u) doesn't match the IDs in the logical expression (==%u)\n", tokens_count - 3, subsigs);
 	    return -1;
 	}
4cf78d9e
 	for(i = 0; i < tokens_count - 3; i++) {
 	    if(i >= subsigs)
 		mprintf(" * BYTECODE SUBSIG\n");
 	    else
 		mprintf(" * SUBSIG ID %d\n", i);
9c3bcd84
 	    if((pt = strchr(tokens[3 + i], ':'))) {
 		*pt++ = 0;
7d54e231
 		mprintf(" +-> OFFSET: %s\n", tokens[3 + i]);
9c3bcd84
 	    } else {
 		mprintf(" +-> OFFSET: ANY\n");
 	    }
a96eead4
 	    if(fd == -1) {
 		mprintf(" +-> DECODED SUBSIGNATURE:\n");
7d54e231
 		decodehex(pt ? pt : tokens[3 + i]);
a96eead4
 	    } else {
7d54e231
 		mprintf(" +-> MATCH: %s\n", matchsig(pt ? pt : tokens[3 + i], fd) ? "YES" : "** NO **");
a96eead4
 	    }
9c3bcd84
 	}
93173872
     } else if(strchr(sig, ':')) { /* ndb */
b1cdc75a
 	tokens_count = cli_strtokenize(sig, ':', 6 + 1, tokens);
 	if(tokens_count < 4 || tokens_count > 6) {
 	    mprintf("!decodesig: Invalid or not supported signature format\n");
 	    mprintf("TOKENS COUNT: %u\n", tokens_count);
 	    return -1;
 	}
 	mprintf("VIRUS NAME: %s\n", tokens[0]);
 	if(tokens_count == 5)
 	    mprintf("FUNCTIONALITY LEVEL: >=%s\n", tokens[4]);
 	else if(tokens_count == 6)
 	    mprintf("FUNCTIONALITY LEVEL: %s..%s\n", tokens[4], tokens[5]);
 
 	if(!cli_isnumber(tokens[1])) {
 	    mprintf("!decodesig: Invalid target type\n");
 	    return -1;
 	}
 	mprintf("TARGET TYPE: ");
 	switch(atoi(tokens[1])) {
 	    case 0:
 		mprintf("ANY FILE\n");
 		break;
 	    case 1:
 		mprintf("PE\n");
 		break;
 	    case 2:
 		mprintf("OLE2\n");
 		break;
 	    case 3:
 		mprintf("HTML\n");
 		break;
 	    case 4:
 		mprintf("MAIL\n");
 		break;
 	    case 5:
 		mprintf("GRAPHICS\n");
 		break;
 	    case 6:
 		mprintf("ELF\n");
 		break;
 	    case 7:
 		mprintf("NORMALIZED ASCII TEXT\n");
 		break;
 	    case 8:
 		mprintf("DISASM DATA\n");
 		break;
 	    case 9:
 		mprintf("MACHO\n");
 		break;
 	    default:
 		mprintf("!decodesig: Invalid target type\n");
 		return -1;
 	}
 	mprintf("OFFSET: %s\n", tokens[2]);
a96eead4
 	if(fd == -1) {
 	    mprintf("DECODED SIGNATURE:\n");
 	    decodehex(tokens[3]);
 	} else {
 	    mprintf("MATCH: %s\n", matchsig(tokens[3], fd) ? "YES" : "** NO **");
 	}
93173872
     } else if((pt = strchr(sig, '='))) {
7e8402ac
 	*pt++ = 0;
 	mprintf("VIRUS NAME: %s\n", sig);
a96eead4
 	if(fd == -1) {
 	    mprintf("DECODED SIGNATURE:\n");
 	    decodehex(pt);
 	} else {
 	    mprintf("MATCH: %s\n", matchsig(pt, fd) ? "YES" : "** NO **");
 	}
93173872
     } else {
 	mprintf("decodesig: Not supported signature format\n");
 	return -1;
     }
7e8402ac
 
     return 0;
 }
 
 static int decodesigs(void)
 {
 	char buffer[32769];
 
     fflush(stdin);
     while(fgets(buffer, sizeof(buffer), stdin)) {
 	cli_chomp(buffer);
 	if(!strlen(buffer))
 	    break;
a96eead4
 	if(decodesig(buffer, -1) == -1)
7e8402ac
 	    return -1;
     }
     return 0;
93173872
 }
 
a96eead4
 static int testsigs(const struct optstruct *opts)
 {
 	char buffer[32769];
 	FILE *sigs;
 	int ret = 0, fd;
 
 
     if(!opts->filename) {
 	mprintf("!--test-sigs requires two arguments\n");
 	return -1;
     }
 
     if(cl_init(CL_INIT_DEFAULT) != CL_SUCCESS) {
 	mprintf("!testsigs: Can't initialize libclamav: %s\n", cl_strerror(ret));
 	return -1;
     }
 
     sigs = fopen(optget(opts, "test-sigs")->strarg, "rb");
     if(!sigs) {
 	mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
 	return -1;
     }
 
     fd = open(opts->filename[0], O_RDONLY|O_BINARY);
     if(fd == -1) {
 	mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
 	fclose(sigs);
 	return -1;
     }
 
     while(fgets(buffer, sizeof(buffer), sigs)) {
 	cli_chomp(buffer);
 	if(!strlen(buffer))
 	    break;
 	if(decodesig(buffer, fd) == -1) {
 	    ret = -1;
 	    break;
 	}
     }
 
     close(fd);
     fclose(sigs);
     return ret;
 }
 
1aa405c3
 static int diffdirs(const char *old, const char *new, const char *patch)
37eca903
 {
 	FILE *diff;
 	DIR *dd;
 	struct dirent *dent;
5dc9a067
 	char cwd[512], path[1024];
1aa405c3
 
 
e18f93ee
     if(!getcwd(cwd, sizeof(cwd))) {
 	mprintf("!diffdirs: getcwd() failed\n");
 	return -1;
     }
1aa405c3
 
     if(!(diff = fopen(patch, "w"))) {
         mprintf("!diffdirs: Can't open %s for writing\n", patch);
 	return -1;
     }
 
     if(chdir(new) == -1) {
 	mprintf("!diffdirs: Can't chdir to %s\n", new);
 	fclose(diff);
 	return -1;
     }
 
     if((dd = opendir(new)) == NULL) {
         mprintf("!diffdirs: Can't open directory %s\n", new);
 	fclose(diff);
 	return -1;
     }
 
     while((dent = readdir(dd))) {
 	if(dent->d_ino)
 	{
 	    if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 		continue;
 
58481352
 	    snprintf(path, sizeof(path), "%s"PATHSEP"%s", old, dent->d_name);
5dc9a067
 	    if(compare(path, dent->d_name, diff) == -1) {
37b12377
 		if(chdir(cwd) == -1)
 		    mprintf("^diffdirs: Can't chdir to %s\n", cwd);
1aa405c3
 		fclose(diff);
 		unlink(patch);
 		closedir(dd);
 		return -1;
 	    }
 	}
     }
5dc9a067
     closedir(dd);
 
     /* check for removed files */
     if((dd = opendir(old)) == NULL) {
         mprintf("!diffdirs: Can't open directory %s\n", old);
 	fclose(diff);
 	return -1;
     }
1aa405c3
 
5dc9a067
     while((dent = readdir(dd))) {
 	if(dent->d_ino)
 	{
 	    if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 		continue;
 
58481352
 	    snprintf(path, sizeof(path), "%s"PATHSEP"%s", new, dent->d_name);
5dc9a067
 	    if(access(path, R_OK))
 		fprintf(diff, "UNLINK %s\n", dent->d_name);
 	}
     }
1aa405c3
     closedir(dd);
 
     fclose(diff);
5b68299b
     mprintf("Generated diff file %s\n", patch);
e18f93ee
     if(chdir(cwd) == -1)
 	mprintf("^diffdirs: Can't chdir to %s\n", cwd);
1aa405c3
 
     return 0;
 }
 
d6af38e7
 static int makediff(const struct optstruct *opts)
1aa405c3
 {
 	char *odir, *ndir, name[32], broken[32];
37eca903
 	struct cl_cvd *cvd;
 	unsigned int oldver, newver;
1aa405c3
 	int ret;
37eca903
 
 
d6af38e7
     if(!opts->filename) {
37eca903
 	mprintf("!makediff: --diff requires two arguments\n");
 	return -1;
     }
 
19c17946
     if(!(cvd = cl_cvdhead(opts->filename[0]))) {
 	mprintf("!makediff: Can't read CVD header from %s\n", opts->filename[0]);
37eca903
 	return -1;
     }
     newver = cvd->version;
     free(cvd);
 
d6af38e7
     if(!(cvd = cl_cvdhead(optget(opts, "diff")->strarg))) {
 	mprintf("!makediff: Can't read CVD header from %s\n", optget(opts, "diff")->strarg);
37eca903
 	return -1;
     }
     oldver = cvd->version;
     free(cvd);
 
     if(oldver + 1 != newver) {
 	mprintf("!makediff: The old CVD must be %u\n", newver - 1);
 	return -1;
     }
 
     odir = cli_gentemp(NULL);
     if(!odir) {
 	mprintf("!makediff: Can't generate temporary name for odir\n");
 	return -1;
     }
 
     if(mkdir(odir, 0700) == -1) {
 	mprintf("!makediff: Can't create directory %s\n", odir);
 	free(odir);
 	return -1;
     }
 
afff80ef
     if(cli_cvdunpack(optget(opts, "diff")->strarg, odir) == -1) {
d6af38e7
 	mprintf("!makediff: Can't unpack CVD file %s\n", optget(opts, "diff")->strarg);
66ba1785
 	cli_rmdirs(odir);
37eca903
 	free(odir);
 	return -1;
     }
 
     ndir = cli_gentemp(NULL);
     if(!ndir) {
 	mprintf("!makediff: Can't generate temporary name for ndir\n");
66ba1785
 	cli_rmdirs(odir);
37eca903
 	free(odir);
 	return -1;
     }
 
     if(mkdir(ndir, 0700) == -1) {
 	mprintf("!makediff: Can't create directory %s\n", ndir);
 	free(ndir);
66ba1785
 	cli_rmdirs(odir);
37eca903
 	free(odir);
 	return -1;
     }
 
afff80ef
     if(cli_cvdunpack(opts->filename[0], ndir) == -1) {
19c17946
 	mprintf("!makediff: Can't unpack CVD file %s\n", opts->filename[0]);
66ba1785
 	cli_rmdirs(odir);
 	cli_rmdirs(ndir);
37eca903
 	free(odir);
 	free(ndir);
 	return -1;
     }
 
03527bee
     snprintf(name, sizeof(name), "%s-%u.script", getdbname(opts->filename[0]), newver);
1aa405c3
     ret = diffdirs(odir, ndir, name);
37eca903
 
66ba1785
     cli_rmdirs(odir);
     cli_rmdirs(ndir);
37eca903
     free(odir);
     free(ndir);
6edef583
 
1aa405c3
     if(ret == -1)
 	return -1;
6edef583
 
d6af38e7
     if(verifydiff(name, optget(opts, "diff")->strarg, NULL) == -1) {
6edef583
 	snprintf(broken, sizeof(broken), "%s.broken", name);
 	if(rename(name, broken)) {
 	    unlink(name);
 	    mprintf("!Generated file is incorrect, removed");
 	} else {
 	    mprintf("!Generated file is incorrect, renamed to %s\n", broken);
 	}
 	return -1;
     }
 
37eca903
     return 0;
 }
 
797c5b1e
 static void help(void)
ee039e40
 {
     mprintf("\n");
add738d2
     mprintf("             Clam AntiVirus: Signature Tool (sigtool)  %s\n", get_version());
6670464f
     printf("           By The ClamAV Team: http://www.clamav.net/team\n");
     printf("           (C) 2007-2009 Sourcefire, Inc. et al.\n\n");
ee039e40
 
     mprintf("    --help                 -h              show help\n");
     mprintf("    --version              -V              print version number and exit\n");
     mprintf("    --quiet                                be quiet, output only error messages\n");
     mprintf("    --debug                                enable debug messages\n");
     mprintf("    --stdout                               write to stdout instead of stderr\n");
     mprintf("    --hex-dump                             convert data from stdin to a hex\n");
     mprintf("                                           string and print it on stdout\n");
bcf3dc79
     mprintf("    --md5 [FILES]                          generate MD5 checksum from stdin\n");
     mprintf("                                           or MD5 sigs for FILES\n");
60892bc1
     mprintf("    --mdb [FILES]                          generate .mdb sigs\n");
08d6b1e3
     mprintf("    --html-normalise=FILE                  create normalised parts of HTML file\n");
ec5e029e
     mprintf("    --utf16-decode=FILE                    decode UTF16 encoded files\n");
ee039e40
     mprintf("    --info=FILE            -i FILE         print database information\n");
e18f93ee
     mprintf("    --build=NAME [cvd] -b NAME             build a CVD file\n");
03527bee
     mprintf("    --no-cdiff                             Don't generate .cdiff file\n");
ee039e40
     mprintf("    --server=ADDR                          ClamAV Signing Service address\n");
e18f93ee
     mprintf("    --unpack=FILE          -u FILE         Unpack a CVD/CLD file\n");
     mprintf("    --unpack-current=SHORTNAME             Unpack local CVD/CLD into cwd\n");
ee039e40
     mprintf("    --list-sigs[=FILE]     -l[FILE]        List signature names\n");
b4561aa2
     mprintf("    --find-sigs=REGEX      -fREGEX         Find signatures matching REGEX\n");
7e8402ac
     mprintf("    --decode-sigs                          Decode signatures from stdin\n");
a96eead4
     mprintf("    --test-sigs=DATABASE TARGET_FILE       Test signatures from DATABASE against TARGET_FILE\n");
9d0b7ebd
     mprintf("    --vba=FILE                             Extract VBA/Word6 macro code\n");
     mprintf("    --vba-hex=FILE                         Extract Word6 macro code with hex values\n");
37eca903
     mprintf("    --diff=OLD NEW         -d OLD NEW      Create diff for OLD and NEW CVDs\n");
bb990ecd
     mprintf("    --run-cdiff=FILE       -r FILE         Execute update script FILE in cwd\n");
e18f93ee
     mprintf("    --verify-cdiff=DIFF CVD/CLD            Verify DIFF against CVD/CLD\n");
ee039e40
     mprintf("\n");
 
3d167464
     return;
 }
 
 int main(int argc, char **argv)
 {
 	int ret = 1;
d6af38e7
         struct optstruct *opts;
f25fa788
 	struct stat sb;
d6af38e7
 
     opts = optparse(NULL, argc, argv, 1, OPT_SIGTOOL, 0, NULL);
     if(!opts) {
 	mprintf("!Can't parse command line options\n");
3d167464
 	return 1;
     }
 
d6af38e7
     if(optget(opts, "quiet")->enabled)
3d167464
 	mprintf_quiet = 1;
 
d6af38e7
     if(optget(opts, "stdout")->enabled)
3d167464
 	mprintf_stdout = 1;
 
d6af38e7
     if(optget(opts, "debug")->enabled)
3d167464
 	cl_debug();
 
d6af38e7
     if(optget(opts, "version")->enabled) {
0aa3ba06
 	print_version(NULL);
d6af38e7
 	optfree(opts);
3d167464
 	return 0;
     }
 
d6af38e7
     if(optget(opts, "help")->enabled) {
 	optfree(opts);
3d167464
     	help();
6edef583
 	return 0;
3d167464
     }
 
d6af38e7
     if(optget(opts, "hex-dump")->enabled)
3d167464
 	ret = hexdump();
d6af38e7
     else if(optget(opts, "md5")->enabled)
 	ret = md5sig(opts, 0);
     else if(optget(opts, "mdb")->enabled)
 	ret = md5sig(opts, 1);
     else if(optget(opts, "html-normalise")->enabled)
 	ret = htmlnorm(opts);
     else if(optget(opts, "utf16-decode")->enabled)
 	ret = utf16decode(opts);
     else if(optget(opts, "build")->enabled)
 	ret = build(opts);
     else if(optget(opts, "unpack")->enabled)
 	ret = unpack(opts);
     else if(optget(opts, "unpack-current")->enabled)
 	ret = unpack(opts);
     else if(optget(opts, "info")->enabled)
 	ret = cvdinfo(opts);
     else if(optget(opts, "list-sigs")->active)
b4561aa2
 	ret = listsigs(opts, 0);
     else if(optget(opts, "find-sigs")->active)
 	ret = listsigs(opts, 1);
7e8402ac
     else if(optget(opts, "decode-sigs")->active)
 	ret = decodesigs();
a96eead4
     else if(optget(opts, "test-sigs")->enabled)
 	ret = testsigs(opts);
d6af38e7
     else if(optget(opts, "vba")->enabled || optget(opts, "vba-hex")->enabled)
 	ret = vbadump(opts);
     else if(optget(opts, "diff")->enabled)
 	ret = makediff(opts);
     else if(optget(opts, "run-cdiff")->enabled)
 	ret = rundiff(opts);
     else if(optget(opts, "verify-cdiff")->enabled) {
 	if(!opts->filename) {
1aa405c3
 	    mprintf("!--verify-cdiff requires two arguments\n");
6edef583
 	    ret = -1;
 	} else {
19c17946
 	    if(stat(opts->filename[0], &sb) == -1) {
 		mprintf("--verify-cdiff: Can't get status of %s\n", opts->filename[0]);
f25fa788
 		ret = -1;
 	    } else {
 		if(S_ISDIR(sb.st_mode))
19c17946
 		    ret = verifydiff(optget(opts, "verify-cdiff")->strarg, NULL, opts->filename[0]);
f25fa788
 		else
19c17946
 		    ret = verifydiff(optget(opts, "verify-cdiff")->strarg, opts->filename[0], NULL);
f25fa788
 	    }
6edef583
 	}
     } else
3d167464
 	help();
 
d6af38e7
     optfree(opts);
3d167464
     return ret ? 1 : 0;
0a2ad257
 }