libclamav/others_common.c
02b5f26c
 /*
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
  *
  *  Authors: Tomasz Kojm, Trog
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  *
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
 #ifdef	HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifndef	C_WINDOWS
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <dirent.h>
 #endif
 #include <time.h>
 #include <fcntl.h>
 #ifndef	C_WINDOWS
 #include <pwd.h>
 #endif
 #include <errno.h>
 #include "target.h"
 #ifndef	C_WINDOWS
 #include <sys/time.h>
 #endif
 #ifdef	HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
 #ifdef	HAVE_MALLOC_H
 #include <malloc.h>
 #endif
 #if	defined(_MSC_VER) && defined(_DEBUG)
 #include <crtdbg.h>
 #endif
 
 #include "clamav.h"
 #include "others.h"
 #include "md5.h"
 #include "cltypes.h"
 #include "regex/regex.h"
 #include "ltdl.h"
 #include "matcher-ac.h"
20473dec
 #include "md5.h"
02b5f26c
 
afff80ef
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
20473dec
 static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
 
b070a1d2
 #ifdef CL_NOTHREADS
 #undef CL_THREAD_SAFE
 #endif
 
02b5f26c
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
 
20473dec
 static pthread_mutex_t cli_gentemp_mutex = PTHREAD_MUTEX_INITIALIZER;
02b5f26c
 # ifndef HAVE_CTIME_R
 static pthread_mutex_t cli_ctime_mutex = PTHREAD_MUTEX_INITIALIZER;
 # endif
e68d70e7
 static pthread_mutex_t cli_strerror_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
02b5f26c
 uint8_t cli_debug_flag = 0;
 
 #define MSGCODE(x)					    \
 	va_list args;					    \
 	int len = sizeof(x) - 1;			    \
 	char buff[BUFSIZ];				    \
     strncpy(buff, x, len);				    \
     buff[BUFSIZ-1]='\0';				    \
     va_start(args, str);				    \
     vsnprintf(buff + len, sizeof(buff) - len, str, args);   \
     buff[sizeof(buff) - 1] = '\0';			    \
     fputs(buff, stderr);				    \
     va_end(args)
 
 void cli_warnmsg(const char *str, ...)
 {
     MSGCODE("LibClamAV Warning: ");
 }
 
 void cli_errmsg(const char *str, ...)
 {
     MSGCODE("LibClamAV Error: ");
 }
 
 void cli_dbgmsg_internal(const char *str, ...)
 {
     MSGCODE("LibClamAV debug: ");
 }
 
 int cli_matchregex(const char *str, const char *regex)
 {
 	regex_t reg;
 	int match;
 
     if(cli_regcomp(&reg, regex, REG_EXTENDED | REG_NOSUB) == 0) {
 	match = (cli_regexec(&reg, str, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1;
 	cli_regfree(&reg);
 	return match;
     }
 
     return 0;
 }
 void *cli_malloc(size_t size)
 {
 	void *alloc;
 
 
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("cli_malloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
 	return NULL;
     }
 
 #if defined(_MSC_VER) && defined(_DEBUG)
     alloc = _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__);
 #else
     alloc = malloc(size);
 #endif
 
     if(!alloc) {
 	cli_errmsg("cli_malloc(): Can't allocate memory (%lu bytes).\n", (unsigned long int) size);
 	perror("malloc_problem");
 	return NULL;
     } else return alloc;
 }
 
 void *cli_calloc(size_t nmemb, size_t size)
 {
 	void *alloc;
 
 
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("cli_calloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
 	return NULL;
     }
 
 #if defined(_MSC_VER) && defined(_DEBUG)
     alloc = _calloc_dbg(nmemb, size, _NORMAL_BLOCK, __FILE__, __LINE__);
 #else
     alloc = calloc(nmemb, size);
 #endif
 
     if(!alloc) {
 	cli_errmsg("cli_calloc(): Can't allocate memory (%lu bytes).\n", (unsigned long int) (nmemb * size));
 	perror("calloc_problem");
 	return NULL;
     } else return alloc;
 }
 
 void *cli_realloc(void *ptr, size_t size)
 {
 	void *alloc;
 
 
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("cli_realloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
 	return NULL;
     }
 
     alloc = realloc(ptr, size);
 
     if(!alloc) {
 	cli_errmsg("cli_realloc(): Can't re-allocate memory to %lu bytes.\n", (unsigned long int) size);
 	perror("realloc_problem");
 	return NULL;
     } else return alloc;
 }
 
 void *cli_realloc2(void *ptr, size_t size)
 {
 	void *alloc;
 
 
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("cli_realloc2(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
 	return NULL;
     }
 
     alloc = realloc(ptr, size);
 
     if(!alloc) {
 	cli_errmsg("cli_realloc2(): Can't re-allocate memory to %lu bytes.\n", (unsigned long int) size);
 	perror("realloc_problem");
 	if(ptr)
 	    free(ptr);
 	return NULL;
     } else return alloc;
 }
 
 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", (unsigned int) strlen(s));
         perror("strdup_problem");
         return NULL;
     }
 
     return alloc;
 }
 
 /* returns converted timestamp, in case of error the returned string contains at least one character */
 const char* cli_ctime(const time_t *timep, char *buf, const size_t bufsize)
 {
 	const char *ret;
 	if(bufsize < 26) {
 		/* standard says we must have at least 26 bytes buffer */
 		cli_warnmsg("buffer too small for ctime\n");
 		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;
 	}
 
 #ifdef HAVE_CTIME_R	
 # ifdef HAVE_CTIME_R_2
 	ret = ctime_r(timep, buf);
 # else
 	ret = ctime_r(timep, buf, bufsize);
 # endif
 #else /* no ctime_r */
 
 # ifdef CL_THREAD_SAFE
 	pthread_mutex_lock(&cli_ctime_mutex);
 # endif
 	ret = ctime(timep);
 	if(ret) {
 		strncpy(buf, ret, bufsize-1);
 		buf[bufsize-1] = '\0';
 		ret = buf;
 	}
 # ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ctime_mutex);
 # endif
 #endif
 	/* common */
 	if(!ret) {
 		buf[0] = ' ';
 		buf[1] = '\0';
 		return buf;
 	}
 	return ret;
 }
 
afff80ef
 /* 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;
 
 
         todo = count;
         current = (unsigned char *) buff;
 
         do {
                 retval = read(fd, current, todo);
                 if (retval == 0) {
                         return (count - todo);
                 }
                 if (retval < 0) {
e68d70e7
 			char err[128];
afff80ef
 			if (errno == EINTR) {
 				continue;
 			}
e68d70e7
 			cli_errmsg("cli_readn: read error: %s\n", cli_strerror(errno, err, sizeof(err)));
afff80ef
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
 
 
         return count;
 }
 
 /* Function: writen
         Try hard to write the specified number of bytes
 */
 int cli_writen(int fd, const void *buff, unsigned int count)
 {
         int retval;
         unsigned int todo;
         const unsigned char *current;
 
 
         todo = count;
         current = (const unsigned char *) buff;
 
         do {
                 retval = write(fd, current, todo);
                 if (retval < 0) {
e68d70e7
 			char err[128];
afff80ef
 			if (errno == EINTR) {
 				continue;
 			}
e68d70e7
 			cli_errmsg("cli_writen: write error: %s\n", cli_strerror(errno, err, sizeof(err)));
afff80ef
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
 
 
         return count;
 }
 
 int cli_filecopy(const char *src, const char *dest)
 {
 	char *buffer;
 	int s, d, bytes;
 
 
     if((s = open(src, O_RDONLY|O_BINARY)) == -1)
 	return -1;
 
     if((d = open(dest, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, S_IRWXU)) == -1) {
 	close(s);
 	return -1;
     }
 
     if(!(buffer = cli_malloc(FILEBUFF))) {
 	close(s);
 	close(d);
 	return -1;
     }
 
     while((bytes = cli_readn(s, buffer, FILEBUFF)) > 0)
 	cli_writen(d, buffer, bytes);
 
     free(buffer);
     close(s);
 
     return close(d);
 }
e5e4a554
 struct dirent_data {
     char *filename;
     const char *dirname;
     struct stat *statbuf;
     long  ino; /* -1: inode not available */
fe389c84
     int   is_dir;/* 0 - no, 1 - yes */
e5e4a554
 };
 
 /* sort files before directories, and lower inodes before higher inodes */
 static int ftw_compare(const void *a, const void *b)
 {
     const struct dirent_data *da = a;
     const struct dirent_data *db = b;
     long diff = da->is_dir - db->is_dir;
     if (!diff) {
 	diff = da->ino - db->ino;
     }
     return diff;
 }
 
 enum filetype {
     ft_unknown,
     ft_link,
     ft_directory,
     ft_regular,
     ft_skipped_special,
     ft_skipped_link
 };
 
 static inline int ft_skipped(enum filetype ft)
 {
     return ft != ft_regular && ft != ft_directory;
 }
 
 #define FOLLOW_SYMLINK_MASK (CLI_FTW_FOLLOW_FILE_SYMLINK | CLI_FTW_FOLLOW_DIR_SYMLINK)
 static int get_filetype(const char *fname, int flags, int need_stat,
 			 struct stat *statbuf, enum filetype *ft)
 {
     int stated = 0;
 
     if (*ft == ft_unknown || *ft == ft_link) {
 	need_stat = 1;
 
 	if ((flags & FOLLOW_SYMLINK_MASK) != FOLLOW_SYMLINK_MASK) {
 	    /* Following only one of directory/file symlinks, or none, may
 	     * need to lstat.
 	     * If we're following both file and directory symlinks, we don't need
 	     * to lstat(), we can just stat() directly.*/
 	    if (*ft != ft_link) {
 		/* need to lstat to determine if it is a symlink */
 		if (lstat(fname, statbuf) == -1)
 		    return -1;
 		if (S_ISLNK(statbuf->st_mode)) {
 		    *ft = ft_link;
 		} else {
 		    /* It was not a symlink, stat() not needed */
 		    need_stat = 0;
 		    stated = 1;
 		}
 	    }
 	    if (*ft == ft_link && !(flags & FOLLOW_SYMLINK_MASK)) {
 		/* This is a symlink, but we don't follow any symlinks */
 		*ft = ft_skipped_link;
 		return 0;
 	    }
 	}
     }
 
     if (need_stat) {
 	if (stat(fname, statbuf) == -1)
 	    return -1;
 	stated = 1;
     }
 
     if (*ft == ft_unknown || *ft == ft_link) {
 	if (S_ISDIR(statbuf->st_mode) &&
 	    (*ft != ft_link || (flags & CLI_FTW_FOLLOW_DIR_SYMLINK))) {
 	    /* A directory, or (a symlink to a directory and we're following dir
 	     * symlinks) */
 	    *ft = ft_directory;
 	} else if (S_ISREG(statbuf->st_mode) &&
 		   (*ft != ft_link || (flags & CLI_FTW_FOLLOW_FILE_SYMLINK))) {
 	    /* A file, or (a symlink to a file and we're following file symlinks) */
 	    *ft = ft_regular;
 	} else {
 	    /* default: skipped */
 	    *ft = S_ISLNK(statbuf->st_mode) ?
 		ft_skipped_link : ft_skipped_special;
 	}
     }
     return stated;
 }
 
 static int handle_filetype(const char *fname, int flags,
 			   struct stat *statbuf, int *stated, enum filetype *ft,
 			   cli_ftw_cb callback, struct cli_ftw_cbdata *data)
 {
     int ret;
 
     *stated = get_filetype(fname, flags, flags & CLI_FTW_NEED_STAT , statbuf, ft);
 
     if (*stated == -1) {
 	/*  we failed a stat() or lstat() */
 	ret = callback(NULL, NULL, fname, error_stat, data);
 	if (ret != CL_SUCCESS)
 	    return ret;
 	*ft = ft_unknown;
     } else if (*ft == ft_skipped_link || *ft == ft_skipped_special) {
 	/* skipped filetype */
 	ret = callback(stated ? statbuf : NULL, NULL, fname,
 		       *ft == ft_skipped_link ?
 		       warning_skipped_link : warning_skipped_special, data);
 	if (ret != CL_SUCCESS)
 	    return ret;
     }
     return CL_SUCCESS;
 }
 
51bbedb1
 static int cli_ftw_dir(const char *dirname, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk);
 static int handle_entry(struct dirent_data *entry, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk)
e5e4a554
 {
     if (!entry->is_dir) {
 	return callback(entry->statbuf, entry->filename, entry->filename, visit_file, data);
     } else {
51bbedb1
 	return cli_ftw_dir(entry->dirname, flags, maxdepth, callback, data, pathchk);
e5e4a554
     }
 }
 
51bbedb1
 int cli_ftw(char *path, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk)
e5e4a554
 {
     struct stat statbuf;
     enum filetype ft = ft_unknown;
     struct dirent_data entry;
     int stated = 0;
a1598d7c
     int ret;
 
51bbedb1
     if (((flags & CLI_FTW_TRIM_SLASHES) || pathchk) && path[0] && path[1]) {
56c97aa0
 	char *pathend;
a1598d7c
 	/* trim slashes so that dir and dir/ behave the same when
 	 * they are symlinks, and we are not following symlinks */
 	while (path[0] == '/' && path[1] == '/') path++;
 	pathend = path + strlen(path);
 	while (pathend > path && pathend[-1] == '/') --pathend;
 	*pathend = '\0';
     }
51bbedb1
     if(pathchk && pathchk(path, data) == 1)
 	return CL_SUCCESS;
a1598d7c
     ret = handle_filetype(path, flags, &statbuf, &stated, &ft, callback, data);
e5e4a554
     if (ret != CL_SUCCESS)
 	return ret;
     if (ft_skipped(ft))
 	return CL_SUCCESS;
     entry.statbuf = stated ? &statbuf : NULL;
     entry.is_dir = ft == ft_directory;
     entry.filename = entry.is_dir ? NULL : strdup(path);
     entry.dirname = entry.is_dir ? path : NULL;
     if (entry.is_dir) {
 	ret = callback(entry.statbuf, NULL, path, visit_directory_toplev, data);
 	if (ret != CL_SUCCESS)
 	    return ret;
     }
51bbedb1
     return handle_entry(&entry, flags, maxdepth, callback, data, pathchk);
e5e4a554
 }
 
51bbedb1
 static int cli_ftw_dir(const char *dirname, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk)
e5e4a554
 {
     DIR *dd;
 #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
     struct dirent_data *entries = NULL;
     size_t i, entries_cnt = 0;
     int ret;
 
     if (maxdepth < 0) {
 	/* exceeded recursion limit */
 	ret = callback(NULL, NULL, dirname, warning_skipped_dir, data);
 	return ret;
     }
 
     if((dd = opendir(dirname)) != NULL) {
 	struct dirent *dent;
 	errno = 0;
 	ret = CL_SUCCESS;
 #ifdef HAVE_READDIR_R_3
 	while(!readdir_r(dd, &result.d, &dent) && dent) {
 #elif defined(HAVE_READDIR_R_2)
 	while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
 #else
 	while((dent = readdir(dd))) {
 #endif
 	    int stated = 0;
 	    enum filetype ft;
 	    char *fname;
 	    struct stat statbuf;
 	    struct stat *statbufp;
 
 	    if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 		continue;
 #ifdef _DIRENT_HAVE_D_TYPE
 	    switch (dent->d_type) {
 		case DT_DIR:
 		    ft = ft_directory;
 		    break;
 		case DT_LNK:
 		    if (!(flags & FOLLOW_SYMLINK_MASK)) {
 			/* we don't follow symlinks, don't bother
 			 * stating it */
 			errno = 0;
 			continue;
 		    }
 		    ft = ft_link;
 		    break;
 		case DT_REG:
 		    ft = ft_regular;
 		    break;
 		case DT_UNKNOWN:
 		    ft = ft_unknown;
 		    break;
 		default:
 		    ft = ft_skipped_special;
 		    break;
 	    }
 #else
 	    ft = ft_unknown;
 #endif
 	    fname = (char *) cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
 	    if(!fname) {
 		ret = callback(NULL, NULL, dirname, error_mem, data);
 		if (ret != CL_SUCCESS)
 		    break;
 	    }
215fe6cc
             if(!strcmp(dirname, "/"))
 		sprintf(fname, "/%s", dent->d_name);
 	    else
 		sprintf(fname, "%s/%s", dirname, dent->d_name);
e5e4a554
 
51bbedb1
 	    if(pathchk && pathchk(fname, data) == 1) {
 		free(fname);
 		continue;
 	    }
 
e5e4a554
 	    ret = handle_filetype(fname, flags, &statbuf, &stated, &ft, callback, data);
 	    if (ret != CL_SUCCESS) {
 		free(fname);
 		break;
 	    }
 
 	    if (ft_skipped(ft)) { /* skip */
 		free(fname);
 		errno = 0;
 		continue;
 	    }
 
 	    if (stated && (flags & CLI_FTW_NEED_STAT)) {
 		statbufp = cli_malloc(sizeof(*statbufp));
 		if (!statbufp) {
 		    ret = callback(stated ? &statbuf : NULL, NULL, fname, error_mem, data);
 		    free(fname);
 		    if (ret != CL_SUCCESS)
 			break;
 		    else {
 			errno = 0;
 			continue;
 		    }
 		}
 		memcpy(statbufp, &statbuf, sizeof(statbuf));
 	    } else {
 		statbufp = 0;
 	    }
 
 	    entries_cnt++;
 	    entries = cli_realloc(entries, entries_cnt*sizeof(*entries));
 	    if (!entries) {
 		ret = callback(stated ? &statbuf : NULL, NULL, fname, error_mem, data);
 		free(fname);
 		if (statbufp)
 		    free(statbufp);
 		break;
 	    } else {
 		struct dirent_data *entry = &entries[entries_cnt-1];
 		entry->filename = fname;
 		entry->statbuf = statbufp;
 		entry->is_dir = ft == ft_directory;
 		entry->dirname = entry->is_dir ? fname : NULL;
 #ifdef _XOPEN_UNIX
 		entry->ino = dent->d_ino;
 #else
 		entry->ino = -1;
 #endif
 	    }
 	    errno = 0;
 	}
 	closedir(dd);
 
 	if (entries) {
 	    qsort(entries, entries_cnt, sizeof(*entries), ftw_compare);
 	    for (i = 0; i < entries_cnt; i++) {
 		struct dirent_data *entry = &entries[i];
51bbedb1
 		ret = handle_entry(entry, flags, maxdepth-1, callback, data, pathchk);
e5e4a554
 		if (entry->is_dir)
 		    free(entry->filename);
 		if (entry->statbuf)
 		    free(entry->statbuf);
 		if (ret != CL_SUCCESS)
 		    break;
 	    }
 	    free(entries);
 	}
     } else {
 	ret = callback(NULL, NULL, dirname, error_stat, data);
     }
     return ret;
 }
e68d70e7
 
 /* strerror_r is not available everywhere, (and when it is there are two variants,
  * the XSI, and the GNU one, so provide a wrapper to make sure correct one is
  * used */
 const char* cli_strerror(int errnum, char *buf, size_t len)
 {
05b0e635
     char *err;
2f4e80d8
 # ifdef CL_THREAD_SAFE
05b0e635
     pthread_mutex_lock(&cli_strerror_mutex);
2f4e80d8
 #endif
05b0e635
     err = strerror(errnum);
     strncpy(buf, err, len);
2f4e80d8
 # ifdef CL_THREAD_SAFE
05b0e635
     pthread_mutex_unlock(&cli_strerror_mutex);
2f4e80d8
 #endif
e68d70e7
     return buf;
 }
 
20473dec
 static char *cli_md5buff(const unsigned char *buffer, unsigned int len, unsigned char *dig)
 {
 	unsigned char digest[16];
 	char *md5str, *pt;
 	cli_md5_ctx ctx;
 	int i;
 
 
     cli_md5_init(&ctx);
     cli_md5_update(&ctx, buffer, len);
     cli_md5_final(digest, &ctx);
 
     if(dig)
 	memcpy(dig, digest, 16);
 
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
 
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
 
     return md5str;
 }
 
 unsigned int cli_rndnum(unsigned int max)
 {
     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());
     }
 
     return 1 + (unsigned int) (max * (rand() / (1.0 + RAND_MAX)));
 }
 
 char *cli_gentemp(const char *dir)
 {
 	char *name, *tmp;
         const char *mdir;
 	unsigned char salt[16 + 32];
 	int i;
 
     if(!dir) {
 	if((mdir = getenv("TMPDIR")) == NULL)
 #ifdef P_tmpdir
 	    mdir = P_tmpdir;
 #else
 	    mdir = "/tmp";
 #endif
     } else
 	mdir = dir;
 
     name = (char *) cli_calloc(strlen(mdir) + 1 + 32 + 1 + 7, sizeof(char));
     if(!name) {
 	cli_dbgmsg("cli_gentemp('%s'): out of memory\n", mdir);
 	return NULL;
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_gentemp_mutex);
 #endif
 
     memcpy(salt, name_salt, 16);
 
     for(i = 16; i < 48; i++)
 	salt[i] = cli_rndnum(255);
 
     tmp = cli_md5buff(salt, 48, name_salt);
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_gentemp_mutex);
 #endif
 
     if(!tmp) {
 	free(name);
 	cli_dbgmsg("cli_gentemp('%s'): out of memory\n", mdir);
 	return NULL;
     }
 
 #ifdef	C_WINDOWS
 	sprintf(name, "%s\\clamav-", mdir);
 #else
 	sprintf(name, "%s/clamav-", mdir);
 #endif
     strncat(name, tmp, 32);
     free(tmp);
 
     return(name);
 }
 
 int cli_gentempfd(const char *dir, char **name, int *fd)
 {
 
     *name = cli_gentemp(dir);
     if(!*name)
 	return CL_EMEM;
 
     *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) {
 	cli_errmsg("cli_gentempfd: Can't create temporary file %s: %s\n", *name, strerror(errno));
 	free(*name);
 	return CL_ECREAT;
     }
 
     return CL_SUCCESS;
 }
2bc065d4
 
 int cli_regcomp(regex_t *preg, const char *pattern, int cflags)
 {
     if (!strncmp(pattern, "(?i)", 4)) {
 	pattern += 4;
 	cflags |= REG_ICASE;
     }
     return cli_regcomp_real(preg, pattern, cflags);
 }