libclamav/others.c
e3aaff8e
 /*
8f84357e
  *  Copyright (C) 1999 - 2005 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 <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
a946fc1c
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
a946fc1c
 #endif
e3aaff8e
 #include <sys/types.h>
 #include <sys/stat.h>
a946fc1c
 #ifndef	C_WINDOWS
e3aaff8e
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <dirent.h>
a946fc1c
 #endif
e3aaff8e
 #include <time.h>
 #include <fcntl.h>
a946fc1c
 #ifndef	C_WINDOWS
e3aaff8e
 #include <pwd.h>
a946fc1c
 #endif
e3aaff8e
 #include <errno.h>
a946fc1c
 #include "target.h"
 #ifndef	C_WINDOWS
feec9e31
 #include <sys/time.h>
a946fc1c
 #endif
 #ifdef	HAVE_SYS_PARAM_H
15edd45f
 #include <sys/param.h>
a946fc1c
 #endif
 #ifdef	HAVE_MALLOC_H
 #include <malloc.h>
 #endif
 #if	defined(_MSC_VER) && defined(_DEBUG)
 #include <crtdbg.h>
 #endif
e3aaff8e
 
9b8e00a0
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
6d5c43a1
 static pthread_mutex_t cli_gentempname_mutex = PTHREAD_MUTEX_INITIALIZER;
9b8e00a0
 #endif
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
e3aaff8e
 #include "clamav.h"
 #include "others.h"
 #include "md5.h"
5ae8bdd5
 #include "cltypes.h"
e3aaff8e
 
b067a7dd
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
97eb8797
 #ifdef        C_WINDOWS
 #undef        P_tmpdir
 #define       P_tmpdir        "C:\\WINDOWS\\TEMP"
 #endif
 
5f4897ae
 #define CL_FLEVEL 18 /* don't touch it */
a3ee0766
 
590135f9
 short cli_debug_flag = 0, cli_leavetemps_flag = 0;
d4d14218
 
b0e2e765
 static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
feec9e31
 
d704c68e
 
e3aaff8e
 void cli_warnmsg(const char *str, ...)
 {
 	va_list args;
d704c68e
 	int sz = sizeof("LibClamAV Warning: ") - 1;
 	char buff[256];
e3aaff8e
 
d704c68e
     strncpy(buff, "LibClamAV Warning: ", sz);
e3aaff8e
     va_start(args, str);
d704c68e
     vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
     buff[sizeof(buff) - 1] = '\0';
     fputs(buff, stderr);
e3aaff8e
     va_end(args);
 }
 
 void cli_errmsg(const char *str, ...)
 {
 	va_list args;
d704c68e
 	int sz = sizeof("LibClamAV Error: ") - 1;
 	char buff[256];
e3aaff8e
 
d704c68e
     strncpy(buff, "LibClamAV Error: ", sz);
e3aaff8e
     va_start(args, str);
d704c68e
     vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
     buff[sizeof(buff) - 1] = '\0';
     fputs(buff, stderr);
e3aaff8e
     va_end(args);
 }
 
 void cli_dbgmsg(const char *str, ...)
 {
 
d4d14218
     if(cli_debug_flag) {
d704c68e
 	    va_list args;
 	    int sz = sizeof("LibClamAV debug: ") - 1;
a946fc1c
 	    char buff[BUFSIZ];
d704c68e
 
 	memcpy(buff, "LibClamAV debug: ", sz);
d4d14218
 	va_start(args, str);
d704c68e
 	vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
 	buff[sizeof(buff) - 1] = '\0';
 	fputs(buff, stderr);
d4d14218
 	va_end(args);
     } else
 	return;
 }
 
 void cl_debug(void)
 {
     cli_debug_flag = 1;
e3aaff8e
 }
 
b5134815
 unsigned int cl_retflevel(void)
a3ee0766
 {
     return CL_FLEVEL;
 }
 
8d78fc78
 const char *cl_retver(void)
 {
     return VERSION;
 }
 
68a6f51f
 const char *cl_strerror(int clerror)
e3aaff8e
 {
     switch(clerror) {
 	case CL_CLEAN:
ac75a532
 	    return "No viruses detected";
e3aaff8e
 	case CL_VIRUS:
ac75a532
 	    return "Virus(es) detected";
e3aaff8e
 	case CL_EMAXREC:
ac75a532
 	    return "Recursion limit exceeded";
e3aaff8e
 	case CL_EMAXSIZE:
ac75a532
 	    return "File size limit exceeded";
e3aaff8e
 	case CL_EMAXFILES:
ac75a532
 	    return "Files number limit exceeded";
e3aaff8e
 	case CL_ERAR:
ac75a532
 	    return "RAR module failure";
e3aaff8e
 	case CL_EZIP:
ac75a532
 	    return "Zip module failure";
e3aaff8e
 	case CL_EGZIP:
ac75a532
 	    return "GZip module failure";
341e5433
 	case CL_EMSCOMP:
ac75a532
 	    return "MS Expand module failure";
414abe87
 	case CL_EMSCAB:
ac75a532
 	    return "MS CAB module failure";
47bbbc56
 	case CL_EOLE2:
ac75a532
 	    return "OLE2 module failure";
e3aaff8e
 	case CL_ETMPFILE:
ac75a532
 	    return "Unable to create temporary file";
e3aaff8e
 	case CL_ETMPDIR:
ac75a532
 	    return "Unable to create temporary directory";
e3aaff8e
 	case CL_EFSYNC:
ac75a532
 	    return "Unable to synchronize file <-> disk";
e3aaff8e
 	case CL_EMEM:
ac75a532
 	    return "Unable to allocate memory";
e3aaff8e
 	case CL_EOPEN:
ac75a532
 	    return "Unable to open file or directory";
e3aaff8e
 	case CL_EMALFDB:
ac75a532
 	    return "Malformed database";
e3aaff8e
 	case CL_EPATSHORT:
ac75a532
 	    return "Too short pattern detected";
d9e258d5
 	case CL_ECVD:
ac75a532
 	    return "Broken or not a CVD file";
8139fd99
 	case CL_ECVDEXTR:
ac75a532
 	    return "CVD extraction failure";
d9e258d5
 	case CL_EMD5:
ac75a532
 	    return "MD5 verification error";
d9e258d5
 	case CL_EDSIG:
ac75a532
 	    return "Digital signature verification error";
e3aaff8e
 	case CL_ENULLARG:
ac75a532
 	    return "Null argument passed while initialized is required";
 	case CL_EIO:
 	    return "Input/Output error";
4a7c1c22
 	case CL_EFORMAT:
 	    return "Bad format or broken data";
f133da7a
 	case CL_ESUPPORT:
 	    return "Not supported data format";
b5134815
 	case CL_ENCINIT:
 	    return "NodalCore initialization failure";
 	case CL_ENCLOAD:
 	    return "Error loading NodalCore database";
 	case CL_ENCIO:
 	    return "NodalCore accelerator Input/Output error";
06646acf
 	case CL_ELOCKDB:
 	    return "Unable to lock database directory";
e3aaff8e
 	default:
ac75a532
 	    return "Unknown error code";
e3aaff8e
     }
 }
 
db65451b
 unsigned char *cli_md5digest(int desc)
 {
 	unsigned char *digest;
 	char buff[FILEBUFF];
2a9e6ac8
 	cli_md5_ctx ctx;
db65451b
 	int bytes;
 
 
     if(!(digest = cli_malloc(16)))
 	return NULL;
 
2a9e6ac8
     cli_md5_init(&ctx);
db65451b
 
     while((bytes = cli_readn(desc, buff, FILEBUFF)))
2a9e6ac8
 	cli_md5_update(&ctx, buff, bytes);
db65451b
 
2a9e6ac8
     cli_md5_final(digest, &ctx);
db65451b
 
     return digest;
 }
 
335d1663
 char *cli_md5stream(FILE *fs, unsigned char *digcpy)
e3aaff8e
 {
335d1663
 	unsigned char digest[16];
 	char buff[FILEBUFF];
2a9e6ac8
 	cli_md5_ctx ctx;
335d1663
 	char *md5str, *pt;
 	int i, bytes;
 
 
2a9e6ac8
     cli_md5_init(&ctx);
e3aaff8e
 
335d1663
     while((bytes = fread(buff, 1, FILEBUFF, fs)))
2a9e6ac8
 	cli_md5_update(&ctx, buff, bytes);
335d1663
 
2a9e6ac8
     cli_md5_final(digest, &ctx);
335d1663
 
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
e3aaff8e
 
335d1663
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
e3aaff8e
 
335d1663
     if(digcpy)
 	memcpy(digcpy, digest, 16);
e3aaff8e
 
335d1663
     return md5str;
e3aaff8e
 }
 
8000d078
 char *cli_md5file(const char *filename)
d9e258d5
 {
335d1663
 	FILE *fs;
d9e258d5
 	char *md5str;
 
8000d078
 
335d1663
     if((fs = fopen(filename, "rb")) == NULL) {
8000d078
 	cli_errmsg("cli_md5file(): Can't read file %s\n", filename);
 	return NULL;
     }
 
335d1663
     md5str = cli_md5stream(fs, NULL);
     fclose(fs);
d9e258d5
 
335d1663
     return md5str;
d9e258d5
 }
 
bedc58de
 static char *cli_md5buff(const unsigned char *buffer, unsigned int len, unsigned char *dig)
e3aaff8e
 {
335d1663
 	unsigned char digest[16];
 	char *md5str, *pt;
2a9e6ac8
 	cli_md5_ctx ctx;
335d1663
 	int i;
e3aaff8e
 
 
2a9e6ac8
     cli_md5_init(&ctx);
bedc58de
     cli_md5_update(&ctx, buffer, len);
2a9e6ac8
     cli_md5_final(digest, &ctx);
b0e2e765
 
     if(dig)
 	memcpy(dig, digest, 16);
e3aaff8e
 
335d1663
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
e3aaff8e
 
335d1663
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
e3aaff8e
 
335d1663
     return md5str;
e3aaff8e
 }
 
 void *cli_malloc(size_t size)
 {
 	void *alloc;
 
8d5e0ac0
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
2e5a3528
 	cli_errmsg("cli_malloc(): Attempt to allocate %u bytes. Please report to http://bugs.clamav.net\n", size);
8d5e0ac0
 	return NULL;
     }
 
a946fc1c
 #if defined(_MSC_VER) && defined(_DEBUG)
     alloc = _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__);
 #else
e3aaff8e
     alloc = malloc(size);
a946fc1c
 #endif
e3aaff8e
 
     if(!alloc) {
16328002
 	cli_errmsg("cli_malloc(): Can't allocate memory (%u bytes).\n", size);
e3aaff8e
 	perror("malloc_problem");
b5b62ca7
 	/* _exit(1); */
e3aaff8e
 	return NULL;
     } else return alloc;
 }
 
 void *cli_calloc(size_t nmemb, size_t size)
 {
 	void *alloc;
 
5edae84a
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
2e5a3528
 	cli_errmsg("cli_calloc(): Attempt to allocate %u bytes. Please report to http://bugs.clamav.net\n", size);
8d5e0ac0
 	return NULL;
     }
 
a946fc1c
 #if defined(_MSC_VER) && defined(_DEBUG)
     alloc = _calloc_dbg(nmemb, size, _NORMAL_BLOCK, __FILE__, __LINE__);
 #else
e3aaff8e
     alloc = calloc(nmemb, size);
a946fc1c
 #endif
e3aaff8e
 
     if(!alloc) {
16328002
 	cli_errmsg("cli_calloc(): Can't allocate memory (%u bytes).\n", nmemb * size);
e3aaff8e
 	perror("calloc_problem");
b5b62ca7
 	/* _exit(1); */
e3aaff8e
 	return NULL;
     } else return alloc;
 }
 
8139fd99
 void *cli_realloc(void *ptr, size_t size)
 {
 	void *alloc;
 
8d5e0ac0
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
2e5a3528
 	cli_errmsg("cli_realloc(): Attempt to allocate %u bytes. Please report to http://bugs.clamav.net\n", size);
f67d5029
 	return NULL;
     }
 
8139fd99
     alloc = realloc(ptr, size);
 
     if(!alloc) {
786b43b6
 	cli_errmsg("cli_realloc(): Can't re-allocate memory to %u bytes.\n", size);
8139fd99
 	perror("realloc_problem");
 	return NULL;
     } else return alloc;
 }
 
423d6b6a
 void *cli_realloc2(void *ptr, size_t size)
 {
 	void *alloc;
 
 
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("cli_realloc2(): Attempt to allocate %u bytes. Please report to http://bugs.clamav.net\n", size);
 	return NULL;
     }
 
     alloc = realloc(ptr, size);
 
     if(!alloc) {
 	cli_errmsg("cli_realloc2(): Can't re-allocate memory to %u bytes.\n", size);
 	perror("realloc_problem");
 	if(ptr)
 	    free(ptr);
 	return NULL;
     } else return alloc;
 }
 
786b43b6
 char *cli_strdup(const char *s)
 {
         char *alloc;
 
 
     if(s == NULL) {
         cli_errmsg("cli_strdup(): s == NULL. Please report to http://bugs.clamav.net\n");
         return NULL;
     }
 
 #if defined(_MSC_VER) && defined(_DEBUG)
     alloc = _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__);
 #else
     alloc = strdup(s);
 #endif
 
     if(!alloc) {
         cli_errmsg("cli_strdup(): Can't allocate memory (%u bytes).\n", strlen(s));
         perror("strdup_problem");
         return NULL;
     }
 
     return alloc;
 }
 
8000d078
 unsigned int cli_rndnum(unsigned int max)
e3aaff8e
 {
     struct timeval tv;
 
   gettimeofday(&tv, (struct timezone *) 0);
   srand(tv.tv_usec+clock());
 
   return rand() % max;
 }
 
590135f9
 void cl_settempdir(const char *dir, short leavetemps)
 {
 	char *var;
 
     if(dir) {
 	var = (char *) cli_malloc(8 + strlen(dir));
 	sprintf(var, "TMPDIR=%s", dir);
 	if(!putenv(var))
 	    cli_dbgmsg("Setting %s as global temporary directory\n", dir);
 	else
 	    cli_warnmsg("Can't set TMPDIR variable - insufficient space in the environment.\n");
 
 	/* WARNING: var must not be released - see putenv(3) */
     }
 
     cli_leavetemps_flag = leavetemps;
 }
 
0e621e7d
 static char *cli_gentempname(const char *dir)
e3aaff8e
 {
68a6f51f
 	char *name, *tmp;
         const char *mdir;
feec9e31
 	unsigned char salt[16 + 32];
 	int i;
e3aaff8e
 
0c7019c5
     if(!dir) {
 	if((mdir = getenv("TMPDIR")) == NULL)
 #ifdef P_tmpdir
 	    mdir = P_tmpdir;
 #else
 	    mdir = "/tmp";
 #endif
     } else
 	mdir = dir;
e3aaff8e
 
b0e2e765
     name = (char *) cli_calloc(strlen(mdir) + 1 + 32 + 1 + 7, sizeof(char));
     if(!name) {
0e621e7d
 	cli_dbgmsg("cli_gentempname('%s'): out of memory\n", mdir);
2d70a403
 	return NULL;
     }
feec9e31
 
b0e2e765
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_gentempname_mutex);
 #endif
 
     memcpy(salt, name_salt, 16);
e3aaff8e
 
b0e2e765
     for(i = 16; i < 48; i++)
 	salt[i] = cli_rndnum(256);
e3aaff8e
 
bedc58de
     tmp = cli_md5buff(salt, 48, name_salt);
b0e2e765
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_gentempname_mutex);
 #endif
 
     if(!tmp) {
 	free(name);
 	cli_dbgmsg("cli_gentempname('%s'): out of memory\n", mdir);
 	return NULL;
     }
 
a946fc1c
 #ifdef	C_WINDOWS
 	sprintf(name, "%s\\clamav-", mdir);
 #else
 	sprintf(name, "%s/clamav-", mdir);
 #endif
b0e2e765
     strncat(name, tmp, 32);
     free(tmp);
e3aaff8e
 
0e621e7d
     return(name);
 }
 
 char *cli_gentemp(const char *dir)
 {
 	char *name;
 
     name = cli_gentempname(dir);
 
e3aaff8e
     return(name);
 }
 
0e621e7d
 
 char *cli_gentempdir(const char *dir)
 {
 	char *name;
 
     name = cli_gentempname(dir);
 
     if(name && mkdir(name, 0700)) {
 	cli_dbgmsg("cli_gentempdir(): can't create temp directory: %s\n", name);
         free(name);
         name = NULL;
     }
 
     return(name);
 }
 
 char *cli_gentempdesc(const char *dir, int *fd)
 {
 	char *name;
 
     name = cli_gentempname(dir);
 
     if(name && ((*fd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0)) {
 	cli_dbgmsg("cli_gentempdesc(): can't create temp file: %s\n", name);
         free(name);
         name = NULL;
     }
 
     return(name);
 }
 
 char *cli_gentempstream(const char *dir, FILE **fs)
 {
 	char *name;
4a89b73a
 	mode_t omask;
 
0e621e7d
 
     name = cli_gentempname(dir);
4a89b73a
     if(!name)
 	return NULL;
0e621e7d
 
4a89b73a
     omask = umask(077);
     if((*fs = fopen(name, "wb+")) == NULL) {
0e621e7d
 	cli_dbgmsg("cli_gentempstream(): can't create temp file: %s\n", name);
         free(name);
         name = NULL;
     }
4a89b73a
     umask(omask);
0e621e7d
 
4a89b73a
     return name;
0e621e7d
 }
 
a946fc1c
 #ifdef	C_WINDOWS
 /*
  * Windows doesn't allow you to delete a directory while it is still open
  */
 int
 cli_rmdirs(const char *name)
 {
 	int rc;
 	struct stat statb;	
 	DIR *dd;
 	struct dirent *dent;
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
 #endif
 
 
     if(stat(name, &statb) < 0) {
 	cli_warnmsg("Can't locate %s: %s\n", name, strerror(errno));
 	return -1;
     }
 
     if(!S_ISDIR(statb.st_mode)) {
 	if(unlink(name) < 0) {
 	    cli_warnmsg("Can't remove %s: %s\n", name, strerror(errno));
 	    return -1;
 	}
 	return 0;
     }
 
     if((dd = opendir(name)) == NULL)
 	return -1;
 
     rc = 0;
 
 #ifdef HAVE_READDIR_R_3
     while((readdir_r(dd, &result.d, &dent) == 0) && dent) {
 #elif defined(HAVE_READDIR_R_2)
     while((dent = (struct dirent *)readdir_r(dd, &result.d)) != NULL) {
 #else
     while((dent = readdir(dd)) != NULL) {
 #endif
 	    char *fname;
 
 	if(strcmp(dent->d_name, ".") == 0)
 	    continue;
 	if(strcmp(dent->d_name, "..") == 0)
 	    continue;
 
220d58ba
 	fname = cli_malloc(strlen(name) + strlen(dent->d_name) + 2);
 
a946fc1c
 	if(fname == NULL) {
 	    closedir(dd);
 	    return -1;
 	}
 
 	sprintf(fname, "%s\\%s", name, dent->d_name);
 	rc = cli_rmdirs(fname);
 	free(fname);
 	if(rc != 0)
 	    break;
     }
 
     closedir(dd);
 
     if(rmdir(name) < 0) {
 	cli_errmsg("Can't remove temporary directory %s: %s\n", name, strerror(errno));
 	return -1;
     }
 
     return rc;	
 }
 #else
e3aaff8e
 int cli_rmdirs(const char *dirname)
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	struct stat maind, statbuf;
 	char *fname;
1d670fef
 	int ret;
e3aaff8e
 
a9ebff44
 
     chmod(dirname, 0700);
e3aaff8e
     if((dd = opendir(dirname)) != NULL) {
 	while(stat(dirname, &maind) != -1) {
 	    if(!rmdir(dirname)) break;
ee212426
 	    if(errno != ENOTEMPTY && errno != EEXIST && errno != EBADF) {
256e2dd4
 		cli_errmsg("Can't remove temporary directory %s: %s\n", dirname, strerror(errno));
 		closedir(dd);
1d670fef
 		return -1;
256e2dd4
 	    }
e3aaff8e
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
 	    while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
 	    while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
 	    while((dent = readdir(dd))) {
72a1b240
 #endif
a946fc1c
 #if	(!defined(C_CYGWIN)) && (!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 		if(dent->d_ino)
 #endif
 		{
e3aaff8e
 		    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
6a4078f2
 			fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
a946fc1c
 			if(!fname) {
 			    closedir(dd);
 			    return -1;
 			}
 
 #ifdef	C_WINDOWS
 			sprintf(fname, "%s\\%s", dirname, dent->d_name);
 #else
e3aaff8e
 			sprintf(fname, "%s/%s", dirname, dent->d_name);
a946fc1c
 #endif
e3aaff8e
 
 			/* stat the file */
 			if(lstat(fname, &statbuf) != -1) {
 			    if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
 				if(rmdir(fname) == -1) { /* can't be deleted */
 				    if(errno == EACCES) {
 					cli_errmsg("Can't remove some temporary directories due to access problem.\n");
 					closedir(dd);
049a18b9
 					free(fname);
1d670fef
 					return -1;
 				    }
 				    ret = cli_rmdirs(fname);
 				    if(ret) {
 					cli_warnmsg("Can't remove directory %s\n", fname);
 					free(fname);
 					closedir(dd);
 					return -1;
e3aaff8e
 				    }
 				}
 			    } else
1d670fef
 				if(unlink(fname) < 0) {
a946fc1c
 				    cli_warnmsg("Couldn't remove %s: %s\n", fname, strerror(errno));
1d670fef
 				    free(fname);
 				    closedir(dd);
 				    return -1;
 				}
e3aaff8e
 			}
 
 			free(fname);
 		    }
 		}
 	    }
 
 	    rewinddir(dd);
 	}
 
     } else { 
a946fc1c
 	return -1;
e3aaff8e
     }
 
     closedir(dd);
     return 0;
 }
a946fc1c
 #endif
5b25b5e8
 
 /* Function: readn
         Try hard to read the requested number of bytes
 */
 int cli_readn(int fd, void *buff, unsigned int count)
 {
         int retval;
         unsigned int todo;
         unsigned char *current;
4152ad50
 
 
5b25b5e8
         todo = count;
         current = (unsigned char *) buff;
4152ad50
 
5b25b5e8
         do {
                 retval = read(fd, current, todo);
                 if (retval == 0) {
                         return (count - todo);
                 }
                 if (retval < 0) {
7a5530b5
 			if (errno == EINTR) {
 				continue;
 			}
ba460451
 			cli_errmsg("cli_readn: read error: %s\n", strerror(errno));
5b25b5e8
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
4152ad50
 
 
5b25b5e8
         return count;
 }
4152ad50
 
5b25b5e8
 /* Function: writen
         Try hard to write the specified number of bytes
 */
5d2d2d77
 int cli_writen(int fd, const void *buff, unsigned int count)
5b25b5e8
 {
         int retval;
         unsigned int todo;
5d2d2d77
         const unsigned char *current;
4152ad50
 
 
5b25b5e8
         todo = count;
5d2d2d77
         current = (const unsigned char *) buff;
4152ad50
 
5b25b5e8
         do {
                 retval = write(fd, current, todo);
                 if (retval < 0) {
7a5530b5
 			if (errno == EINTR) {
 				continue;
 			}
5d2d2d77
 			cli_errmsg("cli_writen: write error: %s\n", strerror(errno));
5b25b5e8
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
4152ad50
 
 
5b25b5e8
         return count;
 }
 
b067a7dd
 int cli_filecopy(const char *src, const char *dest)
 {
 	char *buffer;
 	int s, d, bytes;
 
 
97eb8797
     if((s = open(src, O_RDONLY|O_BINARY)) == -1)
b067a7dd
 	return -1;
 
979d32e4
     if((d = open(dest, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, S_IRWXU)) == -1) {
b067a7dd
 	close(s);
 	return -1;
     }
 
     if(!(buffer = cli_malloc(FILEBUFF)))
 	return -1;
 
     while((bytes = cli_readn(s, buffer, FILEBUFF)) > 0)
 	cli_writen(d, buffer, bytes);
 
     free(buffer);
     close(s);
 
     return close(d);
 }
8a9c2d19
 
49760905
 /* Implement a generic bitset, trog@clamav.net */
8a9c2d19
 
 #define BITS_PER_CHAR (8)
 #define BITSET_DEFAULT_SIZE (1024)
 #define FALSE (0)
 #define TRUE (1)
 
 static unsigned long nearest_power(unsigned long num)
 {
 	unsigned long n = BITSET_DEFAULT_SIZE;
 
 	while (n < num) {
 		n <<= 1;
 		if (n == 0) {
 			return num;
 		}
 	}
 	return n;
 }
 
079229d6
 bitset_t *cli_bitset_init(void)
8a9c2d19
 {
 	bitset_t *bs;
 	
49760905
 	bs = cli_malloc(sizeof(bitset_t));
8a9c2d19
 	if (!bs) {
 		return NULL;
 	}
 	bs->length = BITSET_DEFAULT_SIZE;
49760905
 	bs->bitset = cli_calloc(BITSET_DEFAULT_SIZE, 1);
8a9c2d19
 	return bs;
 }
 
 void cli_bitset_free(bitset_t *bs)
 {
 	if (!bs) {
 		return;
 	}
 	if (bs->bitset) {
 		free(bs->bitset);
 	}
 	free(bs);
 }
 
 static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size)
 {
 	unsigned long new_length;
 	
 	new_length = nearest_power(min_size);
84fd5a61
 	bs->bitset = (unsigned char *) cli_realloc2(bs->bitset, new_length);
8a9c2d19
 	if (!bs->bitset) {
 		return NULL;
 	}
 	memset(bs->bitset+bs->length, 0, new_length-bs->length);
 	bs->length = new_length;
 	return bs;
 }
 
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset)
 {
 	unsigned long char_offset;
 	
 	char_offset = bit_offset / BITS_PER_CHAR;
 	bit_offset = bit_offset % BITS_PER_CHAR;
 
 	if (char_offset >= bs->length) {
 		bs = bitset_realloc(bs, char_offset+1);
 		if (!bs) {
 			return FALSE;
 		}
 	}
 	bs->bitset[char_offset] |= ((unsigned char)1 << bit_offset);
 	return TRUE;
 }
 
 int cli_bitset_test(bitset_t *bs, unsigned long bit_offset)
 {
 	unsigned long char_offset;
 	
 	char_offset = bit_offset / BITS_PER_CHAR;
 	bit_offset = bit_offset % BITS_PER_CHAR;
7bd10c36
 
 	if (char_offset >= bs->length) {	
 		return FALSE;
 	}
8a9c2d19
 	return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
 }