/*

oer+MySQL - IRC bot

See ftp://nic.funet.fi/pub/unix/irc/docs/FAQ.gz section 11 for the
definition of the word bot.

Copyright (C) 2000-2002 EQU <equ@equnet.org>

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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "oer+MySQL-common.h"
#include "oer+MySQL.h"

/* prototype definitions */
char *mysqldbname(char *, char *, int);
char *mysqlmatch(char *);
char *strtolower(char *);
char *strtoupper(char *);
int emptyline(char *);
int iscomment(char *);
int isnumber(char *);
int isserver(char *);
int isvaliduserhost(char *);
int isansi(char *);
int isaction(char *);
int isctcp(char *);
int wordcount(char *);
int parse(char *, int, char *, char *, int, int);
int oer_doquery(char *, int level, const char *, ...);
int issamenickandhost(char *, char *, char *, char *);
int issimilarstring(char *, char *);
unsigned int longestword(char *);
unsigned int isgoodquote(char *);
void striplf(char *);
void stripchars(char *, char *);
void stripcntrl(char *);
void stripansi(char *);
void stripmirc(char *);
void oer_debug(int, const char *, ...);
void secondstostring(time_t, char *, int);
void sortstring(char *);

#ifndef HAVE_SNPRINTF
int snprintf(char *, size_t, const char *, ...);
#endif

char mysql_safe_str_debug[(WRITE_BUFFER_LENGTH * 2) + 1];

#ifndef HAVE_SNPRINTF
int snprintf(char *str, size_t size, const char *format, ...)
{
	int slen;
        va_list ap;
	char *ptr;

	/* what would be a "safe" size, beats me */
	if((ptr = malloc(TXT_BUFFER * 10)) == NULL) {
		return -1;
	}
	va_start(ap, format);
	vsprintf(ptr, format, ap);
	va_end(ap);
	strncpy(str, ptr, size);
	if(strlen(ptr) > size) {
		free(ptr);
		return -1;
	}
	slen = strlen(ptr);
	free(ptr);
	return slen;
}
#endif

#ifndef HAVE_VSNPRINTF
int vsnprintf(char *, size_t, const char *, va_list);
#endif

#ifndef HAVE_VSNPRINTF
int vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
	char *ptr;

	/* what would be a "safe" size, beats me */
	if((ptr = malloc(TXT_BUFFER * 10)) == NULL) {
		return -1;
	}
	vsprintf(ptr, format, ap);
	strncpy(str, ptr, size);
	if(strlen(ptr) > size) {
		free(ptr);
		return -1;
	}
	free(ptr);
	return strlen(ptr);
}
#endif

void oer_debug(int level, const char *fmt, ...)
{
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];
        char query[WRITE_BUFFER_LENGTH + 1];
        va_list ap;

	if(do_tempoutput) {
		va_start(ap, fmt);
		vfprintf(stdout, fmt, ap);
		va_end(ap);
		return;
	}
	if(!do_console && (index(mystate->flags, (int)'o') == NULL)) {
		return;
	}
        if(level <= do_debug) {
                va_start(ap, fmt);
		vsnprintf(stringbuffer, WRITE_BUFFER_LENGTH, fmt, ap);
		striplf(stringbuffer);
		if((index(mystate->flags, (int)'o') != NULL) && (strstr(mystate->state, "+ro") == NULL)) {
			/* do not use oer_doquery here and do not
			   check for errors */
			mysql_real_escape_string(&mystate->mysqldb.mysqldbconn, mysql_safe_str_debug, stringbuffer, strlen(stringbuffer));
			snprintf(query, WRITE_BUFFER_LENGTH, "INSERT INTO oer_output VALUES (%lu, '%s', '%s')", mystate->now, mystate->ident, mysql_safe_str_debug);
			mysql_query(&mystate->mysqldb.mysqldbconn, query);
		}
		if(do_console) {
			fprintf(stdout, "%s\n", stringbuffer);
		}
                va_end(ap);
        }
}

int oer_doquery(char *function, int level, const char *fmt, ...)
{
        va_list ap;
	struct timeval tv1;
	struct timeval tv2;
        char stringbuffer[TXT_BUFFER + 1];

	va_start(ap, fmt);
	vsnprintf(stringbuffer, TXT_BUFFER, fmt, ap);
	va_end(ap);
#ifdef DEBUG
	oer_debug(level, "oer_doquery->%s\n", stringbuffer);
#endif
	gettimeofday(&tv1, NULL);
	if(mysql_query(&mystate->mysqldb.mysqldbconn, stringbuffer)) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_FATAL, "%s->mysql_query() failed: %s\n", function, mysql_error(&mystate->mysqldb.mysqldbconn));
#endif
		return 0;
	}
	gettimeofday(&tv2, NULL);
	if(tv2.tv_sec == tv1.tv_sec) {
		snprintf(stringbuffer, TXT_BUFFER, "%ld usecs", tv2.tv_usec - tv1.tv_usec);
	} else {
		snprintf(stringbuffer, TXT_BUFFER, "%ld secs and %ld usecs", tv2.tv_sec - tv1.tv_sec, ABS(tv2.tv_usec - tv1.tv_usec));
	}
#ifdef DEBUG
	oer_debug(level, "oer_doquery->mysql_query took %s\n", stringbuffer);
#endif
	return 1;
}

int wordcount(char *line)
{
	int ppos;
	int nppos;
	int words;
	char word[WRITE_BUFFER_LENGTH + 1];

	ppos = 0;
	words = 0;
	while(1) {
		nppos = parse(line, ppos, " ", word, WRITE_BUFFER_LENGTH, 0);
		if(nppos == ppos || nppos < 0) {
			break;
		}
		ppos = nppos;
		words++;
	}
	return words;
}

int isansi(char *line)
{
        int i;
        int length;
	char stringbuffer[WRITE_BUFFER_LENGTH + 1];

	strncpy(stringbuffer, line, WRITE_BUFFER_LENGTH);
	stripansi(stringbuffer);
	for(i = 0, length = strlen(stringbuffer); i < length; i++) {
                if(iscntrl((int)stringbuffer[i])) {
			return 1;
		}
        }
	return 0;
}

int isctcp(char *line)
{
	int i;
	int length;

	/* The first char has to be ^A */
	if(line[0] != 1) {
		return 0;
	}
	for(i = 1, length = strlen(line); i < length; i++) {
		if(line[i] == 1) {
			return 1;
		}
	}
	return 0;
}

int isaction(char *line)
{
	char chars[STRINGLEN + 1];

	snprintf(chars, STRINGLEN, "%cACTION", 1);
	if(!strncasecmp(line, chars, strlen(chars))) {
		return 1;
	} else {
		return 0;
	}
}

void stripcntrl(char *source)
{
	int i;
	int k;
	int length;
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];
	
	for(i = 0, k = 0, length = strlen(source); i < length && k < WRITE_BUFFER_LENGTH; i++) {
		if(!iscntrl((int)source[i])) {
			stringbuffer[k++] = source[i];
		}
	}
	stringbuffer[k] = 0;
	/* the outstring is at max. equal length to the
	   original source string */
	strcpy(source, stringbuffer);
}

void stripmirc(char *source)
{
	int i;
	int k;
	int iscolor;
	int incolor;
	int length;
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];
	
	for(i = 0, k = 0, iscolor = 0, incolor = 0, length = strlen(source); i < length && k < WRITE_BUFFER_LENGTH; i++) {
		if(source[i] == 3) {
			iscolor = (iscolor) ? 0 : 1;
			incolor = (incolor) ? 0 : 1;
			continue;
		}
		if(iscolor && incolor && (isdigit((int)source[i]) || source[i] == ',')) {
			continue;
		}
		incolor = 0;
		stringbuffer[k++] = source[i];
	}
	stringbuffer[k] = 0;
	/* the outstring is at max. equal length to the
	   original source string */
	strcpy(source, stringbuffer);
}

void stripchars(char *source, char *what)
{
	int i;
	int j;
	int k;
	int length;
	int length2;
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];

	for(i = 0, length = strlen(what); i < length; i++) {
		for(k = 0, j = 0, length2 = strlen(source); k < length2; k++) {
			if(source[k] != what[i]) {
				stringbuffer[j++] = source[k];
			}
		}
		/* the outstring is at max. equal length to the
		   original source string */
		stringbuffer[j] = 0;
		strcpy(source, stringbuffer);
	}
}

void stripansi(char *source)
{
	char chars[STRINGLEN + 1];

	/* strip ^A which is for ctcp actions */
	chars[0] = 1;
	/* strip ^B which is normal bold */
	chars[1] = 2;
	/* strip ^G which is bell */
	chars[2] = 7;
	/* strip ^V which is normal reverse */
	chars[3] = 22;
	/* strip ^_ which is normal underline */
	chars[4] = 31;
	/* strip ^O which is normal ansi toggle */
	chars[5] = 15;
	/* eol */
	chars[6] = 0;
	stripchars(source, chars);
}

int emptyline(char *line)
{
        int length;
        int i;

        for(i = 0, length = strlen(line); i < length; i++) {
                if(!isspace((int)line[i])) {
                        return 0;
		}
	}
        return 1;
}

int iscomment(char *line)
{
        int length;
        int i;

        for(i = 0, length = strlen(line); i < length && isspace((int)line[i]); i++) {
		;
	}
	if(line[i] == '#') {
		return 1;
	}
        return 0;
}

void striplf(char *line)
{
        int i;

        for(i = strlen(line) - 1; isspace((int)line[i]) && i >= 0; i--) {
                line[i] = '\0';
	}
}

int isnumber(char *line)
{
	int length;
	int i;

	for(i = 0, length = strlen(line); line[i] != '\0' && i < length; i++) {
		if(!isdigit((int)line[i])) {
			return 0;
		}
	}
	return 1;
}

int isserver(char *line)
{
        int i;
        int length;
        int dots;

        for(i = 0, length = strlen(line), dots = 0; i < length; i++) {
                if(line[i] == '.') {
                        dots++;
                }
        }
        if(wordcount(line) >= 2 && dots >= 2) {
                return 1;
        } else {
                return 0;
        }
}

int isvaliduserhost(char *line)
{
        int c;
        int c2;
        char *ptr;
        char *ptr2;

        if((ptr = index(line, '@')) == NULL) {
                return 0;
        }
        ptr2 = ptr + strlen(line);
        c = *(ptr - 1);
        c2 = *(ptr + 1);
        if(ptr == line || ptr == ptr2 || !isgraph(c) || !isgraph(c2)) {
                return 0;
        }
        return 1;
}

int parse(char *from, int pos, char *delim, char *to, int to_length, int fixed)
{
        int i;
        int j;
        int k;
        int len;
	int found_delim;

        memset(to, 0, to_length + 1);
	i = pos;
	k = 0;
	j = strlen(delim);
	len = strlen(from);
	found_delim = 0;
	while(i < len && k < to_length) {
		/* loop until delim found, respecting 
		   length constraints */
		if(fixed && !strncasecmp(from + i, delim, j)) {
			found_delim = 1;
			break;
		}
		if(!fixed && from[i] == delim[0]) {
			found_delim = 1;
			break;
		}
                to[k++] = from[i++];
	}
	if(found_delim) {
		/* advance until next non-delim character */
		if(fixed) {
			i += j;
		} else {
			while((from[i] == delim[0]) && (i < len)) {
				i++;
			}
		}
	}
        if(k >= to_length) {
		/* insufficient buffer space */
#ifdef DEBUG
                oer_debug(OER_DEBUG_WARNING, "parse->source length at least %d, destination length %d\n", k, to_length);
                oer_debug(OER_DEBUG_WARNING, "parse->destination buffer too small!\n");
#endif
                return -1;
        }
        to[k] = '\0';
	return i;
}

void secondstostring(time_t seconds, char *string, int maxlen)
{
	int fields;
        time_t safe;
        time_t days;
        time_t hours;
        time_t minutes;
        char stringbuffer[STRINGLEN + 1];

        safe = seconds;
        days = seconds / 3600 / 24;
        seconds -= days * (3600 * 24);
        hours = seconds / 3600;
        seconds -= hours * (3600);
        minutes = seconds / 60;
        seconds -= minutes * 60;
	memset(string, 0, maxlen);
	fields = 0;
        if(days > 0) {
                snprintf(string, maxlen, "%lu %s ", days, (days == 1) ? "day" : "days");
		fields++;
		fields++;
        }
        if(hours > 0) {
                snprintf(stringbuffer, STRINGLEN, "%lu %s ", hours, (hours == 1) ? "hour" : "hours");
                strncat(string, stringbuffer, maxlen - strlen(string));
		fields++;
        }
        if(minutes > 0) {
                snprintf(stringbuffer, STRINGLEN, "%lu %s ", minutes, (minutes == 1) ? "minute" : "minutes");
                strncat(string, stringbuffer, maxlen - strlen(string));
		fields++;
        }
        if(seconds > 0) {
		if(fields > 0) {
			snprintf(stringbuffer, STRINGLEN, "and %lu %s", seconds, (seconds == 1) ? "second" : "seconds");
		} else {
			snprintf(stringbuffer, STRINGLEN, "%lu %s", seconds, (seconds == 1) ? "second" : "seconds");
		}
                strncat(string, stringbuffer, maxlen - strlen(string));
		fields++;
        }
}

char *mysqldbname(char *from, char *to, int to_len)
{
	int i;
	int k;
        int len;
	char banned[STRINGLEN + 1];

	strncpy(banned, MYSQL_BANNED_CHARS, STRINGLEN);
	for(i = 0, k = 0, len = strlen(from); i < len && k < to_len; i++) {
		if(index(banned, (int)from[i]) != NULL) {
			/* char is in MYSQL_BANNED_CHARS, do not copy */
			continue;
		}
		/* normal char, copy */
		to[k++] = from[i];
        }
	if(k >= to_len) {
		return NULL;
	}
        to[k] = '\0';
	return strtolower(to);
}

char *mysqlmatch(char *str)
{
	int i;
        int len;

        for(len = strlen(str), i = 0; i < len; i++) {
		if(str[i] == '*') {
			str[i] = '%';
			continue;
		}
		if(str[i] == '?') {
			str[i] = '_';
			continue;
		}
        }
	return str;
}

char *strtolower(char *str)
{
	int i;
        int len;

        for(len = strlen(str), i = 0; i < len; i++) {
		str[i] = tolower((int)str[i]);
        }
	return str;
}

char *strtoupper(char *str)
{
	int i;
        int len;

        for(len = strlen(str), i = 0; i < len; i++) {
		str[i] = toupper((int)str[i]);
        }
	return str;
}

int issamenickandhost(char *n1, char *n2, char *h1, char *h2)
{
	if(n1 == NULL || n2 == NULL || h1 == NULL || h2 == NULL) {
		return 0;
	}
	if(!strcasecmp(n1, n2) && !strcasecmp(h1, h2)) {
		return 1;
	}
	return 0;
}

void sortstring(char *string)
{
        int times;
        int pos;
        int u1;
        int u2;
        int c1;
        int c2;
        int c;

	for(times = strlen(string) - 1; times >= 0; times--) {
                for(pos = strlen(string) - 1; pos > 0; pos--) {
                        u1 = 0;
                        u2 = 0;
                        c1 = string[pos - 1];
                        c2 = string[pos];
                        if(isupper(c1)) {
                                u1 = 1;
                                c1 = tolower(c1);
                        }
                        if(isupper(c2)) {
                                u2 = 1;
                                c2 = tolower(c2);
                        }
                        if(c1 < c2) {
                                continue;
                        }
                        if(c1 == c2 && u1) {
                                continue;
			}
			if(u1) {
                                c1 = toupper(c1);
                        }
                        if(u2) {
                                c2 = toupper(c2);
                        }
                        c = c2;
                        string[pos] = c1;
                        string[pos - 1] = c;
                }
        }
}

unsigned int longestword(char *quote)
{
        int len;
        int pos;
        int max;
        int wlen;

        len = strlen(quote);
        pos = 0;
        max = 0;
        while(pos < len) {
                while(pos < len && isspace((int)quote[pos])) {
                        pos++;
                }
                wlen = 0;
                while(pos < len && !isspace((int)quote[pos])) {
                        wlen++;
                        pos++;
                }
                if(wlen > max) {
                        max = wlen;
                }
        }
        return max;
}

unsigned int isgoodquote(char *quote)
{
        /* we want a string longer than OER_QUOTE_MIN_LENGTH */
        if(strlen(quote) < OER_QUOTE_MIN_LENGTH) {
                return 0;
        }
        /* we want a string shorter than OER_QUOTE_MAX_LENGTH */
        if(strlen(quote) > OER_QUOTE_MAX_LENGTH) {
                return 0;
        }
        /* we want the string to contain at least 2 words */
        if(wordcount(quote) < 3) {
                return 0;
        }
        /* no word of the string should be longer than OER_QUOTE_MAX_WORD_LENGTH */
        if(longestword(quote) > OER_QUOTE_MAX_WORD_LENGTH) {
                return 0;
        }
	/* no strings with (c) in them */
	if(strstr(quote, "(c)")) {
		return 0;
	}
	/* no strings with http:// in them */
	if(strstr(quote, "http://")) {
		return 0;
	}
        return 1;
}

int issimilarstring(char *s1, char *s2)
{
        int s1len;
        int s2len;
        int fract;
        /* first do a compare for exact match */
        if(!strcasecmp(s1, s2) || !strcasecmp(s2, s1)) {
                return 1;
        }
        s1len = strlen(s1);
        s2len = strlen(s2);
	if(s1len < 8 || s2len < 8) {
		return 0;
	}
        /* then do a compare of s1 and s2 beginning
           at pos 0 + len/8 and ending at pos len - len/8 */
        if(s1len > s2len) {
		fract = s2len >> 3;
		if(!strncasecmp(s1 + fract, s2 + fract, s1len - (fract << 1))) {
			return 1;
		}
        } else {
		fract = s1len >> 3;
		if(!strncasecmp(s2 + fract, s1 + fract, s2len - (fract << 1))) {
			return 1;
		}
        }
        return 0;
}
