libclamav/others.c
e3aaff8e
 /*
2023340a
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
24555841
  *
2023340a
  *  Authors: Tomasz Kojm, Trog
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>
82ded8f5
 #ifndef CLI_MEMFUNSONLY
a7ac5978
 static pthread_mutex_t cli_gentemp_mutex = PTHREAD_MUTEX_INITIALIZER;
82ded8f5
 #endif
24555841
 
 # ifndef HAVE_CTIME_R
 static pthread_mutex_t cli_ctime_mutex = PTHREAD_MUTEX_INITIALIZER;
 # endif
 
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"
92f585fb
 #include "regex/regex.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
 
51535068
 #define CL_FLEVEL 36 /* don't touch it */
a3ee0766
 
a7ac5978
 uint8_t cli_debug_flag = 0, cli_leavetemps_flag = 0;
d4d14218
 
82ded8f5
 #ifndef CLI_MEMFUNSONLY
b0e2e765
 static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
82ded8f5
 #endif
d704c68e
 
a7ac5978
 #define MSGCODE(x)					    \
 	va_list args;					    \
 	int len = sizeof(x) - 1;			    \
 	char buff[BUFSIZ];				    \
     strncpy(buff, x, len);				    \
72ce4b70
     buff[BUFSIZ-1]='\0';				    \
a7ac5978
     va_start(args, str);				    \
     vsnprintf(buff + len, sizeof(buff) - len, str, args);   \
     buff[sizeof(buff) - 1] = '\0';			    \
     fputs(buff, stderr);				    \
     va_end(args)
 
 
e3aaff8e
 void cli_warnmsg(const char *str, ...)
 {
a7ac5978
     MSGCODE("LibClamAV Warning: ");
e3aaff8e
 }
 
 void cli_errmsg(const char *str, ...)
 {
a7ac5978
     MSGCODE("LibClamAV Error: ");
e3aaff8e
 }
 
7be8d871
 void cli_dbgmsg_internal(const char *str, ...)
e3aaff8e
 {
7be8d871
     MSGCODE("LibClamAV debug: ");
d4d14218
 }
 
3eaf4ea6
 #ifndef CLI_MEMFUNSONLY
d4d14218
 void cl_debug(void)
 {
     cli_debug_flag = 1;
e3aaff8e
 }
 
b5134815
 unsigned int cl_retflevel(void)
a3ee0766
 {
     return CL_FLEVEL;
 }
 
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_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";
9d96e4b6
 	case CL_EARJ:
 	    return "ARJ module failure";
e3aaff8e
 	default:
ac75a532
 	    return "Unknown error code";
e3aaff8e
     }
 }
 
b80ae277
 int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3) {
     int ret = CL_SUCCESS;
     unsigned long needed;
 
     /* if called without limits, go on, unpack, scan */
d91ab809
     if(!ctx || !ctx->limits) return CL_CLEAN;
b80ae277
 
     needed = (need1>need2)?need1:need2;
     needed = (needed>need3)?needed:need3;
 
     /* if we have global scan limits */
d91ab809
     if(needed && ctx->limits->maxscansize) {
b80ae277
         /* if the remaining scansize is too small... */
         if(ctx->limits->maxscansize-ctx->scansize<needed) {
d91ab809
 	    /* ... we tell the caller to skip this file */
850db69e
 	    cli_dbgmsg("%s: scansize exceeded (initial: %lu, remaining: %lu, needed: %lu)\n", who, ctx->limits->maxscansize, ctx->scansize, needed);
b80ae277
 	    ret = CL_EMAXSIZE;
 	}
4ad62d4e
     }
 
b80ae277
     /* if we have per-file size limits, and we are overlimit... */
d91ab809
     if(needed && ctx->limits->maxfilesize && ctx->limits->maxfilesize<needed) {
 	/* ... we tell the caller to skip this file */
850db69e
         cli_dbgmsg("%s: filesize exceeded (allowed: %lu, needed: %lu)\n", who, ctx->limits->maxfilesize, needed);
b80ae277
 	ret = CL_EMAXSIZE;
     }
d91ab809
 
     if(ctx->limits->maxfiles && ctx->scannedfiles>=ctx->limits->maxfiles) {
         cli_dbgmsg("%s: files limit reached (max: %u)\n", who, ctx->limits->maxfiles);
 	return CL_EMAXFILES;
     }
b80ae277
     return ret;
36582be0
 }
 
d91ab809
 int cli_updatelimits(cli_ctx *ctx, unsigned long needed) {
850db69e
     int ret=cli_checklimits("cli_updatelimits", ctx, needed, 0, 0);
b80ae277
 
850db69e
     if (ret != CL_CLEAN) return ret;
d91ab809
     ctx->scannedfiles++;
     ctx->scansize+=needed;
     if(ctx->scansize > ctx->limits->maxscansize)
         ctx->scansize = ctx->limits->maxscansize;
850db69e
     return CL_CLEAN;
4ad62d4e
 }
 
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);
d6df9ffb
     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
 }
3eaf4ea6
 #endif
e3aaff8e
 
 void *cli_malloc(size_t size)
 {
 	void *alloc;
 
8d5e0ac0
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
24555841
 	cli_errmsg("cli_malloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) 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) {
24555841
 	cli_errmsg("cli_malloc(): Can't allocate memory (%lu bytes).\n", (unsigned long int) size);
e3aaff8e
 	perror("malloc_problem");
 	return NULL;
     } else return alloc;
 }
 
 void *cli_calloc(size_t nmemb, size_t size)
 {
 	void *alloc;
 
5edae84a
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
24555841
 	cli_errmsg("cli_calloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) 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) {
24555841
 	cli_errmsg("cli_calloc(): Can't allocate memory (%lu bytes).\n", (unsigned long int) (nmemb * size));
e3aaff8e
 	perror("calloc_problem");
 	return NULL;
     } else return alloc;
 }
 
8139fd99
 void *cli_realloc(void *ptr, size_t size)
 {
 	void *alloc;
 
8d5e0ac0
 
1e9c4900
     if(!size || size > CLI_MAX_ALLOCATION) {
24555841
 	cli_errmsg("cli_realloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
f67d5029
 	return NULL;
     }
 
8139fd99
     alloc = realloc(ptr, size);
 
     if(!alloc) {
24555841
 	cli_errmsg("cli_realloc(): Can't re-allocate memory to %lu bytes.\n", (unsigned long int) 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) {
24555841
 	cli_errmsg("cli_realloc2(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
423d6b6a
 	return NULL;
     }
 
     alloc = realloc(ptr, size);
 
     if(!alloc) {
24555841
 	cli_errmsg("cli_realloc2(): Can't re-allocate memory to %lu bytes.\n", (unsigned long int) size);
423d6b6a
 	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) {
24555841
         cli_errmsg("cli_strdup(): Can't allocate memory (%u bytes).\n", (unsigned int) strlen(s));
786b43b6
         perror("strdup_problem");
         return NULL;
     }
 
     return alloc;
 }
 
3eaf4ea6
 #ifndef CLI_MEMFUNSONLY
8000d078
 unsigned int cli_rndnum(unsigned int max)
e3aaff8e
 {
12a7b4fb
     if(name_salt[0] == 16) { /* minimizes re-seeding after the first call to cli_gentemp() */
 	    struct timeval tv;
 	gettimeofday(&tv, (struct timezone *) 0);
 	srand(tv.tv_usec+clock());
     }
e3aaff8e
 
12a7b4fb
     return 1 + (unsigned int) (max * (rand() / (1.0 + RAND_MAX)));
e3aaff8e
 }
 
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;
 }
 
a7ac5978
 char *cli_gentemp(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) {
a7ac5978
 	cli_dbgmsg("cli_gentemp('%s'): out of memory\n", mdir);
2d70a403
 	return NULL;
     }
feec9e31
 
b0e2e765
 #ifdef CL_THREAD_SAFE
a7ac5978
     pthread_mutex_lock(&cli_gentemp_mutex);
b0e2e765
 #endif
 
     memcpy(salt, name_salt, 16);
e3aaff8e
 
b0e2e765
     for(i = 16; i < 48; i++)
12a7b4fb
 	salt[i] = cli_rndnum(255);
e3aaff8e
 
bedc58de
     tmp = cli_md5buff(salt, 48, name_salt);
b0e2e765
 
 #ifdef CL_THREAD_SAFE
a7ac5978
     pthread_mutex_unlock(&cli_gentemp_mutex);
b0e2e765
 #endif
 
     if(!tmp) {
 	free(name);
a7ac5978
 	cli_dbgmsg("cli_gentemp('%s'): out of memory\n", mdir);
b0e2e765
 	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);
 }
 
a7ac5978
 int cli_gentempfd(const char *dir, char **name, int *fd)
0e621e7d
 {
 
a7ac5978
     *name = cli_gentemp(dir);
     if(!*name)
 	return CL_EMEM;
0e621e7d
 
12a7b4fb
     *fd = open(*name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY|O_EXCL, S_IRWXU);
     /*
      * EEXIST is almost impossible to occur, so we just treat it as other
      * errors
      */
    if(*fd == -1) {
a7ac5978
 	cli_errmsg("cli_gentempfd: Can't create temporary file %s: %s\n", *name, strerror(errno));
 	free(*name);
 	return CL_EIO;
0e621e7d
     }
 
a7ac5978
     return CL_SUCCESS;
0e621e7d
 }
3eaf4ea6
 #endif
0e621e7d
 
c0a95e0c
 /* Function: unlink
         unlink() with error checking
 */
997a0e0b
 int cli_unlink(const char *pathname)
c0a95e0c
 {
997a0e0b
 	if (unlink(pathname)==-1) {
 	    cli_warnmsg("cli_unlink: failure - %s\n", strerror(errno));
 	    return 1;
 	}
 	return 0;
c0a95e0c
 }
 
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) {
a7ac5978
 	cli_warnmsg("cli_rmdirs: Can't locate %s: %s\n", name, strerror(errno));
a946fc1c
 	return -1;
     }
 
     if(!S_ISDIR(statb.st_mode)) {
997a0e0b
 	if(cli_unlink(name)) return -1;
a946fc1c
 	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
a7ac5978
 	    char *path;
a946fc1c
 
 	if(strcmp(dent->d_name, ".") == 0)
 	    continue;
 	if(strcmp(dent->d_name, "..") == 0)
 	    continue;
 
a7ac5978
 	path = cli_malloc(strlen(name) + strlen(dent->d_name) + 2);
220d58ba
 
a7ac5978
 	if(path == NULL) {
a946fc1c
 	    closedir(dd);
 	    return -1;
 	}
 
a7ac5978
 	sprintf(path, "%s\\%s", name, dent->d_name);
 	rc = cli_rmdirs(path);
 	free(path);
a946fc1c
 	if(rc != 0)
 	    break;
     }
 
     closedir(dd);
 
     if(rmdir(name) < 0) {
a7ac5978
 	cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", name, strerror(errno));
a946fc1c
 	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;
a7ac5978
 	char *path;
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) {
a7ac5978
 		cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", dirname, strerror(errno));
256e2dd4
 		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
6670d61d
 #if	(!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 		if(dent->d_ino)
 #endif
 		{
e3aaff8e
 		    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
a7ac5978
 			path = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
 			if(!path) {
a946fc1c
 			    closedir(dd);
 			    return -1;
 			}
 
a7ac5978
 			sprintf(path, "%s/%s", dirname, dent->d_name);
e3aaff8e
 
 			/* stat the file */
a7ac5978
 			if(lstat(path, &statbuf) != -1) {
e3aaff8e
 			    if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
a7ac5978
 				if(rmdir(path) == -1) { /* can't be deleted */
e3aaff8e
 				    if(errno == EACCES) {
a7ac5978
 					cli_errmsg("cli_rmdirs: Can't remove some temporary directories due to access problem.\n");
e3aaff8e
 					closedir(dd);
a7ac5978
 					free(path);
1d670fef
 					return -1;
 				    }
a7ac5978
 				    if(cli_rmdirs(path)) {
 					cli_warnmsg("cli_rmdirs: Can't remove nested directory %s\n", path);
 					free(path);
1d670fef
 					closedir(dd);
 					return -1;
e3aaff8e
 				    }
 				}
997a0e0b
 			    } else {
 				if(cli_unlink(path)) {
 				    free(path);
 				    closedir(dd);
 				    return -1;
 				}
 			    }
e3aaff8e
 			}
a7ac5978
 			free(path);
e3aaff8e
 		    }
 		}
 	    }
 	    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;
     }
 
de5582b6
     if(!(buffer = cli_malloc(FILEBUFF))) {
 	close(s);
 	close(d);
b067a7dd
 	return -1;
de5582b6
     }
b067a7dd
 
     while((bytes = cli_readn(s, buffer, FILEBUFF)) > 0)
 	cli_writen(d, buffer, bytes);
 
     free(buffer);
     close(s);
 
     return close(d);
 }
8a9c2d19
 
c0a95e0c
 
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;
9358b217
 	unsigned char *new_bitset;
8a9c2d19
 	
 	new_length = nearest_power(min_size);
9358b217
 	new_bitset = (unsigned char *) cli_realloc(bs->bitset, new_length);
 	if (!new_bitset) {
8a9c2d19
 		return NULL;
 	}
9358b217
 	bs->bitset = new_bitset;
8a9c2d19
 	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));
 }
24555841
 
ca71efc4
 /* returns converted timestamp, in case of error the returned string contains at least one character */
24555841
 const char* cli_ctime(const time_t *timep, char *buf, const size_t bufsize)
 {
ca71efc4
 	const char *ret;
24555841
 	if(bufsize < 26) {
 		/* standard says we must have at least 26 bytes buffer */
 		cli_warnmsg("buffer too small for ctime\n");
ca71efc4
 		return " ";
 	}
 	if((uint32_t)(*timep) > 0x7fffffff) {
 		/* some systems can consider these timestamps invalid */
 		strncpy(buf, "invalid timestamp", bufsize-1);
 		buf[bufsize-1] = '\0';
 		return buf;
24555841
 	}
ca71efc4
 
24555841
 #ifdef HAVE_CTIME_R	
 # ifdef HAVE_CTIME_R_2
ca71efc4
 	ret = ctime_r(timep, buf);
24555841
 # else
ca71efc4
 	ret = ctime_r(timep, buf, bufsize);
24555841
 # endif
 #else /* no ctime_r */
 
 # ifdef CL_THREAD_SAFE
 	pthread_mutex_lock(&cli_ctime_mutex);
 # endif
ca71efc4
 	ret = ctime(timep);
 	if(ret) {
 		strncpy(buf, ret, bufsize-1);
 		buf[bufsize-1] = '\0';
 		ret = buf;
 	}
24555841
 # ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ctime_mutex);
 # endif
 #endif
ca71efc4
 	/* common */
 	if(!ret) {
 		buf[0] = ' ';
 		buf[1] = '\0';
 		return buf;
 	}
 	return ret;
24555841
 }
 
7a8d49e1
 #ifndef CLI_MEMFUNSONLY
92f585fb
 int cli_matchregex(const char *str, const char *regex)
 {
 	regex_t reg;
 	int match;
 
6628371d
     if(cli_regcomp(&reg, regex, REG_EXTENDED | REG_NOSUB) == 0) {
92f585fb
 	match = (cli_regexec(&reg, str, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1;
 	cli_regfree(&reg);
 	return match;
     }
 
     return 0;
 }
7a8d49e1
 #endif