/*
 * dsyslog - a dumb syslog (e.g. syslog for people who have a clue)
 * Copyright (c) 2008 William Pitcock <nenolod@sacredspiral.co.uk>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "dsyslog.h"
#include <mysql.h>

#define SQL_BUFSIZE	(65535u)

static MYSQL_RES *
dsyslog_mysql_safe_query(MYSQL *handle, const gchar *string, ...)
{
	va_list args;
	gchar query[SQL_BUFSIZE];
	MYSQL_RES *res = NULL;

	_ENTER;

	va_start(args, string);
	vsnprintf(query, SQL_BUFSIZE, string, args);
	va_end(args);

	_DEBUG("query = %s", query);

	mysql_ping(handle);

	if (mysql_query(handle, query)) {
		_ERROR("Query error: %s", mysql_error(handle));
		_LEAVE NULL;
	}

	if ((res = mysql_store_result(handle)) == NULL) {
		if (mysql_field_count(handle) == 0) {
			_LEAVE NULL;
		}

		_ERROR("Query error: %s", mysql_error(handle));
		_LEAVE NULL;
	}

	_LEAVE res;
}

static gboolean
dsyslog_mysql_keepalive_cb(gpointer handle_)
{
	MYSQL *handle = (MYSQL *) handle_;

	_ENTER;

	mysql_ping(handle);

	_LEAVE TRUE;
}

static gchar *
dsyslog_mysql_escape_string(MYSQL *handle, const gchar *str)
{
	gchar *str2;

	_ENTER;

	str2 = g_malloc0((strlen(str) * 2) + 1);
	mysql_real_escape_string(handle, str2, str, strlen(str));

	_LEAVE str2;
}

struct dsyslog_mysql_persistant {
	MYSQL *mysql;
	gint tag;
};

static struct dsyslog_mysql_persistant *
dsyslog_mysql_persistant_new(void)
{
	struct dsyslog_mysql_persistant *mysql;

	_ENTER;

	mysql = g_slice_new0(struct dsyslog_mysql_persistant);
	mysql->mysql = mysql_init(NULL);
	mysql->tag = g_timeout_add_seconds(60, dsyslog_mysql_keepalive_cb, mysql->mysql);

	_LEAVE mysql;
}

static void
output_mysql_destructor(dsyslog_output_t *output)
{
	struct dsyslog_mysql_persistant *mysql = (struct dsyslog_mysql_persistant *) output->opaque;

	_ENTER;

	if (mysql == NULL)
		return;

	g_source_remove(mysql->tag);
	mysql_close(mysql->mysql);
	g_slice_free(struct dsyslog_mysql_persistant, mysql);
	output->opaque = NULL;

	_LEAVE;
}

static void
output_mysql_handler(dsyslog_event_t *event, dsyslog_output_t *output)
{
	MYSQL *mysql;
	struct dsyslog_mysql_persistant *persist_mysql;
	gchar *escaped_datestamp, *escaped_source, *escaped_program, *escaped_message;

	_ENTER;

	if (!output->dbuser || !output->dbpass || !output->dbname || !output->dbhost) {
		_LEAVE;
	}

	if (!output->opaque) {
		persist_mysql = dsyslog_mysql_persistant_new();
		mysql = persist_mysql->mysql;

		if (!output->dbport)
			output->dbport = mysql->port;

		if (mysql_real_connect(mysql, output->dbhost, output->dbuser, output->dbpass, NULL, output->dbport, 0, 0) == NULL) {
			_ERROR("Database error (connection): %s", mysql_error(mysql));
			_LEAVE;
		}

		if (mysql_select_db(mysql, output->dbname)) {
			_ERROR("Database error (selecting %s): %s", output->dbname, mysql_error(mysql));
			_LEAVE;
		}

		output->opaque = persist_mysql;
		output->destructor = output_mysql_destructor;
	}

	persist_mysql = (struct dsyslog_mysql_persistant *) output->opaque;
	mysql = persist_mysql->mysql;

	escaped_datestamp = dsyslog_mysql_escape_string(mysql, event->datestamp);
	escaped_source = dsyslog_mysql_escape_string(mysql, event->source);
	escaped_program = dsyslog_mysql_escape_string(mysql, event->program);
	escaped_message = dsyslog_mysql_escape_string(mysql, event->message);

	dsyslog_mysql_safe_query(mysql, "INSERT INTO `log` (logcode, timestamp, datestamp, source, program, message) "
					"VALUES (%d, %ld, '%s', '%s', '%s', '%s')",
				 event->logcode, time(NULL), escaped_datestamp, escaped_source, escaped_program, escaped_message);

	g_free(escaped_datestamp);
	g_free(escaped_source);
	g_free(escaped_program);
	g_free(escaped_message);

	_LEAVE;
}

void
_modinit(void)
{
	_ENTER;

	dsyslog_output_type_register("mysql", output_mysql_handler);

	_LEAVE;
}

void
_modfini(void)
{
	_ENTER;

	dsyslog_output_type_unregister("mysql");

	_LEAVE;
}
