sigtool/sigtool.c
b151ef55
 /*
2bc31f05
  *  Copyright (C) 2002 - 2006 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
30738099
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
b151ef55
  */
 
 
8b242bb9
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b151ef55
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
4cd4319e
 #include <zlib.h>
 #include <time.h>
 #include <locale.h>
4bf2cf8d
 #include <sys/types.h>
f852d214
 #include <sys/stat.h>
 #include <fcntl.h>
4bf2cf8d
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
b151ef55
 #include <clamav.h>
e8217f5a
 #include <sys/wait.h>
d5314812
 #include <dirent.h>
b151ef55
 
052663e7
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
f58ad3be
 #include "vba.h"
b151ef55
 
9af907dc
 #include "shared/options.h"
 #include "shared/memory.h"
 #include "shared/output.h"
 #include "shared/cfgparser.h"
 #include "shared/misc.h"
5b09aa1f
 #include "shared/cdiff.h"
b151ef55
 
9af907dc
 #include "libclamav/cvd.h"
 #include "libclamav/others.h"
 #include "libclamav/str.h"
 #include "libclamav/ole2_extract.h"
 #include "libclamav/htmlnorm.h"
aa7d9cef
 #include "libclamav/sha256.h"
b151ef55
 
fc6e6260
 #define MAX_DEL_LOOKAHEAD   50
b151ef55
 
9af907dc
 static int hexdump(void)
 {
 	char buffer[FILEBUFF], *pt;
 	int bytes;
c0eb3ceb
 
b151ef55
 
9af907dc
     while((bytes = read(0, buffer, FILEBUFF)) > 0) {
 	pt = cli_str2hex(buffer, bytes);
 	if(write(1, pt, 2 * bytes) == -1) {
42ace199
 	    mprintf("!hexdump: Can't write to stdout\n");
9af907dc
 	    free(pt);
 	    return -1;
 	}
 	free(pt);
b151ef55
     }
 
9af907dc
     if(bytes == -1)
 	return -1;
b151ef55
 
9af907dc
     return 0;
 }
b151ef55
 
9af907dc
 static int md5sig(struct optstruct *opt)
 {
 	char *md5, *filename;
 	int i;
 	struct stat sb;
94da957a
 
25133aef
 
9af907dc
     if(opt->filename) {
 	for(i = 0; (filename = cli_strtok(opt->filename, i, "\t")); i++) {
 	    if(stat(filename, &sb) == -1) {
42ace199
 		mprintf("!md5sig: Can't access file %s\n", filename);
9af907dc
 		perror("md5sig");
 		free(filename);
 		return -1;
 	    } else {
 		if((sb.st_mode & S_IFMT) == S_IFREG) {
 		    if((md5 = cli_md5file(filename))) {
42ace199
 			mprintf("%s:%d:%s\n", md5, sb.st_size, filename);
9af907dc
 			free(md5);
 		    } else {
42ace199
 			mprintf("!md5sig: Can't generate MD5 checksum for %s\n", filename);
9af907dc
 			free(filename);
 			return -1;
25133aef
 		    }
 		}
 	    }
 
9af907dc
 	    free(filename);
f852d214
 	}
 
9af907dc
     } else { /* stream */
 	md5 = cli_md5stream(stdin, NULL);
 	if(!md5) {
42ace199
 	    mprintf("!md5sig: Can't generate MD5 checksum for input stream\n");
9af907dc
 	    return -1;
4bf2cf8d
 	}
42ace199
 	mprintf("%s\n", md5);
9af907dc
 	free(md5);
     }
4cd4319e
 
9af907dc
     return 0;
 }
4cd4319e
 
9af907dc
 static int htmlnorm(struct optstruct *opt)
 {
 	int fd;
42e6f5a6
 
 
9af907dc
     if((fd = open(opt_arg(opt, "html-normalise"), O_RDONLY)) == -1) {
42ace199
 	mprintf("!htmlnorm: Can't open file %s\n", opt_arg(opt, "html-normalise"));
9af907dc
 	return -1;
     }
e1ef4066
 
9af907dc
     html_normalise_fd(fd, ".", NULL);
     close(fd);
e1ef4066
 
9af907dc
     return 0;
 }
4cd4319e
 
9af907dc
 static unsigned int countlines(const char *filename)
 {
 	FILE *fd;
 	char buff[1024];
 	unsigned int lines = 0;
4cd4319e
 
d5314812
 
9af907dc
     if((fd = fopen(filename, "r")) == NULL)
 	return 0;
d5314812
 
9af907dc
     while(fgets(buff, sizeof(buff), fd)) {
 	if(buff[0] == '#') continue;
 	lines++;
     }
d6bf11e3
 
9af907dc
     fclose(fd);
     return lines;
 }
d6bf11e3
 
aa7d9cef
 static char *getdsig(const char *host, const char *user, const char *data, unsigned int datalen, unsigned short mode)
9af907dc
 {
aa7d9cef
 	char buff[512], cmd[128], pass[30], *pt;
9af907dc
         struct sockaddr_in server;
 	int sockd, bread, len;
052663e7
 #ifdef HAVE_TERMIOS_H
 	struct termios old, new;
 #endif
d6bf11e3
 
 
9af907dc
 #ifdef PF_INET
     if((sockd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
 #else
     if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 #endif
 	perror("socket()");
42ace199
 	mprintf("!getdsig: Can't create socket\n");
9af907dc
 	return NULL;
     }
d6bf11e3
 
9af907dc
     server.sin_family = AF_INET;
     server.sin_addr.s_addr = inet_addr(host);
     server.sin_port = htons(33101);
b151ef55
 
9af907dc
     if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0) {
         close(sockd);
 	perror("connect()");
42ace199
 	mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host);
9af907dc
 	return NULL;
b151ef55
     }
 
9af907dc
     memset(cmd, 0, sizeof(cmd));
052663e7
 
     fflush(stdin);
     mprintf("Password: ");
 
 #ifdef HAVE_TERMIOS_H
     if(tcgetattr(0, &old)) {
42ace199
 	mprintf("!getdsig: tcgetattr() failed\n", host);
052663e7
 	close(sockd);
 	return NULL;
     }
     new = old;
     new.c_lflag &= ~ECHO;
     if(tcsetattr(0, TCSAFLUSH, &new)) {
42ace199
 	mprintf("!getdsig: tcsetattr() failed\n", host);
052663e7
 	close(sockd);
 	return NULL;
     }
 #endif
 
     if(fgets(pass, sizeof(pass), stdin)) {
 	cli_chomp(pass);
     } else {
42ace199
 	mprintf("!getdsig: Can't get password\n");
052663e7
 	close(sockd);
 	return NULL;
     }
 
 #ifdef HAVE_TERMIOS_H
     if(tcsetattr(0, TCSAFLUSH, &old)) {
42ace199
 	mprintf("!getdsig: tcsetattr() failed\n", host);
052663e7
 	close(sockd);
 	return NULL;
     }
 #endif
     mprintf("\n");
 
aa7d9cef
     if(mode == 1)
 	snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass);
     else
 	snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
 
9af907dc
     len = strlen(cmd);
     pt = cmd + len;
aa7d9cef
     memcpy(pt, data, datalen);
     len += datalen;
442d8407
 
9af907dc
     if(write(sockd, cmd, len) < 0) {
42ace199
 	mprintf("!getdsig: Can't write to socket\n");
9af907dc
 	close(sockd);
 	memset(cmd, 0, len);
 	memset(pass, 0, strlen(pass));
 	return NULL;
     }
eceef468
 
9af907dc
     memset(cmd, 0, len);
     memset(pass, 0, strlen(pass));
     memset(buff, 0, sizeof(buff));
442d8407
 
9af907dc
     if((bread = read(sockd, buff, sizeof(buff))) > 0) {
 	if(!strstr(buff, "Signature:")) {
42ace199
 	    mprintf("!getdsig: Error generating digital signature\n");
 	    mprintf("!getdsig: Answer from remote server: %s\n", buff);
9af907dc
 	    close(sockd);
 	    return NULL;
 	} else {
aa7d9cef
 	   /* mprintf("Signature received (length = %d)\n", strlen(buff) - 10); */
9af907dc
 	}
eceef468
     }
442d8407
 
9af907dc
     close(sockd);
     pt = buff;
     pt += 10;
     return strdup(pt);
442d8407
 }
 
d1256351
 static int writeinfo(const char *db, const char *header)
28f082e2
 {
 	FILE *fh;
 	int i;
 	struct stat sb;
 	char file[32], *md5;
7a347227
 	char *extlist[] = { "db", "fp", "hdb", "mdb", "ndb", "pdb", "rmd", "zmd", "sdb", NULL };
28f082e2
 
 
     snprintf(file, sizeof(file), "%s.info", db);
     if(stat(file, &sb) != -1) {
 	if(unlink(file) == -1) {
42ace199
 	    mprintf("!writeinfo: Can't unlink %s\n", file);
28f082e2
 	    return -1;
 	}
     }
 
     if(!(fh = fopen(file, "w"))) {
42ace199
 	mprintf("!writeinfo: Can't create file %s\n", file);
28f082e2
 	return -1;
     }
 
d1256351
     if(fprintf(fh, "%s\n", header) < 0) {
 	mprintf("!writeinfo: Can't write to %s\n", file);
28f082e2
 	fclose(fh);
 	return -1;
     }
 
     for(i = 0; extlist[i]; i++) {
 	snprintf(file, sizeof(file), "%s.%s", db, extlist[i]);
 	if(stat(file, &sb) != -1) {
 	    if(!(md5 = cli_md5file(file))) {
42ace199
 		mprintf("!writeinfo: Can't generate MD5 checksum for %s\n", file);
28f082e2
 		fclose(fh);
 		return -1;
 	    }
 	    if(fprintf(fh, "%s.%s:%s\n", db, extlist[i], md5) < 0) {
42ace199
 		mprintf("!writeinfo: Can't write to info file\n");
28f082e2
 		fclose(fh);
 		free(md5);
 		return -1;
 	    }
 	    free(md5);
 	}
     }
 
     fclose(fh);
     return 0;
 }
 
8b0c26c2
 static int diffdirs(const char *old, const char *new, const char *patch);
aa7d9cef
 static int verifydiff(const char *diff, const char *cvd, const char *incdir);
 
 static int script2cdiff(const char *script, const char *builder, struct optstruct *opt)
 {
 	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;
     }
     sscanf(++pt, "%u.script", &ver);
 
     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);
     sha256_final(&ctx);
     sha256_digest(&ctx, digest);
 
     if(!(pt = getdsig(opt_arg(opt, "server"), builder, (char *) digest, 32, 1))) {
 	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;
 }
8b0c26c2
 
9af907dc
 static int build(struct optstruct *opt)
4cd4319e
 {
3b961196
 	int ret, inc = 1, dn;
9af907dc
 	size_t bytes;
56ee0a90
 	unsigned int sigs = 0, oldsigs = 0, lines = 0, version, real_header;
4cd4319e
 	struct stat foo;
8b0c26c2
 	char buffer[FILEBUFF], *tarfile, *gzfile, header[513], smbuff[32],
 	     builder[32], *pt, *dbname, olddb[512], patch[32], broken[32];
4cd4319e
         struct cl_node *root = NULL;
9af907dc
 	FILE *tar, *cvd;
4cd4319e
 	gzFile *gz;
 	time_t timet;
 	struct tm *brokent;
28f082e2
 	struct cl_cvd *oldcvd;
4cd4319e
 
 
9af907dc
     if(!opt_check(opt, "server")) {
42ace199
 	mprintf("!build: --server is required for --build\n");
9af907dc
 	return -1;
4cd4319e
     }
 
9af907dc
     if(stat("COPYING", &foo) == -1) {
42ace199
 	mprintf("!build: COPYING file not found in current working directory.\n");
9af907dc
 	return -1;
4cd4319e
     }
 
9af907dc
     if(stat("main.db", &foo) == -1 && stat("daily.db", &foo) == -1 &&
        stat("main.hdb", &foo) == -1 && stat("daily.hdb", &foo) == -1 &&
344416f6
        stat("main.mdb", &foo) == -1 && stat("daily.mdb", &foo) == -1 &&
9af907dc
        stat("main.ndb", &foo) == -1 && stat("daily.ndb", &foo) == -1 &&
7a347227
        stat("main.pdb", &foo) == -1 && stat("daily.pdb", &foo) == -1 &&
28f082e2
        stat("main.sdb", &foo) == -1 && stat("daily.sdb", &foo) == -1 &&
9af907dc
        stat("main.zmd", &foo) == -1 && stat("daily.zmd", &foo) == -1 &&
        stat("main.rmd", &foo) == -1 && stat("daily.rmd", &foo) == -1)
     {
42ace199
 	mprintf("!build: No virus database file  found in current directory\n");
9af907dc
 	return -1;
4cd4319e
     }
 
9af907dc
     if((ret = cl_loaddbdir(".", &root, &sigs))) {
42ace199
 	mprintf("!build: Can't load database: %s\n", cl_strerror(ret));
9af907dc
 	return -1;
     } else {
 	cl_free(root);
     }
4cd4319e
 
9af907dc
     if(!sigs) {
42ace199
 	mprintf("!build: There are no signatures in database files\n");
42e6f5a6
     } else {
9af907dc
 	lines = countlines("main.db") + countlines("daily.db") +
 		countlines("main.hdb") + countlines("daily.hdb") +
344416f6
 		countlines("main.mdb") + countlines("daily.mdb") +
9af907dc
 		countlines("main.ndb") + countlines("daily.ndb") +
28f082e2
 		countlines("main.sdb") + countlines("daily.sdb") +
9af907dc
 		countlines("main.zmd") + countlines("daily.zmd") +
 		countlines("main.rmd") + countlines("daily.rmd") +
 		countlines("main.fp") + countlines("daily.fp");
 
 	if(lines != sigs) {
42ace199
 	    mprintf("!build: Signatures in database: %d, loaded by libclamav: %d\n", lines, sigs);
 	    mprintf("!build: Please check the current directory and remove unnecessary databases\n");
 	    mprintf("!build: or install the latest ClamAV version.\n");
9af907dc
 	    return -1;
42e6f5a6
 	}
     }
4cd4319e
 
28f082e2
     /* try to read cvd header of current database */
8b0c26c2
     dbname = opt_arg(opt, "build");
     if(strstr(dbname, "main"))
 	dbname = "main";
     else
 	dbname = "daily";
 
     pt = freshdbdir();
     snprintf(olddb, sizeof(olddb), "%s/%s.inc/%s.info", pt, dbname, dbname);
     if(stat(olddb, &foo) == -1) {
 	inc = 0;
 	snprintf(olddb, sizeof(olddb), "%s/%s.cvd", pt, dbname);
     }
 
     free(pt);
 
     if(!(oldcvd = cl_cvdhead(olddb))) {
42ace199
 	mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s\n", buffer);
28f082e2
 	sleep(3);
     }
 
     if(oldcvd) {
 	version = oldcvd->version + 1;
56ee0a90
 	oldsigs = oldcvd->sigs;
28f082e2
 	cl_cvdfree(oldcvd);
     } else {
 	fflush(stdin);
42ace199
 	mprintf("Version number: ");
28f082e2
 	scanf("%u", &version);
     }
 
56ee0a90
     mprintf("Total sigs: %u\n", sigs);
     if(sigs > oldsigs)
 	mprintf("New sigs: %u\n", sigs - oldsigs);
 
d1256351
     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 */
     sprintf(smbuff, ":%d:", version);
     strcat(header, smbuff);
 
     /* number of signatures */
     sprintf(smbuff, "%d:", sigs);
     strcat(header, smbuff);
 
     /* functionality level */
     sprintf(smbuff, "%d:", cl_retflevel());
     strcat(header, smbuff);
 
     real_header = strlen(header);
 
     /* add fake MD5 and dsig (for writeinfo) */
     strcat(header, "X:X:");
 
     /* ask for builder name */
     fflush(stdin);
     mprintf("Builder name: ");
     if(fgets(builder, sizeof(builder), stdin)) {
 	cli_chomp(builder);
     } else {
 	mprintf("!build: Can't get builder name\n");
 	return -1;
     }
 
     /* add builder */
     strcat(header, builder);
 
     /* add current time */
     sprintf(header + strlen(header), ":%d", (int) timet);
 
8b0c26c2
     if(writeinfo(dbname, header) == -1) {
42ace199
 	mprintf("!build: Can't generate info file\n");
28f082e2
 	return -1;
     }
 
d1256351
     header[real_header] = 0;
 
9af907dc
     if(!(tarfile = cli_gentemp("."))) {
42ace199
 	mprintf("!build: Can't generate temporary name for tarfile\n");
9af907dc
 	return -1;
     }
4cd4319e
 
     switch(fork()) {
 	case -1:
42ace199
 	    mprintf("!build: Can't fork.\n");
9af907dc
 	    free(tarfile);
 	    return -1;
4cd4319e
 	case 0:
 	    {
9af907dc
 		char *args[] = { "tar", "-cvf", NULL, "COPYING", "main.db",
 				 "daily.db", "main.hdb", "daily.hdb",
28f082e2
 				 "main.ndb", "daily.ndb", "main.sdb",
 				 "daily.sdb", "main.zmd", "daily.zmd",
 				 "main.rmd", "daily.rmd", "main.fp",
344416f6
 				 "daily.fp", "main.mdb", "daily.mdb",
7a347227
 				 "daily.info", "main.info", "main.wdb",
 				 "daily.wdb", "main.pdb", "daily.pdb",
dc1f29dd
 				 NULL };
2457e0a8
 		args[2] = tarfile;
8b0c26c2
 		if(!opt_check(opt, "debug")) {
3b961196
 		    if((dn = open("/dev/null", O_WRONLY)) == -1) {
 			mprintf("^Cannot open /dev/null\n");
 			close(1);
 			close(2);
 		    } else {
 			dup2(dn, 1);
 			dup2(dn, 2);
 			close(dn);
 		    }
8b0c26c2
 		}
4cd4319e
 		execv("/bin/tar", args);
42ace199
 		mprintf("!build: Can't execute tar\n");
4cd4319e
 		perror("tar");
9af907dc
 		free(tarfile);
 		return -1;
4cd4319e
 	    }
 	default:
 	    wait(NULL);
     }
 
     if(stat(tarfile, &foo) == -1) {
42ace199
 	mprintf("!build: Tar archive was not created\n");
9af907dc
 	free(tarfile);
 	return -1;
4cd4319e
     }
 
     if((tar = fopen(tarfile, "rb")) == NULL) {
42ace199
 	mprintf("!build: Can't open file %s\n", tarfile);
9af907dc
 	free(tarfile);
 	return -1;
     }
 
     if(!(gzfile = cli_gentemp("."))) {
42ace199
 	mprintf("!build: Can't generate temporary name for gzfile\n");
9af907dc
 	free(tarfile);
 	fclose(tar);
 	return -1;
4cd4319e
     }
 
     if((gz = gzopen(gzfile, "wb")) == NULL) {
42ace199
 	mprintf("!build: Can't open file %s to write.\n", gzfile);
9af907dc
 	free(tarfile);
 	fclose(tar);
 	free(gzfile);
 	return -1;
4cd4319e
     }
 
9af907dc
     while((bytes = fread(buffer, 1, FILEBUFF, tar)) > 0) {
 	if(!gzwrite(gz, buffer, bytes)) {
42ace199
 	    mprintf("!build: Can't gzwrite to %s\n", gzfile);
9af907dc
 	    fclose(tar);
 	    gzclose(gz);
 	    free(tarfile);
 	    free(gzfile);
 	    return -1;
 	}
     }
4cd4319e
 
     fclose(tar);
9af907dc
     gzclose(gz);
4cd4319e
     unlink(tarfile);
     free(tarfile);
 
     /* MD5 */
9af907dc
     if(!(pt = cli_md5file(gzfile))) {
42ace199
 	mprintf("!build: Can't generate MD5 checksum for gzfile\n");
9af907dc
 	unlink(gzfile);
 	free(gzfile);
 	return -1;
     }
7cc3891c
     strcat(header, pt);
4bf2cf8d
     free(pt);
7cc3891c
     strcat(header, ":");
4cd4319e
 
     /* digital signature */
9af907dc
     if(!(tar = fopen(gzfile, "rb"))) {
42ace199
 	mprintf("!build: Can't open file %s for reading\n", gzfile);
9af907dc
 	unlink(gzfile);
 	free(gzfile);
 	return -1;
     }
 
     if(!(pt = cli_md5stream(tar, (unsigned char *) buffer))) {
42ace199
 	mprintf("!build: Can't generate MD5 checksum for %s\n", gzfile);
9af907dc
 	unlink(gzfile);
 	free(gzfile);
 	return -1;
     }
d2a12ffd
     free(pt);
9af907dc
     rewind(tar);
 
aa7d9cef
     if(!(pt = getdsig(opt_arg(opt, "server"), builder, buffer, 16, 0))) {
42ace199
 	mprintf("!build: Can't get digital signature from remote server\n");
42e6f5a6
 	unlink(gzfile);
9af907dc
 	free(gzfile);
 	fclose(tar);
 	return -1;
4bf2cf8d
     }
7cc3891c
     strcat(header, pt);
4bf2cf8d
     free(pt);
7cc3891c
     strcat(header, ":");
4cd4319e
 
372063ec
     /* add builder */
d1256351
     strcat(header, builder);
4cd4319e
 
372063ec
     /* add current time */
     sprintf(header + strlen(header), ":%d", (int) timet);
 
4cd4319e
     /* fill up with spaces */
9af907dc
     while(strlen(header) < sizeof(header) - 1)
7cc3891c
 	strcat(header, " ");
4cd4319e
 
     /* build the final database */
2bc31f05
     pt = opt_arg(opt, "build");
9af907dc
     if(!(cvd = fopen(pt, "wb"))) {
42ace199
 	mprintf("!build: Can't create final database %s\n", pt);
42e6f5a6
 	unlink(gzfile);
9af907dc
 	free(gzfile);
 	fclose(tar);
 	return -1;
4cd4319e
     }
 
9af907dc
     if(fwrite(header, 1, 512, cvd) != 512) {
42ace199
 	mprintf("!build: Can't write to %s\n", pt);
9af907dc
 	fclose(cvd);
 	fclose(tar);
 	unlink(pt);
 	unlink(gzfile);
 	free(gzfile);
 	return -1;
4cd4319e
     }
 
9af907dc
     while((bytes = fread(buffer, 1, FILEBUFF, tar)) > 0) {
 	if(fwrite(buffer, 1, bytes, cvd) != bytes) {
 	    fclose(tar);
 	    fclose(cvd);
 	    unlink(pt);
42ace199
 	    mprintf("!build: Can't write to %s\n", gzfile);
9af907dc
 	    unlink(gzfile);
 	    free(gzfile);
 	    return -1;
 	}
     }
4cd4319e
 
     fclose(tar);
     fclose(cvd);
9af907dc
     if(unlink(gzfile) == -1) {
42ace199
 	mprintf("^build: Can't unlink %s\n", gzfile);
9af907dc
 	return -1;
     }
4cd4319e
     free(gzfile);
 
aa7d9cef
     mprintf("Created %s\n", pt);
9af907dc
 
8b0c26c2
     /* generate patch */
     if(inc) {
 	pt = freshdbdir();
 	snprintf(olddb, sizeof(olddb), "%s/%s.inc", pt, dbname);
 	free(pt);
     } else {
 	pt = freshdbdir();
 	snprintf(olddb, sizeof(olddb), "%s/%s.cvd", pt, dbname);
 	free(pt);
 
 	pt = cli_gentemp(NULL);
 	if(mkdir(pt, 0700)) {
 	    mprintf("!build: Can't create temporary directory %s\n", pt);
 	    return -1;
 	}
 	if(cvd_unpack(olddb, pt) == -1) {
 	    mprintf("!build: Can't unpack CVD file %s\n", olddb);
 	    rmdirs(pt);
 	    free(pt);
 	    return -1;
 	}
 	strncpy(olddb, pt, sizeof(olddb));
     }
 
     pt = cli_gentemp(NULL);
     if(mkdir(pt, 0700)) {
 	mprintf("!build: Can't create temporary directory %s\n", pt);
 	free(pt);
 	if(!inc)
 	    rmdirs(olddb);
 	return -1;
     }
     if(cvd_unpack(opt_arg(opt, "build"), pt) == -1) {
 	mprintf("!build: Can't unpack CVD file %s\n", opt_arg(opt, "build"));
 	rmdirs(pt);
 	free(pt);
 	if(!inc)
 	    rmdirs(olddb);
 	return -1;
     }
 
     if(!strcmp(dbname, "main"))
aa7d9cef
 	snprintf(patch, sizeof(patch), "main-%u.script", version);
8b0c26c2
     else
aa7d9cef
 	snprintf(patch, sizeof(patch), "daily-%u.script", version);
8b0c26c2
 
     ret = diffdirs(olddb, pt, patch);
 
     rmdirs(pt);
     free(pt);
 
     if(ret == -1) {
 	if(!inc)
 	    rmdirs(olddb);
 	return -1;
     }
 
aa7d9cef
     ret = verifydiff(patch, NULL, olddb);
8b0c26c2
 
     if(!inc)
 	rmdirs(olddb);
 
     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);
 	}
aa7d9cef
     } else {
 	ret = script2cdiff(patch, builder, opt);
8b0c26c2
     }
 
     return ret;
9af907dc
 }
 
 static int unpack(struct optstruct *opt)
 {
 	char *name, *dbdir;
8b0c26c2
 	struct stat sb;
9af907dc
 
 
     if(opt_check(opt, "unpack-current")) {
 	dbdir = freshdbdir();
8b0c26c2
 	name = mcalloc(strlen(dbdir) + strlen(opt_arg(opt, "unpack-current")) + 32, sizeof(char));
 	sprintf(name, "%s/%s.inc", dbdir, opt_arg(opt, "unpack-current"));
 	if(stat(name, &sb) != -1) {
 
 	    if(dircopy(name, ".") == -1) {
 		mprintf("!unpack: Can't copy incremental directory %s to local directory\n", name);
 		free(name);
 		free(dbdir);
 		return -1;
 	    }
 
 	    return 0;
 
 	} else {
 	    sprintf(name, "%s/%s.cvd", dbdir, opt_arg(opt, "unpack-current"));
 	}
9af907dc
 	free(dbdir);
8b0c26c2
 
9af907dc
     } else
 	name = strdup(opt_arg(opt, "unpack"));
4cd4319e
 
9af907dc
     if(cvd_unpack(name, ".") == -1) {
42ace199
 	mprintf("!unpack: Can't unpack CVD file %s\n", name);
9af907dc
 	free(name);
 	return -1;
     }
 
     free(name);
e8217f5a
     return 0;
4cd4319e
 }
 
9af907dc
 static int cvdinfo(struct optstruct *opt)
4cd4319e
 {
 	struct cl_cvd *cvd;
 	char *pt;
c0eb3ceb
 	int ret;
4cd4319e
 
9af907dc
 
2bc31f05
     pt = opt_arg(opt, "info");
c0eb3ceb
     if((cvd = cl_cvdhead(pt)) == NULL) {
42ace199
 	mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt);
9af907dc
 	return -1;
4cd4319e
     }
 
9af907dc
     pt = strchr(cvd->time, '-');
     *pt = ':';
42ace199
     mprintf("Build time: %s\n", cvd->time);
     mprintf("Version: %d\n", cvd->version);
     mprintf("Signatures: %d\n", cvd->sigs);
     mprintf("Functionality level: %d\n", cvd->fl);
     mprintf("Builder: %s\n", cvd->builder);
     mprintf("MD5: %s\n", cvd->md5);
     mprintf("Digital signature: %s\n", cvd->dsig);
4cd4319e
 
c0eb3ceb
 #ifndef HAVE_GMP
42ace199
     mprintf("^Digital signature support not compiled in.\n");
c0eb3ceb
 #endif
 
9af907dc
     pt = opt_arg(opt, "info");
c0eb3ceb
     if((ret = cl_cvdverify(pt)))
42ace199
 	mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
c0eb3ceb
     else
42ace199
 	mprintf("Verification OK.\n");
c0eb3ceb
 
9af907dc
     cl_cvdfree(cvd);
     return 0;
4cd4319e
 }
 
9af907dc
 static int listdb(const char *filename);
4bf2cf8d
 
9af907dc
 static int listdir(const char *dirname)
 {
 	DIR *dd;
 	struct dirent *dent;
 	char *dbfile;
4bf2cf8d
 
 
9af907dc
     if((dd = opendir(dirname)) == NULL) {
42ace199
         mprintf("!listdir: Can't open directory %s\n", dirname);
9af907dc
         return -1;
4bf2cf8d
     }
 
9af907dc
     while((dent = readdir(dd))) {
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
 	     cli_strbcasestr(dent->d_name, ".hdb") ||
344416f6
 	     cli_strbcasestr(dent->d_name, ".mdb") ||
9af907dc
 	     cli_strbcasestr(dent->d_name, ".ndb") ||
28f082e2
 	     cli_strbcasestr(dent->d_name, ".sdb") ||
9af907dc
 	     cli_strbcasestr(dent->d_name, ".zmd") ||
 	     cli_strbcasestr(dent->d_name, ".rmd") ||
bccc480f
 	     cli_strbcasestr(dent->d_name, ".inc") ||
9af907dc
 	     cli_strbcasestr(dent->d_name, ".cvd"))) {
4bf2cf8d
 
9af907dc
 		dbfile = (char *) mcalloc(strlen(dent->d_name) + strlen(dirname) + 2, sizeof(char));
4bf2cf8d
 
9af907dc
 		if(!dbfile) {
42ace199
 		    mprintf("!listdir: Can't allocate memory for dbfile\n");
9af907dc
 		    closedir(dd);
 		    return -1;
 		}
 		sprintf(dbfile, "%s/%s", dirname, dent->d_name);
4bf2cf8d
 
9af907dc
 		if(listdb(dbfile) == -1) {
42ace199
 		    mprintf("!listdb: Error listing database %s\n", dbfile);
9af907dc
 		    free(dbfile);
 		    closedir(dd);
 		    return -1;
 		}
 		free(dbfile);
 	    }
e8217f5a
 	}
     }
4bf2cf8d
 
9af907dc
     closedir(dd);
     return 0;
42e6f5a6
 }
d5314812
 
9af907dc
 static int listdb(const char *filename)
d5314812
 {
9af907dc
 	FILE *fd;
 	char *buffer, *pt, *start, *dir;
 	int line = 0;
d5314812
 	const char *tmpdir;
 
 
bccc480f
     if(cli_strbcasestr(filename, ".inc")) { /* incremental directory */
 	if(listdir(filename) == -1) {
 	    mprintf("!listdb: Can't list incremental directory %s\n", filename);
 	    return -1;
 	}
 	return 0;
     }
 
d5314812
     if((fd = fopen(filename, "rb")) == NULL) {
42ace199
 	mprintf("!listdb: Can't open file %s\n", filename);
d5314812
 	return -1;
     }
 
9af907dc
     if(!(buffer = (char *) mcalloc(FILEBUFF, 1))) {
42ace199
 	mprintf("!listdb: Can't allocate memory for buffer\n");
25133aef
 	fclose(fd);
d5314812
 	return -1;
     }
 
     /* check for CVD file */
     fgets(buffer, 12, fd);
     rewind(fd);
 
     if(!strncmp(buffer, "ClamAV-VDB:", 11)) {
9af907dc
 	free(buffer);
 	fclose(fd);
d5314812
 
 	tmpdir = getenv("TMPDIR");
 	if(tmpdir == NULL)
 #ifdef P_tmpdir
 	    tmpdir = P_tmpdir;
 #else
 	    tmpdir = "/tmp";
 #endif
 
9af907dc
 	if(!(dir = cli_gentemp(tmpdir))) {
42ace199
 	    mprintf("!listdb: Can't generate temporary name\n");
d5314812
 	    return -1;
 	}
 
9af907dc
 	if(mkdir(dir, 0700)) {
42ace199
 	    mprintf("!listdb: Can't create temporary directory %s\n", dir);
d5314812
 	    free(dir);
 	    return -1;
 	}
 
9af907dc
 	if(cvd_unpack(filename, dir) == -1) {
42ace199
 	    mprintf("!listdb: Can't unpack CVD file %s\n", filename);
9af907dc
 	    rmdirs(dir);
d5314812
 	    free(dir);
 	    return -1;
 	}
 
 	/* list extracted directory */
9af907dc
 	if(listdir(dir) == -1) {
bccc480f
 	    mprintf("!listdb: Can't list directory %s\n", filename);
9af907dc
 	    rmdirs(dir);
 	    free(dir);
 	    return -1;
 	}
d5314812
 
9af907dc
 	rmdirs(dir);
d5314812
 	free(dir);
 
 	return 0;
     }
 
9af907dc
     if(cli_strbcasestr(filename, ".db")) { /* old style database */
372063ec
 
 	while(fgets(buffer, FILEBUFF, fd)) {
 	    line++;
 	    pt = strchr(buffer, '=');
 	    if(!pt) {
42ace199
 		mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename);
372063ec
 		fclose(fd);
 		free(buffer);
 		return -1;
 	    }
d5314812
 
372063ec
 	    start = buffer;
 	    *pt = 0;
d5314812
 
372063ec
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
42ace199
 	    mprintf("%s\n", start);
d5314812
 	}
 
344416f6
     } else if(cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".mdb")) { /* hash database */
d5314812
 
372063ec
 	while(fgets(buffer, FILEBUFF, fd)) {
 	    line++;
 	    cli_chomp(buffer);
 	    start = cli_strtok(buffer, 2, ":");
 
 	    if(!start) {
42ace199
 		mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename);
372063ec
 		fclose(fd);
 		free(buffer);
 		return -1;
 	    }
 
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
42ace199
 	    mprintf("%s\n", start);
372063ec
 	    free(start);
 	}
d5314812
 
28f082e2
     } else if(cli_strbcasestr(filename, ".ndb") || cli_strbcasestr(filename, ".sdb") || cli_strbcasestr(filename, ".zmd") || cli_strbcasestr(filename, ".rmd")) {
856d9c84
 
 	while(fgets(buffer, FILEBUFF, fd)) {
 	    line++;
 	    cli_chomp(buffer);
 	    start = cli_strtok(buffer, 0, ":");
 
 	    if(!start) {
42ace199
 		mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename);
856d9c84
 		fclose(fd);
 		free(buffer);
 		return -1;
 	    }
 
 	    if((pt = strstr(start, " (Clam)")))
 		*pt = 0;
 
42ace199
 	    mprintf("%s\n", start);
856d9c84
 	    free(start);
 	}
d5314812
     }
 
     fclose(fd);
     free(buffer);
     return 0;
 }
 
9af907dc
 static int listsigs(struct optstruct *opt)
d5314812
 {
 	int ret;
 	const char *name;
b2e70235
 	char *dbdir;
d5314812
 
9af907dc
 
d5314812
     mprintf_stdout = 1;
 
2bc31f05
     if((name = opt_arg(opt, "list-sigs"))) {
d5314812
 	ret = listdb(name);
b2e70235
     } else {
 	dbdir = freshdbdir();
 	ret = listdir(dbdir);
 	free(dbdir);
     }
d5314812
 
9af907dc
     return ret;
 }
 
 static int vbadump(struct optstruct *opt)
 {
 	int fd, hex_output=0;
 	char *dir;
 
 
     if(opt_check(opt, "vba-hex"))
 	hex_output = 1;
  
     if((fd = open(opt_arg(opt, "vba"), O_RDONLY)) == -1) {
 	if((fd = open(opt_arg(opt, "vba-hex"), O_RDONLY)) == -1) {
42ace199
 	    mprintf("!vbadump: Can't open file %s\n", opt_arg(opt, "vba"));
9af907dc
 	    return -1;
 	}
     }
 
     /* generate the temporary directory */
     dir = cli_gentemp(NULL);
     if(mkdir(dir, 0700)) {
42ace199
 	mprintf("!vbadump: Can't create temporary directory %s\n", dir);
9af907dc
 	free(dir);
 	close(fd);
         return -1;
     }
 
     if(cli_ole2_extract(fd, dir, NULL)) {
 	cli_rmdirs(dir);
         free(dir);
 	close(fd);
         return -1;
     }
 
     close(fd);
     sigtool_vba_scandir(dir, hex_output);
     rmdirs(dir);
     free(dir);
     return 0;
50099661
 }
 
aa7d9cef
 static int rundiff(struct optstruct *opt)
5b09aa1f
 {
 	int fd, ret;
aa7d9cef
 	unsigned short mode;
 	const char *diff;
 
5b09aa1f
 
aa7d9cef
     diff = opt_arg(opt, "run-cdiff");
     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;
     }
5b09aa1f
 
aa7d9cef
     if((fd = open(diff, O_RDONLY)) == -1) {
 	mprintf("!rundiff: Can't open file %s\n", diff);
5b09aa1f
 	return -1;
7e92b3c8
     }
5b09aa1f
 
aa7d9cef
     ret = cdiff_apply(fd, mode);
5b09aa1f
     close(fd);
 
     return ret;
 }
 
e4e6da67
 static int compare(const char *oldpath, const char *newpath, FILE *diff)
 {
 	FILE *old, *new;
fc6e6260
 	char obuff[1024], nbuff[1024], tbuff[1024], *pt, *omd5, *nmd5;
 	unsigned int oline = 0, tline, found, i;
 	long opos;
e4e6da67
 
 
     if(!(new = fopen(newpath, "r"))) {
 	mprintf("!compare: Can't open file %s for reading\n", newpath);
 	return -1;
     }
 
     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);
 
     old = fopen(oldpath, "r");
 
     while(fgets(nbuff, sizeof(nbuff), new)) {
 	cli_chomp(nbuff);
 
 	if(!old) {
 	    fprintf(diff, "ADD %s\n", nbuff);
 	} else {
 	    if(fgets(obuff, sizeof(obuff), old)) {
fc6e6260
 		oline++;
e4e6da67
 		cli_chomp(obuff);
 		if(!strcmp(nbuff, obuff)) {
 		    continue;
 		} else {
fc6e6260
 		    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));
 			for(i = 0; i < tline; i++) {
 			    tbuff[16] = 0;
 			    if((pt = strchr(tbuff, ' ')))
 				*pt = 0;
 			    fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
 			    fgets(tbuff, sizeof(tbuff), old);
 			}
 			oline += tline;
 
 		    } else {
 			obuff[16] = 0;
e4e6da67
 			if((pt = strchr(obuff, ' ')))
 			    *pt = 0;
fc6e6260
 			fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff);
e4e6da67
 		    }
 		}
 	    } else {
 		fclose(old);
 		old = NULL;
 		fprintf(diff, "ADD %s\n", nbuff);
 	    }
 	}
     }
 
fc6e6260
     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);
 	}
e4e6da67
 	fclose(old);
fc6e6260
     }
 
e4e6da67
     fprintf(diff, "CLOSE\n");
     return 0;
 }
 
aa7d9cef
 static int verifydiff(const char *diff, const char *cvd, const char *incdir)
7e92b3c8
 {
8b0c26c2
 	char *tempdir, cwd[512], buff[1024], info[32], *md5, *pt;
 	const char *cpt;
7e92b3c8
 	FILE *fh;
 	int ret = 0, fd;
aa7d9cef
 	unsigned short mode;
 
7e92b3c8
 
aa7d9cef
     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;
     }
7e92b3c8
 
     tempdir = cli_gentemp(NULL);
     if(!tempdir) {
aa7d9cef
 	mprintf("!verifydiff: Can't generate temporary name for tempdir\n");
7e92b3c8
 	return -1;
     }
 
     if(mkdir(tempdir, 0700) == -1) {
aa7d9cef
 	mprintf("!verifydiff: Can't create directory %s\n", tempdir);
7e92b3c8
 	free(tempdir);
 	return -1;
     }
 
8b0c26c2
     if(cvd) {
 	if(cvd_unpack(cvd, tempdir) == -1) {
aa7d9cef
 	    mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd);
8b0c26c2
 	    rmdirs(tempdir);
 	    free(tempdir);
 	    return -1;
 	}
     } else {
 	if(dircopy(incdir, tempdir) == -1) {
aa7d9cef
 	    mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir);
8b0c26c2
 	    rmdirs(tempdir);
 	    free(tempdir);
 	    return -1;
 	}
7e92b3c8
     }
 
     if((fd = open(diff, O_RDONLY)) == -1) {
aa7d9cef
 	mprintf("!verifydiff: Can't open diff file %s\n", diff);
7e92b3c8
 	rmdirs(tempdir);
 	free(tempdir);
 	return -1;
     }
 
     getcwd(cwd, sizeof(cwd));
 
     if(chdir(tempdir) == -1) {
aa7d9cef
 	mprintf("!verifydiff: Can't chdir to %s\n", tempdir);
7e92b3c8
 	rmdirs(tempdir);
 	free(tempdir);
 	close(fd);
 	return -1;
     }
 
aa7d9cef
     if(cdiff_apply(fd, mode) == -1) {
 	mprintf("!verifydiff: Can't apply %s\n", diff);
7e92b3c8
 	chdir(cwd);
 	rmdirs(tempdir);
 	free(tempdir);
 	close(fd);
 	return -1;
     }
     close(fd);
 
8b0c26c2
     cvd ? (cpt = cvd) : (cpt = incdir);
 
     if(strstr(cpt, "main.cvd"))
7e92b3c8
 	strcpy(info, "main.info");
     else
 	strcpy(info, "daily.info");
 
     if(!(fh = fopen(info, "r"))) {
aa7d9cef
 	mprintf("!verifydiff: Can't open %s\n", info);
7e92b3c8
 	chdir(cwd);
 	rmdirs(tempdir);
 	free(tempdir);
 	return -1;
     }
 
     fgets(buff, sizeof(buff), fh);
 
     if(strncmp(buff, "ClamAV-VDB", 10)) {
aa7d9cef
 	mprintf("!verifydiff: Incorrect info file %s\n", info);
7e92b3c8
 	chdir(cwd);
 	rmdirs(tempdir);
 	free(tempdir);
 	return -1;
     }
 
     while(fgets(buff, sizeof(buff), fh)) {
 	cli_chomp(buff);
 	if(!(pt = strchr(buff, ':'))) {
aa7d9cef
 	    mprintf("!verifydiff: Incorrect format of %s\n", info);
7e92b3c8
 	    ret = -1;
 	    break;
 	}
 	*pt++ = 0;
 	if(!(md5 = cli_md5file(buff))) {
aa7d9cef
 	    mprintf("!verifydiff: Can't generate MD5 for %s\n", buff);
7e92b3c8
 	    ret = -1;
 	    break;
 	}
 	if(strcmp(pt, md5)) {
aa7d9cef
 	    mprintf("!verifydiff: %s has incorrect checksum\n", buff);
7e92b3c8
 	    ret = -1;
 	    break;
 	}
     }
 
     fclose(fh);
     chdir(cwd);
     rmdirs(tempdir);
     free(tempdir);
 
8b0c26c2
     if(!ret) {
 	if(cvd)
 	    mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
 	else
56ee0a90
 	    mprintf("Verification: %s correctly applies to the previous version\n", diff);
8b0c26c2
     }
7e92b3c8
 
     return ret;
 }
 
8b0c26c2
 static int diffdirs(const char *old, const char *new, const char *patch)
e4e6da67
 {
 	FILE *diff;
 	DIR *dd;
 	struct dirent *dent;
8b0c26c2
 	char cwd[512], opath[1024];
 
 
     getcwd(cwd, sizeof(cwd));
 
     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))) {
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
 	    if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 		continue;
 
 	    snprintf(opath, sizeof(opath), "%s/%s", old, dent->d_name);
 	    if(compare(opath, dent->d_name, diff) == -1) {
 		fclose(diff);
 		unlink(patch);
 		closedir(dd);
 		return -1;
 	    }
 	}
     }
 
     closedir(dd);
 
     fclose(diff);
aa7d9cef
     mprintf("Generated diff file %s\n", patch);
8b0c26c2
     chdir(cwd);
 
     return 0;
 }
 
 static int makediff(struct optstruct *opt)
 {
 	char *odir, *ndir, name[32], broken[32];
e4e6da67
 	struct cl_cvd *cvd;
 	unsigned int oldver, newver;
8b0c26c2
 	int ret;
e4e6da67
 
 
     if(!opt->filename) {
 	mprintf("!makediff: --diff requires two arguments\n");
 	return -1;
     }
 
     if(!(cvd = cl_cvdhead(opt->filename))) {
 	mprintf("!makediff: Can't read CVD header from %s\n", opt->filename);
 	return -1;
     }
     newver = cvd->version;
     free(cvd);
 
     if(!(cvd = cl_cvdhead(opt_arg(opt, "diff")))) {
 	mprintf("!makediff: Can't read CVD header from %s\n", opt_arg(opt, "diff"));
 	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;
     }
 
     if(cvd_unpack(opt_arg(opt, "diff"), odir) == -1) {
 	mprintf("!makediff: Can't unpack CVD file %s\n", opt_arg(opt, "diff"));
 	rmdirs(odir);
 	free(odir);
 	return -1;
     }
 
     ndir = cli_gentemp(NULL);
     if(!ndir) {
 	mprintf("!makediff: Can't generate temporary name for ndir\n");
 	rmdirs(odir);
 	free(odir);
 	return -1;
     }
 
     if(mkdir(ndir, 0700) == -1) {
 	mprintf("!makediff: Can't create directory %s\n", ndir);
 	free(ndir);
 	rmdirs(odir);
 	free(odir);
 	return -1;
     }
 
     if(cvd_unpack(opt->filename, ndir) == -1) {
 	mprintf("!makediff: Can't unpack CVD file %s\n", opt->filename);
 	rmdirs(odir);
 	rmdirs(ndir);
 	free(odir);
 	free(ndir);
 	return -1;
     }
 
     if(strstr(opt->filename, "main"))
aa7d9cef
 	snprintf(name, sizeof(name), "main-%u.script", newver);
e4e6da67
     else
aa7d9cef
 	snprintf(name, sizeof(name), "daily-%u.script", newver);
e4e6da67
 
8b0c26c2
     ret = diffdirs(odir, ndir, name);
e4e6da67
 
     rmdirs(odir);
     rmdirs(ndir);
     free(odir);
     free(ndir);
7e92b3c8
 
8b0c26c2
     if(ret == -1)
 	return -1;
7e92b3c8
 
aa7d9cef
     if(verifydiff(name, opt_arg(opt, "diff"), NULL) == -1) {
7e92b3c8
 	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;
     }
 
e4e6da67
     return 0;
 }
 
50099661
 void help(void)
 {
     mprintf("\n");
8ef2ff6f
     mprintf("             Clam AntiVirus: Signature Tool (sigtool)  "VERSION"\n");
9af907dc
     mprintf("    (C) 2002 - 2006 ClamAV Team - http://www.clamav.net/team.html\n\n");
50099661
 
     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");
25133aef
     mprintf("    --md5 [FILES]                          generate MD5 checksum from stdin\n");
     mprintf("                                           or MD5 sigs for FILES\n");
f852d214
     mprintf("    --html-normalise=FILE                  create normalised parts of HTML file\n");
50099661
     mprintf("    --info=FILE            -i FILE         print database information\n");
     mprintf("    --build=NAME           -b NAME         build a CVD file\n");
     mprintf("    --server=ADDR                          ClamAV Signing Service address\n");
     mprintf("    --unpack=FILE          -u FILE         Unpack a CVD file\n");
8b0c26c2
     mprintf("    --unpack-current=SHORTNAME             Unpack local CVD/INCDIR in cwd\n");
50099661
     mprintf("    --list-sigs[=FILE]     -l[FILE]        List signature names\n");
d6bf11e3
     mprintf("    --vba=FILE                             Extract VBA/Word6 macro code\n");
     mprintf("    --vba-hex=FILE                         Extract Word6 macro code with hex values\n");
5b09aa1f
     mprintf("    --vba-hex=FILE                         Extract Word6 macro code with hex values\n");
e4e6da67
     mprintf("    --diff=OLD NEW         -d OLD NEW      Create diff for OLD and NEW CVDs\n");
5b09aa1f
     mprintf("    --run-cdiff=FILE       -r FILE         Execute update script FILE in cwd\n");
8b0c26c2
     mprintf("    --verify-cdiff=DIFF CVD/INCDIR         Verify DIFF against CVD\n");
50099661
     mprintf("\n");
 
9af907dc
     return;
 }
 
 int main(int argc, char **argv)
 {
 	int ret = 1;
         struct optstruct *opt;
dc17bb9c
 	struct stat sb;
e4e6da67
 	const char *short_options = "hvVb:i:u:l::r:d:";
9af907dc
 	static struct option long_options[] = {
 	    {"help", 0, 0, 'h'},
 	    {"quiet", 0, 0, 0},
 	    {"debug", 0, 0, 0},
 	    {"verbose", 0, 0, 'v'},
 	    {"stdout", 0, 0, 0},
 	    {"version", 0, 0, 'V'},
 	    {"tempdir", 1, 0, 0},
 	    {"hex-dump", 0, 0, 0},
 	    {"md5", 0, 0, 0},
 	    {"html-normalise", 1, 0, 0},
 	    {"build", 1, 0, 'b'},
 	    {"server", 1, 0, 0},
 	    {"unpack", 1, 0, 'u'},
 	    {"unpack-current", 1, 0, 0},
 	    {"info", 1, 0, 'i'},
 	    {"list-sigs", 2, 0, 'l'},
 	    {"vba", 1, 0 ,0},
 	    {"vba-hex", 1, 0, 0},
e4e6da67
 	    {"diff", 1, 0, 'd'},
5b09aa1f
 	    {"run-cdiff", 1, 0, 'r'},
7e92b3c8
 	    {"verify-cdiff", 1, 0, 0},
9af907dc
 	    {0, 0, 0, 0}
     	};
 
     opt = opt_parse(argc, argv, short_options, long_options, NULL);
     if(!opt) {
 	mprintf("!Can't parse the command line\n");
 	return 1;
     }
 
     if(opt_check(opt, "quiet"))
 	mprintf_quiet = 1;
 
     if(opt_check(opt, "stdout"))
 	mprintf_stdout = 1;
 
     if(opt_check(opt, "debug"))
 	cl_debug();
 
     if(opt_check(opt, "version")) {
 	print_version();
 	opt_free(opt);
 	return 0;
     }
 
     if(opt_check(opt, "help")) {
 	opt_free(opt);
     	help();
7e92b3c8
 	return 0;
9af907dc
     }
 
     if(opt_check(opt, "hex-dump"))
 	ret = hexdump();
     else if(opt_check(opt, "md5"))
 	ret = md5sig(opt);
     else if(opt_check(opt, "html-normalise"))
 	ret = htmlnorm(opt);
     else if(opt_check(opt, "build"))
 	ret = build(opt);
     else if(opt_check(opt, "unpack"))
 	ret = unpack(opt);
     else if(opt_check(opt, "unpack-current"))
 	ret = unpack(opt);
     else if(opt_check(opt, "info"))
 	ret = cvdinfo(opt);
     else if(opt_check(opt, "list-sigs"))
 	ret = listsigs(opt);
     else if(opt_check(opt, "vba") || opt_check(opt, "vba-hex"))
 	ret = vbadump(opt);
e4e6da67
     else if(opt_check(opt, "diff"))
 	ret = makediff(opt);
5b09aa1f
     else if(opt_check(opt, "run-cdiff"))
aa7d9cef
 	ret = rundiff(opt);
7e92b3c8
     else if(opt_check(opt, "verify-cdiff")) {
 	if(!opt->filename) {
8b0c26c2
 	    mprintf("!--verify-cdiff requires two arguments\n");
7e92b3c8
 	    ret = -1;
 	} else {
dc17bb9c
 	    if(stat(opt->filename, &sb) == -1) {
 		mprintf("--verify-cdiff: Can't get status of %s\n", opt->filename);
 		ret = -1;
 	    } else {
 		if(S_ISDIR(sb.st_mode))
aa7d9cef
 		    ret = verifydiff(opt_arg(opt, "verify-cdiff"), NULL, opt->filename);
dc17bb9c
 		else
aa7d9cef
 		    ret = verifydiff(opt_arg(opt, "verify-cdiff"), opt->filename, NULL);
dc17bb9c
 	    }
7e92b3c8
 	}
     } else
9af907dc
 	help();
 
     opt_free(opt);
     return ret ? 1 : 0;
d5314812
 }