#include "ts3db.h"
#include <libpq-fe.h>
#include <stdbool.h>
#include <string.h>
#include <iniparser.h>
#define MAX_CONNECTIONS 99
typedef struct {
PGconn *conn;
unsigned long long modified_rows;
unsigned long long last_inserted_id;
} pg_connection;
static log_callback log = 0;
static pg_connection connections[MAX_CONNECTIONS];
static unsigned int connection_count = 0;
static dictionary *config = NULL;
static const char *config_keywords[13] = { "host", "hostaddr", "port",
"dbname", "user", "password",
"sslmode", "sslcert", "sslkey",
"sslrootcert", "sslcrl",
"application_name", NULL};
static const char *config_values[13] = { NULL };
char *ts3dbplugin_version()
{
return "0.1";
}
char *ts3dbplugin_name()
{
return "PostgreSQL plugin";
}
int ts3dbplugin_disconnect()
{
unsigned int i;
for (i = 0; i < connection_count; i++) {
PQfinish(connections[i].conn);
}
connection_count = 0;
return 0;
}
void ts3dbplugin_shutdown()
{
ts3dbplugin_disconnect();
if (config != NULL) {
iniparser_freedict(config);
config = NULL;
}
}
int ts3dbplugin_init(log_callback logging, char *parameter)
{
log = logging;
if (parameter != NULL && strlen(parameter) > 0) {
config = iniparser_load(parameter);
}
else {
config = iniparser_load("ts3db_postgres.ini");
}
config_values[0] = iniparser_getstring(config, "config:host", "localhost");
config_values[1] = iniparser_getstring(config, "config:hostaddr", "");;
config_values[2] = iniparser_getstring(config, "config:port", "");
config_values[3] = iniparser_getstring(config, "config:dbname", "ts3");
config_values[4] = iniparser_getstring(config, "config:user", "ts3");
config_values[5] = iniparser_getstring(config, "config:password", "");
config_values[6] = iniparser_getstring(config, "config:sslmode", NULL);
config_values[7] = iniparser_getstring(config, "config:sslcert", NULL);
config_values[8] = iniparser_getstring(config, "config:sslkey", NULL);
config_values[9] = iniparser_getstring(config, "config:sslrootcert", NULL);
config_values[10] = iniparser_getstring(config, "config:sslcrl", NULL);
config_values[11] = "teamspeak3";
config_values[12] = NULL;
return 0;
}
int ts3dbplugin_connect(unsigned int *connection_nr)
{
PGconn *conn;
if (connection_count >= MAX_CONNECTIONS) {
return 0;
}
conn = PQconnectdbParams(config_keywords, config_values, true);
if (PQstatus(conn) != CONNECTION_OK) {
log(PQerrorMessage(conn), LOG_CRITICAL);
return 1280;
}
*connection_nr = connection_count;
connections[connection_count].conn = conn;
connections[connection_count].modified_rows = 0;
connections[connection_count].last_inserted_id = 0;
connection_count++;
return 0;
}
PGconn *get_connection(unsigned int connection_nr)
{
if (connection_nr >= connection_count) {
log("Connection not found.", LOG_ERROR);
return NULL;
}
return connections[connection_nr].conn;
}
void save_last_result(unsigned int connection_nr, PGresult *result)
{
const char *rows, *last_id;
if (connection_nr >= connection_count) {
log("Connection not found.", LOG_ERROR);
return;
}
connections[connection_nr].modified_rows = 0;
connections[connection_nr].last_inserted_id = 0;
rows = PQcmdTuples(result);
sscanf(rows, "%llu", &connections[connection_nr].modified_rows);
if (PQnfields(result) == 1) {
if (strcmp(PQfname(result, 0), "last_inserted_id") == 0) {
last_id = PQgetvalue(result, 0, 0);
log(last_id, LOG_ERROR);
sscanf(last_id, "%llu", &connections[connection_nr].last_inserted_id);
}
}
}
char *ts3dbplugin_getlasterror(unsigned int connection_nr)
{
const PGconn *conn = get_connection(connection_nr);
return PQerrorMessage(conn);
}
unsigned int ts3dbplugin_tableexists(unsigned int connection_nr, const char* table_name)
{
PGconn *conn = get_connection(connection_nr);
const char* const params[] = { table_name };
PGresult *result;
int tables;
result = PQexecParams(conn, "select 1 from pg_tables where \
schemaname='public' and \
tablename = $1 limit 1;",
1, NULL, params, NULL, NULL, 0);
tables = PQntuples(result);
PQclear(result);
if (tables == 0) {
/* table not exists */
return 0;
}
return 1;
}
unsigned long long ts3dbplugin_getmodifiedrowcount(unsigned int connection_nr)
{
return connections[connection_nr].modified_rows;
}
unsigned long long ts3dbplugin_getlastinsertid(unsigned int connection_nr)
{
return connections[connection_nr].last_inserted_id;
}
unsigned int ts3dbplugin_open(unsigned int connection_nr, const char* query,
field_callback new_field, value_callback new_value,
row_callback new_row, void *context)
{
ExecStatusType status;
int col_count, row_count, i, j;
PGconn *conn = get_connection(connection_nr);
PGresult *result = PQexec(conn, query);
save_last_result(connection_nr, result);
status = PQresultStatus(result);
if (status == PGRES_BAD_RESPONSE || status == PGRES_FATAL_ERROR) {
log(PQresultErrorMessage(result), LOG_ERROR);
return 1280;
}
if (status == PGRES_NONFATAL_ERROR) {
log(PQresultErrorMessage(result), LOG_WARNING);
}
col_count = PQnfields(result);
for (i = 0; i < col_count; i++) {
new_field(i, PQfname(result, i), context);
}
row_count = PQntuples(result);
for (i = 0; i < row_count; i++) {
new_row(context);
for (j = 0; j < col_count; j++) {
new_value(j, PQgetvalue(result, i, j), context);
}
}
PQclear(result);
return 0;
}
unsigned int ts3dbplugin_exec(unsigned int connection_nr, const char* query)
{
ExecStatusType status;
PGconn *conn = get_connection(connection_nr);
PGresult *result = PQexec(conn, query);
save_last_result(connection_nr, result);
status = PQresultStatus(result);
if (status == PGRES_BAD_RESPONSE || status == PGRES_FATAL_ERROR) {
log(PQresultErrorMessage(result), LOG_ERROR);
return 1280;
}
if (status == PGRES_NONFATAL_ERROR) {
log(PQresultErrorMessage(result), LOG_WARNING);
}
PQclear(result);
return 0;
}