
/*
 * LIB/SUBS.C
 *
 * (c)Copyright 1998, Matthew Dillon, All Rights Reserved.  Refer to
 *    the COPYRIGHT file in the base directory of this distribution
 *    for specific rights granted.
 *
 */

#include "defs.h"

Prototype const char *PatExpand(const char *pat);
Prototype void diablo_strlcpy(char *d, const char *s, int ssize, int dsize);
Prototype void diablo_strlcpynl(char *d, const char *s, int ssize, int dsize);
Prototype const char *PatExpand(const char *pat);
Prototype const char *PatLibExpand(const char *pat);
Prototype const char *PatDbExpand(const char *pat);
Prototype const char *PatLogExpand(const char *pat);
Prototype const char *PatSpoolExpand(const char *pat);
Prototype const char *PatRunExpand(const char *pat);
Prototype int CidrMatch(const char *cidr, const char *ip);
Prototype int IsIpAddr(char *a);
Prototype int TestForwardLookup(const char *hostName, const struct sockaddr_in *rsin);
Prototype int ValidGroupName(const char *name);
Prototype void SanitizeString(char *name);
Prototype void SanitizeDescString(char *name);
Prototype char *StrnCpyNull(char *dst, const char *src, size_t maxlen);
Prototype const char *ftos(double d);
Prototype long bsizetol(char *p);
Prototype double bsizektod(char *p);
Prototype long btimetol(char *p);
Prototype char *dtlenstr(time_t t);
Prototype int MakeGroupDirectory(char *path);
Prototype char *strdupfree(char **st, char *val, char *empty);
Prototype char *safestr(char *st, char *noval);
Prototype int enabled(char *st);
Prototype int HashFeedMatch(HashFeed *hashfeed, int article);
Prototype int MoveFile(char *from, char *to);

u_long ascToAddr(const char *str);
u_long ascToMask(const char *str);

void 
diablo_strlcpy(char *d, const char *s, int ssize, int dsize)
{
    while (ssize && dsize > 1 && *s) {
	*d = *s;
	--ssize;
	--dsize;
	++s;
	++d;
    }
    *d = 0;
}

void 
diablo_strlcpynl(char *d, const char *s, int ssize, int dsize)
{
    char *dold = d;

    while (ssize && *s == ' ') {
	++s;
	--ssize;
    }

    while (ssize && dsize > 1 && *s) {
	*d = *s;
	--ssize;
	--dsize;
	++s;
	++d;
    }
    *d = 0;

    /*
     * remove newlines and trailing tabs and spaces
     */
    while (d != dold && (d[-1] == '\n' || d[-1] == '\r')) {
	--d;
	*d = 0;
    }
    while (d != dold && (d[-1] == '\t' || d[-1] == ' ')) {
	--d;
	*d = 0;
    }
}

static char PatPath1[128];
static char PatPath2[128];

const char *
PatExpand(const char *pat)
{
    snprintf(PatPath1, sizeof(PatPath1), pat, NewsHome);

    return(PatPath1);
}


const char *
PatLibExpand(const char *pat)
{
    snprintf(PatPath2, sizeof(PatPath2), pat, PatExpand(LibHomePat));
    return(PatPath2);
}


const char *
PatDbExpand(const char *pat)
{
    snprintf(PatPath2, sizeof(PatPath2), pat, PatExpand(DbHomePat));
    return(PatPath2);
}

const char *
PatLogExpand(const char *pat)
{
    snprintf(PatPath2, sizeof(PatPath2), pat, PatExpand(LogHomePat));
    return(PatPath2);
}

const char *
PatSpoolExpand(const char *pat)
{
    snprintf(PatPath2, sizeof(PatPath2), pat, PatExpand(SpoolHomePat));
    return(PatPath2);
}

const char *
PatRunExpand(const char *pat)
{
    snprintf(PatPath2, sizeof(PatPath2), pat, PatExpand(RunHomePat));
    return(PatPath2);
}

int 
IsIpAddr(char *a)
{
    while (*a) {
        if (! (isdigit((int)*a) || *a == '.' || *a == '/' ||
                *a == '*' || *a == '?'))  
            return(0);
        a++;
    }
    return(1);
}

u_long
ascToAddr(const char *str)
{
    char string[32];
    char *ptr;
    u_long i, a, b, c, d;

    snprintf(string, sizeof(string), "%s", str);
    if ((ptr = strchr(string, '/')))
	*ptr = '\0';

    /* Count the dots. */
    ptr = string;
    i = 0;
    while (*ptr)
	if (*ptr++ == '.')
	    i++;

    if (i == 0) {
	sscanf(string, "%ld", &a);
	return(a << 24);
    } else if (i == 1) {
	sscanf(string, "%ld.%ld", &a, &b);
	return((a << 24) + (b << 16));
    } else if (i == 2) {
	sscanf(string, "%ld.%ld.%ld", &a, &b, &c);
	return((a << 24) + (b << 16) + (c << 8));
    } else {
	sscanf(string, "%ld.%ld.%ld.%ld", &a, &b, &c, &d);
	return((a << 24) + (b << 16) + (c << 8) + d);
    }
}

u_long
ascToMask(const char *str)
{
    char *ptr;
    u_long i;

    if ((ptr = strchr(str, '/')))
	ptr++;
    else
	return(0xffffffff);

    /* Accept either CIDR /nn or /xxx.xxx.xxx.xxx */
    if (strstr(ptr, ".")) {
	return(ntohl(inet_addr(ptr)));
    } else {
	i = atoi(ptr);
	if (!i)
	    return(0);
	else
	    return((u_long) 0xffffffff << (32 - i));
    }
}

/*
 * Match CIDR address - return 1 for sucess else 0
 */
int
CidrMatch(const char *cidr, const char *ip)
{
    u_long net;
    u_long mask;
    u_long addr;

    addr = ascToAddr(ip);
    net = ascToAddr(cidr);
    mask = ascToMask(cidr);
    if ((addr & mask) != (net & mask))
	return(0);
    else
	return(1);
}

/*
 * TestForwardLookup(). 
 *
 * Check that Fwd and Rev DNS entries match up
 */

int
TestForwardLookup(const char *hostName, const struct sockaddr_in *rsin)
{
    struct hostent *he;
    int r = -1;

    if ((he = gethostbyname(hostName)) != NULL) {
	int i;

	for (i = 0; he->h_addr_list && he->h_addr_list[i]; ++i) {
	    const struct in_addr *haddr = (const void *)he->h_addr_list[i];
	    if (rsin->sin_addr.s_addr == haddr->s_addr) {
		r = 0;
		break;
	    }
	}
	if (he->h_addr_list[i] == NULL)
	    logit(LOG_NOTICE, "DNS Fwd/Rev mismatch: %s/%s", hostName, inet_ntoa(rsin->sin_addr));
    } else {
	logit(LOG_NOTICE, "DNS Fwd/Rev mismatch: lookup of %s failed", hostName);
    }
    return(r);
}

int 
ValidGroupName(const char *name)
{
    int r = 0;

    if (name[0] == 0 || name[0] == '.') {
	r = -1;
    } else {
	for (; *name; ++name) {
	    if (*name == ' ' || ! isprint((int)*name)) {
		r = -1;
	    }
	    if (*name >= 'a' && *name <= 'z')
		continue;
	    if (*name >= 'A' && *name <= 'Z')
		continue;
	    if (*name >= '0' && *name <= '9')
		continue;
	    if (*name == '+')
		continue;
	    if (*name == '-')
		continue;
	    if (*name == '.') {
		if (name[-1] == '.' || name[1] == '.' || name[1] == 0)
		    r = -1;
	    }
	}
    }
    return(r);
}

void
SanitizeString(char *name)
{
    for (; *name; ++name) {
	if (*name >= 'a' && *name <= 'z')
	    continue;
	if (*name >= 'A' && *name <= 'Z')
	    continue;
	if (*name >= '0' && *name <= '9')
	    continue;
	if (*name == '\t')
	    *name = ' ';
	if (*name == '@' ||
	    *name == '-' ||
	    *name == '+' ||
	    *name == '_' ||
	    *name == '.' ||
	    *name == ',' ||
	    *name == ' '
	) {
	    continue;
	}
	*name = '?';
    }
}

/*
 * Sanitise a group description
 */
void
SanitizeDescString(char *name)
{
    for (; *name; ++name) {
	if (*name >= 'a' && *name <= 'z')
	    continue;
	if (*name >= 'A' && *name <= 'Z')
	    continue;
	if (*name >= '0' && *name <= '9')
	    continue;
	if (*name == '\t')
	    *name = ' ';
	if (*name == '@' ||
	    *name == '-' ||
	    *name == '+' ||
	    *name == '_' ||
	    *name == '.' ||
	    *name == ',' ||
	    *name == '(' ||
	    *name == ')' ||
	    *name == '<' ||
	    *name == '>' ||
	    *name == ' '
	) {
	    continue;
	}
	*name = '?';
    }
}

/*
 * StrnCpyNull()
 *
 * Like strcpy, but gracefully handles null pointers and limits the length
 * of the string copied.
 */
char *
StrnCpyNull(char *dst, const char *src, size_t maxlen)
{
    if (dst) {
    	if (src) {
	    if (strlen(src) >= maxlen) {
		strncpy(dst, src, maxlen-1);
		dst[maxlen] = '\0';
	    }
	    else {
		strcpy(dst, src);
	    }
    	}
    	else {
	    *dst = '\0';
    	}
    }
    return dst;
}

#define ONE_K	1024.0	
#define ONE_M	(1024.0*1024.0)
#define ONE_G	(1024.0*1024.0*1024.0)
#define ONE_T	(1024.0*1024.0*1024.0*1024.0)

const char *
ftos(double d)
{
    static char FBuf[8][32];
    static int FCnt;
    char *p = FBuf[FCnt];

    if (d < 1024.0) {
	sprintf(p, "%d", (int)d);
    } else if (d < ONE_M) {
	sprintf(p, "%d.%03dK", (int)(d / ONE_K), ((int)d % (int)ONE_K) * 1000 / (int)ONE_K);
    } else if (d < ONE_G) {
	sprintf(p, "%d.%03dM", (int)(d / ONE_M), ((int)(d / ONE_K) % (int)ONE_K) * 1000 / (int)ONE_K);
    } else if (d < ONE_T) {
	sprintf(p, "%d.%03dG", (int)(d / ONE_G), ((int)(d / ONE_M) % (int)ONE_K) * 1000 / (int)ONE_K);
    } else {
	sprintf(p, "%d.%03dT", (int)(d / ONE_T), ((int)(d / ONE_G) % (int)(d / ONE_M) % (int)ONE_K) * 1000 / (int)ONE_K);
    }

    FCnt = (FCnt + 1) & 7;
    return(p);
}

/*
 * Convert a string value to a number of bytes. The string can be
 * specified in kb, mb or gb
 */
long
bsizetol(char *p)
{
    long n;

    n = strtol(p, &p, 0);
    switch(*p) {
    case 'g':
    case 'G':
	n *= 1024;
	/* fall through */
    case 'm':
    case 'M':
	n *= 1024;
	/* fall through */
    case 'k':
    case 'K':
	n *= 1024;
	break;
    }
    return(n);
}

/*
 * Convert a string value to a number of kilobytes
 */
double
bsizektod(char *p)
{
    double n;

    n = strtol(p, &p, 0) * 1024.0;
    switch(*p) {
    case 'g':
    case 'G':
	n *= 1024.0;
	/* fall through */
    case 'm':
    case 'M':
	n *= 1024.0;
	break;
	/* fall through */
    case 't':
    case 'T':
	n *= 1024.0;
	break;
    }
    return(n);
}

/*
 * Convert a time specification in days, hours, mins and/or secs to a long
 * representing the number of secs. The specification can include
 * multiple time specifications.
 */
long
btimetol(char *p)
{
    long n;
    long res = 0;

    while (*p) {
	while (*p && !(int)isdigit(*p))
	    p++;
	n = strtol(p, &p, 0);
	while (*p && (int)isdigit(*p))
	    p++;
	switch(*p) {
	    case 'd':
	    case 'D':
		n *= 24;
		/* fall through */
	    case 'h':
	    case 'H':
		n *= 60;
		/* fall through */
	    case 'm':
	    case 'M':
		n *= 60;
		break;
	    case 's':
	    case 'S':
	    case ' ':
		break;
	    default:
		p++;
	}
	p++;
	res += n;
    }
    return(res);
}

char *
dtlenstr(time_t t)
{
    int d = 0;
    int h = 0;
    int m = 0;
    static char tb[64];

    if (t > 24 * 60 * 60) {
	d = t / (24 * 60 * 60);
	t = t % (24 * 60 * 60);
    }
    if (t > 60 * 60) {
	h = t / (60 * 60);
	t = t % (60 * 60);
    }
    if (t > 60) {
	m = t / 60;
	t = t % 60;
    }
    sprintf(tb, "%d days %d hrs %d min %d sec", d, h, m, (int)t);
    return(tb);
}

/*
 * Create the directories required for the file pathname specified in path
 *   path = the full path/filename which gets clobbered.
 */
int
MakeGroupDirectory(char *path)
{
    char dir[PATH_MAX];
    char *p;
    struct stat st;

    strcpy(dir, path);
    if (strchr(dir, '/') == NULL)
	return(0);
    if ((p = strrchr(dir, '/')) != NULL)
	*p = '\0';
    if (stat(dir, &st) == 0)
	return(0);
    if (MakeGroupDirectory(dir) == -1)
	return(-1);
    return(mkdir(dir, 0755));
}

/*
 * If the string exists, free it
 * strdup the value
 * if value is "0", then set string to NULL
 */
char *
strdupfree(char **st, char *val, char *empty)
{
    if (*st != NULL)
	free(*st);
    if (val == NULL || strcmp(val, "0") == 0)
	*st = (empty == NULL) ? NULL : strdup(empty);
    else
	*st = strdup(val);
    return(*st);
}

/*
 * Return a string that isn't NULL
 */
char *
safestr(char *st, char *noval)
{
    if (st == NULL)
	return((noval != NULL) ? noval : "NONE");
    return(st);
}

int
enabled(char *st)
{
    if (st == NULL || !*st || *st == 'y' || *st == 'Y' ||
				strcasecmp(st, "on") == 0 || *st == '1')
	return(1);
    else
	return(0);
}

int
HashFeedMatch(HashFeed *hashfeed, int hash)
{
    if (DebugOpt > 1)
	printf("hash=%d  mod=%d  begin=%d  end=%d  res=%d  match=%d\n", hash,
				hashfeed->hf_Mod,
				hashfeed->hf_Begin,
				hashfeed->hf_End,
				hash % hashfeed->hf_Mod + 1,
		((hash % hashfeed->hf_Mod + 1) >= hashfeed->hf_Begin &&
                        (hash % hashfeed->hf_Mod + 1) <= hashfeed->hf_End)

    );
    return ((hash % hashfeed->hf_Mod + 1) >= hashfeed->hf_Begin &&
			(hash % hashfeed->hf_Mod + 1) <= hashfeed->hf_End);

}

int
MoveFile(char *from, char *to)
{
    int from_fd;
    int to_fd;
    static char buffer[32768];
    int rlen;

    if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
	printf("ERROR: Unable to open input file: %s\n", from);
	return(-1);
    }
    if ((to_fd = open(to, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0) {
	printf("ERROR: Unable to create output file: %s\n", to);
	return(-1);
    }
    while ((rlen = read(from_fd, buffer, 32768)) > 0)
	if (write(to_fd, buffer, rlen) != rlen) {
	    printf("ERROR: Unable to writing to file: %s\n", to);
	    close(from_fd);
	    close(to_fd);
	    unlink(to);
	    return(-1);
	}
    if (rlen < 0) {
	printf("ERROR: Unable to read from file: %s\n", from);
	close(from_fd);
	close(to_fd);
	unlink(to);
	return(-1);
    }
    close(from_fd);
    if (close(to_fd) != 0) {
	printf("ERROR: Unable to close file: %s\n", to);
	unlink(to);
	return(-1);
    }
    unlink(from);
    return(0);
}
