/*
** Library of user functions for use with sqlite.
**
** 2002-11-24	James P. Lyon
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
 */

#include <assert.h>
#include <string.h>

#include "sqlite.h"
#include "sqliteInt.h"

#include "sqaux-base.h"

/************************************************************************/
/* extern declarations for sqlite functions. */

extern char *sqliteStrDup(const char *z);

extern int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out);
extern int sqlite_decode_binary(const unsigned char *in, unsigned char *out);

/************************************************************************/

/*
** Compute a hash value of string argument.
** THIS IS AN SQLITE USER FUNCTION.
**
** argv[0] = string
*/
void FnHashVal(sqlite_func *pfn, int argc, const char **argv)
{
	/* Validate arguments. */
	assert(argc == 1 && argv);

	/* Handle NULL correctly. */
	if (argv[0] == NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
	}
	else
	{
		int nHashVal = 0;

		/* Compute the hash value. */
		if (argc == 1 && argv && argv[0])
		{
			const char* path = argv[0];
			const char* s = path;

			/* Compute the hash value of the string. */
			while (*s)
				nHashVal = nHashVal*31 + *s++;
		}

		/* 'Return' the integer. */
		sqlite_set_result_int(pfn, nHashVal);
	}
}

/*
** Convert a hexadecimal string to a decimal integer.
** This requires some work because standard C library functions
**   expect a leading 0x.
** The hex string can have format ([(+|-)][(0x|0X)]|[(0x|0X)])[0-9a-fA-F]+
** 2002-11-24
*/
void FnHex2Int(sqlite_func *pfn, int argc, const char **argv)
{
	/* Validate arguments */
	assert(argc == 1 && argv);

	/* Handle NULL correctly. */
	if (argv[0] == NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
	}
	else
	{
		char hexstr[32];
		char sign = +1;
		const char *h = argv[0];
		int hexval;

		/* Skip over a leading sign character. */
		if (*h == '-')
			sign = -1;
		if (*h == '+' || *h == '-')
			h++;

		/* Copy the well formatted string into the buffer.
		** strtol() requires format [(+|-)]0x[0-9a-fA-F]+
		*/
		sprintf(hexstr, "0x%s", h);

		hexval = (int)strtol(hexstr, NULL, 16);

		sqlite_set_result_int(pfn, hexval);
	}
}

/*
** Convert a decimal integer to a hexadecimal string.
** The decimal string can have format ([(+|-)][0-9]+
** The output hex string won't have any prefix.
** 2002-11-24
*/
void FnInt2Hex(sqlite_func *pfn, int argc, const char **argv)
{
	/* Validate arguments */
	assert(argc == 1 && argv);

	/* Handle NULL correctly. */
	if (argv[0] == NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
	}
	else
	{
		char hexstr[32];
		int val = atoi(argv[0]);

		sprintf(hexstr, "%x", val);

		sqlite_set_result_string(pfn, hexstr, -1);
	}
}

/*
** Reverse the contents of a string.
** This can be useful when using an index on the tail of a string.
** This won't correctly on UTF8 strings.
** 2002-11-24
*/
void FnStrrev(sqlite_func *pfn, int argc, const char **argv)
{
	/* Validate arguments */
	assert(argc == 1);

	/* Handle NULL correctly. */
	if (argv[0] != NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
	}
	else
	{
		/* Create a duplicate of the original string. */
		char *str = sqliteStrDup(argv[0]);

		if (str)
		{
			/* Reverse the contents of the string. */
			strrev(str);

			sqlite_set_result_string(pfn, str, -1);

			/* Free the duplicate string. */
			sqliteFree(str);
		}
	}
}

/************************************************************************/
/* Functions used with the zlib compression library. */

#define ZLIB_DLL /* Must define for Windows support of ZLIB DLL before including headers. */
#ifndef _WINDOWS
#define _WINDOWS
#endif
#include "zlib.h"

/*
** Compute the adler32 checksum of a string.
** This function is exported from the zlib library.
** Return the checksum as a hex string.
** THIS IS AN SQLITE USER FUNCTION.
**
** argv[0] Data		** encoded data to compute checksum of
*/
void FnAdler32(sqlite_func *pfn, int argc, const char **argv)
{
	unsigned long checksum;
	char buf[8+1]; /* Buffer to hold 8 hex digits. */

	/* Validate arguments. */
	assert(argc == 1 && argv && argv[0]);

	checksum = adler32(0L, Z_NULL, 0);

	/* Compute the checksum */
	if (argc == 1 && argv && argv[0])
	{
		int len = strlen(argv[0]);
		checksum = adler32(checksum, (const unsigned char*)argv[0], len);
	}

	/* Convert checksum to (upper-case) hexadecimal string. */
	sprintf(buf, "%08X", checksum);

	/* 'Return' the string.
	** -1 means use entire string.
	*/
	sqlite_set_result_string(pfn, buf, -1);
}

/*
** Decode string data stored using ZipString().
** This undoes sqlite binary encoding and zip compression.
** <pData> is the NUL-terminated data string stored in the sqlite database.
** <nSize> is the uncompressed size of the data.
**   If -1 is passed for the size, it will be computed from strlen().
** <pzErrMsg> points to a string pointer, to allow returning an error message.
**   This argument can be NULL. It should not be freed by the caller.
** Returns a pointer to dynamically allocated string.
** Returns NULL on failure.
*/
char* UnzipString(const char* pData, long nSize, const char **pzErrMsg)
{
	long nEncSize, nZipSize;
	long nXmlSize;
	char *pZip, *pXml;
	int zret;

	assert(pData); if (!pData) return NULL;
	assert(nSize >= 0); if (nSize < 0) return NULL;

	/*
	** Set up a buffer to hold the unencoded zip data.
	** This data can contain NUL bytes.
	** This will always no larger in size than the encoded binary data.
	*/
	nEncSize = strlen(pData);
	pZip = (char*)malloc(nEncSize+1);
	if (!pZip)
	{
		if (pzErrMsg)
			*pzErrMsg = "UnzipString: malloc() failure.";
		return NULL;
	}

	/*
	** Decode the sqlite encoding of the zip data.
	** This returns the actual size of the unencoded [zip] data.
	*/
	nZipSize = sqlite_decode_binary((const unsigned char *)pData, (unsigned char *)pZip);
	if (nZipSize < 0) /* error */
	{
		if (pzErrMsg)
			*pzErrMsg = "UnzipString: sqlite_decode_binary() failure.";
		free(pZip);
		return NULL;
	}

	/*
	** Set up a buffer to hold the uncompressed data.
	** This data can contain NUL bytes.
	** We allocate extra size just to be safe.
	** NEED to find out how much extra we really need to pad.
	** This will be generally less than 10kB in size.
	*/
	pXml = (char*)malloc(nSize+1);
	if (!pXml)
	{
		if (pzErrMsg)
			*pzErrMsg = "UnzipString: malloc() failure.";
		free(pZip);
		return NULL;
	}

	/* Decompress the data into the XML string. */
	nXmlSize = nSize;
	zret = uncompress((unsigned char*)pXml, (unsigned long*)&nXmlSize, (const unsigned char*)pZip, nZipSize);
	if (zret != Z_OK)
	{
		if (pzErrMsg)
			*pzErrMsg = "UnzipString: uncompress() failure.";
		free(pZip);
		free(pXml);
		return NULL;
	}
	assert(nXmlSize == nSize);

	/* Terminate the string. */
	pXml[nXmlSize] = 0;

	/* Free the zipped data. */
	free(pZip);

	return pXml;
}

/*
** Compress and encode string data to be stored in an sqlite database.
** This does zip compression and sqlite binary encoding on the string.
** <pXml> is the NUL-terminated xml string to be stored in the sqlite Files.Data field.
** <nSize> is the uncompressed size of the xml string. Can be -1 to cause to compute it.
** <pzErrMsg> points to a string pointer, to allow returning an error message.
**   This argument can be NULL. It should not be freed.
** Return pointer to dynamically allocated encoded string.
** Returns NULL on failure.
*/
char* ZipString(const char* pXml, long nXmlSize, const char **pzErrMsg)
{
	unsigned long nZipSize, nZipMax, nDataSize, nDataMax, i;
	char *pZip, *pData;
	int zret;

	assert(pXml); if (!pXml) return NULL;

	/* Compute the size if necessary. */
	if (nXmlSize < 0)
		nXmlSize = strlen(pXml);
	assert((unsigned long)nXmlSize == strlen(pXml));

	/*
	** Set up a buffer to hold the unencoded zip data.
	** This data can contain NUL bytes.
	** This can be larger than the XML data.
	*/
	nZipMax = nXmlSize + nXmlSize/512 + 12;
	pZip = (char*)malloc(nZipMax);
	if (!pZip)
	{
		if (pzErrMsg)
			*pzErrMsg = "ZipString: malloc() failure.";
		return NULL;
	}

	/*
	** Compress the xml data into the zip buffer.
	** This returns the actual size in <nZipSize>.
	*/
	nZipSize = nZipMax;
	zret = compress2((unsigned char*)pZip, &nZipSize, (const unsigned char*)pXml, nXmlSize, Z_BEST_COMPRESSION);
	if (zret != Z_OK)
	{
		if (pzErrMsg)
			*pzErrMsg = "ZipString: compress2() failure.";
		free (pZip);
		return NULL;
	}
	assert(nZipSize <= nZipMax);

	/*
	** Allocate the buffer to hold the encoded binary data.
	** This data will not contain NUL bytes.
	** This buffer will generally be larger than the unencoded data.
	** In general it will be smaller than the size allocated for the zipped data.
	*/
	nDataMax = sqaux_encode_maxsize(nZipSize);
	pData = (char*)malloc(nDataMax+1);
	if (!pData)
	{
		if (pzErrMsg)
			*pzErrMsg = "ZipString: realloc() failure.";
		free(pZip);
		return NULL;
	}

	/*
	** Encode the binary data to convert to a form safe to store in sqlite.
	** This is done in place on the buffer holding the zipped data.
	** The actual size of the encoded data string will be returned.
	*/
	nDataSize = sqlite_encode_binary((const unsigned char*)pZip, nZipSize, (unsigned char*)pData);
	assert(nDataSize <= nDataMax);

	free(pZip);

	/* Terminate the Data string. */
	pData[nDataSize] = 0;

	/* Return the data string. */
	return pData;
}

/*
** Compress and binary encode a data string for storing in an sqlite database.
** Returns NULL if the argument is NULL.
** THIS IS AN SQLITE USER FUNCTION.
**
** argv[0] = data string
*/
void FnZipString(sqlite_func *pfn, int argc, const char **argv)
{
	const char *pXml, *zErrMsg;
	char *pEncXml;
	long nSize;

	/* TODO: validate arguments. */
	assert(argc == 1 && argv);

	pXml = argv[0];

	/* Handle NULL */
	if (pXml == NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
		return;
	}

	nSize = strlen(pXml);

	/*
	** If an error occurs, buffer[] will hold the error string.
	** This error message doesn't have to be freed.
	*/
	zErrMsg = NULL;
	pEncXml = ZipString(pXml, nSize, &zErrMsg);
	if (pEncXml)
		sqlite_set_result_string(pfn, pEncXml, -1);
	else
		sqlite_set_result_error(pfn, zErrMsg, -1);

	free(pEncXml);
}

/*
** Uncompress and binary decode a string compressed with [Fn]ZipString().
** Returns NULL if the argument is NULL.
** THIS IS AN SQLITE USER FUNCTION.
**
** argv[0] = compressed data string
*/
void FnUnzipString(sqlite_func *pfn, int argc, const char **argv)
{
	const char *pZip, *zErrMsg;
	char *pData;
	long nZipSize;

	/* TODO: validate arguments. */
	assert(argc == 1 && argv);

	pZip = argv[0];

	/* Handle NULL */
	if (pZip == NULL)
	{
		sqlite_set_result_string(pfn, NULL, 0);
		return;
	}

	nZipSize = strlen(pZip);

	/*
	** If an error occurs, buffer[] will hold the error string.
	** This error message doesn't have to be freed.
	*/
	zErrMsg = NULL;
	pData = UnzipString(pZip, nZipSize, &zErrMsg);
	if (pData)
		sqlite_set_result_string(pfn, pData, -1);
	else
		sqlite_set_result_error(pfn, zErrMsg, -1);

	free(pData);
}


/************************************************************************/

/*
** Functions to register.
** <fntype> can be either SQLITE_TEXT or SQLITE_NUMERIC,
**   and reflects the return value only.
** The registered function name is the name used in sqlite.
**   It can be different from the actual function name.
*/
struct user_fn_data
{
	const char *fnname;
	int fnargc;
	int fntype;
	void (*fn)(sqlite_func*, int, const char**);
}
user_fns[] = 
{
	"FnAdler32",		1, SQLITE_TEXT,			FnAdler32,
	"FnHashVal",		1, SQLITE_NUMERIC,		FnHashVal,
	"FnHex2Int",		1, SQLITE_NUMERIC,		FnHex2Int,
	"FnInt2Hex",		1, SQLITE_TEXT,			FnInt2Hex,
	"FnStrrev",			1, SQLITE_TEXT,			FnStrrev,
	"FnZipString",		1, SQLITE_TEXT,			FnZipString,
	"FnUnzipString",	1, SQLITE_TEXT,			FnUnzipString,
};
const int num_user_fns = sizeof(user_fns)/sizeof(user_fns[0]);

/*
** Register the user functions with sqlite.
** They must be registered for every open connection.
** This function is safe to call repeatedly.
** Return nonzero on failure.
*/
int register_user_fns(sqlite *db)
{
	int i;

	assert(db);
	if (!db) return SQLITE_ERROR;

	for (i = 0; i < num_user_fns; i++)
	{
		/*
		** Register each function. This will overwrite a function
		**   previously registered using the same name and arguments.
		** We pass in the pointer to the sqlite database as its user data.
		** This is retrieved with sqlite_user_data().
		*/
		int rc = sqlite_create_function(db, user_fns[i].fnname, user_fns[i].fnargc, user_fns[i].fn, (void*)db);
		if (rc != SQLITE_OK)
		{
			/*
			** TODO: add error message or logging.
			*/
			return SQLITE_ERROR;
		}

		/*
		** Function types must be registered separately.
		** This API function was added after sqlite_create_function().
		*/
		sqlite_function_type(db, user_fns[i].fnname, user_fns[i].fntype);
	}

	return SQLITE_OK;
}
