
/* OpenWebSpider
 *
 *  Authors:     Stefano Alimonti AND Stefano Fantin
 *  Version:     0.7
 *  E-Mails:     shen139 [at] openwebspider (dot) org AND stefanofantinguz@yahoo.it
 *
 *
 * This file is part of OpenWebSpider
 *
 * 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
 *
 */

#ifndef __SQLFNCT
#define __SQLFNCT

static int my_mysql_is_transient_disconnect(MYSQL *mysql)
{
	unsigned int errNo;
	const char *errMsg;

	if(!mysql)
		return 0;

	errNo = mysql_errno(mysql);
	if(errNo == 2006 || errNo == 2013 || errNo == 2055)
		return 1;

	errMsg = mysql_error(mysql);
	if(!errMsg)
		return 0;

	if(strstr(errMsg, "Server has gone away") ||
	   strstr(errMsg, "Lost connection to MySQL server"))
		return 1;

	return 0;
}

int sqlConnect(char* hostname, char* username, char* password, char* table,MYSQL* rMysql, unsigned int port)
{

	mysql_init(rMysql);

	mysql_options(rMysql,MYSQL_READ_DEFAULT_GROUP,"OpenWebSpider");
	mysql_options(rMysql,MYSQL_OPT_COMPRESS,0);

	if (!mysql_real_connect(rMysql,hostname,username,password,table,port,NULL,0))
		return 0;

	/* Force client/connection charset to utf8mb4 to avoid "Incorrect string value"
	   when indexing multilingual pages and emoji-like characters. */
	mysql_set_character_set(rMysql, "utf8mb4");
	mysql_query(rMysql, "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");

return 1;
}

void my_mysql_ping(MYSQL* mysql,int iMutex)
{
	if(iMutex==NO_BLOCK)
	{
		mysql_ping(mysql);
		return;
	}
	
	if((unsigned)iLastPing[iMutex]>GetTickCount())
		iLastPing[iMutex]=0;

	if(iLastPing[iMutex]==0 || GetTickCount()-iLastPing[iMutex]>MYSQL_MIN_PING_DELAY)
	{
		thrdBlock(iMutex);
		mysql_ping(mysql);
		thrdUnBlock(iMutex);
		iLastPing[iMutex]=GetTickCount();
	}

return;
}

int my_mysql_query(MYSQL*mysql, char* sqlQuery,int iMutex)
{
int ret;
int lockHndl = iMutex;
size_t qlen;

	if(!mysql || !sqlQuery)
		return 1;

	qlen = strnlen(sqlQuery, MAXQUERYSIZE);
	if(qlen == 0 || qlen >= MAXQUERYSIZE)
		return 1;

	if(iMutex == NO_BLOCK)
	{
		if(mysql == &gMysqlDB1)
			lockHndl = BLOCKDB1;
		else if(mysql == &gMysqlDB2)
			lockHndl = BLOCKINDEX;
	}

	thrdBlock(lockHndl);
	ret=mysql_real_query(mysql, sqlQuery, qlen);
	thrdUnBlock(lockHndl);

return ret;
}

int my_mysql_query_and_store_results(MYSQL*mysql, char* sqlQuery,MYSQL_RES** tRes,MYSQL_RES* srRes,int iMutex)
{
int ret;
int attempt;
int lockHndl = iMutex;

	if(iMutex == NO_BLOCK)
	{
		if(mysql == &gMysqlDB1)
			lockHndl = BLOCKDB1;
		else if(mysql == &gMysqlDB2)
			lockHndl = BLOCKINDEX;
	}

	ret = 1;
	*tRes = NULL;

	for(attempt = 0; attempt < 2; attempt++)
	{
		thrdBlock(lockHndl);

		ret = mysql_query(mysql, sqlQuery);

		if(!ret)
		{
			*tRes = mysql_store_result(mysql);
			if (*tRes)
			{
				memcpy(srRes,*tRes,sizeof(MYSQL_RES));
			}
		}
		else
			*tRes = NULL;

		thrdUnBlock(lockHndl);

		if(!ret)
			break;

			if(attempt == 0 && my_mysql_is_transient_disconnect(mysql))
			{
				my_mysql_ping(mysql, lockHndl);
				my_mysql_query(mysql, "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci", lockHndl);
				continue;
			}

		break;
	}

return ret;
}


/* this is licensed under the GPL. Ben Johnson is the author who borrowed heavily from man pages. */

/* because some limit is required, string
* must be less than 9999 characters long. */
#define MAX_STRMODIFIER_LEN 4
static int
snprintf_mysql_escaped_sql_statement(MYSQL * mysql, char * buf, size_t size, const char * fmt, ...)
{
	int added_length = 0;
	int offset_from_buf = 0;
	int oversized = 0;
	va_list ap;
	int d;
	double f;
	char c, *s;
	char current_char;
	int expecting = 0;
	char strmodifier[MAX_STRMODIFIER_LEN+1];
	int strmodifier_offset = 0;
	int keep_expecting = 0;
	int isoverflowed = 0;
	
	strmodifier[0] = '\0';
	
	va_start(ap, fmt);
	while ((current_char = *fmt++))
	{
		added_length = 0; /* this might not change */
		
		if(! expecting)
		{
			switch(current_char)
			{
			case '%': /* start expecting */
				expecting = 1;
				break;
			default:
				if( offset_from_buf < size-2 )
				{
					buf[offset_from_buf] = current_char;
					buf[offset_from_buf+1] = '\0'; /* null terminate every change */
				}
				else
					isoverflowed = 1;
				added_length = 1;
				break;
			}
		}
		else
		{
			keep_expecting = 0;
			
			switch(current_char)
			{
			case '%': /* literal '%' character */
				if( offset_from_buf < size-2 )
				{
					buf[offset_from_buf] = current_char;
					buf[offset_from_buf+1] = '\0'; /* null terminate every change */
				}
				else
					isoverflowed = 1;
				added_length = 1;
				break;
				/* string length modifiers */
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if( strmodifier_offset < MAX_STRMODIFIER_LEN )
				{
					strmodifier[strmodifier_offset] = current_char;
					strmodifier[strmodifier_offset+1] = '\0'; /* null term every change */
					strmodifier_offset++;
				}
				keep_expecting = 1;
				break;
			case 's': /* string */
				{
					int len = 0;
					char * p = NULL; /* for quick malloc */
					s = va_arg(ap, char *);
					
					/* need string length, one way or another */
					if( *strmodifier )
						len = atoi(strmodifier);
					else
						len = strlen(s);
					
						/* if remaining space is sufficient, use buf
					* as mysql_real_escape_string()'s "to" arg */
					if( size - offset_from_buf > len*2+1 )
						added_length = mysql_real_escape_string(mysql, &buf[offset_from_buf], s, len);
					else
					{
						p = malloc(len*2+1);
						if( p )
						{
							added_length = mysql_real_escape_string(mysql, p, s, len);
							if( offset_from_buf+added_length < size )
								memcpy(&buf[offset_from_buf], p, added_length+1); /* +1 is null */
							else
								isoverflowed = 1;
							free(p);
						}
						else
						{
							oversized = -1;
							goto error_out;
						}
					}
				}
				break;
			case 'd': /* int */
				d = va_arg(ap, int);
				added_length = snprintf(&buf[offset_from_buf], (size-offset_from_buf)+1, "%d", d);
				if( added_length > (size-offset_from_buf)+1 )
					isoverflowed = 1;
				break;
			case 'c': /* char */
					  /* need a cast here since va_arg only
				takes fully promoted types */
				c = (char) va_arg(ap, int);
				added_length = snprintf(&buf[offset_from_buf], (size-offset_from_buf)+1, "%c", c);
				if( added_length > (size-offset_from_buf)+1 )
					isoverflowed = 1;
				break;
			case 'f': /* float/double */
				f = va_arg(ap, double); /* promote floats */
				added_length = snprintf(&buf[offset_from_buf], (size-offset_from_buf)+1, "%f", f);
				if( added_length > (size-offset_from_buf)+1 )
					isoverflowed = 1;
				break;
			default:
			/* invalid format. maybe do an error here.
			printf("%s[%d]: default? not adding: %c\n", __FUNCTION__, offset_from_buf, current_char);
				*/
				break;
			}
			
			if( ! keep_expecting )
			{
				strmodifier[0] = '\0';
				strmodifier_offset = 0;
				expecting = 0;
			}
}

oversized += added_length;
if( added_length <= size - offset_from_buf )
offset_from_buf += added_length;
}

#if 0
if( isoverflowed )
memset(buf, 0, size);
#endif

error_out:
va_end(ap);

return oversized;
}


#endif

/*EOF*/
